jsreport-studio
Version:
jsreport templates editor and designer
323 lines (272 loc) • 8.63 kB
JavaScript
import PropTypes from 'prop-types'
import React, { Component } from 'react'
import { connect } from 'react-redux'
import { actions as entitiesActions, selectors as entitiesSelectors } from '../../redux/entities'
import EntityTreeButton from '../EntityTree/EntityTreeButton'
import EntityTree from '../EntityTree/EntityTree.js'
import api from '../../helpers/api.js'
class NewFolderInline extends Component {
constructor (props) {
super(props)
this.setInputNameNode = this.setInputNameNode.bind(this)
}
componentDidUpdate (prevProps) {
if (
((prevProps.editMode == null && this.props.editMode != null) ||
(prevProps.editMode && this.props.editMode && prevProps.editMode.parentShortid !== this.props.editMode.parentShortid)) &&
this.inputNameNode
) {
setTimeout(() => this.inputNameNode.focus(), 0)
}
if (
prevProps.editMode &&
this.props.editMode &&
prevProps.editMode.parentShortid !== this.props.editMode.parentShortid &&
this.inputNameNode
) {
this.inputNameNode.value = ''
}
}
setInputNameNode (el) {
this.inputNameNode = el
}
renderEditMode () {
const { editMode, onSave, onCancel, getEntityByShortid, resolveEntityPath } = this.props
let parentName
if (editMode.parentShortid != null) {
parentName = resolveEntityPath(getEntityByShortid(editMode.parentShortid))
}
return (
<div>
<div style={{ fontSize: '0.8rem', marginBottom: '5px' }}>
Creating new folder <b>{editMode.parentShortid != null ? `inside folder ${parentName}` : 'in the root level'}</b>
</div>
<input
ref={this.setInputNameNode}
type='text'
placeholder='folder name'
defaultValue=''
style={{ display: 'inline-block', width: '120px' }}
/>
<div style={{ display: 'inline-block' }}>
<button className='button confirmation' onClick={() => onSave(this.inputNameNode.value, editMode.parentShortid)}>
Save
</button>
<button className='button confirmation' onClick={() => onCancel()}>Cancel</button>
</div>
{editMode.error != null && (
<div style={{ color: 'red', marginTop: '3px' }}>
Error when creating folder: {editMode.error}
</div>
)}
</div>
)
}
render () {
const { editMode, onAdd } = this.props
return (
<div title='Add new folder' style={{ display: 'inline-block' }}>
{!editMode ? (
<EntityTreeButton onClick={onAdd}>
<span style={{ display: 'inline-block' }}>
<i className='fa fa-folder' /> New folder
</span>
</EntityTreeButton>
) : (
this.renderEditMode()
)}
</div>
)
}
}
class EntityTreeSelectionModal extends Component {
static propTypes = {
close: PropTypes.func.isRequired,
options: PropTypes.object.isRequired
}
constructor (props) {
super(props)
const initialSelected = props.options.selected != null ? (
!Array.isArray(props.options.selected) ? [props.options.selected] : props.options.selected
) : []
this.state = {
newFolderEdit: null,
selected: initialSelected.reduce((acu, shortid) => {
const entity = props.getEntityByShortid(shortid, false)
if (entity) {
acu[entity._id] = true
}
return acu
}, {})
}
this.cancelRef = React.createRef()
this.createNewFolder = this.createNewFolder.bind(this)
this.handleSelectionChange = this.handleSelectionChange.bind(this)
}
componentDidMount () {
setTimeout(() => this.cancelRef.current.focus(), 0)
}
handleSelectionChange (selected) {
this.setState({
selected: selected
})
}
filterEntities (references) {
const { filter } = this.props.options
if (!filter) {
return references
}
let result = filter(references)
result = result == null ? {} : result
result = Object.keys(references).reduce((acu, k) => {
if (acu[k] == null) {
acu[k] = []
}
return acu
}, result)
return result
}
unselect () {
this.setState({
selected: {}
})
}
async createNewFolder (folderName, parentShortid) {
const entity = {
name: folderName
}
if (parentShortid != null) {
entity.folder = {
shortid: parentShortid
}
}
try {
await api.post('/studio/validate-entity-name', {
data: {
name: entity.name,
entitySet: 'folders',
folderShortid: entity.folder != null ? entity.folder.shortid : null
}
})
const response = await api.post('/odata/folders', {
data: entity
})
response.__entitySet = 'folders'
this.props.addExistingEntity(response)
this.setState({
newFolderEdit: null
})
} catch (e) {
this.setState({
newFolderEdit: {
...this.state.newFolderEdit,
error: e.message
}
})
}
}
save () {
const selected = this.state.selected
const values = []
Object.keys(selected).forEach((_id) => {
const entity = this.props.getEntityById(_id, false)
if (!entity) {
return
}
values.push(entity)
})
this.props.options.onSave(values)
this.props.close()
}
cancel () {
this.props.close()
}
render () {
const {
multiple,
headingLabel,
selectableFilter,
allowNewFolder = false,
treeStyle = {}
} = this.props.options
const { getEntityByShortid, resolveEntityPath } = this.props
const { selected, newFolderEdit } = this.state
const entities = this.filterEntities(this.props.references)
return (
<div>
<div>
<h1>
<i className='fa fa-check-square-o' /> {headingLabel != null ? headingLabel : 'Select entity'}
</h1>
</div>
{allowNewFolder && (
<div>
<NewFolderInline
onAdd={() => this.setState({
newFolderEdit: {}
})}
onSave={this.createNewFolder}
onCancel={() => this.setState({ newFolderEdit: null })}
editMode={newFolderEdit}
getEntityByShortid={getEntityByShortid}
resolveEntityPath={resolveEntityPath}
/>
</div>
)}
{allowNewFolder && (
<br />
)}
<div style={Object.assign({ minHeight: '30rem', maxHeight: '30rem', overflow: 'auto' }, treeStyle)}>
<EntityTree
entities={entities}
selectable
selectionMode={{
mode: multiple ? 'multiple' : 'single',
isSelectable: (isGroup, entity) => {
if (selectableFilter) {
return Boolean(selectableFilter(isGroup, entity))
}
if (isGroup) {
return false
}
return true
}
}}
selected={selected}
onSelectionChanged={this.handleSelectionChange}
getContextMenuItems={allowNewFolder ? ({ entity, isRoot }) => {
if (isRoot) {
return
}
return [{
key: 'New Folder',
title: 'New Folder',
icon: 'fa-folder',
onClick: () => this.setState({
newFolderEdit: { parentShortid: entity.shortid }
})
}]
} : undefined}
/>
</div>
<div className='button-bar'>
<button
ref={this.cancelRef}
className='button confirmation'
onClick={() => this.cancel()}
>
Cancel
</button>
<button className='button confirmation' onClick={() => this.unselect()}>Unselect</button>
<button className='button danger' onClick={() => this.save()}>Ok</button>
</div>
</div>
)
}
}
export default connect((state) => ({
references: entitiesSelectors.getReferences(state),
getEntityById: (_id, ...params) => entitiesSelectors.getById(state, _id, ...params),
getEntityByShortid: (shortid, ...params) => entitiesSelectors.getByShortid(state, shortid, ...params),
resolveEntityPath: (entity, ...params) => entitiesSelectors.resolveEntityPath(state, entity, ...params)
}), { addExistingEntity: entitiesActions.addExisting })(EntityTreeSelectionModal)