1 import React from 'react'
2 import ModalDialog from 'react-modal'
3 import _set from 'lodash/set';
4 import _get from 'lodash/get';
5 import ContainerColumn from './ContainerColumn'
6 import ChoiceColumn from './ChoiceColumn'
7 import ListColumn from './ListColumn'
8 import LoadingColumn from './LoadingColumn'
9 import ListEntryColumn from './ListEntryColumn'
10 import LoadingCard from './LoadingCard'
11 import ListCard from './ListCard'
12 import ContainerCard from './ContainerCard'
13 import EditorDialog from './EditorDialog'
15 function findItemInList(list, key, keyValue) {
16 const keyPath = Array.isArray(keyValue) ? keyValue : key.length > 1 ? JSON.parse(keyValue) : [keyValue];
18 return list.find(item => {
19 return key.every((k, i) => item[k] === keyPath[i]);
23 const match = keyPath[0];
24 return list.find(item => {
25 return item[leaf] === match;
29 function getItemInfoFunction(schema) {
30 return function (item) {
31 return ModelExplorer.getItemInfo(schema.key, item);
35 function makeNameFromKeyPath(key, path, name) {
36 return path.length > 1 ? `${path[0]} (${path.slice(1).join(" ,")})` : name || path[0];
40 constructor(dataModel) {
41 this.dataModel = dataModel;
42 this.topNode = dataModel.path.split('/').pop();
45 const dataModel = this.dataModel;
46 if (dataModel.isLoading) {
49 if (dataModel.updatingPath && dataModel.updatingPath.every((p, i) => path[i] === p)) {
50 return { type: 'loading' }
52 return path.reduce((parent, node, index) => {
53 const element = Object.assign({}, parent);
54 element.path.push(node);
55 if (parent.type === 'list') {
56 element.type = 'list-entry'
57 element.value = findItemInList(parent.value, parent.schema.key, node);
58 element.keyValue = parent.schema.key.map(leaf => element.value[leaf]);
59 element.name = makeNameFromKeyPath(parent.schema.key, element.keyValue, element.value['name']);
60 element.getItemInfo = getItemInfoFunction(parent.schema);
62 element.schema = parent.schema.properties.find(property => property.name === node)
63 element.type = element.schema.type;
64 element.value = element.type === 'choice' ? parent.value : parent.value && parent.value[node];
65 element.name = node.split(':').pop();
66 if (element.type === 'list') {
67 element.getItemInfo = getItemInfoFunction(element.schema);
72 schema: dataModel.schema[this.topNode],
73 getItemInfo: getItemInfoFunction(this.schema),
74 value: dataModel.data,
75 type: dataModel.schema[this.topNode].type,
76 path: dataModel.path.split('/')
82 const columnComponent = {
84 'container': ContainerColumn,
85 'list-entry': ListEntryColumn,
86 'choice': ChoiceColumn,
87 'loading': LoadingColumn
90 class ModelExplorer extends React.Component {
91 static getExplorerModel(dataModel) {
92 return new ExplorerModel(dataModel);
94 static getItemInfo(keyDef, item) {
95 const path = keyDef.map(leaf => item[leaf]);
96 const name = makeNameFromKeyPath(keyDef, path, item.name);
105 const columns = props.columns || [['/']];
106 this.state = { columns };
109 componentWillReceiveProps(nextProps) {
110 if (!this.state.columns && nextProps.model) {
111 this.setState({ columns: [[Array.isArray(nextProps.model) ? '' : '/']] })
116 const { model, onUpdate } = this.props;
117 let { columns, isEditMode, editPath, editOperation } = this.state;
119 const openElement = (col, path) => {
120 columns = columns.slice(0, col + 1);
122 this.setState({ columns })
125 const closeLastColumn = () => {
126 columns = columns.slice();
128 this.setState({ columns })
131 const lastCol = columns.length - 1;
132 const modelColumns = columns && columns.map((path, col) => {
133 const open = openElement.bind(this, col);
138 selected: col < lastCol ? columns[col + 1][path.length] : null,
139 isLast: col === lastCol,
140 openElement: openElement.bind(this, col),
141 columnCloser: col === lastCol && col && closeLastColumn,
142 editElement: (path, op) => this.setState({
144 editOperation: op || 'update',
148 return React.createElement(columnComponent[model.getElement(path).type], props)
151 const updateModel = (data) => {
152 let { columns, isEditMode, editPath, editOperation } = this.state;
153 onUpdate(editPath, editOperation, data) && columns.pop();
163 <div className='model-explorer'>
164 <div style={{ width: '100%', display: 'flex', flexDirection: 'row', overflow: 'scroll' }}>
170 isCreate: editOperation === 'create',
171 isDelete: editOperation === 'delete'}}
174 onCancel={() => this.setState({ isEditMode: false })}
175 onSave={updateModel} />
181 export default ModelExplorer