From e6718f577e600fd91d478f2bd3b7f4e713d64b81 Mon Sep 17 00:00:00 2001 From: Sivan Duijn <sivanduijn@gmail.com> Date: Mon, 28 Feb 2022 13:20:38 +0100 Subject: [PATCH] feat(querybuilder): added entity pill reactflow pill --- .vscode/settings.json | 11 ++- apps/web-graphpolaris/src/app/app.stories.tsx | 4 +- apps/web-graphpolaris/src/app/app.tsx | 5 +- .../entitypill/entitypill.module.scss | 67 +++++++++++++++++ .../customFlowPills/entitypill/entitypill.tsx | 74 +++++++++++++++++++ .../relationpill/relationpill.module.scss | 0 .../relationpill/relationpill.tsx | 0 .../querybuilder/querybuilder.stories.tsx | 35 +++++++++ .../components/querybuilder/querybuilder.tsx | 52 +++++++++++++ .../store/src/lib/colorPaletteConfigSlice.ts | 30 +++++++- .../src/lib/mapColorsConfigToMuiTheme.ts | 20 +++++ 11 files changed, 292 insertions(+), 6 deletions(-) create mode 100644 apps/web-graphpolaris/src/components/querybuilder/customFlowPills/entitypill/entitypill.module.scss create mode 100644 apps/web-graphpolaris/src/components/querybuilder/customFlowPills/entitypill/entitypill.tsx create mode 100644 apps/web-graphpolaris/src/components/querybuilder/customFlowPills/relationpill/relationpill.module.scss create mode 100644 apps/web-graphpolaris/src/components/querybuilder/customFlowPills/relationpill/relationpill.tsx create mode 100644 apps/web-graphpolaris/src/components/querybuilder/querybuilder.stories.tsx create mode 100644 apps/web-graphpolaris/src/components/querybuilder/querybuilder.tsx diff --git a/.vscode/settings.json b/.vscode/settings.json index 84265c462..5cb5dc7c9 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -23,5 +23,14 @@ "storybook", "store" ], - "jest.jestCommandLine": "nx affected:test" + "jest.jestCommandLine": "nx affected:test", + + "jsonColorToken.languages": [ + "json", + "jsonc", + "javascript", + "javascriptreact", + "typescript", + "typescriptreact" + ] } diff --git a/apps/web-graphpolaris/src/app/app.stories.tsx b/apps/web-graphpolaris/src/app/app.stories.tsx index 8f5be4783..eda4ff21d 100644 --- a/apps/web-graphpolaris/src/app/app.stories.tsx +++ b/apps/web-graphpolaris/src/app/app.stories.tsx @@ -3,7 +3,7 @@ import { Meta, Story } from '@storybook/react'; import { App } from './app'; import { Provider } from 'react-redux'; import { store } from '@graphpolaris/shared/data-access/store'; -import { OurThemeProvider } from '@graphpolaris/shared/data-access/theme'; +import { GraphPolarisThemeProvider } from '@graphpolaris/shared/data-access/theme'; export default { component: App, @@ -12,7 +12,7 @@ export default { // using the real store here (story) => ( <Provider store={store}> - <OurThemeProvider>{story()}</OurThemeProvider> + <GraphPolarisThemeProvider>{story()}</GraphPolarisThemeProvider> </Provider> ), ], diff --git a/apps/web-graphpolaris/src/app/app.tsx b/apps/web-graphpolaris/src/app/app.tsx index 1f82f8ee2..c523f1863 100644 --- a/apps/web-graphpolaris/src/app/app.tsx +++ b/apps/web-graphpolaris/src/app/app.tsx @@ -12,6 +12,7 @@ import SemanticSubstrates from '../components/visualisations/semanticsubstrates/ import LoginScreen from '../components/login/loginScreen'; import Schema from '../components/schema/schema'; import { GetUserInfo } from '@graphpolaris/shared/data-access/api'; +import QueryBuilder from '../components/querybuilder/querybuilder'; function useIsAuthorized() { const [userAuthorized, setUserAuthorized] = useState(false); @@ -64,7 +65,9 @@ export function App() { key="query-panel" data-grid={{ x: 3, y: 20, w: 5, h: 10, maxH: 20, isDraggable: false }} > - <Panel content="Query Panel" color="blue"></Panel> + <Panel content="Query Panel" color="blue"> + <QueryBuilder /> + </Panel> </div> <div key="visualisation-panel" diff --git a/apps/web-graphpolaris/src/components/querybuilder/customFlowPills/entitypill/entitypill.module.scss b/apps/web-graphpolaris/src/components/querybuilder/customFlowPills/entitypill/entitypill.module.scss new file mode 100644 index 000000000..4ba771816 --- /dev/null +++ b/apps/web-graphpolaris/src/components/querybuilder/customFlowPills/entitypill/entitypill.module.scss @@ -0,0 +1,67 @@ +.entity { + display: flex; + border-radius: 3px; + font-weight: bold; + font-size: 11px; + padding: 4px 3px; +} + +.handleLeft { + border: 0px; + border-radius: 0px; + left: 12px; + width: 8px; + height: 8px; + margin-bottom: 11px; + background: rgba(0, 0, 0, 0.3); + transform-origin: center; + + &::before { + content: ''; + width: 6px; + height: 6px; + left: 1px; + bottom: 1px; + border: 0px; + border-radius: 0px; + background: rgba(255, 255, 255, 0.6); + z-index: -1; + display: inline-block; + position: fixed; + } +} + +.handleBottom { + border: 0px; + border-radius: 0px; + width: 8px; + height: 8px; + left: 27.5px; + margin-bottom: 11px; + background: rgba(0, 0, 0, 0.3); + transform: rotate(-45deg); + transform-origin: center; + + &::before { + content: ''; + width: 6px; + height: 6px; + left: 1px; + bottom: 1px; + border: 0px; + border-radius: 0px; + background: rgba(255, 255, 255, 0.6); + z-index: -1; + display: inline-block; + position: fixed; + } +} + +.contentWrapper { + margin-left: 45px; + margin-right: 5px; + + span { + float: left; + } +} diff --git a/apps/web-graphpolaris/src/components/querybuilder/customFlowPills/entitypill/entitypill.tsx b/apps/web-graphpolaris/src/components/querybuilder/customFlowPills/entitypill/entitypill.tsx new file mode 100644 index 000000000..2d34c4bb3 --- /dev/null +++ b/apps/web-graphpolaris/src/components/querybuilder/customFlowPills/entitypill/entitypill.tsx @@ -0,0 +1,74 @@ +import { useTheme } from '@mui/material'; +import React, { useEffect } from 'react'; +import { FlowElement, Handle, Position } from 'react-flow-renderer'; +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 + OnAttribute = 'onAttributeHandle', //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(); + + // TODO: Change flow element width when text overflows + // const data = entityNode.data as EntityData; + // const animation = data.fadeIn ? styles.entityFade : ''; + + return ( + <div + className={styles.entity} + style={{ + background: theme.palette.queryBuilder.entity.background, + fontFamily: 'monospace', + color: theme.palette.queryBuilder.entity.text, + // borderTop: `4px solid ${theme.palette.queryBuilder.entity.accent}`, + // borderBottom: `6px solid ${theme.palette.queryBuilder.entity.accent}`, + }} + > + <Handle + id={Handles.ToRelation} + type="source" + position={Position.Bottom} + className={styles.handleLeft} + /> + <Handle + id={Handles.ToAttributeHandle} + type="target" + position={Position.Bottom} + className={styles.handleBottom} + /> + {/* <Handle + id={Handles.ReceiveFunction} + type="target" + position={Position.Bottom} + className={styles.handleFunction + ' ' + styles.handleFunctionEntity} + /> */} + <div + className={styles.contentWrapper} + style={{ + color: theme.palette.queryBuilder.entity.text, + }} + > + <span>{data.name}</span> + </div> + </div> + ); +}); diff --git a/apps/web-graphpolaris/src/components/querybuilder/customFlowPills/relationpill/relationpill.module.scss b/apps/web-graphpolaris/src/components/querybuilder/customFlowPills/relationpill/relationpill.module.scss new file mode 100644 index 000000000..e69de29bb diff --git a/apps/web-graphpolaris/src/components/querybuilder/customFlowPills/relationpill/relationpill.tsx b/apps/web-graphpolaris/src/components/querybuilder/customFlowPills/relationpill/relationpill.tsx new file mode 100644 index 000000000..e69de29bb diff --git a/apps/web-graphpolaris/src/components/querybuilder/querybuilder.stories.tsx b/apps/web-graphpolaris/src/components/querybuilder/querybuilder.stories.tsx new file mode 100644 index 000000000..5f929433b --- /dev/null +++ b/apps/web-graphpolaris/src/components/querybuilder/querybuilder.stories.tsx @@ -0,0 +1,35 @@ +import React from 'react'; +import { colorPaletteConfigSlice } from '@graphpolaris/shared/data-access/store'; +import { GraphPolarisThemeProvider } from '@graphpolaris/shared/data-access/theme'; +import { AnyAction, configureStore, Store } from '@reduxjs/toolkit'; +import { + ComponentMeta, + ComponentStory, + ReactFramework, +} from '@storybook/react'; +import { Provider } from 'react-redux'; +import QueryBuilder from './querybuilder'; + +export default { + component: QueryBuilder, +} as ComponentMeta<typeof QueryBuilder>; + +const Template: ComponentStory<typeof QueryBuilder> = (args) => ( + <QueryBuilder {...args} /> +); + +// Mock palette store +const mockEmptyStore = configureStore({ + reducer: { + colorPaletteConfig: colorPaletteConfigSlice.reducer, + }, +}); + +export const NoData = Template.bind({}); +NoData.decorators = [ + (story) => ( + <Provider store={mockEmptyStore}> + <GraphPolarisThemeProvider>{story()}</GraphPolarisThemeProvider> + </Provider> + ), +]; diff --git a/apps/web-graphpolaris/src/components/querybuilder/querybuilder.tsx b/apps/web-graphpolaris/src/components/querybuilder/querybuilder.tsx new file mode 100644 index 000000000..76f14bc32 --- /dev/null +++ b/apps/web-graphpolaris/src/components/querybuilder/querybuilder.tsx @@ -0,0 +1,52 @@ +import { + AllLayoutAlgorithms, + LayoutFactory, +} from '@graphpolaris/shared/graph-layout'; +import { useEffect, useState } from 'react'; +import ReactFlow, { + ReactFlowProvider, + FlowElement, + Background, +} from 'react-flow-renderer'; +import { EntityRFPill } from './customFlowPills/entitypill/entitypill'; + +const nodeTypes = { + entity: EntityRFPill, +}; + +const initialElements = [ + { + id: '2', + type: 'entity', + position: { x: 100, y: 100 }, + data: { name: 'entityNode' }, + }, +]; + +// const onLoad = (reactFlowInstance: any) => { +// setTimeout(() => reactFlowInstance.fitView(), 0); +// }; + +const QueryBuilder = (props: {}) => { + const [elements, setElements] = useState<FlowElement[]>(initialElements); + + const graphStyles = { width: '100%', height: '500px' }; + + return ( + <div> + <ReactFlowProvider> + <ReactFlow + elements={elements} + style={graphStyles} + snapGrid={[10, 10]} + snapToGrid={true} + nodeTypes={nodeTypes} + > + <Background gap={10} size={0.7} /> + </ReactFlow> + </ReactFlowProvider> + </div> + ); +}; + +export default QueryBuilder; diff --git a/libs/shared/data-access/store/src/lib/colorPaletteConfigSlice.ts b/libs/shared/data-access/store/src/lib/colorPaletteConfigSlice.ts index bb41dc4a8..c4427767f 100644 --- a/libs/shared/data-access/store/src/lib/colorPaletteConfigSlice.ts +++ b/libs/shared/data-access/store/src/lib/colorPaletteConfigSlice.ts @@ -6,6 +6,14 @@ import type { RootState } from './store'; export interface ExtraColorsForMui5 { /** Colors that can be used for data visualisation, e.g. nodes, edges */ dataPointColors: string[]; + + queryBuilder: { + entity: { + background: string; + text: string; + accent: string; + }; + }; } /** Our custom color palette config. With the palette options from MUI that we are going to use. */ @@ -45,7 +53,16 @@ export interface ColorPaletteConfig { // But we don't reference that type directly here to stay decoupled export const initialState: ColorPaletteConfig = { lightPalette: { - custom: { dataPointColors: ['#ff0000', '#00ff00', '#0000ff'] }, + custom: { + dataPointColors: ['#ff0000', '#00ff00', '#0000ff'], + queryBuilder: { + entity: { + background: '#ffac57', + accent: '#C48546', + text: 'black', + }, + }, + }, // If light and dark are not set, these will be calculated using main. // light/dark have nothing with darkmode, these are just a light and dark variation primary: { @@ -60,7 +77,16 @@ export const initialState: ColorPaletteConfig = { }, }, darkPalette: { - custom: { dataPointColors: ['#ff0000', '#00ff00', '#0000ff'] }, + custom: { + dataPointColors: ['#ff0000', '#00ff00', '#0000ff'], + queryBuilder: { + entity: { + background: '#e9e9e9', + accent: '#C48546', + text: 'black', + }, + }, + }, primary: { main: '#e3f3fd', }, diff --git a/libs/shared/data-access/theme/src/lib/mapColorsConfigToMuiTheme.ts b/libs/shared/data-access/theme/src/lib/mapColorsConfigToMuiTheme.ts index 432356844..dd174cdb5 100644 --- a/libs/shared/data-access/theme/src/lib/mapColorsConfigToMuiTheme.ts +++ b/libs/shared/data-access/theme/src/lib/mapColorsConfigToMuiTheme.ts @@ -14,11 +14,31 @@ export default function MapColorsConfigToMuiTheme( primary: colorsConfig.darkPalette.primary, secondary: colorsConfig.darkPalette.secondary, dataPointColors: colorsConfig.darkPalette.custom.dataPointColors, + queryBuilder: { + entity: { + background: + colorsConfig.darkPalette.custom.queryBuilder.entity + .background, + accent: + colorsConfig.darkPalette.custom.queryBuilder.entity.accent, + text: colorsConfig.darkPalette.custom.queryBuilder.entity.text, + }, + }, } : { primary: colorsConfig.lightPalette.primary, secondary: colorsConfig.lightPalette.secondary, dataPointColors: colorsConfig.lightPalette.custom.dataPointColors, + queryBuilder: { + entity: { + background: + colorsConfig.lightPalette.custom.queryBuilder.entity + .background, + accent: + colorsConfig.lightPalette.custom.queryBuilder.entity.accent, + text: colorsConfig.lightPalette.custom.queryBuilder.entity.text, + }, + }, }), }, }; -- GitLab