-
Notifications
You must be signed in to change notification settings - Fork 12
Graph Navigator #603
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Graph Navigator #603
Changes from 18 commits
c7828e7
50652df
4e266e1
0eff226
52b3447
e9c7f66
0f39937
24cec3d
b994acc
5a4e5e6
242fe67
c0e6a98
1e024af
c373a86
8ce784d
e2289c0
ca62fba
f5e35f4
ee5810d
327c0f1
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,23 @@ | ||
| .listWrapper { | ||
| display: flex; | ||
| flex-direction: column; | ||
| font-size: var(--fontSizes-small); | ||
| gap: var(--component-spacing-xs); | ||
| list-style-type: none; | ||
| margin: 0; | ||
| padding: 0; | ||
| user-select: none; | ||
| } | ||
|
|
||
| .listItem { | ||
| cursor: pointer; | ||
| display: inline-flex; | ||
| gap: 0.5ch; | ||
floscr marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| padding-left: 0; | ||
| padding-left: calc(var(--tree-depth, 0) * var(--component-spacing-xs, 1)); | ||
floscr marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| } | ||
|
|
||
| .listItemCount { | ||
| opacity: 0.5; | ||
|
||
| font-weight: normal; | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,109 @@ | ||
| import React from 'react'; | ||
|
|
||
| import { MAIN_GRAPH_ID } from '@/constants.js'; | ||
| import { Stack } from '@tokens-studio/ui'; | ||
| import { TreeNode, graphNodesSelector } from '@/redux/selectors/graph.js'; | ||
| import { currentPanelIdSelector } from '@/redux/selectors/graph.js'; | ||
| import { dockerSelector } from '@/redux/selectors/refs.js'; | ||
| import { flow, get, size } from 'lodash-es'; | ||
| import { useSelector } from 'react-redux'; | ||
| import { useSubgraphExplorerCallback } from '@/hooks/useSubgraphExplorerCallback.js'; | ||
| import styles from './index.module.css'; | ||
|
|
||
| type ListItemProps = { | ||
| label: string; | ||
| count?: number; | ||
| isSelected?: boolean; | ||
| onClick?: () => void; | ||
| depth?: number; | ||
| }; | ||
|
|
||
| const ListItem = function ({ | ||
| label, | ||
| count, | ||
| isSelected, | ||
| onClick, | ||
| depth = 0, | ||
| }: ListItemProps) { | ||
| const style = { | ||
| '--tree-depth': depth, | ||
| fontWeight: isSelected && 'bold', | ||
floscr marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| } as React.CSSProperties; | ||
|
|
||
| return ( | ||
| <li className={styles.listItem} onClick={onClick} style={style}> | ||
| <span>{label}</span> | ||
| {count && <span className={styles.listItemCount}>({count})</span>} | ||
| </li> | ||
| ); | ||
| }; | ||
|
|
||
| const SubgraphNodeItem = function ({ node, isSelected, depth }) { | ||
| const nodeType = node.factory.title || node.nodeType(); | ||
| const onNodeClick = useSubgraphExplorerCallback(node); | ||
| const childNodesCount = flow( | ||
| (x) => get(x, ['_innerGraph', 'nodes'], []), | ||
| size, | ||
| )(node); | ||
|
|
||
| return ( | ||
| <ListItem | ||
| label={nodeType} | ||
| onClick={onNodeClick} | ||
| isSelected={isSelected} | ||
| depth={depth} | ||
| count={childNodesCount > 0 && childNodesCount} | ||
| /> | ||
| ); | ||
| }; | ||
|
|
||
| const RootGraphNodeItem = function () { | ||
| const dockerRef = useSelector(dockerSelector); | ||
| const activeGraphId = useSelector(currentPanelIdSelector); | ||
|
|
||
| return ( | ||
| <ListItem | ||
| label={'Root'} | ||
| isSelected={activeGraphId === MAIN_GRAPH_ID} | ||
| onClick={() => dockerRef.current.updateTab(MAIN_GRAPH_ID, null, true)} | ||
| /> | ||
| ); | ||
| }; | ||
|
|
||
| export const NavigationPanel = () => { | ||
| const nodes = useSelector(graphNodesSelector); | ||
| const activeGraphId = useSelector(currentPanelIdSelector); | ||
|
|
||
| return ( | ||
| <Stack | ||
| direction="column" | ||
| gap={4} | ||
| style={{ | ||
floscr marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| height: '100%', | ||
| flex: 1, | ||
| padding: 'var(--component-spacing-md)', | ||
| overflow: 'auto', | ||
| }} | ||
| > | ||
| <div style={{ padding: 'var(--component-spacing-md)' }}> | ||
| <ul className={styles.listWrapper}> | ||
| <RootGraphNodeItem /> | ||
| {Object.values(nodes || {}).map(({ node, depth }: TreeNode) => { | ||
| const innerGraph = node['_innerGraph']; | ||
| if (!innerGraph) return null; | ||
|
Comment on lines
+89
to
+90
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't know how we can get this type out of I would have liked to do a check like but I'm not sure how importing for types/classes works for such packages |
||
| return ( | ||
| <SubgraphNodeItem | ||
| key={node.id} | ||
| isSelected={ | ||
| activeGraphId === innerGraph?.annotations['engine.id'] | ||
| } | ||
| node={node} | ||
| depth={depth} | ||
| /> | ||
| ); | ||
| })} | ||
| </ul> | ||
| </div> | ||
| </Stack> | ||
| ); | ||
| }; | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1 +1 @@ | ||
| export const version = '4.3.9'; | ||
| export const version = '4.3.6'; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,4 +1,6 @@ | ||
| import { EditorApp } from './graph.js'; | ||
| import { ErrorBoundary } from 'react-error-boundary'; | ||
| import { ErrorBoundaryContent } from '@/components/ErrorBoundaryContent.js'; | ||
| import { GraphEditorProps, ImperativeEditorRef } from './editorTypes.js'; | ||
| import { ReactFlowProvider } from 'reactflow'; | ||
| import React from 'react'; | ||
|
|
@@ -16,3 +18,15 @@ export const GraphEditor = React.forwardRef< | |
| </ReactFlowProvider> | ||
| ); | ||
| }); | ||
|
|
||
| // HACK: Workaround for circular dependency not allowed for nextjs | ||
| // E.g.: when trying to create a new graph editor instance as a tab | ||
| if (typeof window !== 'undefined') { | ||
| window['newGraphEditor'] = function (ref, id) { | ||
| return ( | ||
| <ErrorBoundary fallback={<ErrorBoundaryContent />}> | ||
| <GraphEditor ref={ref} id={id} /> | ||
| </ErrorBoundary> | ||
| ); | ||
| }; | ||
| } | ||
|
Comment on lines
+24
to
+32
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. NextJS was erroring because there's a circular dependency. |
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,47 @@ | ||
| import { ImperativeEditorRef } from '../index.js'; | ||
| import { title as annotatedTitle } from '@/annotations/index.js'; | ||
| import { dockerSelector } from '@/redux/selectors/refs.js'; | ||
| import { useCallback } from 'react'; | ||
| import { useSelector } from 'react-redux'; | ||
|
|
||
| export const useSubgraphExplorerCallback = (node) => { | ||
| const dockerRef = useSelector(dockerSelector); | ||
|
|
||
| const callback = useCallback(() => { | ||
| if (!dockerRef?.current) { | ||
| return; | ||
| } | ||
|
|
||
| let oneShot = false; | ||
| const innerGraph = node['_innerGraph']; | ||
| const graphId = innerGraph.annotations['engine.id']; | ||
| const title = | ||
| node.annotations[annotatedTitle] || | ||
| innerGraph.annotations['engine.title'] || | ||
| 'Subgraph'; | ||
| const existing = dockerRef.current.find(graphId); | ||
|
|
||
| if (!existing) { | ||
| const ref = (o: ImperativeEditorRef) => { | ||
| if (o && !oneShot) { | ||
| o.load(innerGraph); | ||
| oneShot = true; | ||
| } | ||
| }; | ||
|
|
||
| const newTab = { | ||
| cached: true, | ||
| closable: true, | ||
| id: graphId, | ||
| group: 'graph', | ||
| title, | ||
| content: window && window['newGraphEditor'](ref, graphId), | ||
| }; | ||
| dockerRef.current.dockMove(newTab, 'graphs', 'middle'); | ||
| } else { | ||
| dockerRef.current.updateTab(graphId, null, true); | ||
| } | ||
| }, [dockerRef, node['_innerGraph'], node.annotations]); | ||
|
|
||
| return callback; | ||
| }; |


Uh oh!
There was an error while loading. Please reload this page.