From fd7477c669bca6726747752a1b5c95d1e2b77799 Mon Sep 17 00:00:00 2001 From: Michael Behrisch <m.behrisch@uu.nl> Date: Sat, 2 Apr 2022 20:34:13 +0200 Subject: [PATCH] fix(schema): :art: adds ui/pills shared library and uses it for schema Pills and React Flow components impl can be shared for consistency. Schema vs QueryBuilder can provide subclasses. --- apps/web-graphpolaris/project.json | 36 ++---- .../components/querybuilder/querybuilder.tsx | 1 + .../src/components/schema/schema.tsx | 24 ++++ .../usecases/src/lib/schema-usecases.ts | 112 ++++++++++-------- .../graph-layout/src/lib/cytoscape-layouts.ts | 4 +- libs/shared/ui/pills/.babelrc | 12 ++ libs/shared/ui/pills/.eslintrc.json | 18 +++ libs/shared/ui/pills/README.md | 7 ++ libs/shared/ui/pills/jest.config.js | 9 ++ libs/shared/ui/pills/project.json | 23 ++++ .../pills/src/customFlowLines/connection.tsx | 74 ++++++++++++ .../src/customFlowLines/connectionDrag.tsx | 41 +++++++ .../attributepill/attributepill.module.scss | 60 ++++++++++ .../attributepill/attributepill.tsx | 96 +++++++++++++++ .../attributepill/operatorselect.module.scss | 70 +++++++++++ .../attributepill/operatorselect.tsx | 85 +++++++++++++ .../attributepill/variables.module.scss | 3 + .../entitypill/entitypill.module.scss | 49 ++++++++ .../customFlowPills/entitypill/entitypill.tsx | 64 ++++++++++ .../src/customFlowPills/entitypill/index.ts | 1 + .../src/customFlowPills/relationpill/index.ts | 1 + .../relationpill/relationpill.module.scss | 110 +++++++++++++++++ .../relationpill/relationpill.tsx | 98 +++++++++++++++ libs/shared/ui/pills/src/index.ts | 9 ++ .../pills/src/lib/shared-ui-pills.module.scss | 0 .../ui/pills/src/lib/shared-ui-pills.spec.tsx | 10 ++ .../ui/pills/src/lib/shared-ui-pills.tsx | 14 +++ .../pills/src/schema/entitypill.component.tsx | 62 ++++++++++ .../pills/src/schema/entitypill.module.scss | 49 ++++++++ libs/shared/ui/pills/src/schema/index.ts | 0 libs/shared/ui/pills/tsconfig.json | 25 ++++ libs/shared/ui/pills/tsconfig.lib.json | 22 ++++ libs/shared/ui/pills/tsconfig.spec.json | 19 +++ tsconfig.base.json | 5 +- workspace.json | 1 + 35 files changed, 1136 insertions(+), 78 deletions(-) create mode 100644 libs/shared/ui/pills/.babelrc create mode 100644 libs/shared/ui/pills/.eslintrc.json create mode 100644 libs/shared/ui/pills/README.md create mode 100644 libs/shared/ui/pills/jest.config.js create mode 100644 libs/shared/ui/pills/project.json create mode 100644 libs/shared/ui/pills/src/customFlowLines/connection.tsx create mode 100644 libs/shared/ui/pills/src/customFlowLines/connectionDrag.tsx create mode 100644 libs/shared/ui/pills/src/customFlowPills/attributepill/attributepill.module.scss create mode 100644 libs/shared/ui/pills/src/customFlowPills/attributepill/attributepill.tsx create mode 100644 libs/shared/ui/pills/src/customFlowPills/attributepill/operatorselect.module.scss create mode 100644 libs/shared/ui/pills/src/customFlowPills/attributepill/operatorselect.tsx create mode 100644 libs/shared/ui/pills/src/customFlowPills/attributepill/variables.module.scss create mode 100644 libs/shared/ui/pills/src/customFlowPills/entitypill/entitypill.module.scss create mode 100644 libs/shared/ui/pills/src/customFlowPills/entitypill/entitypill.tsx create mode 100644 libs/shared/ui/pills/src/customFlowPills/entitypill/index.ts create mode 100644 libs/shared/ui/pills/src/customFlowPills/relationpill/index.ts create mode 100644 libs/shared/ui/pills/src/customFlowPills/relationpill/relationpill.module.scss create mode 100644 libs/shared/ui/pills/src/customFlowPills/relationpill/relationpill.tsx create mode 100644 libs/shared/ui/pills/src/index.ts create mode 100644 libs/shared/ui/pills/src/lib/shared-ui-pills.module.scss create mode 100644 libs/shared/ui/pills/src/lib/shared-ui-pills.spec.tsx create mode 100644 libs/shared/ui/pills/src/lib/shared-ui-pills.tsx create mode 100644 libs/shared/ui/pills/src/schema/entitypill.component.tsx create mode 100644 libs/shared/ui/pills/src/schema/entitypill.module.scss create mode 100644 libs/shared/ui/pills/src/schema/index.ts create mode 100644 libs/shared/ui/pills/tsconfig.json create mode 100644 libs/shared/ui/pills/tsconfig.lib.json create mode 100644 libs/shared/ui/pills/tsconfig.spec.json diff --git a/apps/web-graphpolaris/project.json b/apps/web-graphpolaris/project.json index 864038895..7cc05ad71 100644 --- a/apps/web-graphpolaris/project.json +++ b/apps/web-graphpolaris/project.json @@ -19,9 +19,7 @@ }, "build": { "executor": "@nrwl/web:webpack", - "outputs": [ - "{options.outputPath}" - ], + "outputs": ["{options.outputPath}"], "defaultConfiguration": "production", "options": { "compiler": "babel", @@ -35,18 +33,18 @@ "apps/web-graphpolaris/src/favicon.ico", "apps/web-graphpolaris/src/assets" ], - "styles": [ - "apps/web-graphpolaris/src/styles.scss" - ], + "styles": ["apps/web-graphpolaris/src/styles.scss"], "scripts": [], "webpackConfig": "@nrwl/react/plugins/webpack" }, "configurations": { "production": { - "fileReplacements": [{ - "replace": "apps/graphpolaris/src/environments/environment.ts", - "with": "apps/graphpolaris/src/environments/environment.prod.ts" - }], + "fileReplacements": [ + { + "replace": "apps/graphpolaris/src/environments/environment.ts", + "with": "apps/graphpolaris/src/environments/environment.prod.ts" + } + ], "optimization": true, "outputHashing": "all", "sourceMap": false, @@ -71,20 +69,14 @@ }, "lint": { "executor": "@nrwl/linter:eslint", - "outputs": [ - "{options.outputFile}" - ], + "outputs": ["{options.outputFile}"], "options": { - "lintFilePatterns": [ - "apps/graphpolaris/**/*.{ts,tsx,js,jsx}" - ] + "lintFilePatterns": ["apps/graphpolaris/**/*.{ts,tsx,js,jsx}"] } }, "test": { "executor": "@nrwl/jest:jest", - "outputs": [ - "coverage/apps/graphpolaris" - ], + "outputs": ["coverage/apps/graphpolaris"], "options": { "jestConfig": "apps/web-graphpolaris/jest.config.js", "passWithNoTests": true @@ -113,9 +105,7 @@ }, "build-storybook": { "executor": "@nrwl/storybook:build", - "outputs": [ - "{options.outputPath}" - ], + "outputs": ["{options.outputPath}"], "options": { "uiFramework": "@storybook/react", "outputPath": "dist/storybook/graphpolaris", @@ -131,4 +121,4 @@ } }, "tags": [] -} \ No newline at end of file +} diff --git a/apps/web-graphpolaris/src/components/querybuilder/querybuilder.tsx b/apps/web-graphpolaris/src/components/querybuilder/querybuilder.tsx index 3870f76de..063330aa0 100644 --- a/apps/web-graphpolaris/src/components/querybuilder/querybuilder.tsx +++ b/apps/web-graphpolaris/src/components/querybuilder/querybuilder.tsx @@ -40,6 +40,7 @@ const QueryBuilder = (props: {}) => { const nodes = useQuerybuilderNodes(); const dispatch = useAppDispatch(); const isDraggingPill = useRef(false); + console.log('inputnodes', nodes); const elements = useMemo(() => createReactFlowElements(nodes), [nodes]); diff --git a/apps/web-graphpolaris/src/components/schema/schema.tsx b/apps/web-graphpolaris/src/components/schema/schema.tsx index e0a4d713b..dfe7a159b 100644 --- a/apps/web-graphpolaris/src/components/schema/schema.tsx +++ b/apps/web-graphpolaris/src/components/schema/schema.tsx @@ -18,6 +18,18 @@ import ReactFlow, { } from 'react-flow-renderer'; import styles from './schema.module.scss'; +import { + EntityRFPill, + RelationRFPill, + AttributeRFPill, + ConnectionDragLine, + ConnectionLine +} from '@graphpolaris/shared/ui/pills'; +// import ConnectionDragLine from '@graphpolaris/shared/ui/pills'; +// import AttributeRFPill from '@graphpolaris/shared/ui/pills'; +// import EntityRFPill from '@graphpolaris/shared/ui/pills'; +// import RelationRFPill from '@graphpolaris/shared/ui/pills'; + interface Props { // content: string; } @@ -26,6 +38,15 @@ const onLoad = (reactFlowInstance: any) => { setTimeout(() => reactFlowInstance.fitView(), 100); }; +const nodeTypes = { + entity: EntityRFPill, + relation: RelationRFPill, + attribute: AttributeRFPill, +}; +const edgeTypes = { + connection: ConnectionLine, +}; + const Schema = (props: Props) => { const [elements, setElements] = useState([] as FlowElement[]); // In case the schema is updated @@ -76,6 +97,9 @@ const Schema = (props: Props) => { className={styles.schemaPanel} onlyRenderVisibleElements={false} nodesDraggable={false} + nodeTypes={nodeTypes} + edgeTypes={edgeTypes} + connectionLineComponent={ConnectionDragLine} elements={elements} style={graphStyles} onLoad={onLoad} diff --git a/libs/schema/usecases/src/lib/schema-usecases.ts b/libs/schema/usecases/src/lib/schema-usecases.ts index 630db92a8..d3151bfe9 100644 --- a/libs/schema/usecases/src/lib/schema-usecases.ts +++ b/libs/schema/usecases/src/lib/schema-usecases.ts @@ -8,19 +8,25 @@ const ANIMATEDEDGES = false; export function expandSchema(graph: Graph): Graph { const newGraph = graph.copy(); - // newGraph.forEachNode((node, attributes) => { - // console.log(node, attributes); - // }); + newGraph.forEachNode((node, attributes) => { + // console.log(node, attributes); + newGraph.mergeNodeAttributes(node, { type: 'entity' }); + }); //makeNewRelationNodes graph.forEachEdge((edge, attributes, source, target): void => { const newID = 'RelationNode:' + edge; - console.log('making relationnode', edge, attributes, source, target, newID); + // console.log('making relationnode', edge, attributes, source, target, newID); newGraph.addNode(newID, { name: edge, + data: { + label: edge, + name: edge, + }, attributes, x: 0, y: 0, + type: 'relation', }); const id = 'RelationEdge' + source + '->' + newID; @@ -58,8 +64,10 @@ export function createReactFlowNodes(graph: Graph): Elements<Node> { id: node, data: { label: attributes.name, + name: attributes.name, }, position: { x: attributes.x, y: attributes.y }, + type: attributes.type, }; nodeElements.push(newNode); }); @@ -67,53 +75,6 @@ export function createReactFlowNodes(graph: Graph): Elements<Node> { return nodeElements; } -export function createReactFlowRelationNodes(graph: Graph): Elements<Node> { - const nodeElements: Elements<Node> = []; - graph.forEachEdge((edge, attributes, source, target): void => { - const newRelationNode: Node = { - id: edge, - data: { - label: edge, - }, - position: { x: attributes.x, y: attributes.y }, - }; - nodeElements.push(newRelationNode); - }); - - return nodeElements; -} - -export function createReactFlowRelationEdges(graph: Graph): Elements<Edge> { - const edgeElements: Elements<Edge> = []; - graph.forEachEdge((edge, attributes, source, target): void => { - const newEdgeIncoming: Edge = { - //into relation node - id: edge + '' + source, - source: source, - target: edge, - // label: edge, - type: 'smoothstep', - animated: ANIMATEDEDGES, - arrowHeadType: ArrowHeadType.ArrowClosed, - }; - edgeElements.push(newEdgeIncoming); - - const newEdgeOutgoing: Edge = { - //out of relation node - id: edge + '' + target, - source: edge, - target: target, - // label: edge, - type: 'smoothstep', - animated: ANIMATEDEDGES, - arrowHeadType: ArrowHeadType.ArrowClosed, - }; - edgeElements.push(newEdgeOutgoing); - }); - - return edgeElements; -} - export function createReactFlowEdges(graph: Graph): Elements<Edge> { const edgeElements: Elements<Edge> = []; @@ -134,6 +95,55 @@ export function createReactFlowEdges(graph: Graph): Elements<Edge> { return edgeElements; } +// export function createReactFlowRelationNodes(graph: Graph): Elements<Node> { +// const nodeElements: Elements<Node> = []; +// graph.forEachEdge((edge, attributes, source, target): void => { +// const newRelationNode: Node = { +// id: edge, +// data: { +// label: edge, +// name: edge, +// }, +// position: { x: attributes.x, y: attributes.y }, +// type: 'relation', +// }; +// nodeElements.push(newRelationNode); +// }); + +// return nodeElements; +// } + +// export function createReactFlowRelationEdges(graph: Graph): Elements<Edge> { +// const edgeElements: Elements<Edge> = []; +// graph.forEachEdge((edge, attributes, source, target): void => { +// const newEdgeIncoming: Edge = { +// //into relation node +// id: edge + '' + source, +// source: source, +// target: edge, +// // label: edge, +// type: 'smoothstep', +// animated: ANIMATEDEDGES, +// arrowHeadType: ArrowHeadType.ArrowClosed, +// }; +// edgeElements.push(newEdgeIncoming); + +// const newEdgeOutgoing: Edge = { +// //out of relation node +// id: edge + '' + target, +// source: edge, +// target: target, +// // label: edge, +// type: 'smoothstep', +// animated: ANIMATEDEDGES, +// arrowHeadType: ArrowHeadType.ArrowClosed, +// }; +// edgeElements.push(newEdgeOutgoing); +// }); + +// return edgeElements; +// } + // export function parseSchemaFromBackend( // schemaFromBackend: SchemaFromBackend // ): Graph { diff --git a/libs/shared/graph-layout/src/lib/cytoscape-layouts.ts b/libs/shared/graph-layout/src/lib/cytoscape-layouts.ts index 0d1903a98..ea32eba73 100644 --- a/libs/shared/graph-layout/src/lib/cytoscape-layouts.ts +++ b/libs/shared/graph-layout/src/lib/cytoscape-layouts.ts @@ -244,10 +244,10 @@ class CytoscapeKlay extends Cytoscape { // boundingBox: undefined, ready: function () { - console.log('Layout.ready'); + console.info('Layout.ready'); }, // on layoutready stop: function () { - console.log('Layout.stop'); + console.debug('Layout.stop'); }, // on layoutstop } as any); layout.run(); diff --git a/libs/shared/ui/pills/.babelrc b/libs/shared/ui/pills/.babelrc new file mode 100644 index 000000000..ccae900be --- /dev/null +++ b/libs/shared/ui/pills/.babelrc @@ -0,0 +1,12 @@ +{ + "presets": [ + [ + "@nrwl/react/babel", + { + "runtime": "automatic", + "useBuiltIns": "usage" + } + ] + ], + "plugins": [] +} diff --git a/libs/shared/ui/pills/.eslintrc.json b/libs/shared/ui/pills/.eslintrc.json new file mode 100644 index 000000000..3cd642175 --- /dev/null +++ b/libs/shared/ui/pills/.eslintrc.json @@ -0,0 +1,18 @@ +{ + "extends": ["plugin:@nrwl/nx/react", "../../../../.eslintrc.json"], + "ignorePatterns": ["!**/*"], + "overrides": [ + { + "files": ["*.ts", "*.tsx", "*.js", "*.jsx"], + "rules": {} + }, + { + "files": ["*.ts", "*.tsx"], + "rules": {} + }, + { + "files": ["*.js", "*.jsx"], + "rules": {} + } + ] +} diff --git a/libs/shared/ui/pills/README.md b/libs/shared/ui/pills/README.md new file mode 100644 index 000000000..84610a47f --- /dev/null +++ b/libs/shared/ui/pills/README.md @@ -0,0 +1,7 @@ +# shared-ui-pills + +This library was generated with [Nx](https://nx.dev). + +## Running unit tests + +Run `nx test shared-ui-pills` to execute the unit tests via [Jest](https://jestjs.io). diff --git a/libs/shared/ui/pills/jest.config.js b/libs/shared/ui/pills/jest.config.js new file mode 100644 index 000000000..7d0623362 --- /dev/null +++ b/libs/shared/ui/pills/jest.config.js @@ -0,0 +1,9 @@ +module.exports = { + displayName: 'shared-ui-pills', + preset: '../../../../jest.preset.js', + transform: { + '^.+\\.[tj]sx?$': 'babel-jest', + }, + moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx'], + coverageDirectory: '../../../../coverage/libs/shared/ui/pills', +}; diff --git a/libs/shared/ui/pills/project.json b/libs/shared/ui/pills/project.json new file mode 100644 index 000000000..ea222183f --- /dev/null +++ b/libs/shared/ui/pills/project.json @@ -0,0 +1,23 @@ +{ + "root": "libs/shared/ui/pills", + "sourceRoot": "libs/shared/ui/pills/src", + "projectType": "library", + "tags": [], + "targets": { + "lint": { + "executor": "@nrwl/linter:eslint", + "outputs": ["{options.outputFile}"], + "options": { + "lintFilePatterns": ["libs/shared/ui/pills/**/*.{ts,tsx,js,jsx}"] + } + }, + "test": { + "executor": "@nrwl/jest:jest", + "outputs": ["coverage/libs/shared/ui/pills"], + "options": { + "jestConfig": "libs/shared/ui/pills/jest.config.js", + "passWithNoTests": true + } + } + } +} diff --git a/libs/shared/ui/pills/src/customFlowLines/connection.tsx b/libs/shared/ui/pills/src/customFlowLines/connection.tsx new file mode 100644 index 000000000..23fb1ec2e --- /dev/null +++ b/libs/shared/ui/pills/src/customFlowLines/connection.tsx @@ -0,0 +1,74 @@ +import { handles } from '@graphpolaris/querybuilder/usecases'; +import React from 'react'; +import { EdgeProps, getSmoothStepPath, Position } from 'react-flow-renderer'; + +/** + * A custom query element edge line component. + * @param {EdgeProps} param0 The coordinates for the start and end point, the id and the style. + */ +// export const EntityRFPill = React.memo(({ data }: { data: any }) => { +export function ConnectionLine({ + id, + sourceX, + sourceY, + targetX, + targetY, + style, + sourceHandleId, + targetHandleId, +}: EdgeProps) { + //Centering the line + sourceY -= 3; + targetY -= 3; + + // Correct line positions with hardcoded numbers, because react flow lacks this functionality + // if (sourceHandleId == ) sourceX += 2; + + // if (targetHandleId == Handles.ToAttributeHandle) targetX += 2; + + let spos: Position = Position.Bottom; + if (sourceHandleId == handles.relation.fromEntity) { + spos = Position.Left; + sourceX += 7; + sourceY += 3; + } else if (sourceHandleId == handles.relation.toEntity) { + spos = Position.Right; + sourceX -= 2; + sourceY -= 3; + } else if ( + sourceHandleId !== undefined && + sourceHandleId !== null && + sourceHandleId.includes('functionHandle') + ) { + spos = Position.Top; + sourceX -= 4; + sourceY += 3; + } + + let tpos: Position = Position.Bottom; + if (targetHandleId == handles.relation.fromEntity) { + tpos = Position.Left; + targetX += 7; + targetY += 3; + } else if (targetHandleId == handles.relation.toEntity) { + tpos = Position.Right; + targetX -= 2; + targetY -= 3; + } + + // Create smoothstep line + const path = getSmoothStepPath({ + sourceX: sourceX, + sourceY: sourceY, + sourcePosition: spos, + targetX: targetX, + targetY: targetY, + targetPosition: tpos, + }); + + return ( + <g stroke="#2e2e2e"> + <path id={id} fill="none" strokeWidth={3} style={style} d={path} /> + </g> + ); +} diff --git a/libs/shared/ui/pills/src/customFlowLines/connectionDrag.tsx b/libs/shared/ui/pills/src/customFlowLines/connectionDrag.tsx new file mode 100644 index 000000000..0d567e584 --- /dev/null +++ b/libs/shared/ui/pills/src/customFlowLines/connectionDrag.tsx @@ -0,0 +1,41 @@ +import React from 'react'; +import { ConnectionLineComponentProps } from 'react-flow-renderer'; + +/** + * A custom query element to render the line when connecting flow elements. + * @param {ConnectionLineComponentProps} param0 Source and target coordinates of the edges. + */ +export function ConnectionDragLine({ + sourceX, + sourceY, + targetX, + targetY, +}: ConnectionLineComponentProps) { + return ( + <g> + <path + fill="none" + stroke="#222" + strokeWidth={2.5} + className="animated" + d={`M${sourceX},${sourceY}L ${targetX},${targetY}`} + /> + <circle + cx={sourceX} + cy={sourceY} + fill="#fff" + r={3} + stroke="#222" + strokeWidth={1.5} + /> + <circle + cx={targetX} + cy={targetY} + fill="#fff" + r={3} + stroke="#222" + strokeWidth={1.5} + /> + </g> + ); +} diff --git a/libs/shared/ui/pills/src/customFlowPills/attributepill/attributepill.module.scss b/libs/shared/ui/pills/src/customFlowPills/attributepill/attributepill.module.scss new file mode 100644 index 000000000..9ba5ba22c --- /dev/null +++ b/libs/shared/ui/pills/src/customFlowPills/attributepill/attributepill.module.scss @@ -0,0 +1,60 @@ +@use './variables.module.scss'; + +.attribute { + display: flex; + font-family: monospace; + font-weight: bold; + font-size: variables.$fontsize; + border-radius: 2px; +} + +// .handle { +// border: 0px; +// border-radius: 10px; +// left: 12px; +// width: 7px; +// height: 7px; +// margin-bottom: 11px; +// background: rgba(255, 255, 255, 0.6); +// box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.3); +// transform-origin: center; +// } + +.contentWrapper { + display: flex; + align-items: center; + + .content { + padding: variables.$ypad 0 variables.$ypad 1ch; + max-width: 15ch; + text-overflow: ellipsis; + overflow: hidden; + white-space: nowrap; + } +} + +.attributeInput { + float: right; + padding: 0 1ch 0 0; + display: flex; + align-items: center; + + input { + background-color: rgba(100, 100, 100, 0.1); + font-family: monospace; + font-size: variables.$fontsize; + border: 1px solid rgba(100, 100, 100, 0.3); + border-radius: 2px; + height: variables.$height; + outline: none; + transition: border 0.3s; + color: black; + &::placeholder { + color: black; + } + + &:focus { + border: 1px solid rgba(0, 0, 0, 0.3); + } + } +} diff --git a/libs/shared/ui/pills/src/customFlowPills/attributepill/attributepill.tsx b/libs/shared/ui/pills/src/customFlowPills/attributepill/attributepill.tsx new file mode 100644 index 000000000..41b5803cc --- /dev/null +++ b/libs/shared/ui/pills/src/customFlowPills/attributepill/attributepill.tsx @@ -0,0 +1,96 @@ +import { + CheckDatatypeConstraint, + GetAttributeBoolOperators, +} from '@graphpolaris/querybuilder/usecases'; +import { + updateQBAttributeOperator, + updateQBAttributeValue, + useAppDispatch, +} from '@graphpolaris/shared/data-access/store'; +import { useTheme } from '@mui/material'; +import React, { useMemo, useState } from 'react'; +import styles from './attributepill.module.scss'; +import AttributeOperatorSelect from './operatorselect'; + +/** + * Component to render an attribute flow element + * @param {FlowElement<EntityData>)} param0 The data of an entity flow element. + */ +export const AttributeRFPill = React.memo( + ({ id, data }: { id: string; data: any }) => { + const theme = useTheme(); + const dispatch = useAppDispatch(); + const [value, setValue] = useState(data?.value || ''); + + const onChange = (e: any) => { + setValue(e.target.value); + }; + const validateInput = () => { + const newValue = CheckDatatypeConstraint(data.datatype, value); + setValue(newValue); + dispatch(updateQBAttributeValue({ id, value: newValue })); + }; + + // Calculates the size of the input + const getInputWidth = () => { + if (value == '') return 1; + else if (value.length > 10) return 10; + return value.length; + }; + + const boolOperators = useMemo( + () => GetAttributeBoolOperators(data?.datatype), + [data?.datatype] + ); + + // Determine the backgroundcolor based on if the attribute is connected to a entity or relation + let bgcolor; + if (data?.attributeOfA == 'entity') + bgcolor = theme.palette.queryBuilder.entity.lighterbg; + else if (data?.attributeOfA == 'relation') + bgcolor = theme.palette.queryBuilder.relation.lighterbg; + else bgcolor = theme.palette.queryBuilder.attribute.background; + + return ( + <div + className={styles.attribute} + style={{ + background: bgcolor, + color: theme.palette.queryBuilder.text, + }} + > + {/* <Handle + id={Handles.Attribute} + type="source" + position={Position.Bottom} + className={styles.handle} + /> */} + <div className={styles.contentWrapper}> + <span className={styles.content} title={data.name}> + {data.name} + </span> + <AttributeOperatorSelect + selected={data?.operator} + options={boolOperators} + changed={(o) => + dispatch(updateQBAttributeOperator({ id, operator: o.value })) + } + /> + <span className={styles.attributeInput}> + <input + style={{ maxWidth: `${getInputWidth()}ch` }} + type="string" + placeholder={'?'} + value={value} + onChange={onChange} + onBlur={validateInput} + onKeyDown={(e) => e.key == 'Enter' && validateInput()} + ></input> + </span> + </div> + </div> + ); + } +); + +export default AttributeRFPill; diff --git a/libs/shared/ui/pills/src/customFlowPills/attributepill/operatorselect.module.scss b/libs/shared/ui/pills/src/customFlowPills/attributepill/operatorselect.module.scss new file mode 100644 index 000000000..1c31d2744 --- /dev/null +++ b/libs/shared/ui/pills/src/customFlowPills/attributepill/operatorselect.module.scss @@ -0,0 +1,70 @@ +@use './variables.module.scss'; + +.container { + position: relative; + vertical-align: baseline; + margin: 0 1ch; + font-weight: normal; + font-size: 7px; +} + +.valueContainer { + color: #6a6a6a; + border: 1px solid rgba(0, 0, 0, 0); + border-radius: 2px; + background-color: transparent; + + transition: border-color 0.2s; + + height: variables.$height; + align-items: center; + display: flex; + padding: 0 1px 1px 1px; + + &.highlighted, + &:hover { + border-color: rgba(0, 0, 0, 0.4); + } +} + +.listbox { + font-size: 10px; + box-sizing: border-box; + padding: 5px; + margin: 5px 0 0 0; + list-style: none; + position: absolute; + height: auto; + box-shadow: 0 5px 13px -3px #e0e3e7; + background: white; + border: 1px solid #cdd2d7; + border-radius: 0.75em; + color: #1a2027; + overflow: auto; + z-index: 1; + outline: 0px; + left: -8px; + + &.hidden { + opacity: 0; + visibility: hidden; + transition: opacity 0.4s 0.1s ease, visibility 0.4s 0.1s step-end; + } + + & > li { + padding: 1px 4px; + border-radius: 2px; + + &.selected { + background: #f1f1f1; + } + + &:hover { + background: #e7ebf0; + } + + &[aria-selected='true'] { + background: #e0e3e7; + } + } +} diff --git a/libs/shared/ui/pills/src/customFlowPills/attributepill/operatorselect.tsx b/libs/shared/ui/pills/src/customFlowPills/attributepill/operatorselect.tsx new file mode 100644 index 000000000..470c87618 --- /dev/null +++ b/libs/shared/ui/pills/src/customFlowPills/attributepill/operatorselect.tsx @@ -0,0 +1,85 @@ +import * as React from 'react'; +import { SelectOption } from '@mui/base'; +import styles from './operatorselect.module.scss'; +import { useRef, useState } from 'react'; + +// const grey = { +// 100: '#E7EBF0', +// 200: '#E0E3E7', +// 300: '#CDD2D7', +// 400: '#B2BAC2', +// 500: '#A0AAB4', +// 600: '#6F7E8C', +// 700: '#3E5060', +// 800: '#2D3843', +// 900: '#1A2027', +// }; + +interface Props { + options: SelectOption<string>[]; + selected: string; + changed?: (newSelected: SelectOption<string>) => void; +} + +function AttributeOperatorSelect({ + options, + selected, + changed = () => {}, +}: Props) { + const listboxRef = useRef<HTMLUListElement>(null); + const [listboxVisible, setListboxVisible] = useState(false); + const [currSelected, setCurrSelected] = useState( + options.find((o) => o.value == selected)?.label || options[0].label + ); + + React.useEffect(() => { + if (listboxVisible) { + listboxRef.current?.focus(); + } + }, [listboxVisible]); + + const changeSelected = (option: SelectOption<string>) => { + if (option.label != currSelected) { + setCurrSelected(option.label); + changed(option); + } + }; + + return ( + <div + className={styles.container} + // onMouseOver={() => setListboxVisible(true)} + onMouseOut={() => setListboxVisible(false)} + onClick={() => setListboxVisible(true)} + onFocus={() => setListboxVisible(true)} + onBlur={() => setListboxVisible(false)} + > + <div + className={ + styles.valueContainer + ' ' + (listboxVisible && styles.highlighted) + } + > + <span>{currSelected}</span> + </div> + {options.length > 1 && ( + <ul + className={styles.listbox + ' ' + (!listboxVisible && styles.hidden)} + ref={listboxRef} + onMouseOver={() => setListboxVisible(true)} + > + {options.map((option) => ( + <li + className={option.label == currSelected ? styles.selected : ''} + key={option.value} + onClick={() => changeSelected(option)} + > + {option.label} + </li> + ))} + </ul> + )} + </div> + ); +} + +export default AttributeOperatorSelect; diff --git a/libs/shared/ui/pills/src/customFlowPills/attributepill/variables.module.scss b/libs/shared/ui/pills/src/customFlowPills/attributepill/variables.module.scss new file mode 100644 index 000000000..08bc31bb6 --- /dev/null +++ b/libs/shared/ui/pills/src/customFlowPills/attributepill/variables.module.scss @@ -0,0 +1,3 @@ +$height: 5px; +$fontsize: 6px; +$ypad: 2px; diff --git a/libs/shared/ui/pills/src/customFlowPills/entitypill/entitypill.module.scss b/libs/shared/ui/pills/src/customFlowPills/entitypill/entitypill.module.scss new file mode 100644 index 000000000..755d2b41d --- /dev/null +++ b/libs/shared/ui/pills/src/customFlowPills/entitypill/entitypill.module.scss @@ -0,0 +1,49 @@ +.entity { + display: flex; + font-family: monospace; + font-weight: bold; + font-size: 10px; + padding: 4px 2ch; + border-radius: 3px; +} + +.highlighted { + box-shadow: black 0 0 2px; +} + +.handleLeft { + border: 0px; + border-radius: 0px; + left: 12px; + width: 7px; + height: 7px; + margin-bottom: 11px; + background: rgba(255, 255, 255, 0.6); + box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.3); + transform-origin: center; +} + +// .handleBottom { +// border: 0px; +// border-radius: 0px; +// width: 7px; +// height: 7px; +// left: 27.5px; +// margin-bottom: 11px; +// background: rgba(255, 255, 255, 0.6); +// box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.3); +// transform: rotate(-45deg); +// transform-origin: center; +// } + +.contentWrapper { + margin-left: 3ch; + + span { + max-width: 20ch; + text-overflow: ellipsis; + overflow: hidden; + white-space: nowrap; + display: block; + } +} diff --git a/libs/shared/ui/pills/src/customFlowPills/entitypill/entitypill.tsx b/libs/shared/ui/pills/src/customFlowPills/entitypill/entitypill.tsx new file mode 100644 index 000000000..c6ce0d86f --- /dev/null +++ b/libs/shared/ui/pills/src/customFlowPills/entitypill/entitypill.tsx @@ -0,0 +1,64 @@ +import { handles } from '@graphpolaris/querybuilder/usecases'; +import { useTheme } from '@mui/material'; +import React, { useEffect } from 'react'; +import { FlowElement, Handle, Position } from 'react-flow-renderer'; +import styles from './entitypill.module.scss'; +import cn from 'classnames'; + +// export const Handless = { +// entity: { +// attributes: 'attributesHandle', +// relations: 'relationsHandle', +// }, +// }; + +// /** Links need handles to what they are connected to (and which side) */ +// export enum Handles { +// RelationLeft = 'leftEntityHandle', //target +// RelationRight = 'rightEntityHandle', //target +// ToAttributeHandle = 'attributesHandle', //target +// ToRelation = 'relationsHandle', //source +// Attribute = 'AttributeHandle', //source +// ReceiveFunction = 'receiveFunctionHandle', //target +// FunctionBase = 'functionHandle_', // + name from FunctionTypes args //source +// } + +/** + * Component to render an entity flow element + * @param {FlowElement<EntityData>)} param0 The data of an entity flow element. + */ +export const EntityRFPill = React.memo(({ data }: { data: any }) => { + const theme = useTheme(); + + // console.log('EntityRFPill', data); + return ( + <div + className={cn(styles['entity'], { + [styles['highlighted']]: data.suggestedForConnection, + })} + style={{ + background: theme.palette.queryBuilder.entity.background, + color: theme.palette.queryBuilder.text, + }} + > + <Handle + id={handles.entity.relation} + type="source" + position={Position.Bottom} + className={styles['handleLeft']} + style={data?.isConnected ? { backgroundColor: '#2e2e2e' } : {}} + /> + {/* <Handle + id={Handles.ToAttributeHandle} + type="target" + position={Position.Bottom} + className={styles.handleBottom} + /> */} + <div className={styles['contentWrapper']}> + <span title={data.name}>{data.name}</span> + </div> + </div> + ); +}); + +export default EntityRFPill; diff --git a/libs/shared/ui/pills/src/customFlowPills/entitypill/index.ts b/libs/shared/ui/pills/src/customFlowPills/entitypill/index.ts new file mode 100644 index 000000000..8f909017f --- /dev/null +++ b/libs/shared/ui/pills/src/customFlowPills/entitypill/index.ts @@ -0,0 +1 @@ +export * from './entitypill'; diff --git a/libs/shared/ui/pills/src/customFlowPills/relationpill/index.ts b/libs/shared/ui/pills/src/customFlowPills/relationpill/index.ts new file mode 100644 index 000000000..deef2e611 --- /dev/null +++ b/libs/shared/ui/pills/src/customFlowPills/relationpill/index.ts @@ -0,0 +1 @@ +export * from './relationpill'; diff --git a/libs/shared/ui/pills/src/customFlowPills/relationpill/relationpill.module.scss b/libs/shared/ui/pills/src/customFlowPills/relationpill/relationpill.module.scss new file mode 100644 index 000000000..aff84b2e5 --- /dev/null +++ b/libs/shared/ui/pills/src/customFlowPills/relationpill/relationpill.module.scss @@ -0,0 +1,110 @@ +.relation { + display: flex; + text-align: center; + font-family: monospace; + font-weight: bold; + font-size: 10px; + background-color: transparent; +} + +.highlighted { + box-shadow: black 0 0 2px; +} + +.contentWrapper { + display: flex; + align-items: center; + + .handleLeft { + position: relative; + z-index: 3; + + top: 25%; + border: 0px; + border-radius: 0px; + + background: transparent; + transform-origin: center; + + width: 0; + height: 0; + border-top: 5px solid transparent; + border-bottom: 5px solid transparent; + border-right: rgba(255, 255, 255, 0.7) 6px solid; + + &::after { + content: ''; + display: block; + position: absolute; + width: 0; + height: 0; + border-top: 7px solid transparent; + border-bottom: 7px solid transparent; + border-right: rgba(0, 0, 0, 0.1) 8px solid; + top: -7px; + right: -7px; + } + } + .highlighted { + z-index: -1; + box-shadow: 0 0 2px 1px gray; + } + + .content { + margin: 0 2ch; + padding: 3px 0; + max-width: 20ch; + text-overflow: ellipsis; + overflow: hidden; + white-space: nowrap; + } + + .handleRight { + position: relative; + top: 25%; + border: 0px; + border-radius: 0px; + + background: transparent; + transform-origin: center; + + width: 0; + height: 0; + border-top: 5px solid transparent; + border-bottom: 5px solid transparent; + border-left: rgba(255, 255, 255, 0.7) 6px solid; + + &::after { + content: ''; + display: block; + position: absolute; + width: 0; + height: 0; + border-top: 7px solid transparent; + border-bottom: 7px solid transparent; + border-left: rgba(0, 0, 0, 0.1) 8px solid; + top: -7px; + left: -7px; + } + } +} + +$height: 10px; +.arrowLeft { + z-index: 2; + width: 0; + height: 0; + border-top: $height solid transparent; + border-bottom: $height solid transparent; + + border-right: $height solid; +} + +.arrowRight { + width: 0; + height: 0; + border-top: $height solid transparent; + border-bottom: $height solid transparent; + + border-left: $height solid; +} diff --git a/libs/shared/ui/pills/src/customFlowPills/relationpill/relationpill.tsx b/libs/shared/ui/pills/src/customFlowPills/relationpill/relationpill.tsx new file mode 100644 index 000000000..8861d5e0e --- /dev/null +++ b/libs/shared/ui/pills/src/customFlowPills/relationpill/relationpill.tsx @@ -0,0 +1,98 @@ +import { handles } from '@graphpolaris/querybuilder/usecases'; +import { useTheme } from '@mui/material'; +import { Handle, Position } from 'react-flow-renderer'; +import cn from 'classnames'; + +import styles from './relationpill.module.scss'; +import React from 'react'; + +/** + * Component to render a relation flow element + * @param { FlowElement<RelationData>} param0 The data of a relation flow element. + */ +export const RelationRFPill = React.memo(({ data }: { data: any }) => { + // export default function RelationRFPill({ data }: { data: any }) { + const theme = useTheme(); + + // const minRef = useRef<HTMLInputElement>(null); + // const maxRef = useRef<HTMLInputElement>(null); + + // const [readOnlyMin, setReadOnlyMin] = useState(true); + // const [readOnlyMax, setReadOnlyMax] = useState(true); + + // const onDepthChanged = (depth: string) => { + // // Don't allow depth above 99 + // const limit = 99; + // if (data != undefined) { + // data.depth.min = data.depth.min >= limit ? limit : data.depth.min; + // data.depth.max = data.depth.max >= limit ? limit : data.depth.max; + + // // Check for for valid depth: min <= max + // if (depth == 'min') { + // if (data.depth.min > data.depth.max) data.depth.max = data.depth.min; + // setReadOnlyMin(true); + // } else if (depth == 'max') { + // if (data.depth.max < data.depth.min) data.depth.min = data.depth.max; + // setReadOnlyMax(true); + // } + + // // Set to the correct width + // if (maxRef.current) + // maxRef.current.style.maxWidth = calcWidth(data.depth.max); + // if (minRef.current) + // minRef.current.style.maxWidth = calcWidth(data.depth.min); + // } + // }; + + // const calcWidth = (data: number) => { + // return data.toString().length + 0.5 + 'ch'; + // }; + + return ( + <div className={styles['relation']}> + <div + className={styles['arrowLeft']} + style={{ + borderRightColor: theme.palette.queryBuilder.relation.background, + }} + /> + <div + className={cn(styles['contentWrapper'], { + [styles['highlighted']]: data.suggestedForConnection, + })} + style={{ + color: theme.palette.queryBuilder.text, + background: theme.palette.queryBuilder.relation.background, + }} + > + <Handle + id={handles.relation.fromEntity} + type="target" + position={Position.Left} + className={styles['handleLeft']} + style={ + data?.isFromEntityConnected ? { borderRightColor: '#2e2e2e' } : {} // TODO: this should be color from theme + } + /> + <span className={styles['content']} title={data.name}> + {data.name} + </span> + <Handle + id={handles.relation.toEntity} + type="source" + position={Position.Right} + className={styles['handleRight']} + style={ + data?.isToEntityConnected ? { borderLeftColor: '#2e2e2e' } : {} + } + /> + </div> + <div + className={styles['arrowRight']} + style={{ + borderLeftColor: theme.palette.queryBuilder.relation.background, + }} + /> + </div> + ); +}); diff --git a/libs/shared/ui/pills/src/index.ts b/libs/shared/ui/pills/src/index.ts new file mode 100644 index 000000000..6ac42e28d --- /dev/null +++ b/libs/shared/ui/pills/src/index.ts @@ -0,0 +1,9 @@ +export * from './lib/shared-ui-pills'; + +export * from './customFlowLines/connection'; +export * from './customFlowLines/connectionDrag'; + +export * from './customFlowPills/entitypill/entitypill'; +export * from './customFlowPills/attributepill/attributepill'; +export * from './customFlowPills/attributepill/operatorselect'; +export * from './customFlowPills/relationpill/relationpill'; diff --git a/libs/shared/ui/pills/src/lib/shared-ui-pills.module.scss b/libs/shared/ui/pills/src/lib/shared-ui-pills.module.scss new file mode 100644 index 000000000..e69de29bb diff --git a/libs/shared/ui/pills/src/lib/shared-ui-pills.spec.tsx b/libs/shared/ui/pills/src/lib/shared-ui-pills.spec.tsx new file mode 100644 index 000000000..d9561ecbd --- /dev/null +++ b/libs/shared/ui/pills/src/lib/shared-ui-pills.spec.tsx @@ -0,0 +1,10 @@ +import { render } from '@testing-library/react'; + +import SharedUiPills from './shared-ui-pills'; + +describe('SharedUiPills', () => { + it('should render successfully', () => { + const { baseElement } = render(<SharedUiPills />); + expect(baseElement).toBeTruthy(); + }); +}); diff --git a/libs/shared/ui/pills/src/lib/shared-ui-pills.tsx b/libs/shared/ui/pills/src/lib/shared-ui-pills.tsx new file mode 100644 index 000000000..6a804d611 --- /dev/null +++ b/libs/shared/ui/pills/src/lib/shared-ui-pills.tsx @@ -0,0 +1,14 @@ +import './shared-ui-pills.module.scss'; + +/* eslint-disable-next-line */ +export interface SharedUiPillsProps {} + +export function SharedUiPills(props: SharedUiPillsProps) { + return ( + <div> + <h1>Welcome to SharedUiPills!</h1> + </div> + ); +} + +export default SharedUiPills; diff --git a/libs/shared/ui/pills/src/schema/entitypill.component.tsx b/libs/shared/ui/pills/src/schema/entitypill.component.tsx new file mode 100644 index 000000000..049328d00 --- /dev/null +++ b/libs/shared/ui/pills/src/schema/entitypill.component.tsx @@ -0,0 +1,62 @@ +import { useTheme } from '@mui/material'; +import React, { useEffect } from 'react'; +import { FlowElement, Handle, Position } from 'react-flow-renderer'; +import cn from 'classnames'; +import styles from './entitypill.module.scss'; + +// export const Handless = { +// entity: { +// attributes: 'attributesHandle', +// relations: 'relationsHandle', +// }, +// }; + +// /** Links need handles to what they are connected to (and which side) */ +// export enum Handles { +// RelationLeft = 'leftEntityHandle', //target +// RelationRight = 'rightEntityHandle', //target +// ToAttributeHandle = 'attributesHandle', //target +// ToRelation = 'relationsHandle', //source +// Attribute = 'AttributeHandle', //source +// ReceiveFunction = 'receiveFunctionHandle', //target +// FunctionBase = 'functionHandle_', // + name from FunctionTypes args //source +// } + +/** + * Component to render an entity flow element + * @param {FlowElement<EntityData>)} param0 The data of an entity flow element. + */ +export const EntityRFPill = React.memo(({ data }: { data: any }) => { + const theme = useTheme(); + + return ( + <div + className={cn(styles['entity'], { + [styles['highlighted']]: data.suggestedForConnection, + })} + style={{ + background: theme.palette.queryBuilder.entity.background, + color: theme.palette.queryBuilder.text, + }} + > + <Handle + id={handles.entity.relation} + type="source" + position={Position.Bottom} + className={styles['handleLeft']} + style={data?.isConnected ? { backgroundColor: '#2e2e2e' } : {}} + /> + {/* <Handle + id={Handles.ToAttributeHandle} + type="target" + position={Position.Bottom} + className={styles.handleBottom} + /> */} + <div className={styles['contentWrapper']}> + <span title={data.name}>{data.name}</span> + </div> + </div> + ); +}); + +export default EntityRFPill; diff --git a/libs/shared/ui/pills/src/schema/entitypill.module.scss b/libs/shared/ui/pills/src/schema/entitypill.module.scss new file mode 100644 index 000000000..755d2b41d --- /dev/null +++ b/libs/shared/ui/pills/src/schema/entitypill.module.scss @@ -0,0 +1,49 @@ +.entity { + display: flex; + font-family: monospace; + font-weight: bold; + font-size: 10px; + padding: 4px 2ch; + border-radius: 3px; +} + +.highlighted { + box-shadow: black 0 0 2px; +} + +.handleLeft { + border: 0px; + border-radius: 0px; + left: 12px; + width: 7px; + height: 7px; + margin-bottom: 11px; + background: rgba(255, 255, 255, 0.6); + box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.3); + transform-origin: center; +} + +// .handleBottom { +// border: 0px; +// border-radius: 0px; +// width: 7px; +// height: 7px; +// left: 27.5px; +// margin-bottom: 11px; +// background: rgba(255, 255, 255, 0.6); +// box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.3); +// transform: rotate(-45deg); +// transform-origin: center; +// } + +.contentWrapper { + margin-left: 3ch; + + span { + max-width: 20ch; + text-overflow: ellipsis; + overflow: hidden; + white-space: nowrap; + display: block; + } +} diff --git a/libs/shared/ui/pills/src/schema/index.ts b/libs/shared/ui/pills/src/schema/index.ts new file mode 100644 index 000000000..e69de29bb diff --git a/libs/shared/ui/pills/tsconfig.json b/libs/shared/ui/pills/tsconfig.json new file mode 100644 index 000000000..3512bf7af --- /dev/null +++ b/libs/shared/ui/pills/tsconfig.json @@ -0,0 +1,25 @@ +{ + "extends": "../../../../tsconfig.base.json", + "compilerOptions": { + "jsx": "react-jsx", + "allowJs": true, + "esModuleInterop": true, + "allowSyntheticDefaultImports": true, + "forceConsistentCasingInFileNames": true, + "strict": true, + "noImplicitOverride": true, + "noPropertyAccessFromIndexSignature": true, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true + }, + "files": [], + "include": [], + "references": [ + { + "path": "./tsconfig.lib.json" + }, + { + "path": "./tsconfig.spec.json" + } + ] +} diff --git a/libs/shared/ui/pills/tsconfig.lib.json b/libs/shared/ui/pills/tsconfig.lib.json new file mode 100644 index 000000000..1ab8e06a7 --- /dev/null +++ b/libs/shared/ui/pills/tsconfig.lib.json @@ -0,0 +1,22 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../../../dist/out-tsc", + "types": ["node"] + }, + "files": [ + "../../../../node_modules/@nrwl/react/typings/cssmodule.d.ts", + "../../../../node_modules/@nrwl/react/typings/image.d.ts" + ], + "exclude": [ + "**/*.spec.ts", + "**/*.test.ts", + "**/*.spec.tsx", + "**/*.test.tsx", + "**/*.spec.js", + "**/*.test.js", + "**/*.spec.jsx", + "**/*.test.jsx" + ], + "include": ["**/*.js", "**/*.jsx", "**/*.ts", "**/*.tsx"] +} diff --git a/libs/shared/ui/pills/tsconfig.spec.json b/libs/shared/ui/pills/tsconfig.spec.json new file mode 100644 index 000000000..315a5b0bb --- /dev/null +++ b/libs/shared/ui/pills/tsconfig.spec.json @@ -0,0 +1,19 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../../../dist/out-tsc", + "module": "commonjs", + "types": ["jest", "node"] + }, + "include": [ + "**/*.test.ts", + "**/*.spec.ts", + "**/*.test.tsx", + "**/*.spec.tsx", + "**/*.test.js", + "**/*.spec.js", + "**/*.test.jsx", + "**/*.spec.jsx", + "**/*.d.ts" + ] +} diff --git a/tsconfig.base.json b/tsconfig.base.json index a5bafb69c..1d989caf1 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -17,11 +17,11 @@ "paths": { "@graphpolaris/graph-layout": ["libs/shared/graph-layout/src/index.ts"], "@graphpolaris/models": ["libs/shared/models/src/index.ts"], - "@graphpolaris/schema-utils": ["libs/shared/schema-utils/src/index.ts"], - "@graphpolaris/schema/usecases": ["libs/schema/usecases/src/index.ts"], "@graphpolaris/querybuilder/usecases": [ "libs/querybuilder/usecases/src/index.ts" ], + "@graphpolaris/schema-utils": ["libs/shared/schema-utils/src/index.ts"], + "@graphpolaris/schema/usecases": ["libs/schema/usecases/src/index.ts"], "@graphpolaris/shared/data-access/api": [ "libs/shared/data-access/api/src/index.ts" ], @@ -35,6 +35,7 @@ "libs/shared/data-access/theme/src/index.ts" ], "@graphpolaris/shared/mock-data": ["libs/shared/mock-data/src/index.ts"], + "@graphpolaris/shared/ui/pills": ["libs/shared/ui/pills/src/index.ts"], "@mui/styled-engine": ["./node_modules/@mui/styled-engine-sc"] } }, diff --git a/workspace.json b/workspace.json index d829d3ca0..d5bbe8a52 100644 --- a/workspace.json +++ b/workspace.json @@ -11,6 +11,7 @@ "shared-mock-data": "libs/shared/mock-data", "shared-models": "libs/shared/models", "shared-schema-utils": "libs/shared/schema-utils", + "shared-ui-pills": "libs/shared/ui/pills", "web-graphpolaris": "apps/web-graphpolaris", "web-graphpolaris-e2e": "apps/web-graphpolaris-e2e" } -- GitLab