diff --git a/.gitignore b/.gitignore index 81298264fd1a8b12bb82d005229e5cfd9b2e9e61..9101d3294069ecf175fdd2f7c1fb3372480c7be4 100644 --- a/.gitignore +++ b/.gitignore @@ -33,6 +33,7 @@ npm-debug.log yarn-error.log testem.log /typings +debug.log # System Files .DS_Store diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 692ad0934834f56991870ef680e0b15b9352e4e7..647402e214df631263ffb209d200c6ca9bd300f7 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -2,6 +2,7 @@ image: node:16-alpine stages: - setup - test + - versioning - build - dockerize @@ -67,7 +68,7 @@ build-docker: only: - main script: - - docker build --progress plain -t $CI_PROJECT_NAME-webserver-service:latest . + - docker build --progress plain -t datastropheregistry.azurecr.io/frontend:latest:latest . # after_script: # - docker login datastropheregistry.azurecr.io -u $REGISTRY_USERNAME -p $REGISTRY_PASSWORD # - if [[ ! -z $CI_COMMIT_BRANCH+x ]]; then DOCKER_TAG=$CI_COMMIT_BRANCH; else DOCKER_TAG=$CI_MERGE_REQUEST_TARGET_BRANCH_NAME; fi @@ -75,3 +76,23 @@ build-docker: # - docker push datastropheregistry.azurecr.io/$CI_PROJECT_NAME-webserver-service:$DOCKER_TAG dependencies: - build + +##################### +# STAGE: versioning # +##################### + +versioning: + image: node:13 + stage: versioning + only: + refs: + - release + - master + before_script: + # Skip before_script since we will not need the requirements + - '' + script: + - nx affected --target version + artifacts: + paths: + - CHANGELOG.md \ No newline at end of file diff --git a/.storybook/main.js b/.storybook/main.js index 1492ac61f6fd000a73c9f0d5995e3cfa7074c06c..7ea37db6d3a850ab3f1d94223bb9dcefe37b664e 100644 --- a/.storybook/main.js +++ b/.storybook/main.js @@ -1,11 +1,27 @@ module.exports = { - stories: [], + stories: [ + // '../../../libs/**/*.stories.@(tsx|md)', + { + // 👇 The directory field sets the directory your stories + directory: '../../../libs/shared/ui/pills/src/customFlowPills/entitypill', + // 👇 The titlePrefix field will generate automatic titles for your stories + titlePrefix: 'SharedUIComponents', + // 👇 Storybook will load all files that contain the stories extension + files: '*.stories.*', + }, + ], + // async (list) => [...list, ...findStories()], addons: ['@storybook/addon-essentials'], // uncomment the property below if you want to apply some webpack config globally - // webpackFinal: async (config, { configType }) => { - // // Make whatever fine-grained changes you need that should apply to all storybook configs - - // // Return the altered config - // return config; - // }, + webpackFinal: async (config, { configType }) => { + // Make whatever fine-grained changes you need that should apply to all storybook configs + // console.log('current main.js config', config); + // Return the altered config + return config; + }, }; + +function findStories() { + // your custom logic returns a list of files + console.log('findStories========================'); +} diff --git a/.vscode/settings.json b/.vscode/settings.json index 0c27507c7c9016a219a2527fa8d4b61b0302bfad..5ff6bea5dbd51c1594e94eb604de8bb22dfeb099 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -14,15 +14,15 @@ "editor.defaultFormatter": "esbenp.prettier-vscode" }, "conventionalCommits.scopes": [ - "vis-rawjsonview", + "json", "panels", - "vis-semsub", - "vis-nl", - "vis-paoh", - "vis-schema", + "semsub", + "nl", + "paoh", + "schema", "storybook", "store", - "libs" + "libs", ], "jest.jestCommandLine": "nx affected:test", diff --git a/README.md b/README.md index 236cf9add7ef289499bfc11b39d6e919a4745dda..ca4510171dcf47798262c1568dbd6ee2e993c729 100644 --- a/README.md +++ b/README.md @@ -7,20 +7,20 @@ Due to the way auth works (using a sameSite cookie), the procedure for running l ### MacOS / Linux 1. `sudo vim /etc/hosts` open the hosts file with your prefered text editor as root -2. Add a new row containing `127.0.0.1 local.datastrophe.science.uu.nl`, this will route traffic from `local.datastrophe.science.uu.nl` to `127.0.0.1` +2. Add a new row containing `127.0.0.1 local.graphpolaris.com`, this will route traffic from `local.graphpolaris.com` to `127.0.0.1` 3. `brew install mkcert` install mkcert utility 4. `mkcert -install` generate local CA (certificate authority) 5. Move into the /certs folder at the project root using `cd` -6. `mkcert --key-file local-key.pem --cert-file local-cert.pem local.datastrophe.science.uu.nl` create certificates for local SSL +6. `mkcert --key-file local-key.pem --cert-file local-cert.pem local.graphpolaris.com` create certificates for local SSL ### Windows 1. Open the `hosts` file under `C:\Windows\System32\drivers\etc` using a text editor, as administrator -2. Add a new row containing `127.0.0.1 local.datastrophe.science.uu.nl`, this will route traffic from `local.datastrophe.science.uu.nl` to `127.0.0.1` +2. Add a new row containing `127.0.0.1 local.graphpolaris.com`, this will route traffic from `local.graphpolaris.com` to `127.0.0.1` 3. Install mkcert using any of the ways described [here](https://github.com/FiloSottile/mkcert#windows) 4. Open an elevated Powershell or CMD session 5. Move into the /certs folder at the project root using `cd` -6. `mkcert --key-file local-key.pem --cert-file local-cert.pem local.datastrophe.science.uu.nl` create certificates for local SSL +6. `mkcert --key-file local-key.pem --cert-file local-cert.pem local.graphpolaris.com` create certificates for local SSL > No idea if the Windows steps work diff --git a/apps/web-graphpolaris/.storybook/.babelrc b/apps/web-graphpolaris/.storybook/.babelrc new file mode 100644 index 0000000000000000000000000000000000000000..0f97847325a64a312ae586f7c5f43dea3fb17956 --- /dev/null +++ b/apps/web-graphpolaris/.storybook/.babelrc @@ -0,0 +1 @@ +{ "extends": "../.babelrc" } diff --git a/apps/web-graphpolaris/.storybook/main.js b/apps/web-graphpolaris/.storybook/main.js index 530fe3a06e224f618059baa7b1a5192c812459be..02ada918184b76e65ab9ac4b225326342c6db56c 100644 --- a/apps/web-graphpolaris/.storybook/main.js +++ b/apps/web-graphpolaris/.storybook/main.js @@ -28,4 +28,8 @@ module.exports = { return config; }, + babel: async (options) => ({ + ...options, + // any extra options you want to set + }), }; diff --git a/apps/web-graphpolaris/.storybook/tsconfig.json b/apps/web-graphpolaris/.storybook/tsconfig.json index 7a1170995999ff179ae31ea7b87133b1c2161bc2..5a96b7a33c312784ae2c9381a0cdbfc439e83250 100644 --- a/apps/web-graphpolaris/.storybook/tsconfig.json +++ b/apps/web-graphpolaris/.storybook/tsconfig.json @@ -1,7 +1,7 @@ { "extends": "../tsconfig.json", "compilerOptions": { - "emitDecoratorMetadata": true, + "emitDecoratorMetadata": false, "outDir": "" }, "files": [ @@ -14,6 +14,5 @@ "../**/*.spec.js", "../**/*.spec.tsx", "../**/*.spec.jsx" - ], - "include": ["../src/**/*", "*.js"] + ] } diff --git a/apps/web-graphpolaris/project.json b/apps/web-graphpolaris/project.json index dc9c691acc30a77b498ee4ee9c7abf79928cff15..7cc05ad711a6509e0b2b6c346c1149ca1d7e3d2f 100644 --- a/apps/web-graphpolaris/project.json +++ b/apps/web-graphpolaris/project.json @@ -7,7 +7,7 @@ "executor": "@nrwl/web:dev-server", "options": { "buildTarget": "web-graphpolaris:build", - "host": "local.datastrophe.science.uu.nl", + "host": "local.graphpolaris.com", "port": 4200, "watch": true, "hmr": true, diff --git a/apps/web-graphpolaris/src/assets/icons/ExportIcon.png b/apps/web-graphpolaris/src/assets/icons/ExportIcon.png new file mode 100644 index 0000000000000000000000000000000000000000..62976b8a7902bc100cad532959e3d9e0d5fea42c Binary files /dev/null and b/apps/web-graphpolaris/src/assets/icons/ExportIcon.png differ diff --git a/apps/web-graphpolaris/src/components/login/loginScreen.tsx b/apps/web-graphpolaris/src/components/login/loginScreen.tsx index 93339edaadd13653822233ecf9aac3468f9f4435..fc1cf4a89f19194a218396348dbcc5cf4dc1e608 100644 --- a/apps/web-graphpolaris/src/components/login/loginScreen.tsx +++ b/apps/web-graphpolaris/src/components/login/loginScreen.tsx @@ -102,7 +102,7 @@ const LoginScreen = () => { <img onClick={() => openSignInWindow( - 'https://datastrophe.science.uu.nl/user/sign-in?provider=1' + 'https://api.graphpolaris.com/user/sign-in?provider=1' ) } src="assets/login-screen/google.png" @@ -111,7 +111,7 @@ const LoginScreen = () => { <img onClick={() => openSignInWindow( - 'https://datastrophe.science.uu.nl/user/sign-in?provider=2' + 'https://api.graphpolaris.com/user/sign-in?provider=2' ) } src="assets/login-screen/github.png" @@ -119,9 +119,7 @@ const LoginScreen = () => { /> <p onClick={() => - openSignInWindow( - 'https://datastrophe.science.uu.nl/user/create_free/' - ) + openSignInWindow('https://api.graphpolaris.com/user/create_free/') } > Developer diff --git a/apps/web-graphpolaris/src/components/querybuilder/querybuilder.stories.tsx b/apps/web-graphpolaris/src/components/querybuilder/querybuilder.stories.tsx index b406d60b5879ace0bfe90fa43b1a151fd83198f8..2de5deb8795590c12bdd020c65687f5d72e3e4b4 100644 --- a/apps/web-graphpolaris/src/components/querybuilder/querybuilder.stories.tsx +++ b/apps/web-graphpolaris/src/components/querybuilder/querybuilder.stories.tsx @@ -14,6 +14,7 @@ import { addPill, handles } from '@graphpolaris/querybuilder/usecases'; export default { component: QueryBuilder, + title: 'Panels/QueryBuilder', } as ComponentMeta<typeof QueryBuilder>; const Template: ComponentStory<typeof QueryBuilder> = (args) => ( diff --git a/apps/web-graphpolaris/src/components/querybuilder/querybuilder.tsx b/apps/web-graphpolaris/src/components/querybuilder/querybuilder.tsx index 3870f76de645a2d47757c5e0a40e2c8bea89503b..063330aa016123b37498bf5d1f85690ccb247391 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.module.scss b/apps/web-graphpolaris/src/components/schema/schema.module.scss new file mode 100644 index 0000000000000000000000000000000000000000..192a2fd7ace9b001b68c1e12af2bb2c048418546 --- /dev/null +++ b/apps/web-graphpolaris/src/components/schema/schema.module.scss @@ -0,0 +1,227 @@ +/** + * This program has been developed by students from the bachelor Computer Science at + * Utrecht University within the Software Project course. + * © Copyright Utrecht University (Department of Information and Computing Sciences) + */ + +/* istanbul ignore file */ +/* The comment above was added so the code coverage wouldn't count this file towards code coverage. + * We do not test components/renderfunctions/styling files. + * See testing plan for more details.*/ +// import { makeStyles, useTheme, Theme, createStyles } from '@material-ui/core/styles'; +// import { BorderTopOutlined, PowerInputSharp } from '@material-ui/icons'; +// import { ClassNameMap } from '@material-ui/styles'; +// import { SchemaThemeHolder } from '../../../domain/entity/css-themes/themeHolder'; + +.schemaPanel { + display: 'flex'; + flex-direction: 'column'; + justify-content: 'center'; + // '& div': { + // '& svg' { + // zindex: 2; + // } + // } +} + +.controls { + left: 'auto !important'; + bottom: 'auto !important'; + top: '10px'; + right: '20px'; + width: 'auto !important'; +} + +// export const useStyles = (theme: Theme) => +// createStyles({ +// schemaPanel: { +// display: 'flex', +// flexDirection: 'column', +// justifyContent: 'center', +// '& div': { +// '& svg': { +// zIndex: 2, +// }, +// }, +// }, + +// /** +// * When changing the height of the nodes, or the height of the buttons on the nodes, note that there is a hardcoded value 'height' +// * in schemaViewModelImpl (in the method 'setRelationNodePosition') that is based on these heights. +// */ +// entityNode: { +// background: SchemaThemeHolder.entity.baseColor, +// display: 'flex', +// borderRadius: '1px', +// fontFamily: SchemaThemeHolder.fontFamily, +// fontWeight: 'bold', +// fontSize: `${SchemaThemeHolder.fontSize}px`, +// width: `${SchemaThemeHolder.entity.width}px`, +// lineHeight: `${SchemaThemeHolder.entity.height}px`, +// }, + +// nodeWrapper: { +// display: 'inherit', +// color: SchemaThemeHolder.entity.textColor, +// textAlign: 'center', +// justifyContent: 'space-between', +// alignItems: 'center', +// width: 'inherit', +// }, + +// nodeSpan: { +// margin: '0 0.5em 0 1em', +// float: 'right', +// }, + +// nodeData: { +// flexGrow: 2, +// }, + +// entityNodeAttributesBox: { +// height: 20, +// position: 'absolute', +// left: '115px', +// top: '-33%', +// transform: 'translateX(50%)', +// borderRadius: '1px', +// boxShadow: '-1px 2px 5px #888888', +// textAlign: 'right', +// }, + +// entityNodeNodesBox: { +// height: 20, +// position: 'absolute', +// left: '115px', +// top: '54%', +// transform: 'translateX(50%)', +// borderRadius: '1px', +// boxShadow: '-1px 2px 5px #888888', +// textAlign: 'right', +// }, + +// /** +// * Code for the shape of the relationNodes +// */ +// relationNode: { +// height: 36, +// display: 'flex', +// fontFamily: SchemaThemeHolder.fontFamily, +// fontWeight: 'bold', +// fontSize: `${SchemaThemeHolder.fontSize}px`, +// width: `${SchemaThemeHolder.relation.width}px`, +// lineHeight: `${SchemaThemeHolder.relation.height}px`, +// }, + +// relationNodeTriangleGeneral: { +// position: 'absolute', +// width: 0, +// height: 0, +// margin: 'auto 0', +// borderStyle: 'solid', +// borderColor: 'transparent', +// }, + +// relationNodeTriangleLeft: { +// transform: 'translateX(-100%)', +// top: '0px', +// borderWidth: '18px 24px 18px 0', +// }, + +// relationNodeSmallTriangleLeft: { +// transform: 'translateX(-100%)', +// top: '30px', +// borderWidth: '0 8px 6px 0', +// }, + +// relationNodeTriangleRight: { +// transform: `translateX(${SchemaThemeHolder.relation.width}px)`, +// top: '0px', +// borderWidth: '18px 0 18px 24px', +// }, + +// relationNodeSmallTriangleRight: { +// transform: `translateX(${SchemaThemeHolder.relation.width}px)`, +// top: '30px', +// borderWidth: '0 0 6px 8px', +// }, + +// relationNodeAttributesBox: { +// position: 'absolute', +// top: '-4px', +// transform: `translateX(${SchemaThemeHolder.relation.width + 4}px)`, +// clipPath: 'polygon(0 0, 100% 0, 100% 100%, 26.5px 100%)', +// height: 20, +// textAlign: 'right', +// }, + +// relationNodeNodesBox: { +// position: 'absolute', +// top: '20px', +// transform: `translateX(${SchemaThemeHolder.relation.width + 4}px)`, +// clipPath: 'polygon(26.5px 0, 100% 0, 100% 100%, 0 100%)', +// height: 20, +// textAlign: 'right', +// }, + +// arrowup: { +// width: 0, +// height: 0, +// position: 'absolute', +// left: '50%', +// top: '-20%', +// transform: 'translateX(-50%)', +// margin: '0 auto', +// backgroundColor: 'transparent', +// borderStyle: 'solid', +// borderTopWidth: 0, +// borderRightWidth: 8, +// borderBottomWidth: 5, +// borderLeftWidth: 8, +// borderTopColor: 'transparent', +// borderRightColor: 'transparent', +// borderLeftColor: 'transparent', +// }, + +// arrowdown: { +// width: 0, +// height: 0, +// position: 'absolute', +// left: '50%', +// bottom: '-20%', +// transform: 'translateX(-50%)', +// margin: '0 auto', +// backgroundColor: 'transparent', +// borderStyle: 'solid', +// borderTopWidth: 5, +// borderRightWidth: 8, +// borderBottomWidth: 0, +// borderLeftWidth: 8, +// borderRightColor: 'transparent', +// borderBottomColor: 'transparent', +// borderLeftColor: 'transparent', +// }, + +// controls: { +// left: 'auto !important', +// bottom: 'auto !important', +// top: '10px', +// right: '20px', +// width: 'auto !important', +// }, + +// exportButton: { +// left: 'auto !important', +// bottom: 'auto !important', +// top: '10px', +// right: '20px', +// '& svg': { +// transform: 'scale(1.4)', +// }, +// }, + +// menuText: { +// fontSize: 'small', +// fontFamily: 'Poppins, sans-serif', +// }, +// }); diff --git a/apps/web-graphpolaris/src/components/schema/schema.spec.ts b/apps/web-graphpolaris/src/components/schema/schema.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..d975c32fbdce50259e67bdb1161cfdc6992f8390 --- /dev/null +++ b/apps/web-graphpolaris/src/components/schema/schema.spec.ts @@ -0,0 +1,330 @@ +import { SchemaUtils } from '@graphpolaris/schema-utils'; +import { SchemaFromBackend } from '@graphpolaris/models'; + + +const twitterSchemaRaw: SchemaFromBackend = { + nodes: [ + { + name: 'Me', + attributes: [ + { + name: 'screen_name', + type: 'string', + }, + { + name: 'name', + type: 'string', + }, + { + name: 'location', + type: 'string', + }, + { + name: 'followers', + type: 'int', + }, + { + name: 'following', + type: 'int', + }, + { + name: 'url', + type: 'string', + }, + { + name: 'profile_image_url', + type: 'string', + }, + ], + }, + { + name: 'Link', + attributes: [ + { + name: 'url', + type: 'string', + }, + ], + }, + { + name: 'Source', + attributes: [ + { + name: 'name', + type: 'string', + }, + ], + }, + { + name: 'Hashtag', + attributes: [ + { + name: 'name', + type: 'string', + }, + ], + }, + { + name: 'User', + attributes: [ + { + name: 'screen_name', + type: 'string', + }, + { + name: 'name', + type: 'string', + }, + { + name: 'location', + type: 'string', + }, + { + name: 'followers', + type: 'int', + }, + { + name: 'following', + type: 'int', + }, + { + name: 'url', + type: 'string', + }, + { + name: 'profile_image_url', + type: 'string', + }, + { + name: 'screen_name', + type: 'string', + }, + { + name: 'name', + type: 'string', + }, + { + name: 'location', + type: 'string', + }, + { + name: 'followers', + type: 'int', + }, + { + name: 'following', + type: 'int', + }, + { + name: 'statuses', + type: 'int', + }, + { + name: 'url', + type: 'string', + }, + { + name: 'profile_image_url', + type: 'string', + }, + ], + }, + { + name: 'Tweet', + attributes: [ + { + name: 'id', + type: 'int', + }, + { + name: 'id_str', + type: 'string', + }, + { + name: 'text', + type: 'string', + }, + { + name: 'favorites', + type: 'int', + }, + { + name: 'import_method', + type: 'string', + }, + ], + }, + ], + edges: [ + { + name: 'USING', + collection: 'USING', + from: 'Tweet', + to: 'Source', + attributes: [], + }, + { + name: 'SIMILAR_TO', + collection: 'SIMILAR_TO', + from: 'User', + to: 'User', + attributes: [ + { + name: 'score', + type: 'float', + }, + ], + }, + { + name: 'SIMILAR_TO', + collection: 'SIMILAR_TO', + from: 'User', + to: 'Me', + attributes: [ + { + name: 'score', + type: 'float', + }, + ], + }, + { + name: 'AMPLIFIES', + collection: 'AMPLIFIES', + from: 'Me', + to: 'User', + attributes: [], + }, + { + name: 'AMPLIFIES', + collection: 'AMPLIFIES', + from: 'User', + to: 'User', + attributes: [], + }, + { + name: 'RT_MENTIONS', + collection: 'RT_MENTIONS', + from: 'Me', + to: 'User', + attributes: [], + }, + { + name: 'RT_MENTIONS', + collection: 'RT_MENTIONS', + from: 'User', + to: 'User', + attributes: [], + }, + { + name: 'FOLLOWS', + collection: 'FOLLOWS', + from: 'User', + to: 'Me', + attributes: [], + }, + { + name: 'FOLLOWS', + collection: 'FOLLOWS', + from: 'Me', + to: 'User', + attributes: [], + }, + { + name: 'FOLLOWS', + collection: 'FOLLOWS', + from: 'User', + to: 'User', + attributes: [], + }, + { + name: 'FOLLOWS', + collection: 'FOLLOWS', + from: 'Me', + to: 'Me', + attributes: [], + }, + { + name: 'INTERACTS_WITH', + collection: 'INTERACTS_WITH', + from: 'User', + to: 'User', + attributes: [], + }, + { + name: 'INTERACTS_WITH', + collection: 'INTERACTS_WITH', + from: 'Me', + to: 'User', + attributes: [], + }, + { + name: 'RETWEETS', + collection: 'RETWEETS', + from: 'Tweet', + to: 'Tweet', + attributes: [], + }, + { + name: 'REPLY_TO', + collection: 'REPLY_TO', + from: 'Tweet', + to: 'Tweet', + attributes: [], + }, + { + name: 'CONTAINS', + collection: 'CONTAINS', + from: 'Tweet', + to: 'Link', + attributes: [], + }, + { + name: 'MENTIONS', + collection: 'MENTIONS', + from: 'Tweet', + to: 'User', + attributes: [], + }, + { + name: 'MENTIONS', + collection: 'MENTIONS', + from: 'Tweet', + to: 'Me', + attributes: [], + }, + { + name: 'TAGS', + collection: 'TAGS', + from: 'Tweet', + to: 'Hashtag', + attributes: [], + }, + { + name: 'POSTS', + collection: 'POSTS', + from: 'User', + to: 'Tweet', + attributes: [], + }, + { + name: 'POSTS', + collection: 'POSTS', + from: 'Me', + to: 'Tweet', + attributes: [], + }, + ], +}; + +const twitterSchema = + SchemaUtils.ParseSchemaFromBackend(twitterSchemaRaw); + + + + describe('import tests', () => { + it('should resolve all imports appropriately for _external_ libs @graphpolaris/schema-utils and @graphpolaris/model ', () => { + const graph = twitterSchema; + expect(graph); + }); + + }); \ No newline at end of file diff --git a/apps/web-graphpolaris/src/components/schema/schema.stories.tsx b/apps/web-graphpolaris/src/components/schema/schema.stories.tsx index 873afdf6b36983cd9d6a493235df20b3ed654452..01e070c34b6fc7f427ef2c558e738bab10d9d1b5 100644 --- a/apps/web-graphpolaris/src/components/schema/schema.stories.tsx +++ b/apps/web-graphpolaris/src/components/schema/schema.stories.tsx @@ -11,12 +11,24 @@ import React from 'react'; import { Provider } from 'react-redux'; import Schema from './schema'; +import { + movieSchemaRaw, + northwindSchemaRaw, + simpleSchemaRaw, + twitterSchemaRaw, + movieSchema, + northWindSchema, + simpleSchema, + twitterSchema, +} from '@graphpolaris/shared/mock-data'; + export default { /* 👇 The title prop is optional. * See https://storybook.js.org/docs/react/configure/overview#configure-story-loading * to learn how to generate automatic titles */ - title: 'Schema', + title: 'Panels/Schema', + component: Schema, decorators: [ (story) => ( @@ -146,5 +158,44 @@ TestWithSchema.play = async () => { console.info('dispatch dummy schema', schema.order); dispatch(setSchema(schema.export())); - // handleSchemaLayout(schema); +}; + +export const TestWithMockNorthWindSchema = Template.bind({}); + +TestWithMockNorthWindSchema.play = async () => { + const dispatch = Mockstore.dispatch; + + const schema = northWindSchema; + console.info('dispatch northWindSchema schema', schema.order); + dispatch(setSchema(schema.export())); +}; + +export const TestWithMockTwitterSchema = Template.bind({}); + +TestWithMockTwitterSchema.play = async () => { + const dispatch = Mockstore.dispatch; + + const schema = twitterSchema; + console.info('dispatch TestWithMockTwitterSchema schema', schema.order); + dispatch(setSchema(schema.export())); +}; + +export const TestWithMockSimpleSchema = Template.bind({}); + +TestWithMockSimpleSchema.play = async () => { + const dispatch = Mockstore.dispatch; + + const schema = simpleSchema; + console.info('dispatch simpleSchema schema', schema.order); + dispatch(setSchema(schema.export())); +}; + +export const TestWithMockMovieSchema = Template.bind({}); + +TestWithMockMovieSchema.play = async () => { + const dispatch = Mockstore.dispatch; + + const schema = movieSchema; + console.info('dispatch movieSchema schema', schema.order); + dispatch(setSchema(schema.export())); }; diff --git a/apps/web-graphpolaris/src/components/schema/schema.tsx b/apps/web-graphpolaris/src/components/schema/schema.tsx index 4167487adf58fa5068e4a8698a1490bf26ad8421..dfe7a159b4b15dbe838d81aadb3e55992ea585f7 100644 --- a/apps/web-graphpolaris/src/components/schema/schema.tsx +++ b/apps/web-graphpolaris/src/components/schema/schema.tsx @@ -1,20 +1,50 @@ import { AllLayoutAlgorithms, LayoutFactory } from '@graphpolaris/graph-layout'; -import { createReactFlowElements } from '@graphpolaris/schema/usecases'; +import { + createReactFlowElements, + expandSchema, +} from '@graphpolaris/schema/usecases'; import { useSchema, useSchemaLayout, } from '@graphpolaris/shared/data-access/store'; import { MultiGraph } from 'graphology'; // import { AllLayoutAlgorithms, LayoutFactory } from '@graphpolaris/graph-layout'; -import { useEffect, useState } from 'react'; -import ReactFlow, { FlowElement, ReactFlowProvider } from 'react-flow-renderer'; +import { useEffect, useMemo, useState } from 'react'; +import ReactFlow, { + ControlButton, + Controls, + FlowElement, + ReactFlowProvider, +} 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; } const onLoad = (reactFlowInstance: any) => { - setTimeout(() => reactFlowInstance.fitView(), 0); + setTimeout(() => reactFlowInstance.fitView(), 100); +}; + +const nodeTypes = { + entity: EntityRFPill, + relation: RelationRFPill, + attribute: AttributeRFPill, +}; +const edgeTypes = { + connection: ConnectionLine, }; const Schema = (props: Props) => { @@ -24,27 +54,25 @@ const Schema = (props: Props) => { // const [dbschema, setSchema] = useState(useSchema()); const [schemaLayout, setSchemaLayout] = useState(useSchemaLayout()); - useEffect(() => { - console.log('dbSchema', dbschema, dbschema.order); - }, [dbschema]); + // useEffect(() => { + // console.log('dbSchema', dbschema, dbschema.order); + // }, [dbschema]); + + const expandedSchema = useMemo(() => expandSchema(dbschema), [dbschema]); useEffect(() => { if (dbschema == undefined || dbschema.order == 0) { return; } + const layoutFactory = new LayoutFactory(); - console.log('schema Layout', schemaLayout, 'order', dbschema.order); + console.log('schema Layout', schemaLayout, 'order', expandedSchema); const layout = layoutFactory.createLayout( schemaLayout as AllLayoutAlgorithms ); - layout?.layout(dbschema); - - // dbschema.forEachNode((node, attr) => { - // console.log('x', dbschema.getNodeAttribute(node, 'x')); - // console.log('y', dbschema.getNodeAttribute(node, 'y')); - // }); + layout?.layout(expandedSchema); - const flowElements = createReactFlowElements(dbschema); + const flowElements = createReactFlowElements(expandedSchema); setElements(flowElements); console.log( 'update schema useEffect', @@ -65,13 +93,38 @@ const Schema = (props: Props) => { > {elements.length == 0 && <p>DEBUG: No Elements</p>} <ReactFlowProvider> - <ReactFlow elements={elements} style={graphStyles} onLoad={onLoad} /> - {/* // onElementsRemove={onElementsRemove} - // onConnect={onConnect} - // - snapToGrid={true} - snapGrid={[15, 15]} - ></ReactFlow> */} + <ReactFlow + className={styles.schemaPanel} + onlyRenderVisibleElements={false} + nodesDraggable={false} + nodeTypes={nodeTypes} + edgeTypes={edgeTypes} + connectionLineComponent={ConnectionDragLine} + elements={elements} + style={graphStyles} + onLoad={onLoad} + > + <Controls + showInteractive={false} + showZoom={false} + showFitView={true} + className={styles.controls} + > + <ControlButton + className={styles.exportButton} + title={'Export graph schema'} + onClick={(event) => { + event.stopPropagation(); + // this.setState({ + // ...this.state, + // exportMenuAnchor: event.currentTarget, + // }); + }} + > + {/* <img src={exportIcon} width={21}></img> */} + </ControlButton> + </Controls> + </ReactFlow> </ReactFlowProvider> </div> ); diff --git a/apps/web-graphpolaris/src/components/visualisations/nodelink/nodelinkvis.stories.tsx b/apps/web-graphpolaris/src/components/visualisations/nodelink/nodelinkvis.stories.tsx index fb22505b674ca26e9acb79192fb100fa11c3c159..1d688b93798fbb0aeeb50d159547128c96204da1 100644 --- a/apps/web-graphpolaris/src/components/visualisations/nodelink/nodelinkvis.stories.tsx +++ b/apps/web-graphpolaris/src/components/visualisations/nodelink/nodelinkvis.stories.tsx @@ -7,7 +7,7 @@ export default { * See https://storybook.js.org/docs/react/configure/overview#configure-story-loading * to learn how to generate automatic titles */ - title: 'NodeLink', + title: 'Components/Visualizations/NodeLink', component: NodeLink, decorators: [(story) => <div style={{ padding: '3rem' }}>{story()}</div>], } as ComponentMeta<typeof NodeLink>; diff --git a/apps/web-graphpolaris/src/components/visualisations/paohvis/paohvis.stories.tsx b/apps/web-graphpolaris/src/components/visualisations/paohvis/paohvis.stories.tsx index 795a79aba58096b8ee5beb94af287ec7fadaab51..866d8b316ab2fd9711e5c95db2ea64e6466baab5 100644 --- a/apps/web-graphpolaris/src/components/visualisations/paohvis/paohvis.stories.tsx +++ b/apps/web-graphpolaris/src/components/visualisations/paohvis/paohvis.stories.tsx @@ -7,7 +7,7 @@ export default { * See https://storybook.js.org/docs/react/configure/overview#configure-story-loading * to learn how to generate automatic titles */ - title: 'PaohVis', + title: 'Components/Visualizations/PaohVis', component: PaohVis, decorators: [(story) => <div style={{ padding: '3rem' }}>{story()}</div>], } as ComponentMeta<typeof PaohVis>; diff --git a/apps/web-graphpolaris/src/components/visualisations/rawjsonvis/rawjsonvis.stories.tsx b/apps/web-graphpolaris/src/components/visualisations/rawjsonvis/rawjsonvis.stories.tsx index 3b10ca2d12d79022af4ed24afd69c60c3235f4e0..a5c7561eb920505ebf17db4217495016a707cf83 100644 --- a/apps/web-graphpolaris/src/components/visualisations/rawjsonvis/rawjsonvis.stories.tsx +++ b/apps/web-graphpolaris/src/components/visualisations/rawjsonvis/rawjsonvis.stories.tsx @@ -18,7 +18,7 @@ export default { * See https://storybook.js.org/docs/react/configure/overview#configure-story-loading * to learn how to generate automatic titles */ - title: 'RawJSONVIS', + title: 'Components/Visualizations/RawJSONVIS', component: RawJSONVis, decorators: [ (story) => ( diff --git a/apps/web-graphpolaris/src/components/visualisations/semanticsubstrates/semanticsubstrates.stories.tsx b/apps/web-graphpolaris/src/components/visualisations/semanticsubstrates/semanticsubstrates.stories.tsx index 944011b532a61cef8159459e8922b79f496dbb5c..8ffade3001311a731d9041c5a154d5bd78d1232c 100644 --- a/apps/web-graphpolaris/src/components/visualisations/semanticsubstrates/semanticsubstrates.stories.tsx +++ b/apps/web-graphpolaris/src/components/visualisations/semanticsubstrates/semanticsubstrates.stories.tsx @@ -14,7 +14,7 @@ export default { * See https://storybook.js.org/docs/react/configure/overview#configure-story-loading * to learn how to generate automatic titles */ - title: 'SemanticSubstrates', + title: 'Components/Visualizations/SemanticSubstrates', component: SemanticSubstrates, decorators: [ (story) => ( diff --git a/deployment.yml b/deployment.yml new file mode 100644 index 0000000000000000000000000000000000000000..fc945c18719db08baaf0e539fd9a16bcecb04609 --- /dev/null +++ b/deployment.yml @@ -0,0 +1,35 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: frontend-webserver-deployment + labels: + app: frontend-webserver +spec: + replicas: 1 + selector: + matchLabels: + app: frontend-webserver + template: + metadata: + labels: + app: frontend-webserver + spec: + containers: + - name: container + image: datastropheregistry.azurecr.io/frontend:latest + ports: + - containerPort: 3000 + imagePullSecrets: + - name: docker-regcred + +--- +kind: Service +apiVersion: v1 +metadata: + name: frontend-webserver +spec: + selector: + app: frontend-webserver + ports: + - port: 3000 + targetPort: 80 diff --git a/libs/schema/usecases/src/lib/schema-usecases.ts b/libs/schema/usecases/src/lib/schema-usecases.ts index 5efe7de805e90dcc79a9d4e468c75026b5d18899..d3151bfe9fbfc42e4e2cdc54e64575c5382b7ef9 100644 --- a/libs/schema/usecases/src/lib/schema-usecases.ts +++ b/libs/schema/usecases/src/lib/schema-usecases.ts @@ -1,164 +1,149 @@ import Graph from 'graphology'; import { Attributes } from 'graphology-types'; -import { Edge, Elements, Node } from 'react-flow-renderer'; - -// type CytoNode = { -// data: { -// id: string; -// type: string; -// source?: string; -// target?: string; -// position?: { -// x: number; -// y: number; -// }; -// }; -// }; - -// // Layouts a given schema -// export function handleSchemaLayout(graph: Graph): void { -// const layout = createSchemaLayout(graph); - -// layout.then((cy) => { -// //cy.cy.elements().forEach((elem) => { -// cy.cy.nodes().forEach((elem: any) => { -// const position = elem.position(); -// console.log(elem.id()); - -// graph.setNodeAttribute(elem.id(), 'x', position.x); -// graph.setNodeAttribute(elem.id(), 'y', position.y); -// }); - -// store.dispatch(setSchema(graph)); -// }); -// } - -// // Creates a schema layout (async) -// function createSchemaLayout(graph: Graph): Promise<cytoscape.EventObject> { -// const cytonodes: CytoNode[] = trimSchema(graph); - -// const cy = cytoscape({ -// elements: cytonodes, -// }); - -// const options = { -// name: 'cose', - -// // Whether to animate while running the layout -// // true : Animate continuously as the layout is running -// // false : Just show the end result -// // 'end' : Animate with the end result, from the initial positions to the end positions -// animate: true, - -// // Easing of the animation for animate:'end' -// animationEasing: undefined, - -// // The duration of the animation for animate:'end' -// animationDuration: undefined, - -// // A function that determines whether the node should be animated -// // All nodes animated by default on animate enabled -// // Non-animated nodes are positioned immediately when the layout starts -// // animateFilter: function (node: any, i: any) { -// // return true; -// // }, - -// // The layout animates only after this many milliseconds for animate:true -// // (prevents flashing on fast runs) -// animationThreshold: 250, - -// // Number of iterations between consecutive screen positions update -// refresh: 20, - -// // Whether to fit the network view after when done -// fit: true, - -// // Padding on fit -// padding: 30, - -// // Constrain layout bounds; { x1, y1, x2, y2 } or { x1, y1, w, h } -// boundingBox: undefined, - -// // Excludes the label when calculating node bounding boxes for the layout algorithm -// nodeDimensionsIncludeLabels: false, - -// // Randomize the initial positions of the nodes (true) or use existing positions (false) -// randomize: false, - -// // Extra spacing between components in non-compound graphs -// componentSpacing: 200, // 40 - -// // Node repulsion (non overlapping) multiplier -// nodeRepulsion: function (node: any) { -// return 2048; -// }, - -// // Node repulsion (overlapping) multiplier -// nodeOverlap: 4, - -// // Ideal edge (non nested) length -// idealEdgeLength: function (edge: any) { -// return 32; -// }, - -// // Divisor to compute edge forces -// edgeElasticity: function (edge: any) { -// return 32; -// }, - -// // Nesting factor (multiplier) to compute ideal edge length for nested edges -// nestingFactor: 1.2, +import { ArrowHeadType, Edge, Elements, Node } from 'react-flow-renderer'; -// // Gravity force (constant) -// gravity: 1, +//TODO does not belong here; maybe should go into the GraphPolarisThemeProvider +const ANIMATEDEDGES = false; -// // Maximum number of iterations to perform -// numIter: 1000, +export function expandSchema(graph: Graph): Graph { + const newGraph = graph.copy(); -// // Initial temperature (maximum node displacement) -// initialTemp: 1000, - -// // Cooling factor (how the temperature is reduced between consecutive iterations -// coolingFactor: 0.99, - -// // Lower temperature threshold (below this point the layout will end) -// minTemp: 1.0, -// }; - -// const layout = cy.layout(options); + newGraph.forEachNode((node, attributes) => { + // console.log(node, attributes); + newGraph.mergeNodeAttributes(node, { type: 'entity' }); + }); -// layout.run(); + //makeNewRelationNodes + graph.forEachEdge((edge, attributes, source, target): void => { + const newID = 'RelationNode:' + edge; + // 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; + newGraph.addDirectedEdgeWithKey(id, source, newID, { + name: edge, + }); + + const id2 = 'RelationEdge' + newID + '->' + target; + // console.log('making newEdgeOutgoing', edge, attributes, source, target, id); + newGraph.addDirectedEdgeWithKey(id2, newID, target, {}); + + newGraph.dropEdge(edge); + }); -// return layout.pon('layoutstop'); -// } + return newGraph; +} // Takes the schema as an imput and creates basic react flow elements for them. export function createReactFlowElements(graph: Graph): Elements<Node | Edge> { const initialElements: Elements<Node | Edge> = []; + initialElements.push(...createReactFlowNodes(graph)); + initialElements.push(...createReactFlowEdges(graph)); + // initialElements.push(...createReactFlowRelationNodes(graph)); + // initialElements.push(...createReactFlowRelationEdges(graph)); + // console.log(initialElements); + + return initialElements; +} + +export function createReactFlowNodes(graph: Graph): Elements<Node> { + const nodeElements: Elements<Node> = []; graph.forEachNode((node: string, attributes: Attributes): void => { const newNode: Node = { id: node, data: { label: attributes.name, + name: attributes.name, }, position: { x: attributes.x, y: attributes.y }, + type: attributes.type, }; - initialElements.push(newNode); + nodeElements.push(newNode); }); - graph.forEachEdge((edge, _attributes, source, target): void => { + return nodeElements; +} + +export function createReactFlowEdges(graph: Graph): Elements<Edge> { + const edgeElements: Elements<Edge> = []; + + graph.forEachEdge((edge, attributes, source, target): void => { + // console.log('edges', attributes, edge); const newEdge: Edge = { id: edge, source: source, target: target, + // label: edge, + type: 'smoothstep', + animated: ANIMATEDEDGES, + arrowHeadType: ArrowHeadType.ArrowClosed, }; - initialElements.push(newEdge); + edgeElements.push(newEdge); }); - return initialElements; + 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/data-access/api/src/lib/database.ts b/libs/shared/data-access/api/src/lib/database.ts index 8d6700de6f55dde670227d1cedf1080c44637e28..da706c42a71c1cedc8a777afd5870480b3ec17eb 100644 --- a/libs/shared/data-access/api/src/lib/database.ts +++ b/libs/shared/data-access/api/src/lib/database.ts @@ -14,7 +14,7 @@ export type AddDatabaseRequest = { export function AddDatabase(request: AddDatabaseRequest): Promise<void> { return new Promise((resolve, reject) => { - fetch('https://datastrophe.science.uu.nl/user/database', { + fetch('https://api.graphpolaris.com/user/database', { method: 'POST', credentials: 'same-origin', headers: new Headers({ @@ -34,7 +34,7 @@ export function AddDatabase(request: AddDatabaseRequest): Promise<void> { export function GetAllDatabases(): Promise<Array<string>> { return new Promise<Array<string>>((resolve, reject) => { - fetch('https://datastrophe.science.uu.nl/user/database', { + fetch('https://api.graphpolaris.com/user/database', { method: 'GET', credentials: 'same-origin', headers: new Headers({ @@ -57,7 +57,7 @@ export function GetAllDatabases(): Promise<Array<string>> { export function DeleteDatabase(name: string): Promise<void> { return new Promise((resolve, reject) => { - fetch('https://datastrophe.science.uu.nl/user/database/' + name, { + fetch('https://api.graphpolaris.com/user/database/' + name, { method: 'DELETE', credentials: 'same-origin', headers: new Headers({ diff --git a/libs/shared/data-access/api/src/lib/user.ts b/libs/shared/data-access/api/src/lib/user.ts index 457ce20f6af6c8be31727f2b9757134827b4fc70..b3d7b3fc9e6b5411369a4a5b11e3ed8e7e009065 100644 --- a/libs/shared/data-access/api/src/lib/user.ts +++ b/libs/shared/data-access/api/src/lib/user.ts @@ -10,7 +10,7 @@ export type User = { export function GetUserInfo(): Promise<User> { return new Promise<User>((resolve, reject) => { - fetch('https://datastrophe.science.uu.nl/user/', { + fetch('https://api.graphpolaris.com/user/', { method: 'GET', credentials: 'same-origin', headers: new Headers({ diff --git a/libs/shared/data-access/authorization/src/lib/authorizationHandler.ts b/libs/shared/data-access/authorization/src/lib/authorizationHandler.ts index fc92922419ad609e85767e55dd35b06b2a581012..bc6001fd2aed8d24d60abf15411167af64629e81 100644 --- a/libs/shared/data-access/authorization/src/lib/authorizationHandler.ts +++ b/libs/shared/data-access/authorization/src/lib/authorizationHandler.ts @@ -1,5 +1,3 @@ -import { Cookies } from 'react-cookie'; - export class AuthorizationHandler { private static _instance: AuthorizationHandler; private accessToken = ''; @@ -52,7 +50,7 @@ export class AuthorizationHandler { */ private async getNewAccessToken(): Promise<authResponse> { // If we have an access token already, append it to the url as a query param to keep sessionID the same - let url = 'https://datastrophe.science.uu.nl/auth/refresh'; + let url = 'https://api.graphpolaris.com/auth/refresh'; if (this.accessToken != '') { url += '?access_token=' + this.accessToken; } @@ -104,7 +102,7 @@ export class AuthorizationHandler { * initialiseRefreshToken attempts to initialise a refresh token */ private async initialiseRefreshToken() { - fetch('https://datastrophe.science.uu.nl/auth/refresh', { + fetch('https://api.graphpolaris.com/auth/refresh', { method: 'POST', credentials: 'include', }) diff --git a/libs/shared/data-access/store/src/lib/schemaSlice.ts b/libs/shared/data-access/store/src/lib/schemaSlice.ts index 9ff253e0a0611c3f3ff91edef3f19a6d015aaf73..c7d1febd677be2187fb20b67074f1839e87c9770 100644 --- a/libs/shared/data-access/store/src/lib/schemaSlice.ts +++ b/libs/shared/data-access/store/src/lib/schemaSlice.ts @@ -1,15 +1,17 @@ import { createSlice, PayloadAction } from '@reduxjs/toolkit'; import Graph, { MultiGraph } from 'graphology'; import { SerializedGraph } from 'graphology-types'; -import { SchemaFromBackend } from 'libs/shared/models/src'; +import { SchemaFromBackend } from '@graphpolaris/models'; import type { RootState } from './store'; +import { AllLayoutAlgorithms } from '@graphpolaris/graph-layout'; /**************************************************************** */ // Define the initial state using that type export const initialState = { graphologySerialized: new MultiGraph().export(), - layoutName: 'Cytoscape_fcose', + // layoutName: 'Cytoscape_fcose', + layoutName: 'Cytoscape_cise' as AllLayoutAlgorithms, }; export const schemaSlice = createSlice({ @@ -24,8 +26,7 @@ export const schemaSlice = createSlice({ setSchemaLayout: (state, action: PayloadAction<string>) => { console.log('setSchemaLayout', action); - - state.layoutName = action.payload; + state.layoutName = action.payload as AllLayoutAlgorithms; }, readInSchemaFromBackend: ( diff --git a/libs/shared/data-access/theme/src/lib/graphPolarisThemeProvider.spec.tsx b/libs/shared/data-access/theme/src/lib/graphPolarisThemeProvider.spec.tsx new file mode 100644 index 0000000000000000000000000000000000000000..66c2e4c384d4a5079ab54cda0772757e078a73a4 --- /dev/null +++ b/libs/shared/data-access/theme/src/lib/graphPolarisThemeProvider.spec.tsx @@ -0,0 +1,36 @@ +// import Button from '@mui/material/Button'; +import React from 'react'; +import ReactDOM from 'react-dom'; +import { MockTheme, MockThemeGraphPolaris } from './mockTheme'; + +describe('<MockTheme GraphPolarisThemeProvider>', () => { + // TODO: This test should be implemented and running, but I get import issues in the mono-repo with jest (MIB) + // https://emotion.sh/docs/@emotion/jest + + + it('renders without crashing', () => { + }); + // it('passes smoke test no config of mocktheme', () => { + // const div = document.createElement('div'); + + // ReactDOM.render( + // <MockTheme> + // <Button variant="text">Text</Button> + // </MockTheme>, + // div + // ); + // }); + + + + // it('passes smoke test config GraphPolaris', () => { + // const div = document.createElement('div'); + + // ReactDOM.render( + // <MockThemeGraphPolaris> + // <Button variant="text">Text</Button> + // </MockThemeGraphPolaris>, + // div + // ); + // }); +}); diff --git a/libs/shared/data-access/theme/src/lib/mockTheme.tsx b/libs/shared/data-access/theme/src/lib/mockTheme.tsx new file mode 100644 index 0000000000000000000000000000000000000000..fccc611d7f80317eab5d898853171fb4e2404459 --- /dev/null +++ b/libs/shared/data-access/theme/src/lib/mockTheme.tsx @@ -0,0 +1,25 @@ +import React from 'react'; + +import { createTheme, ThemeProvider } from '@mui/material/styles'; + +export function MockTheme({ children }: any) { + const theme = React.useMemo( + () => + // Map our color palette config (stored in redux) to the mui 5 format + createTheme({}), + [] + ); + + return <ThemeProvider theme={theme}>{children}</ThemeProvider>; +} + +export function MockThemeGraphPolaris({ children }: any) { + const theme = React.useMemo( + () => + // Map our color palette config (stored in redux) to the mui 5 format + createTheme({}), + [] + ); + + return <ThemeProvider theme={theme}>{children}</ThemeProvider>; +} diff --git a/libs/shared/graph-layout/jest.config.js b/libs/shared/graph-layout/jest.config.js index ae3d05af50147b0e99ce13f1af409e0ca0752c64..4b12ff63c43b71caa0404e62ff739ba710d00f6e 100644 --- a/libs/shared/graph-layout/jest.config.js +++ b/libs/shared/graph-layout/jest.config.js @@ -6,4 +6,5 @@ module.exports = { }, moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx'], coverageDirectory: '../../../coverage/libs/shared/graph-layout', + setupFiles: ['jest-canvas-mock'], }; diff --git a/libs/shared/graph-layout/package.json b/libs/shared/graph-layout/package.json index 52928d5e97f1f340d2b653e45eedea657b842575..608983d8170b59d3d1ce3b3b99474e8c6f122259 100644 --- a/libs/shared/graph-layout/package.json +++ b/libs/shared/graph-layout/package.json @@ -17,6 +17,8 @@ "web-worker": "^1.2.0" }, "devDependencies": { - "graphology-generators": "^0.11.2" + "canvas": "^2.9.1", + "graphology-generators": "^0.11.2", + "jest-canvas-mock": "^2.4.0" } } diff --git a/libs/shared/graph-layout/src/lib/cytoscape-layouts.ts b/libs/shared/graph-layout/src/lib/cytoscape-layouts.ts index b5190896b5916d8652368bff7f39477f62b17a47..dfca253dc04244dde98885839c6f84312a68e046 100644 --- a/libs/shared/graph-layout/src/lib/cytoscape-layouts.ts +++ b/libs/shared/graph-layout/src/lib/cytoscape-layouts.ts @@ -99,7 +99,7 @@ export abstract class Cytoscape extends Layout<CytoscapeProvider> { cytonodes.push({ data: { id: node, - // type: 'node', + type: 'node', label: 'start', count: 50, color: 'green', @@ -111,7 +111,7 @@ export abstract class Cytoscape extends Layout<CytoscapeProvider> { cytonodes.push({ data: { id: edge, - // type: 'edge', + type: 'edge', source: source, target: target, }, @@ -208,12 +208,12 @@ export abstract class Cytoscape extends Layout<CytoscapeProvider> { }; }); - console.log( - 'width', - node.numericStyle('width'), - 'height', - node.numericStyle('height') - ); + // console.log( + // 'width', + // node.numericStyle('width'), + // 'height', + // node.numericStyle('height') + // ); }); } } @@ -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(); @@ -432,10 +432,10 @@ class CytoscapeElk extends Cytoscape { separateConnectedComponents: false, }, ready: function () { - console.log('Layout.ready'); + // console.log('Layout.start'); }, // on layoutready stop: function () { - console.log('Layout.stop'); + // console.log('Layout.stop'); }, // on layoutstop } as any); layout.run(); @@ -464,10 +464,10 @@ class CytoscapeDagre extends Cytoscape { const layout = cy.layout({ name: 'dagre', ready: function () { - console.log('Layout.ready'); + // console.log('Layout.start'); }, // on layoutready stop: function () { - console.log('Layout.stop'); + // // console.log('Layout.stop'); }, // on layoutstop } as any); layout.run(); @@ -497,10 +497,10 @@ class CytoscapeFCose extends Cytoscape { name: 'fcose', animate: false, ready: function () { - console.log('Layout.ready'); + // // console.log('Layout.start'); }, // on layoutready stop: function () { - console.log('Layout.stop'); + // // console.log('Layout.stop'); }, // on layoutstop } as any); layout.run(); @@ -531,10 +531,10 @@ class CytoscapeCoseBilkent extends Cytoscape { name: 'cose-bilkent', animate: false, ready: function () { - console.log('Layout.ready'); + // console.log('Layout.start'); }, // on layoutready stop: function () { - console.log('Layout.stop'); + // console.log('Layout.stop'); }, // on layoutstop } as any); layout.run(); @@ -563,10 +563,10 @@ class CytoscapeCise extends Cytoscape { const layout = cy.layout({ name: 'cise', ready: function () { - console.log('Layout.ready'); + // console.log('Layout.start'); }, // on layoutready stop: function () { - console.log('Layout.stop'); + // console.log('Layout.stop'); }, // on layoutstop } as any); layout.run(); diff --git a/libs/shared/graph-layout/src/lib/layout-creator-usecase.spec.ts b/libs/shared/graph-layout/src/lib/layout-creator-usecase.spec.ts index 2e86bb3556715997545768ad8d2822a5a0f9e45d..d41dc0b8b79e126989b8882efb49bcd99a855e0b 100644 --- a/libs/shared/graph-layout/src/lib/layout-creator-usecase.spec.ts +++ b/libs/shared/graph-layout/src/lib/layout-creator-usecase.spec.ts @@ -37,15 +37,7 @@ describe('LayoutFactory Graphology Libries', () => { const layoutAlgorithm = layoutFactory.createLayout('Graphology_noverlap'); layoutAlgorithm?.layout(graph); - // const positionMap = new Set<string>(); - graph.forEachNode((node, attr) => { - expect(graph.getNodeAttribute(node, 'x')).toBeDefined(); - expect(graph.getNodeAttribute(node, 'y')).toBeDefined(); - - // const pos = '' + attr['x'] + '' + attr['y']; - // expect(positionMap.has(pos)).toBeFalsy(); - // positionMap.add(pos); - }); + checkNodeAttributesDefined(graph); }, TIMEOUT ); @@ -66,13 +58,16 @@ describe('LayoutFactory Graphology Libries', () => { const layout = layoutFactory.createLayout('Graphology_noverlap'); layout?.layout(graph); - const positionMap = new Set<string>(); - graph.forEachNode((node, attr) => { - const pos = '' + attr['x'] + '' + attr['y']; + checkNodeAttributesDefined(graph); + checkPositionOverlap(graph); - expect(positionMap.has(pos)).toBeFalsy(); - positionMap.add(pos); - }); + // const positionMap = new Set<string>(); + // graph.forEachNode((node, attr) => { + // const pos = '' + attr['x'] + '' + attr['y']; + + // expect(positionMap.has(pos)).toBeFalsy(); + // positionMap.add(pos); + // }); }, TIMEOUT ); @@ -94,13 +89,16 @@ describe('LayoutFactory Graphology Libries', () => { layout?.setDimensions(100, 100); layout?.layout(graph); - const positionMap = new Set<string>(); - graph.forEachNode((node, attr) => { - const pos = '' + attr['x'] + '' + attr['y']; + checkNodeAttributesDefined(graph); + checkPositionOverlap(graph); - expect(positionMap.has(pos)).toBeFalsy(); - positionMap.add(pos); - }); + // const positionMap = new Set<string>(); + // graph.forEachNode((node, attr) => { + // const pos = '' + attr['x'] + '' + attr['y']; + + // expect(positionMap.has(pos)).toBeFalsy(); + // positionMap.add(pos); + // }); }, TIMEOUT ); @@ -122,13 +120,16 @@ describe('LayoutFactory Graphology Libries', () => { layout?.setDimensions(100, 100); layout?.layout(graph); - const positionMap = new Set<string>(); - graph.forEachNode((node, attr) => { - const pos = '' + attr['x'] + '' + attr['y']; + checkNodeAttributesDefined(graph); + checkPositionOverlap(graph); - expect(positionMap.has(pos)).toBeFalsy(); - positionMap.add(pos); - }); + // const positionMap = new Set<string>(); + // graph.forEachNode((node, attr) => { + // const pos = '' + attr['x'] + '' + attr['y']; + + // expect(positionMap.has(pos)).toBeFalsy(); + // positionMap.add(pos); + // }); }, TIMEOUT ); @@ -153,20 +154,17 @@ describe('LayoutFactory Graphology Libries', () => { // console.log('after'); - const positionMap = new Set<string>(); - graph.forEachNode((node, attr) => { - expect(graph.getNodeAttribute(node, 'x')).toBeDefined(); - expect(graph.getNodeAttribute(node, 'y')).toBeDefined(); + checkNodeAttributesDefined(graph); + checkPositionOverlap(graph); - const pos = '' + attr['x'] + '' + attr['y']; - console.log(pos); - - expect(positionMap.has(pos)).toBeFalsy(); - positionMap.add(pos); + // const positionMap = new Set<string>(); + // graph.forEachNode((node, attr) => { + // const pos = '' + attr['x'] + '' + attr['y']; + // // console.log(pos); - expect(isNaN(graph.getNodeAttribute(node, 'x'))).toBeFalsy(); - expect(isNaN(graph.getNodeAttribute(node, 'y'))).toBeFalsy(); - }); + // expect(positionMap.has(pos)).toBeFalsy(); + // positionMap.add(pos); + // }); }, TIMEOUT ); @@ -193,7 +191,15 @@ describe('LayoutFactory Cytoscape Libraries', () => { // console.log('Number of nodes', graph.order); // console.log('Number of edges', graph.size); - expect(nodes).toBe(graph.order); + try { + expect(nodes).toBe(graph.order); + } catch (error) { + console.error( + 'expect(nodes).toBe(graph.order) but was', + nodes, + graph.order + ); + } expect(edges).toBe(graph.size); }); @@ -215,15 +221,15 @@ describe('LayoutFactory Cytoscape Libraries', () => { const layoutAlgorithm = layoutFactory.createLayout('Cytoscape_klay'); layoutAlgorithm?.layout(graph); - const positionMap = new Set<string>(); - graph.forEachNode((node, attr) => { - expect(graph.getNodeAttribute(node, 'x')).toBeDefined(); - expect(graph.getNodeAttribute(node, 'y')).toBeDefined(); + checkNodeAttributesDefined(graph); + checkPositionOverlap(graph); - const pos = '' + attr['x'] + '' + attr['y']; - expect(positionMap.has(pos)).toBeFalsy(); - positionMap.add(pos); - }); + // const positionMap = new Set<string>(); + // graph.forEachNode((node, attr) => { + // const pos = '' + attr['x'] + '' + attr['y']; + // expect(positionMap.has(pos)).toBeFalsy(); + // positionMap.add(pos); + // }); }, TIMEOUT ); @@ -246,15 +252,14 @@ describe('LayoutFactory Cytoscape Libraries', () => { const layoutAlgorithm = layoutFactory.createLayout('Cytoscape_elk'); layoutAlgorithm?.layout(graph, false); - const positionMap = new Set<string>(); - graph.forEachNode((node, attr) => { - expect(graph.getNodeAttribute(node, 'x')).toBeDefined(); - expect(graph.getNodeAttribute(node, 'y')).toBeDefined(); - - const pos = '' + attr['x'] + '' + attr['y']; - expect(positionMap.has(pos)).toBeFalsy(); - positionMap.add(pos); - }); + checkNodeAttributesDefined(graph); + checkPositionOverlap(graph); + // const positionMap = new Set<string>(); + // graph.forEachNode((node, attr) => { + // const pos = '' + attr['x'] + '' + attr['y']; + // expect(positionMap.has(pos)).toBeFalsy(); + // positionMap.add(pos); + // }); }, TIMEOUT ); @@ -277,15 +282,8 @@ describe('LayoutFactory Cytoscape Libraries', () => { const layoutAlgorithm = layoutFactory.createLayout('Cytoscape_dagre'); layoutAlgorithm?.layout(graph); - const positionMap = new Set<string>(); - graph.forEachNode((node, attr) => { - expect(graph.getNodeAttribute(node, 'x')).toBeDefined(); - expect(graph.getNodeAttribute(node, 'y')).toBeDefined(); - - const pos = '' + attr['x'] + '' + attr['y']; - expect(positionMap.has(pos)).toBeFalsy(); - positionMap.add(pos); - }); + checkNodeAttributesDefined(graph); + checkPositionOverlap(graph); }, TIMEOUT ); @@ -308,15 +306,8 @@ describe('LayoutFactory Cytoscape Libraries', () => { const layoutAlgorithm = layoutFactory.createLayout('Cytoscape_fcose'); layoutAlgorithm?.layout(graph, false); - const positionMap = new Set<string>(); - graph.forEachNode((node, attr) => { - expect(graph.getNodeAttribute(node, 'x')).toBeDefined(); - expect(graph.getNodeAttribute(node, 'y')).toBeDefined(); - - const pos = '' + attr['x'] + '' + attr['y']; - expect(positionMap.has(pos)).toBeFalsy(); - positionMap.add(pos); - }); + checkNodeAttributesDefined(graph); + checkPositionOverlap(graph); }, TIMEOUT ); @@ -341,15 +332,8 @@ describe('LayoutFactory Cytoscape Libraries', () => { ); layoutAlgorithm?.layout(graph); - const positionMap = new Set<string>(); - graph.forEachNode((node, attr) => { - expect(graph.getNodeAttribute(node, 'x')).toBeDefined(); - expect(graph.getNodeAttribute(node, 'y')).toBeDefined(); - - const pos = '' + attr['x'] + '' + attr['y']; - expect(positionMap.has(pos)).toBeFalsy(); - positionMap.add(pos); - }); + checkNodeAttributesDefined(graph); + checkPositionOverlap(graph); }, TIMEOUT ); @@ -372,16 +356,50 @@ describe('LayoutFactory Cytoscape Libraries', () => { const layoutAlgorithm = layoutFactory.createLayout('Cytoscape_cise'); layoutAlgorithm?.layout(graph); - const positionMap = new Set<string>(); - graph.forEachNode((node, attr) => { - expect(graph.getNodeAttribute(node, 'x')).toBeDefined(); - expect(graph.getNodeAttribute(node, 'y')).toBeDefined(); - - const pos = '' + attr['x'] + '' + attr['y']; - expect(positionMap.has(pos)).toBeFalsy(); - positionMap.add(pos); - }); + checkNodeAttributesDefined(graph); + checkPositionOverlap(graph); }, TIMEOUT ); }); + +function checkNodeAttributesDefined(graph: Graph) { + try { + graph.forEachNode((node, attr) => { + expect(graph.getNodeAttribute(node, 'x')).toBeDefined(); + }); + } catch (error) { + console.error('Node attribute x is not defined'); + } + + try { + graph.forEachNode((node, attr) => { + expect(graph.getNodeAttribute(node, 'y')).toBeDefined(); + }); + } catch (error) { + console.error('Node attribute y is not defined'); + } +} + +function checkPositionOverlap(graph: Graph) { + // try { + // const positionMap = new Set<string>(); + // graph.forEachNode((node, attr) => { + // const pos = '' + attr['x'] + '' + attr['y']; + // expect(positionMap.has(pos)).toBeFalsy(); + // positionMap.add(pos); + // }); + // } catch (error) { + // console.error('A node position overlap was found'); + // } + + try { + const positionMap = new Set<string>(); + graph.forEachNode((node, attr) => { + expect(isNaN(graph.getNodeAttribute(node, 'x'))).toBeFalsy(); + expect(isNaN(graph.getNodeAttribute(node, 'y'))).toBeFalsy(); + }); + } catch (error) { + console.error('A node position may not be NaN'); + } +} diff --git a/libs/shared/graph-layout/src/lib/layout.ts b/libs/shared/graph-layout/src/lib/layout.ts index caa834f545a7e013965f979ce71839498f4c6b4a..f58a7c8ae01116689e9780f2defdb848a76bf414 100644 --- a/libs/shared/graph-layout/src/lib/layout.ts +++ b/libs/shared/graph-layout/src/lib/layout.ts @@ -10,7 +10,7 @@ export abstract class Layout<provider extends Providers> { public provider: provider, public algorithm: LayoutAlgorithm<provider> ) { - console.log( + console.info( `Created the following Layout: ${provider} - ${this.algorithm}` ); } diff --git a/libs/shared/graph-layout/yarn.lock b/libs/shared/graph-layout/yarn.lock index a648f2038763e7f89799b53b4144232254ac577a..d6417799638911037b94648cfd91ac818d88b357 100644 --- a/libs/shared/graph-layout/yarn.lock +++ b/libs/shared/graph-layout/yarn.lock @@ -2,11 +2,56 @@ # yarn lockfile v1 +"@mapbox/node-pre-gyp@^1.0.0": + version "1.0.9" + resolved "https://registry.yarnpkg.com/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.9.tgz#09a8781a3a036151cdebbe8719d6f8b25d4058bc" + integrity sha512-aDF3S3rK9Q2gey/WAttUlISduDItz5BU3306M9Eyv6/oS40aMprnopshtlKTykxRNIBEZuRMaZAnbrQ4QtKGyw== + dependencies: + detect-libc "^2.0.0" + https-proxy-agent "^5.0.0" + make-dir "^3.1.0" + node-fetch "^2.6.7" + nopt "^5.0.0" + npmlog "^5.0.1" + rimraf "^3.0.2" + semver "^7.3.5" + tar "^6.1.11" + "@yomguithereal/helpers@^1.1.1": version "1.1.1" resolved "https://registry.yarnpkg.com/@yomguithereal/helpers/-/helpers-1.1.1.tgz#185dfb0f88ca2beec53d0adf6eed15c33b1c549d" integrity sha512-UYvAq/XCA7xoh1juWDYsq3W0WywOB+pz8cgVnE1b45ZfdMhBvHDrgmSFG3jXeZSr2tMTYLGHFHON+ekG05Jebg== +abbrev@1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" + integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q== + +agent-base@6: + version "6.0.2" + resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-6.0.2.tgz#49fff58577cfee3f37176feab4c22e00f86d7f77" + integrity sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ== + dependencies: + debug "4" + +ansi-regex@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" + integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== + +"aproba@^1.0.3 || ^2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/aproba/-/aproba-2.0.0.tgz#52520b8ae5b569215b354efc0caa3fe1e45a8adc" + integrity sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ== + +are-we-there-yet@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz#372e0e7bd279d8e94c653aaa1f67200884bf3e1c" + integrity sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw== + dependencies: + delegates "^1.0.0" + readable-stream "^3.6.0" + avsdf-base@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/avsdf-base/-/avsdf-base-1.0.0.tgz#80c437d7d15d2bd201d9c31804e7b7a15a84781a" @@ -14,6 +59,53 @@ avsdf-base@^1.0.0: dependencies: layout-base "^1.0.0" +balanced-match@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" + integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== + +brace-expansion@^1.1.7: + version "1.1.11" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" + integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== + dependencies: + balanced-match "^1.0.0" + concat-map "0.0.1" + +canvas@^2.9.1: + version "2.9.1" + resolved "https://registry.yarnpkg.com/canvas/-/canvas-2.9.1.tgz#58ec841cba36cef0675bc7a74ebd1561f0b476b0" + integrity sha512-vSQti1uG/2gjv3x6QLOZw7TctfufaerTWbVe+NSduHxxLGB+qf3kFgQ6n66DSnuoINtVUjrLLIK2R+lxrBG07A== + dependencies: + "@mapbox/node-pre-gyp" "^1.0.0" + nan "^2.15.0" + simple-get "^3.0.3" + +chownr@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/chownr/-/chownr-2.0.0.tgz#15bfbe53d2eab4cf70f18a8cd68ebe5b3cb1dece" + integrity sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ== + +color-name@^1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" + integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== + +color-support@^1.1.2: + version "1.1.3" + resolved "https://registry.yarnpkg.com/color-support/-/color-support-1.1.3.tgz#93834379a1cc9a0c61f82f52f0d04322251bd5a2" + integrity sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg== + +concat-map@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" + integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= + +console-control-strings@^1.0.0, console-control-strings@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e" + integrity sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4= + cose-base@^1.0.0: version "1.0.3" resolved "https://registry.yarnpkg.com/cose-base/-/cose-base-1.0.3.tgz#650334b41b869578a543358b80cda7e0abe0a60a" @@ -28,6 +120,11 @@ cose-base@^2.0.0: dependencies: layout-base "^2.0.0" +cssfontparser@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/cssfontparser/-/cssfontparser-1.2.1.tgz#f4022fc8f9700c68029d542084afbaf425a3f3e3" + integrity sha1-9AIvyPlwDGgCnVQghK+69CWj8+M= + cytoscape-cise@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/cytoscape-cise/-/cytoscape-cise-1.0.0.tgz#29ac061c74e036441c0b5687e7ebc25e919d507c" @@ -90,16 +187,84 @@ dagre@^0.8.5: graphlib "^2.1.8" lodash "^4.17.15" +debug@4: + version "4.3.4" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" + integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== + dependencies: + ms "2.1.2" + +decompress-response@^4.2.0: + version "4.2.1" + resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-4.2.1.tgz#414023cc7a302da25ce2ec82d0d5238ccafd8986" + integrity sha512-jOSne2qbyE+/r8G1VU+G/82LBs2Fs4LAsTiLSHOCOMZQl2OKZ6i8i4IyHemTe+/yIXOtTcRQMzPcgyhoFlqPkw== + dependencies: + mimic-response "^2.0.0" + +delegates@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a" + integrity sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o= + +detect-libc@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-2.0.1.tgz#e1897aa88fa6ad197862937fbc0441ef352ee0cd" + integrity sha512-463v3ZeIrcWtdgIg6vI6XUncguvr2TnGl4SzDXinkt9mSLpBJKXT3mW6xT3VQdDN11+WVs29pgvivTc4Lp8v+w== + elkjs@^0.7.0: version "0.7.1" resolved "https://registry.yarnpkg.com/elkjs/-/elkjs-0.7.1.tgz#4751c5e918a4988139baf7f214e010aea22de969" integrity sha512-lD86RWdh480/UuRoHhRcnv2IMkIcK6yMDEuT8TPBIbO3db4HfnVF+1lgYdQi99Ck0yb+lg5Eb46JCHI5uOsmAw== +emoji-regex@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" + integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== + events@^3.3.0: version "3.3.0" resolved "https://registry.yarnpkg.com/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400" integrity sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q== +fs-minipass@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-2.1.0.tgz#7f5036fdbf12c63c169190cbe4199c852271f9fb" + integrity sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg== + dependencies: + minipass "^3.0.0" + +fs.realpath@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" + integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= + +gauge@^3.0.0: + version "3.0.2" + resolved "https://registry.yarnpkg.com/gauge/-/gauge-3.0.2.tgz#03bf4441c044383908bcfa0656ad91803259b395" + integrity sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q== + dependencies: + aproba "^1.0.3 || ^2.0.0" + color-support "^1.1.2" + console-control-strings "^1.0.0" + has-unicode "^2.0.1" + object-assign "^4.1.1" + signal-exit "^3.0.0" + string-width "^4.2.3" + strip-ansi "^6.0.1" + wide-align "^1.1.2" + +glob@^7.1.3: + version "7.2.0" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.0.tgz#d15535af7732e02e948f4c41628bd910293f6023" + integrity sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.0.4" + once "^1.3.0" + path-is-absolute "^1.0.0" + graphlib@^2.1.8: version "2.1.8" resolved "https://registry.yarnpkg.com/graphlib/-/graphlib-2.1.8.tgz#5761d414737870084c92ec7b5dbcb0592c9d35da" @@ -177,11 +342,50 @@ graphology@^0.24.1: events "^3.3.0" obliterator "^2.0.2" +has-unicode@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9" + integrity sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk= + heap@^0.2.6: version "0.2.7" resolved "https://registry.yarnpkg.com/heap/-/heap-0.2.7.tgz#1e6adf711d3f27ce35a81fe3b7bd576c2260a8fc" integrity sha512-2bsegYkkHO+h/9MGbn6KWcE45cHZgPANo5LXF7EvWdT0yT2EguSVO1nDgU5c8+ZOPwp2vMNa7YFsJhVcDR9Sdg== +https-proxy-agent@^5.0.0: + version "5.0.1" + resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz#c59ef224a04fe8b754f3db0063a25ea30d0005d6" + integrity sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA== + dependencies: + agent-base "6" + debug "4" + +inflight@^1.0.4: + version "1.0.6" + resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" + integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk= + dependencies: + once "^1.3.0" + wrappy "1" + +inherits@2, inherits@^2.0.3: + version "2.0.4" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" + integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== + +is-fullwidth-code-point@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" + integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== + +jest-canvas-mock@^2.4.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/jest-canvas-mock/-/jest-canvas-mock-2.4.0.tgz#947b71442d7719f8e055decaecdb334809465341" + integrity sha512-mmMpZzpmLzn5vepIaHk5HoH3Ka4WykbSoLuG/EKoJd0x0ID/t+INo1l8ByfcUJuDM+RIsL4QDg/gDnBbrj2/IQ== + dependencies: + cssfontparser "^1.2.1" + moo-color "^1.0.2" + klayjs@^0.4.1: version "0.4.1" resolved "https://registry.yarnpkg.com/klayjs/-/klayjs-0.4.1.tgz#5bf9fadc7a3e44b94082bba501e7d803076dcfc2" @@ -222,6 +426,52 @@ lodash@^4.17.15: resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== +lru-cache@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94" + integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA== + dependencies: + yallist "^4.0.0" + +make-dir@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-3.1.0.tgz#415e967046b3a7f1d185277d84aa58203726a13f" + integrity sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw== + dependencies: + semver "^6.0.0" + +mimic-response@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-2.1.0.tgz#d13763d35f613d09ec37ebb30bac0469c0ee8f43" + integrity sha512-wXqjST+SLt7R009ySCglWBCFpjUygmCIfD790/kVbiGmUgfYGuB14PiTd5DwVxSV4NcYHjzMkoj5LjQZwTQLEA== + +minimatch@^3.0.4: + version "3.1.2" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" + integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== + dependencies: + brace-expansion "^1.1.7" + +minipass@^3.0.0: + version "3.1.6" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-3.1.6.tgz#3b8150aa688a711a1521af5e8779c1d3bb4f45ee" + integrity sha512-rty5kpw9/z8SX9dmxblFA6edItUmwJgMeYDZRrwlIVN27i8gysGbznJwUggw2V/FVqFSDdWy040ZPS811DYAqQ== + dependencies: + yallist "^4.0.0" + +minizlib@^2.1.1: + version "2.1.2" + resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-2.1.2.tgz#e90d3466ba209b932451508a11ce3d3632145931" + integrity sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg== + dependencies: + minipass "^3.0.0" + yallist "^4.0.0" + +mkdirp@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" + integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== + mnemonist@^0.39.0: version "0.39.0" resolved "https://registry.yarnpkg.com/mnemonist/-/mnemonist-0.39.0.tgz#4c83dd22e8d9d05dfb721ff66a905fec4c460041" @@ -229,17 +479,207 @@ mnemonist@^0.39.0: dependencies: obliterator "^2.0.1" +moo-color@^1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/moo-color/-/moo-color-1.0.3.tgz#d56435f8359c8284d83ac58016df7427febece74" + integrity sha512-i/+ZKXMDf6aqYtBhuOcej71YSlbjT3wCO/4H1j8rPvxDJEifdwgg5MaFyu6iYAT8GBZJg2z0dkgK4YMzvURALQ== + dependencies: + color-name "^1.1.4" + +ms@2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" + integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== + +nan@^2.15.0: + version "2.15.0" + resolved "https://registry.yarnpkg.com/nan/-/nan-2.15.0.tgz#3f34a473ff18e15c1b5626b62903b5ad6e665fee" + integrity sha512-8ZtvEnA2c5aYCZYd1cvgdnU6cqwixRoYg70xPLWUws5ORTa/lnw+u4amixRS/Ac5U5mQVgp9pnlSUnbNWFaWZQ== + +node-fetch@^2.6.7: + version "2.6.7" + resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.7.tgz#24de9fba827e3b4ae44dc8b20256a379160052ad" + integrity sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ== + dependencies: + whatwg-url "^5.0.0" + +nopt@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/nopt/-/nopt-5.0.0.tgz#530942bb58a512fccafe53fe210f13a25355dc88" + integrity sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ== + dependencies: + abbrev "1" + +npmlog@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-5.0.1.tgz#f06678e80e29419ad67ab964e0fa69959c1eb8b0" + integrity sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw== + dependencies: + are-we-there-yet "^2.0.0" + console-control-strings "^1.1.0" + gauge "^3.0.0" + set-blocking "^2.0.0" + +object-assign@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" + integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM= + obliterator@^2.0.1, obliterator@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/obliterator/-/obliterator-2.0.2.tgz#25f50dc92e1181371b9d8209d11890f1a3c2fc21" integrity sha512-g0TrA7SbUggROhDPK8cEu/qpItwH2LSKcNl4tlfBNT54XY+nOsqrs0Q68h1V9b3HOSpIWv15jb1lax2hAggdIg== +once@^1.3.0, once@^1.3.1: + version "1.4.0" + resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" + integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E= + dependencies: + wrappy "1" + pandemonium@^1.5.0: version "1.5.0" resolved "https://registry.yarnpkg.com/pandemonium/-/pandemonium-1.5.0.tgz#93f35af555de1420022b341e730215c51c725be3" integrity sha512-9PU9fy93rJhZHLMjX+4M1RwZPEYl6g7DdWKGmGNhkgBZR5+tOBVExNZc00kzdEGMxbaAvWdQy9MqGAScGwYlcA== +path-is-absolute@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" + integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= + +readable-stream@^3.6.0: + version "3.6.0" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198" + integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA== + dependencies: + inherits "^2.0.3" + string_decoder "^1.1.1" + util-deprecate "^1.0.1" + +rimraf@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" + integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== + dependencies: + glob "^7.1.3" + +safe-buffer@~5.2.0: + version "5.2.1" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" + integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== + +semver@^6.0.0: + version "6.3.0" + resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" + integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== + +semver@^7.3.5: + version "7.3.7" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.7.tgz#12c5b649afdbf9049707796e22a4028814ce523f" + integrity sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g== + dependencies: + lru-cache "^6.0.0" + +set-blocking@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" + integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc= + +signal-exit@^3.0.0: + version "3.0.7" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" + integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== + +simple-concat@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/simple-concat/-/simple-concat-1.0.1.tgz#f46976082ba35c2263f1c8ab5edfe26c41c9552f" + integrity sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q== + +simple-get@^3.0.3: + version "3.1.1" + resolved "https://registry.yarnpkg.com/simple-get/-/simple-get-3.1.1.tgz#cc7ba77cfbe761036fbfce3d021af25fc5584d55" + integrity sha512-CQ5LTKGfCpvE1K0n2us+kuMPbk/q0EKl82s4aheV9oXjFEz6W/Y7oQFVJuU6QG77hRT4Ghb5RURteF5vnWjupA== + dependencies: + decompress-response "^4.2.0" + once "^1.3.1" + simple-concat "^1.0.0" + +"string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.2.3: + version "4.2.3" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + +string_decoder@^1.1.1: + version "1.3.0" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" + integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== + dependencies: + safe-buffer "~5.2.0" + +strip-ansi@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + +tar@^6.1.11: + version "6.1.11" + resolved "https://registry.yarnpkg.com/tar/-/tar-6.1.11.tgz#6760a38f003afa1b2ffd0ffe9e9abbd0eab3d621" + integrity sha512-an/KZQzQUkZCkuoAA64hM92X0Urb6VpRhAFllDzz44U2mcD5scmT3zBc4VgVpkugF580+DQn8eAFSyoQt0tznA== + dependencies: + chownr "^2.0.0" + fs-minipass "^2.0.0" + minipass "^3.0.0" + minizlib "^2.1.1" + mkdirp "^1.0.3" + yallist "^4.0.0" + +tr46@~0.0.3: + version "0.0.3" + resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" + integrity sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o= + +util-deprecate@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" + integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= + web-worker@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/web-worker/-/web-worker-1.2.0.tgz#5d85a04a7fbc1e7db58f66595d7a3ac7c9c180da" integrity sha512-PgF341avzqyx60neE9DD+XS26MMNMoUQRz9NOZwW32nPQrF6p77f1htcnjBSEV8BGMKZ16choqUG4hyI0Hx7mA== + +webidl-conversions@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871" + integrity sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE= + +whatwg-url@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d" + integrity sha1-lmRU6HZUYuN2RNNib2dCzotwll0= + dependencies: + tr46 "~0.0.3" + webidl-conversions "^3.0.0" + +wide-align@^1.1.2: + version "1.1.5" + resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.5.tgz#df1d4c206854369ecf3c9a4898f1b23fbd9d15d3" + integrity sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg== + dependencies: + string-width "^1.0.2 || 2 || 3 || 4" + +wrappy@1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" + integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= + +yallist@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" + integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== diff --git a/libs/shared/mock-data/src/schema/mock-data.spec.ts b/libs/shared/mock-data/src/schema/mock-data.spec.ts index f86e6d316a9867914bc99061442e2b3f8f56e854..7aa1fa5766f3badc02e1c7e851bddb7968037e25 100644 --- a/libs/shared/mock-data/src/schema/mock-data.spec.ts +++ b/libs/shared/mock-data/src/schema/mock-data.spec.ts @@ -1,8 +1,8 @@ import Graph from 'graphology'; -import { movieSchemaRaw, movieSchema } from './moviesSchemaRaw'; -import { northWindSchema, northwindSchemaRaw } from './northwindSchemaRaw'; -import { simpleSchema, simpleSchemaRaw } from './simpleRaw'; -import { twitterSchema, twitterSchemaRaw } from './twitterSchemaRaw'; +import { movieSchemaRaw, movieSchema } from '..'; +import { northWindSchema, northwindSchemaRaw } from '..'; +import { simpleSchema, simpleSchemaRaw } from '..'; +import { twitterSchema, twitterSchemaRaw } from '..'; describe('MockData Tests', () => { it('should have raw data available movie', () => { @@ -29,27 +29,35 @@ describe('MockData Tests', () => { const graph = movieSchema; expect(graph); - expect(graph.constructor.name.toLowerCase().indexOf('graph') != -1).toBeTruthy(); + expect( + graph.constructor.name.toLowerCase().indexOf('graph') != -1 + ).toBeTruthy(); }); it('should have data available as graphology model northwind', () => { const graph = northWindSchema; expect(graph); - expect(graph.constructor.name.toLowerCase().indexOf('graph') != -1).toBeTruthy(); + expect( + graph.constructor.name.toLowerCase().indexOf('graph') != -1 + ).toBeTruthy(); }); it('should have data available as graphology model simpleSchemaRaw', () => { const graph = simpleSchema; expect(graph); - expect(graph.constructor.name.toLowerCase().indexOf('graph') != -1).toBeTruthy(); + expect( + graph.constructor.name.toLowerCase().indexOf('graph') != -1 + ).toBeTruthy(); }); it('should have data available as graphology model twitterSchemaRaw', () => { const graph = twitterSchema; expect(graph); - expect(graph.constructor.name.toLowerCase().indexOf('graph') != -1).toBeTruthy(); + expect( + graph.constructor.name.toLowerCase().indexOf('graph') != -1 + ).toBeTruthy(); }); }); diff --git a/libs/shared/mock-data/src/schema/moviesSchemaRaw.ts b/libs/shared/mock-data/src/schema/moviesSchemaRaw.ts index 762bce33c65af64ba2bdac7928697224afd5d651..678ff5fcfd56cd12ee2d4d45cb2564d998f7a533 100644 --- a/libs/shared/mock-data/src/schema/moviesSchemaRaw.ts +++ b/libs/shared/mock-data/src/schema/moviesSchemaRaw.ts @@ -1,7 +1,7 @@ import { SchemaUtils } from '@graphpolaris/schema-utils'; -import { SchemaFromBackend } from 'libs/shared/models/src'; +import { SchemaFromBackend } from '@graphpolaris/models'; -export const movieSchemaRaw = { +export const movieSchemaRaw: SchemaFromBackend = { nodes: [ { name: 'Movie', @@ -98,6 +98,5 @@ export const movieSchemaRaw = { ], }; -export const movieSchema = SchemaUtils.ParseSchemaFromBackend( - movieSchemaRaw as SchemaFromBackend -); +export const movieSchema = SchemaUtils.ParseSchemaFromBackend(movieSchemaRaw); + diff --git a/libs/shared/mock-data/src/schema/northwindSchemaRaw.ts b/libs/shared/mock-data/src/schema/northwindSchemaRaw.ts index 59f248f5fbd9117a1021cc1852100e24c02f85ec..f38a405228273fe992d3aea70a624ea0c01acf74 100644 --- a/libs/shared/mock-data/src/schema/northwindSchemaRaw.ts +++ b/libs/shared/mock-data/src/schema/northwindSchemaRaw.ts @@ -1,7 +1,7 @@ import { SchemaUtils } from '@graphpolaris/schema-utils'; -import { SchemaFromBackend } from 'libs/shared/models/src'; +import { SchemaFromBackend } from '@graphpolaris/models'; -export const northwindSchemaRaw = { +export const northwindSchemaRaw: SchemaFromBackend = { nodes: [ { name: 'Order', @@ -286,6 +286,5 @@ export const northwindSchemaRaw = { ], }; -export const northWindSchema = SchemaUtils.ParseSchemaFromBackend( - northwindSchemaRaw as SchemaFromBackend -); +export const northWindSchema = + SchemaUtils.ParseSchemaFromBackend(northwindSchemaRaw); diff --git a/libs/shared/mock-data/src/schema/simpleRaw.ts b/libs/shared/mock-data/src/schema/simpleRaw.ts index e8fee0ca1eea766231f802433ee7f52ecd7b273d..668cf403c8fcb3b030c9bd883400d98efa101484 100644 --- a/libs/shared/mock-data/src/schema/simpleRaw.ts +++ b/libs/shared/mock-data/src/schema/simpleRaw.ts @@ -1,7 +1,7 @@ import { SchemaUtils } from '@graphpolaris/schema-utils'; import { SchemaFromBackend } from '@graphpolaris/models'; -export const simpleSchemaRaw = { +export const simpleSchemaRaw: SchemaFromBackend = { nodes: [ { name: 'Thijs', @@ -103,6 +103,4 @@ export const simpleSchemaRaw = { ], }; -export const simpleSchema = SchemaUtils.ParseSchemaFromBackend( - simpleSchemaRaw as SchemaFromBackend -); +export const simpleSchema = SchemaUtils.ParseSchemaFromBackend(simpleSchemaRaw); diff --git a/libs/shared/mock-data/src/schema/twitterSchemaRaw.ts b/libs/shared/mock-data/src/schema/twitterSchemaRaw.ts index ba922222c3fa2d9600f1318649ae59c7d0033f90..871ab8e755c4704668fef01d8a0f57e63db139df 100644 --- a/libs/shared/mock-data/src/schema/twitterSchemaRaw.ts +++ b/libs/shared/mock-data/src/schema/twitterSchemaRaw.ts @@ -1,7 +1,7 @@ import { SchemaUtils } from '@graphpolaris/schema-utils'; -import { SchemaFromBackend } from 'libs/shared/models/src'; +import { SchemaFromBackend } from '@graphpolaris/models'; -export const twitterSchemaRaw = { +export const twitterSchemaRaw: SchemaFromBackend = { nodes: [ { name: 'Me', @@ -315,6 +315,5 @@ export const twitterSchemaRaw = { ], }; -export const twitterSchema = SchemaUtils.ParseSchemaFromBackend( - twitterSchemaRaw as SchemaFromBackend -); +export const twitterSchema = + SchemaUtils.ParseSchemaFromBackend(twitterSchemaRaw); diff --git a/libs/shared/mock-data/tsconfig.spec.json b/libs/shared/mock-data/tsconfig.spec.json index 4afc999ad429eea63e777308527c4e8f629e4198..59041160ab21de130a3c8d6c7c916e5d8094b8dc 100644 --- a/libs/shared/mock-data/tsconfig.spec.json +++ b/libs/shared/mock-data/tsconfig.spec.json @@ -2,7 +2,7 @@ "extends": "./tsconfig.json", "compilerOptions": { "outDir": "../../../dist/out-tsc", - "module": "commonjs", + "module": "esnext", "types": ["jest", "node"], "composite": true }, diff --git a/libs/shared/schema-utils/src/lib/schema-utils.ts b/libs/shared/schema-utils/src/lib/schema-utils.ts index 0ca15baedb7cdbe011e12d9eaf688c0fa23a73ed..accb03be132218c8f6633a6b53004d02622c439d 100644 --- a/libs/shared/schema-utils/src/lib/schema-utils.ts +++ b/libs/shared/schema-utils/src/lib/schema-utils.ts @@ -20,6 +20,8 @@ export class SchemaUtils { }); }); + + // The name of the edge will be name + from + to, since edge names are not unique edges.forEach((edge: Edge) => { const edgeID = [edge.name, '_', edge.from, edge.to].join(''); //ensure that all interpreted as string diff --git a/libs/shared/ui/pills/.babelrc b/libs/shared/ui/pills/.babelrc new file mode 100644 index 0000000000000000000000000000000000000000..ccae900be42788285eb6e1b3a2af4b81f56f14fe --- /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 0000000000000000000000000000000000000000..3cd64217595c25dc61c2fd055ec4dbe21195a3e3 --- /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 0000000000000000000000000000000000000000..84610a47f2335ec607ead16b69bc23b460a48655 --- /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 0000000000000000000000000000000000000000..7d0623362ab495667bd3a33c5c51f96c3383564b --- /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 0000000000000000000000000000000000000000..ea222183fbe8abb6af31b3ddf45edfbbf6fbd836 --- /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 0000000000000000000000000000000000000000..23fb1ec2ee51d04477f18aad63ae5b0f15dd6dfb --- /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 0000000000000000000000000000000000000000..0d567e584d66c26537abc2c1f6f3a97469dad1a5 --- /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 0000000000000000000000000000000000000000..9ba5ba22c49c279617faef06e92d4256e49a36fb --- /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 0000000000000000000000000000000000000000..41b5803cc251e5f345702e9a364ee9eac4c6b113 --- /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 0000000000000000000000000000000000000000..1c31d2744f029e77c53efbfac2aae8983640770a --- /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 0000000000000000000000000000000000000000..470c87618fdbd49fd8d70b3de926e0d3605cd12a --- /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 0000000000000000000000000000000000000000..08bc31bb67c70a043d0114880fceada930fe14b1 --- /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 0000000000000000000000000000000000000000..755d2b41d564abe3f9e4eb41f56865ef7d2432f4 --- /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.stories.tsx b/libs/shared/ui/pills/src/customFlowPills/entitypill/entitypill.stories.tsx new file mode 100644 index 0000000000000000000000000000000000000000..1ece902308aec3688acd3edd7086ffa89b87b821 --- /dev/null +++ b/libs/shared/ui/pills/src/customFlowPills/entitypill/entitypill.stories.tsx @@ -0,0 +1,61 @@ +import React from 'react'; +import { ComponentMeta, ComponentStory } from '@storybook/react'; +import EntityRFPill from './entitypill'; +import { configureStore } from '@reduxjs/toolkit'; +import { Provider } from 'react-redux'; +import { GraphPolarisThemeProvider } from '@graphpolaris/shared/data-access/theme'; + +import { + colorPaletteConfigSlice, + querybuilderSlice, + schemaSlice, +} from '@graphpolaris/shared/data-access/store'; + +// import createTheme from '@mui/system/createTheme'; +// import { ThemeProvider } from '@mui/material'; +// const theme = createTheme({}); + +export default { + /* 👇 The title prop is optional. + * See https://storybook.js.org/docs/react/configure/overview#configure-story-loading + * to learn how to generate automatic titles + */ + title: 'Components/Pills/EntityRFPill', + component: EntityRFPill, + decorators: [ + (story) => ( + <Provider store={Mockstore}> + <GraphPolarisThemeProvider>{story()}</GraphPolarisThemeProvider> + </Provider> + ), + ], +} as ComponentMeta<typeof EntityRFPill>; + +// A super-simple mock of a redux store +const Mockstore = configureStore({ + reducer: { + colorPaletteConfig: colorPaletteConfigSlice.reducer, + querybuilder: querybuilderSlice.reducer, + // schema: schemaSlice.reducer, + }, +}); + +const Template: ComponentStory<typeof EntityRFPill> = (args) => ( + <EntityRFPill {...args} /> +); +// const Template = (args: any) => <EntityRFPill {...args} />; + +export const Default = Template.bind({}); +Default.args = { + data: { + name: 'TestEntity', + }, +}; + +// Default.decorators = [ +// (story) => ( +// <Provider store={Mockstore}> +// <GraphPolarisThemeProvider>{story()}</GraphPolarisThemeProvider> +// </Provider> +// ), +// ]; 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 0000000000000000000000000000000000000000..29bd77d043e8b63c00ce46588efbf910e121bdcf --- /dev/null +++ b/libs/shared/ui/pills/src/customFlowPills/entitypill/entitypill.tsx @@ -0,0 +1,72 @@ +// 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} + /> */} + <Handle + type="target" + position={Position.Left} + isValidConnection={(connection) => connection.source === 'some-id'} + onConnect={(params) => console.log('handle onConnect', params)} + style={{ background: '#fff' }} + /> + <div className={styles['contentWrapper']}> + <span title={data.name}>{data.name}</span> + </div> + FooBar + </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 0000000000000000000000000000000000000000..8f909017f21464b2b42ba9cb57c7c4a40535601d --- /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/entitypill/test.stories.tsx b/libs/shared/ui/pills/src/customFlowPills/entitypill/test.stories.tsx new file mode 100644 index 0000000000000000000000000000000000000000..cff92b119bc74df2e4ba2b488f2e4705fe1b77e3 --- /dev/null +++ b/libs/shared/ui/pills/src/customFlowPills/entitypill/test.stories.tsx @@ -0,0 +1,19 @@ +// Button.stories.ts|tsx + +import React from 'react'; + +import { ComponentStory, ComponentMeta } from '@storybook/react'; +import Button from '@mui/material/Button'; + +export default { + /* 👇 The title prop is optional. + * See https://storybook.js.org/docs/react/configure/overview#configure-story-loading + * to learn how to generate automatic titles + */ + title: 'Shared Component Loading Works', + component: Button, +} as ComponentMeta<typeof Button>; + +export const Primary: ComponentStory<typeof Button> = () => ( + <Button>Component Loading works</Button> +); 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 0000000000000000000000000000000000000000..deef2e611ad7700adac469c821310a527f7a3989 --- /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 0000000000000000000000000000000000000000..aff84b2e5d8b9caba6dd9fdd34a99b54614946d4 --- /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 0000000000000000000000000000000000000000..8861d5e0e4079d4605075dc58a3a7acf490dbf27 --- /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 0000000000000000000000000000000000000000..6ac42e28dc43ff2a7181b86bdc5488baf8631e8e --- /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 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 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 0000000000000000000000000000000000000000..d9561ecbdb4508a43221e49c5ae140ead5e8fe85 --- /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 0000000000000000000000000000000000000000..6a804d611fbd303b2d569e6c44d76d7810116ca8 --- /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 0000000000000000000000000000000000000000..049328d0084cc0d1401870c7a5ce5d3066151dc0 --- /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 0000000000000000000000000000000000000000..755d2b41d564abe3f9e4eb41f56865ef7d2432f4 --- /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 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/libs/shared/ui/pills/tsconfig.json b/libs/shared/ui/pills/tsconfig.json new file mode 100644 index 0000000000000000000000000000000000000000..3512bf7afeea1bf2e5fdb72b7dea8114f6c9fbe1 --- /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 0000000000000000000000000000000000000000..1ab8e06a7111b168e84debca80bb4ac82f72f2aa --- /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 0000000000000000000000000000000000000000..315a5b0bbebaca96617a8dd5353901287ebd8e68 --- /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/nginx/nginx.conf b/nginx/nginx.conf new file mode 100644 index 0000000000000000000000000000000000000000..ef2e21a890fc402b46472b5c18fd5b95c3a53a2e --- /dev/null +++ b/nginx/nginx.conf @@ -0,0 +1,16 @@ +server { + listen 80; + + location / { + root /usr/share/nginx/html; + index index.html index.htm; + try_files $uri $uri/ /index.html; + } + + error_page 500 502 503 504 /50x.html; + + location = /50x.html { + root /usr/share/nginx/html; + } + +} diff --git a/package.json b/package.json index 9d07a7788766fc39c5af14ad5142bf853f2776fe..8f417b0ea80e3803a8226572f4d7f5a90a2eeefa 100644 --- a/package.json +++ b/package.json @@ -4,6 +4,8 @@ "license": "MIT", "scripts": { "start": "nx serve", + "storybook": "nx run web-graphpolaris:storybook", + "dev": "nx run web-graphpolaris:dev", "build": "nx build", "test": "nx test", "test:all": "nx affected:test --all --codeCoverage --skip-nx-cache", diff --git a/tsconfig.base.json b/tsconfig.base.json index a5bafb69c61a39d4c988430267936172aeb36df7..1d989caf1546265d9884792e5fb894129aa5af64 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 d829d3ca0515a309a5a268383dcf3764953f2251..d5bbe8a52437f534858ca07379e372480fbeed95 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" }