@finos/legend-studio
Version:
165 lines • 8.54 kB
JavaScript
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
/**
* Copyright (c) 2020-present, Goldman Sachs
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { useState, useEffect } from 'react';
import { isNonNullable, IllegalStateError, assertType, addUniqueEntry, printObject, } from '@finos/legend-shared';
import { PURE_EnumValueIcon, clsx, TreeView, ChevronDownIcon, ChevronRightIcon, } from '@finos/legend-art';
import { useDrag } from 'react-dnd';
import { TypeDragSource, CORE_DND_TYPE } from '../../stores/shared/DnDUtil.js';
import { getClassPropertyIcon } from './ElementIconUtils.js';
import { Enumeration, Class, DEFAULT_SOURCE_PARAMETER_NAME, VARIABLE_REFERENCE_TOKEN, getAllClassProperties, getAllClassDerivedProperties, getRawGenericType, } from '@finos/legend-graph';
import { CLASS_PROPERTY_TYPE, getClassPropertyType, } from '../../stores/shared/ModelUtil.js';
const getEnumTypeTreeNodeData = (_enum, parent) => {
// NOTE: Enum is not a type
assertType(parent.type, Enumeration, 'type of parent node for enum node must be enumeration');
return {
id: `${parent.id}.${_enum.name}`,
label: _enum.name,
parent: parent.type,
dndType: CORE_DND_TYPE.TYPE_TREE_ENUM,
};
};
const getPropertyTypeTreeNodeData = (property, parent) => {
assertType(parent.type, Class, 'type of parent node for class property node must be class');
const nodeData = {
id: `${parent.id}.${property.name}`,
label: property.name,
type: property.genericType.value.rawType,
parent: parent.type,
dndType: CORE_DND_TYPE.TYPE_TREE_PRIMITIVE,
property,
};
switch (getClassPropertyType(property.genericType.value.rawType)) {
case CLASS_PROPERTY_TYPE.CLASS:
nodeData.childrenIds = getAllClassProperties(getRawGenericType(property.genericType.value, Class)).map((p) => `${nodeData.id}.${p.name}`);
nodeData.dndType = CORE_DND_TYPE.TYPE_TREE_CLASS;
break;
case CLASS_PROPERTY_TYPE.ENUMERATION:
nodeData.childrenIds = getRawGenericType(property.genericType.value, Enumeration).values.map((p) => `${nodeData.id}.${p.name}`);
nodeData.dndType = CORE_DND_TYPE.TYPE_TREE_ENUMERATION;
break;
default:
}
return nodeData;
};
const getTypeTreeData = (type) => {
const rootIds = [];
const nodes = new Map();
if (type instanceof Class) {
getAllClassProperties(type)
.concat(getAllClassDerivedProperties(type))
.sort((a, b) => a.name.localeCompare(b.name))
.sort((a, b) => (b instanceof Class ? 2 : b instanceof Enumeration ? 1 : 0) -
(a instanceof Class ? 2 : a instanceof Enumeration ? 1 : 0))
.forEach((property) => {
const propertyTreeNodeData = getPropertyTypeTreeNodeData(property, {
id: `${VARIABLE_REFERENCE_TOKEN}${DEFAULT_SOURCE_PARAMETER_NAME}`,
label: '',
parent: type,
dndType: CORE_DND_TYPE.TYPE_TREE_CLASS,
type: type,
});
addUniqueEntry(rootIds, propertyTreeNodeData.id);
nodes.set(propertyTreeNodeData.id, propertyTreeNodeData);
});
}
else if (type instanceof Enumeration) {
type.values.forEach((enumValue) => {
const propertyTreeNodeData = getEnumTypeTreeNodeData(enumValue, {
id: `${VARIABLE_REFERENCE_TOKEN}${DEFAULT_SOURCE_PARAMETER_NAME}`,
label: '',
parent: type,
dndType: CORE_DND_TYPE.TYPE_TREE_ENUMERATION,
type: type,
});
addUniqueEntry(rootIds, propertyTreeNodeData.id);
nodes.set(propertyTreeNodeData.id, propertyTreeNodeData);
});
}
else {
throw new IllegalStateError(`Can't use type tree with node type other than class and enumeration. Found:\n${printObject(type)}`);
}
return { rootIds, nodes };
};
const TypeTreeNodeContainer = (props) => {
const { node, level, stepPaddingInRem, onNodeSelect, innerProps } = props;
const { selectedType } = innerProps;
const [, dragRef] = useDrag(() => ({ type: node.dndType, item: new TypeDragSource(node) }), [node]);
const isExpandable = Boolean(node.childrenIds?.length);
const nodeTypeIcon = node.type ? (getClassPropertyIcon(node.type)) : (_jsx(PURE_EnumValueIcon, {}));
const nodeExpandIcon = isExpandable ? (node.isOpen ? (_jsx(ChevronDownIcon, {})) : (_jsx(ChevronRightIcon, {}))) : (_jsx("div", {}));
const selectNode = () => onNodeSelect?.(node);
return (_jsxs("div", { className: clsx('tree-view__node__container', {
'type-tree__node__container--highlighted': node.type === selectedType,
}), onClick: selectNode, ref: dragRef, style: {
paddingLeft: `${(level - 1) * (stepPaddingInRem ?? 1)}rem`,
display: 'flex',
}, children: [_jsxs("div", { className: "tree-view__node__icon type-tree__node__icon", children: [_jsx("div", { className: "tree-view__node__expand-icon", children: nodeExpandIcon }), _jsx("div", { className: "type-tree__type-icon", children: nodeTypeIcon })] }), _jsxs("div", { className: "tree-view__node__label type-tree__node__label", children: [_jsx("button", { tabIndex: -1, title: `${node.id}`, children: node.label }), Boolean(node.type) && (_jsx("div", { className: "type-tree__node__type", children: _jsx("button", { className: clsx('type-tree__node__type__label', {
'type-tree__node__type__label--highlighted': node.type === selectedType,
}), tabIndex: -1, title: node.type?.path ?? '', children: node.type?.name ?? 'unknown' }) }))] })] }));
};
export const TypeTree = (props) => {
const { type, selectedType } = props;
// NOTE: We only need to compute this once so we use lazy initial state syntax
// See https://reactjs.org/docs/hooks-reference.html#lazy-initial-state
const [treeData, setTreeData] = useState(() => getTypeTreeData(type));
const onNodeSelect = (node) => {
if (node.childrenIds?.length) {
node.isOpen = !node.isOpen;
if (node.type instanceof Class) {
getAllClassProperties(node.type)
.concat(getAllClassDerivedProperties(node.type))
.forEach((property) => {
const propertyTreeNodeData = getPropertyTypeTreeNodeData(property, node);
treeData.nodes.set(propertyTreeNodeData.id, propertyTreeNodeData);
});
}
else if (node.type instanceof Enumeration) {
node.type.values.forEach((enumValue) => {
const propertyTreeNodeData = getEnumTypeTreeNodeData(enumValue, node);
treeData.nodes.set(propertyTreeNodeData.id, propertyTreeNodeData);
});
}
}
setTreeData({ ...treeData });
};
const getChildNodes = (node) => {
if (!node.childrenIds || !node.type) {
return [];
}
const childrenNodes = node.childrenIds
.map((id) => treeData.nodes.get(id))
.filter(isNonNullable)
// class comes first then enumeration then primitive
.sort((a, b) => a.label.localeCompare(b.label))
.sort((a, b) => (b.type instanceof Class
? 2
: b.type instanceof Enumeration
? 1
: 0) -
(a.type instanceof Class ? 2 : a.type instanceof Enumeration ? 1 : 0));
return childrenNodes;
};
useEffect(() => {
setTreeData(() => getTypeTreeData(type));
}, [type]);
return (_jsx(TreeView, { components: {
TreeNodeContainer: TypeTreeNodeContainer,
}, treeData: treeData, onNodeSelect: onNodeSelect, getChildNodes: getChildNodes, innerProps: {
selectedType,
} }));
};
//# sourceMappingURL=TypeTree.js.map