UNPKG

@bigfishtv/cockpit

Version:

236 lines (216 loc) 6.28 kB
import React, { Component } from 'react' import classnames from 'classnames' import { connect } from 'react-redux' import isEqual from 'lodash/isEqual' import { get } from '../../api/xhrUtils' import { userCanAccess } from '../../utils/roleUtils' import { collectValues, getChildByKeyValue } from '../../utils/treeUtils' import Button from '../button/Button' import Icon from '../Icon' import Tree from '../tree/Tree' import Modal from '../modal/Modal' import Spinner from '../Spinner' function markRecursive(children) { children.map(page => { page.userCanAccess = true markRecursive(page.children) }) } function filterData(data, user) { return data.map(page => { page.userCanAccess = userCanAccess([{ model: 'Pages', foreign_key: page.id }], user) if (page.userCanAccess) { markRecursive(page.children) } else { page.children = filterData(page.children, user) } return page }) } const ModalToolbar = props => { const { allCollapsed, collapsedIds, onCollapseAll, onExpandAll } = props.modalToolbarProps return ( <div> <Button text="Collapse All" onClick={onCollapseAll} disabled={allCollapsed} /> <Button text="Expand All" onClick={onExpandAll} disabled={!collapsedIds.length} /> </div> ) } const ModalActions = props => { const { selectedId, primaryActionText, onSave, onClose } = props.modalActionProps return ( <div> <Button text={primaryActionText} style="primary" onClick={onSave} disabled={selectedId === null} /> <Button text="Cancel" onClick={onClose} /> </div> ) } const TreeCell = props => { const { title, status, isCollapsed, selectedDrag, showIndicator, onIndicatorClick, onIndicatorDoubleClick, isOver, position, onClick, onDoubleClick, selected, } = props const userCanAccess = props.userCanAccess !== false return ( <div className={classnames('tree-item', isOver && 'drag-' + position)}> <div className={classnames('tree-cell', { dragging: selectedDrag, selected: selected, disabled: !userCanAccess })} onClick={userCanAccess ? onClick : null} onDoubleClick={userCanAccess ? onDoubleClick : null}> <div className="tree-cell-icon"> {showIndicator && ( <div className={classnames('tree-cell-control', isCollapsed && 'collapsed')} onClick={onIndicatorClick} onDoubleClick={onIndicatorDoubleClick}> <Icon name={'chevron-' + (isCollapsed ? 'right' : 'down')} size="18" /> </div> )} </div> {status !== undefined && ( <div className="tree-cell-status"> <div className={classnames('status', status)} /> </div> )} {!userCanAccess && ( <div className="tree-cell-icon"> <Icon name="lock" size={12} /> </div> )} <div className={classnames('tree-cell-title', { disabled: !userCanAccess })}>{title}</div> </div> </div> ) } @connect(({ viewer }) => ({ viewer })) export default class TreeModal extends Component { constructor() { super() this.state = { data: [], selectedId: null, collapsedIds: [], selectedIds: [], allCollapsed: false, loading: true, } } static defaultProps = { data: [], url: '/admin/pages.json', title: 'Select Parent Page', primaryActionText: 'Select Page', startCollapsed: true, filterData, size: 'small', } componentDidMount() { const { url } = this.props if (!url) { if (this.props.data && this.props.data.length > 0) { this.setState({ loading: false, data: this.props.data }, () => { if (this.props.startCollapsed) this.handleCollapseAll() }) } return } get({ url, quiet: true, callback: data => this.setState({ data: this._filterData(data), loading: false }, () => { if (this.props.startCollapsed) this.handleCollapseAll() }), errorCallback: data => console.warn('Error getting pages', data), }) } _filterData(data) { if (!this.props.filterData) { return data } return this.props.filterData(data, this.props.viewer) } handleSelectionChange = selectedIds => { const selectedId = selectedIds.length ? selectedIds[0] : null this.setState({ selectedId, selectedIds }) } handleCollapseChange = collapsedIds => { const collapsableIds = collectValues(this.state.data, 'id', item => item.children && item.children.length > 0) const allCollapsed = isEqual(collapsableIds.sort(), collapsedIds.sort()) this.setState({ collapsedIds, allCollapsed }) } handleCollapseAll = () => { const collapsedIds = collectValues(this.state.data, 'id', item => item.children && item.children.length > 0) this.setState({ collapsedIds, allCollapsed: true }) } handleSelectedItem = item => { this.props.onSave(item) this.props.closeModal() } handleCombinationChange = mixed => { const newState = {} Object.keys(mixed).map(key => { if (key in this.state) newState[key] = mixed[key] }) this.setState(newState) } handleSave = () => { this.handleSelectedItem(getChildByKeyValue(this.state.data, 'id', this.state.selectedId)) } handleClose = () => { this.props.onClose() this.props.closeModal() } render() { const modalToolbarProps = { ...this.state, onCollapseAll: this.handleCollapseAll, onExpandAll: () => this.setState({ collapsedIds: [], allCollapsed: false }), } const modalActionProps = { ...this.state, primaryActionText: this.props.primaryActionText, onSave: this.handleSave, onClose: this.handleClose, } return ( <Modal title={this.props.title} size={this.props.size} ModalToolbar={ModalToolbar} modalToolbarProps={modalToolbarProps} ModalActions={ModalActions} modalActionProps={modalActionProps} onClose={this.handleClose} onSave={this.handleSave}> {this.state.loading ? ( <div className="loader-center">{<Spinner />}</div> ) : ( <Tree value={this.state.data} TreeCell={TreeCell} multiselect={false} reorderable={false} breadcrumbs={false} selectedIds={this.state.selectedIds} onSelectItem={this.handleSelectedItem} onSelectionChange={this.handleSelectionChange} collapsedIds={this.state.collapsedIds} onCollapseChange={this.handleCollapseChange} onCombinationChange={this.handleCombinationChange} /> )} </Modal> ) } }