@likec4/diagram 1.4.0 → 1.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/LikeC4Diagram.css.d.ts +0 -1
- package/dist/LikeC4Diagram.props.d.ts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.mjs +1398 -1114
- package/dist/state/DiagramContext.d.ts +86 -4159
- package/dist/state/diagramStore.d.ts +88 -4162
- package/dist/state/hooks.d.ts +4 -0
- package/dist/state/index.d.ts +1 -2
- package/dist/style.css +182 -156
- package/dist/ui/options/EdgeOptions.d.ts +19 -0
- package/dist/ui/options/NodeOptions.d.ts +2 -2
- package/dist/xyflow/EdgeMarkers.d.ts +11 -0
- package/dist/xyflow/XYFlowEvents.d.ts +8 -5
- package/dist/xyflow/const.d.ts +3 -3
- package/dist/xyflow/diagram-to-xyflow.d.ts +1 -1
- package/dist/xyflow/edges/RelationshipEdge.d.ts +3 -2
- package/dist/xyflow/edges/edges.css.d.ts +1 -0
- package/dist/xyflow/edges/utils.d.ts +1 -0
- package/dist/xyflow/hooks/index.d.ts +3 -2
- package/dist/xyflow/hooks/useXYFlow.d.ts +13 -12
- package/dist/xyflow/nodes/compound/CompoundNode.css.d.ts +1 -0
- package/dist/xyflow/nodes/element/ElementIcon.d.ts +1 -4
- package/dist/xyflow/nodes/element/element.css.d.ts +2 -1
- package/dist/xyflow/types.d.ts +2 -32
- package/package.json +19 -19
- package/src/LikeC4Diagram.css.ts +0 -8
- package/src/LikeC4Diagram.props.ts +2 -2
- package/src/LikeC4Diagram.tsx +14 -20
- package/src/globals.css.ts +10 -3
- package/src/index.ts +1 -1
- package/src/mantine.css.ts +2 -2
- package/src/state/DiagramContext.tsx +44 -13
- package/src/state/diagramStore.ts +64 -52
- package/src/state/hooks.ts +5 -0
- package/src/state/index.ts +1 -2
- package/src/ui/BackwardForwardPanel.css.ts +9 -9
- package/src/ui/BackwardForwardPanel.tsx +1 -1
- package/src/ui/DiagramTitlePanel.css.ts +7 -2
- package/src/ui/DiagramTitlePanel.tsx +5 -3
- package/src/ui/DynamicViewWalkthrough.tsx +6 -4
- package/src/ui/OptionsPanel.tsx +37 -27
- package/src/ui/options/EdgeOptions.tsx +157 -0
- package/src/ui/options/NodeOptions.tsx +37 -29
- package/src/xyflow/EdgeMarkers.tsx +148 -0
- package/src/xyflow/FitviewOnDiagramChange.tsx +8 -4
- package/src/xyflow/SelectEdgesOnNodeFocus.tsx +59 -55
- package/src/xyflow/SyncWithDiagram.tsx +13 -20
- package/src/xyflow/XYFlow.tsx +2 -4
- package/src/xyflow/XYFlowEvents.tsx +48 -33
- package/src/xyflow/XYFlowInner.tsx +1 -1
- package/src/xyflow/const.ts +3 -3
- package/src/xyflow/diagram-to-xyflow.ts +17 -41
- package/src/xyflow/edges/RelationshipEdge.tsx +207 -118
- package/src/xyflow/edges/edges.css.ts +36 -22
- package/src/xyflow/edges/utils.ts +10 -0
- package/src/xyflow/hooks/index.ts +4 -2
- package/src/xyflow/hooks/useLayoutConstraints.ts +3 -4
- package/src/xyflow/hooks/useXYFlow.ts +28 -1
- package/src/xyflow/nodes/compound/CompoundNode.css.ts +34 -24
- package/src/xyflow/nodes/compound/CompoundNode.tsx +52 -53
- package/src/xyflow/nodes/element/ElementIcon.tsx +11 -22
- package/src/xyflow/nodes/element/ElementLink.tsx +11 -16
- package/src/xyflow/nodes/element/ElementNode.tsx +64 -108
- package/src/xyflow/nodes/element/element.css.ts +30 -10
- package/src/xyflow/nodes/shared/NavigateToBtn.tsx +1 -1
- package/src/xyflow/types.ts +2 -4
- package/src/xyflow/utils.ts +3 -6
- package/dist/KeepAspectRatioContainer.d.ts +0 -9
- package/dist/icons/Clipboard.d.ts +0 -3
- package/dist/icons/Link.d.ts +0 -3
- package/dist/icons/ZoomIn.d.ts +0 -3
- package/dist/icons/index.d.ts +0 -3
- package/dist/mantine/theme.d.ts +0 -3
- package/dist/state/WhenInitialized.d.ts +0 -3
- package/dist/ui/Camera.d.ts +0 -3
- package/src/KeepAspectRatioContainer.tsx +0 -36
- package/src/icons/Clipboard.tsx +0 -24
- package/src/icons/Link.tsx +0 -23
- package/src/icons/ZoomIn.tsx +0 -25
- package/src/icons/index.ts +0 -3
- package/src/mantine/theme.ts +0 -20
- package/src/state/WhenInitialized.tsx +0 -24
- package/src/ui/Camera.tsx +0 -174
|
@@ -1,31 +1,51 @@
|
|
|
1
|
+
import { shallowEqual } from 'fast-equals'
|
|
1
2
|
import { createContext, type PropsWithChildren, useEffect, useRef } from 'react'
|
|
2
|
-
import { hasSubObject,
|
|
3
|
+
import { hasSubObject, isNonNullish, pickBy } from 'remeda'
|
|
3
4
|
import { useUpdateEffect } from '../hooks'
|
|
4
|
-
import { useXYFlow } from '../xyflow/hooks'
|
|
5
|
+
import { useXYFlow, useXYStoreApi } from '../xyflow/hooks'
|
|
5
6
|
import { createDiagramStore, type DiagramInitialState } from './diagramStore'
|
|
6
7
|
|
|
7
8
|
export type DiagramZustandStore = ReturnType<typeof createDiagramStore>
|
|
8
9
|
export const DiagramContext = createContext<DiagramZustandStore | null>(null)
|
|
9
10
|
|
|
10
|
-
export
|
|
11
|
+
export type DiagramContextProviderProps = PropsWithChildren<
|
|
12
|
+
DiagramInitialState & {
|
|
13
|
+
className: string
|
|
14
|
+
keepAspectRatio: boolean
|
|
15
|
+
}
|
|
16
|
+
>
|
|
17
|
+
|
|
18
|
+
export function DiagramContextProvider({
|
|
19
|
+
children,
|
|
20
|
+
view,
|
|
21
|
+
className,
|
|
22
|
+
keepAspectRatio,
|
|
23
|
+
...props
|
|
24
|
+
}: DiagramContextProviderProps) {
|
|
25
|
+
const containerRef = useRef<HTMLDivElement>(null)
|
|
26
|
+
const xystore = useXYStoreApi()
|
|
11
27
|
const xyflow = useXYFlow()
|
|
12
28
|
const store = useRef<DiagramZustandStore>()
|
|
13
29
|
|
|
14
30
|
if (!store.current) {
|
|
15
31
|
store.current = createDiagramStore({
|
|
32
|
+
xystore,
|
|
16
33
|
xyflow,
|
|
17
34
|
view,
|
|
35
|
+
getContainer: () => containerRef.current,
|
|
18
36
|
...props
|
|
19
37
|
})
|
|
20
38
|
}
|
|
21
39
|
|
|
22
40
|
useEffect(
|
|
23
41
|
() => {
|
|
24
|
-
if (store.current
|
|
25
|
-
|
|
42
|
+
if (!store.current) return
|
|
43
|
+
const state = store.current.getState()
|
|
44
|
+
if (state.xyflow !== xyflow || state.xystore !== xystore) {
|
|
45
|
+
store.current.setState({ xyflow, xystore }, false, 'update xyflow and xystore')
|
|
26
46
|
}
|
|
27
47
|
},
|
|
28
|
-
[xyflow]
|
|
48
|
+
[xyflow, xystore]
|
|
29
49
|
)
|
|
30
50
|
|
|
31
51
|
useUpdateEffect(
|
|
@@ -35,16 +55,27 @@ export function DiagramContextProvider({ children, view, ...props }: PropsWithCh
|
|
|
35
55
|
if (state.view !== view) {
|
|
36
56
|
state.updateView(view)
|
|
37
57
|
}
|
|
38
|
-
|
|
39
|
-
|
|
58
|
+
const newProps = pickBy(props, isNonNullish)
|
|
59
|
+
if (!hasSubObject(state, newProps)) {
|
|
60
|
+
store.current.setState(newProps, false, 'update incoming props')
|
|
40
61
|
}
|
|
41
62
|
},
|
|
42
|
-
[view, props]
|
|
63
|
+
[view, props],
|
|
64
|
+
shallowEqual
|
|
43
65
|
)
|
|
44
|
-
|
|
45
66
|
return (
|
|
46
|
-
<
|
|
47
|
-
{
|
|
48
|
-
|
|
67
|
+
<div
|
|
68
|
+
ref={containerRef}
|
|
69
|
+
className={className}
|
|
70
|
+
{...(keepAspectRatio && {
|
|
71
|
+
style: {
|
|
72
|
+
aspectRatio: `${Math.ceil(view.width)}/${Math.ceil(view.height)}`,
|
|
73
|
+
maxHeight: Math.ceil(view.height)
|
|
74
|
+
}
|
|
75
|
+
})}>
|
|
76
|
+
<DiagramContext.Provider value={store.current}>
|
|
77
|
+
{children}
|
|
78
|
+
</DiagramContext.Provider>
|
|
79
|
+
</div>
|
|
49
80
|
)
|
|
50
81
|
}
|
|
@@ -17,6 +17,7 @@ import type { XYFlowEdge, XYFlowInstance, XYFlowNode } from '../xyflow/types'
|
|
|
17
17
|
import { bezierControlPoints, isSamePoint } from '../xyflow/utils'
|
|
18
18
|
|
|
19
19
|
export type DiagramStore = {
|
|
20
|
+
readonly storeDevId: string
|
|
20
21
|
// Incoming props
|
|
21
22
|
view: DiagramView
|
|
22
23
|
readonly: boolean
|
|
@@ -28,11 +29,14 @@ export type DiagramStore = {
|
|
|
28
29
|
nodesDraggable: boolean
|
|
29
30
|
nodesSelectable: boolean
|
|
30
31
|
experimentalEdgeEditing: boolean
|
|
32
|
+
// Diagram Container, see DiagramContainer.tsx
|
|
33
|
+
getContainer: () => HTMLDivElement | null
|
|
31
34
|
|
|
32
35
|
// Internal state
|
|
33
36
|
viewSyncDebounceTimeout: number | null
|
|
34
37
|
|
|
35
38
|
xyflow: XYFlowInstance
|
|
39
|
+
xystore: XYStoreApi
|
|
36
40
|
initialized: boolean
|
|
37
41
|
xyflowSynced: boolean
|
|
38
42
|
|
|
@@ -82,13 +86,6 @@ export type DiagramInitialState = // Required properties
|
|
|
82
86
|
| 'nodesSelectable'
|
|
83
87
|
| 'experimentalEdgeEditing'
|
|
84
88
|
>
|
|
85
|
-
// Optional properties
|
|
86
|
-
// & Partial<
|
|
87
|
-
// Pick<
|
|
88
|
-
// DiagramStore,
|
|
89
|
-
// 'colorScheme'
|
|
90
|
-
// >
|
|
91
|
-
// >
|
|
92
89
|
& LikeC4DiagramEventHandlers
|
|
93
90
|
|
|
94
91
|
interface DiagramStoreActions {
|
|
@@ -110,9 +107,9 @@ interface DiagramStoreActions {
|
|
|
110
107
|
triggerChangeElementStyle: (change: Changes.ChangeElementStyle) => void
|
|
111
108
|
|
|
112
109
|
cancelSaveManualLayout: () => void
|
|
113
|
-
triggerSaveManualLayout: (
|
|
110
|
+
triggerSaveManualLayout: () => void
|
|
114
111
|
triggerOnNavigateTo: (xynodeId: string, event: ReactMouseEvent) => void
|
|
115
|
-
fitDiagram: (
|
|
112
|
+
fitDiagram: (duration?: number) => void
|
|
116
113
|
|
|
117
114
|
nextDynamicStep: (increment?: number) => void
|
|
118
115
|
stopDynamicView: () => void
|
|
@@ -120,7 +117,10 @@ interface DiagramStoreActions {
|
|
|
120
117
|
|
|
121
118
|
export type DiagramState = Simplify<DiagramStore & DiagramStoreActions>
|
|
122
119
|
|
|
123
|
-
const DEFAULT_PROPS: Except<
|
|
120
|
+
const DEFAULT_PROPS: Except<
|
|
121
|
+
DiagramStore,
|
|
122
|
+
RequiredKeysOf<DiagramInitialState> | 'xystore' | 'xyflow' | 'getContainer' | 'storeDevId'
|
|
123
|
+
> = {
|
|
124
124
|
viewSyncDebounceTimeout: null,
|
|
125
125
|
initialized: false,
|
|
126
126
|
xyflowSynced: false,
|
|
@@ -145,15 +145,16 @@ const DEFAULT_PROPS: Except<DiagramStore, RequiredKeysOf<DiagramInitialState> |
|
|
|
145
145
|
onCanvasDblClick: null
|
|
146
146
|
}
|
|
147
147
|
|
|
148
|
-
type CreateDiagramStore = DiagramInitialState & Pick<DiagramStore, 'xyflow'>
|
|
148
|
+
export type CreateDiagramStore = DiagramInitialState & Pick<DiagramStore, 'xystore' | 'xyflow' | 'getContainer'>
|
|
149
149
|
|
|
150
150
|
const noReplace = false
|
|
151
151
|
|
|
152
|
-
let
|
|
152
|
+
let StoreDevId = 1
|
|
153
153
|
|
|
154
154
|
const EmptyStringSet: ReadonlySet<string> = new StringSet()
|
|
155
155
|
|
|
156
156
|
export function createDiagramStore<T extends Exact<CreateDiagramStore, T>>(props: T) {
|
|
157
|
+
const storeDevId = 'DiagramStore' + String(StoreDevId++).padStart(2, '0')
|
|
157
158
|
return createWithEqualityFn<
|
|
158
159
|
DiagramState,
|
|
159
160
|
[
|
|
@@ -164,9 +165,9 @@ export function createDiagramStore<T extends Exact<CreateDiagramStore, T>>(props
|
|
|
164
165
|
subscribeWithSelector(
|
|
165
166
|
devtools(
|
|
166
167
|
(set, get) => ({
|
|
168
|
+
storeDevId,
|
|
167
169
|
...DEFAULT_PROPS,
|
|
168
170
|
...(props as CreateDiagramStore),
|
|
169
|
-
|
|
170
171
|
isDynamicView: () => {
|
|
171
172
|
return get().view?.__ === 'dynamic'
|
|
172
173
|
},
|
|
@@ -188,16 +189,16 @@ export function createDiagramStore<T extends Exact<CreateDiagramStore, T>>(props
|
|
|
188
189
|
hoveredNodeId
|
|
189
190
|
} = get()
|
|
190
191
|
|
|
191
|
-
if (viewSyncDebounceTimeout !== null) {
|
|
192
|
-
clearTimeout(viewSyncDebounceTimeout)
|
|
193
|
-
viewSyncDebounceTimeout = null
|
|
194
|
-
}
|
|
195
|
-
|
|
196
192
|
if (shallowEqual(current, nextView)) {
|
|
197
193
|
DEV && console.debug('store: skip updateView')
|
|
198
194
|
return
|
|
199
195
|
}
|
|
200
196
|
|
|
197
|
+
if (viewSyncDebounceTimeout !== null) {
|
|
198
|
+
clearTimeout(viewSyncDebounceTimeout)
|
|
199
|
+
viewSyncDebounceTimeout = null
|
|
200
|
+
}
|
|
201
|
+
|
|
201
202
|
const isSameView = current.id === nextView.id
|
|
202
203
|
|
|
203
204
|
if (isSameView) {
|
|
@@ -275,7 +276,7 @@ export function createDiagramStore<T extends Exact<CreateDiagramStore, T>>(props
|
|
|
275
276
|
dimmed
|
|
276
277
|
},
|
|
277
278
|
noReplace,
|
|
278
|
-
'update-view'
|
|
279
|
+
isSameView ? 'update-view [same]' : 'update-view [another]'
|
|
279
280
|
)
|
|
280
281
|
},
|
|
281
282
|
|
|
@@ -374,7 +375,7 @@ export function createDiagramStore<T extends Exact<CreateDiagramStore, T>>(props
|
|
|
374
375
|
return origin
|
|
375
376
|
}
|
|
376
377
|
let element = origin
|
|
377
|
-
for (const [key, value] of entries
|
|
378
|
+
for (const [key, value] of entries(change.style)) {
|
|
378
379
|
switch (key) {
|
|
379
380
|
case 'shape':
|
|
380
381
|
if (value !== element.shape) {
|
|
@@ -443,7 +444,7 @@ export function createDiagramStore<T extends Exact<CreateDiagramStore, T>>(props
|
|
|
443
444
|
}
|
|
444
445
|
},
|
|
445
446
|
|
|
446
|
-
triggerSaveManualLayout: (
|
|
447
|
+
triggerSaveManualLayout: () => {
|
|
447
448
|
let { viewSyncDebounceTimeout: debounced, onChange } = get()
|
|
448
449
|
if (!onChange) {
|
|
449
450
|
DEV && console.debug('ignore triggerSaveManualLayout, as no onChange handler is set')
|
|
@@ -453,8 +454,8 @@ export function createDiagramStore<T extends Exact<CreateDiagramStore, T>>(props
|
|
|
453
454
|
clearTimeout(debounced)
|
|
454
455
|
}
|
|
455
456
|
debounced = setTimeout(() => {
|
|
457
|
+
const { xyflow, onChange, xystore } = get()
|
|
456
458
|
const { nodeLookup } = xystore.getState()
|
|
457
|
-
const { xyflow, onChange } = get()
|
|
458
459
|
set({ viewSyncDebounceTimeout: null })
|
|
459
460
|
|
|
460
461
|
const movedNodes = new StringSet()
|
|
@@ -500,7 +501,7 @@ export function createDiagramStore<T extends Exact<CreateDiagramStore, T>>(props
|
|
|
500
501
|
}
|
|
501
502
|
|
|
502
503
|
onChange?.({ change })
|
|
503
|
-
}, 2000)
|
|
504
|
+
}, 2000) as any as number // explicit typecast to number to suppress TS error in astro build
|
|
504
505
|
set(
|
|
505
506
|
{
|
|
506
507
|
viewSyncDebounceTimeout: debounced
|
|
@@ -513,8 +514,9 @@ export function createDiagramStore<T extends Exact<CreateDiagramStore, T>>(props
|
|
|
513
514
|
triggerOnNavigateTo: (xynodeId, event) => {
|
|
514
515
|
const { xyflow, view } = get()
|
|
515
516
|
const xynode = xyflow.getInternalNode(xynodeId)
|
|
516
|
-
|
|
517
|
-
|
|
517
|
+
invariant(!!xynode, `node not found: ${xynodeId}`)
|
|
518
|
+
const element = xynode.data.element as DiagramNodeWithNavigate
|
|
519
|
+
invariant(element.navigateTo, `node is not navigable: ${xynodeId}`)
|
|
518
520
|
set(
|
|
519
521
|
{
|
|
520
522
|
lastClickedNodeId: xynodeId,
|
|
@@ -540,20 +542,23 @@ export function createDiagramStore<T extends Exact<CreateDiagramStore, T>>(props
|
|
|
540
542
|
)
|
|
541
543
|
},
|
|
542
544
|
|
|
543
|
-
fitDiagram: (
|
|
544
|
-
const {
|
|
545
|
-
const {
|
|
545
|
+
fitDiagram: (duration = 500) => {
|
|
546
|
+
const { fitViewPadding, view, focusedNodeId, activeDynamicViewStep, xystore } = get()
|
|
547
|
+
const { width, height, panZoom, transform } = xystore.getState()
|
|
548
|
+
|
|
546
549
|
const bounds = {
|
|
547
550
|
x: 0,
|
|
548
551
|
y: 0,
|
|
549
552
|
width: view.width,
|
|
550
553
|
height: view.height
|
|
551
554
|
}
|
|
552
|
-
const
|
|
553
|
-
|
|
554
|
-
|
|
555
|
+
const maxZoom = Math.max(1, transform[2])
|
|
556
|
+
const viewport = getViewportForBounds(bounds, width, height, MinZoom, maxZoom, fitViewPadding)
|
|
557
|
+
panZoom?.setViewport(viewport, { duration })
|
|
558
|
+
if ((focusedNodeId ?? activeDynamicViewStep) !== null) {
|
|
555
559
|
set(
|
|
556
560
|
{
|
|
561
|
+
activeDynamicViewStep: null,
|
|
557
562
|
focusedNodeId: null,
|
|
558
563
|
dimmed: EmptyStringSet
|
|
559
564
|
},
|
|
@@ -564,7 +569,7 @@ export function createDiagramStore<T extends Exact<CreateDiagramStore, T>>(props
|
|
|
564
569
|
},
|
|
565
570
|
|
|
566
571
|
nextDynamicStep: (increment = 1) => {
|
|
567
|
-
const { view, activeDynamicViewStep, xyflow, fitViewPadding } = get()
|
|
572
|
+
const { view, activeDynamicViewStep, xyflow, xystore, fitViewPadding } = get()
|
|
568
573
|
invariant(view.__ === 'dynamic', 'view is not dynamic')
|
|
569
574
|
const nextStep = (activeDynamicViewStep ?? 0) + increment
|
|
570
575
|
if (nextStep <= 0 || nextStep > view.edges.length) {
|
|
@@ -589,36 +594,43 @@ export function createDiagramStore<T extends Exact<CreateDiagramStore, T>>(props
|
|
|
589
594
|
}
|
|
590
595
|
dimmed.add(n.id)
|
|
591
596
|
}
|
|
592
|
-
|
|
597
|
+
const { fitView, transform } = xystore.getState()
|
|
598
|
+
fitView({
|
|
593
599
|
duration: 400,
|
|
594
|
-
|
|
600
|
+
includeHiddenNodes: true,
|
|
601
|
+
maxZoom: Math.max(1, transform[2]),
|
|
595
602
|
minZoom: MinZoom,
|
|
596
|
-
padding: fitViewPadding,
|
|
603
|
+
padding: Math.max(fitViewPadding, 0.2),
|
|
597
604
|
nodes: selected
|
|
598
605
|
})
|
|
599
|
-
set(
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
606
|
+
set(
|
|
607
|
+
{
|
|
608
|
+
focusedNodeId: null,
|
|
609
|
+
activeDynamicViewStep: nextStep,
|
|
610
|
+
dimmed
|
|
611
|
+
},
|
|
612
|
+
noReplace,
|
|
613
|
+
'nextDynamicStep'
|
|
614
|
+
)
|
|
604
615
|
},
|
|
605
616
|
|
|
606
617
|
stopDynamicView: () => {
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
+
if (get().activeDynamicViewStep !== null) {
|
|
619
|
+
set(
|
|
620
|
+
{
|
|
621
|
+
activeDynamicViewStep: null,
|
|
622
|
+
focusedNodeId: null,
|
|
623
|
+
dimmed: EmptyStringSet
|
|
624
|
+
},
|
|
625
|
+
noReplace,
|
|
626
|
+
'stopDynamicView'
|
|
627
|
+
)
|
|
628
|
+
get().fitDiagram()
|
|
629
|
+
}
|
|
618
630
|
}
|
|
619
631
|
}),
|
|
620
632
|
{
|
|
621
|
-
name:
|
|
633
|
+
name: `${storeDevId} - ${props.view.id}`,
|
|
622
634
|
enabled: DEV
|
|
623
635
|
}
|
|
624
636
|
)
|
package/src/state/index.ts
CHANGED
|
@@ -3,20 +3,20 @@ import { mantine } from '../mantine.css'
|
|
|
3
3
|
import { vars } from '../theme.css'
|
|
4
4
|
|
|
5
5
|
export const panel = style({
|
|
6
|
-
top: fallbackVar(vars.navigationPanel.top, '
|
|
6
|
+
top: fallbackVar(vars.navigationPanel.top, '0.8rem'),
|
|
7
7
|
left: fallbackVar(vars.navigationPanel.left, '1rem'),
|
|
8
8
|
margin: 0
|
|
9
9
|
})
|
|
10
10
|
|
|
11
11
|
globalStyle(`${panel} .mantine-ActionIcon-root`, {
|
|
12
12
|
vars: {
|
|
13
|
-
['--ai-size']: 'var(--ai-size-
|
|
14
|
-
},
|
|
15
|
-
'@media': {
|
|
16
|
-
[mantine.largerThan('md')]: {
|
|
17
|
-
vars: {
|
|
18
|
-
['--ai-size']: 'var(--ai-size-lg)'
|
|
19
|
-
}
|
|
20
|
-
}
|
|
13
|
+
['--ai-size']: 'var(--ai-size-lg)'
|
|
21
14
|
}
|
|
15
|
+
// '@media': {
|
|
16
|
+
// [mantine.largerThan('lg')]: {
|
|
17
|
+
// vars: {
|
|
18
|
+
// ['--ai-size']: 'var(--ai-size-xl)'
|
|
19
|
+
// }
|
|
20
|
+
// }
|
|
21
|
+
// }
|
|
22
22
|
})
|
|
@@ -2,7 +2,7 @@ import { ActionIcon, Group } from '@mantine/core'
|
|
|
2
2
|
import { IconChevronLeft, IconChevronRight } from '@tabler/icons-react'
|
|
3
3
|
import clsx from 'clsx'
|
|
4
4
|
import { AnimatePresence, m } from 'framer-motion'
|
|
5
|
-
import { useNavigationHistory } from '../state/
|
|
5
|
+
import { useNavigationHistory } from '../state/hooks'
|
|
6
6
|
import * as css from './BackwardForwardPanel.css'
|
|
7
7
|
|
|
8
8
|
export function BackwardForwardPanel() {
|
|
@@ -11,14 +11,19 @@ export const container = style({
|
|
|
11
11
|
|
|
12
12
|
export const card = style({
|
|
13
13
|
cursor: 'default',
|
|
14
|
-
minWidth:
|
|
14
|
+
minWidth: 200,
|
|
15
15
|
maxWidth: 'calc(100vw - 16px)',
|
|
16
16
|
backgroundColor: `color-mix(in srgb, ${mantine.colors.body}, transparent 20%)`,
|
|
17
17
|
WebkitBackdropFilter: fallbackVar(vars.safariAnimationHook, 'blur(8px)'),
|
|
18
18
|
backdropFilter: fallbackVar(vars.safariAnimationHook, 'blur(8px)'),
|
|
19
19
|
'@media': {
|
|
20
|
+
[mantine.largerThan('sm')]: {
|
|
21
|
+
minWidth: 300,
|
|
22
|
+
maxWidth: '90vw'
|
|
23
|
+
},
|
|
20
24
|
[mantine.largerThan('md')]: {
|
|
21
|
-
|
|
25
|
+
minWidth: 400,
|
|
26
|
+
maxWidth: '70vw'
|
|
22
27
|
}
|
|
23
28
|
},
|
|
24
29
|
selectors: {
|
|
@@ -3,7 +3,7 @@ import { useLocalStorage } from '@mantine/hooks'
|
|
|
3
3
|
import { IconMenu } from '@tabler/icons-react'
|
|
4
4
|
import clsx from 'clsx'
|
|
5
5
|
import { AnimatePresence, m } from 'framer-motion'
|
|
6
|
-
import { type DiagramState, useDiagramState } from '../state'
|
|
6
|
+
import { type DiagramState, useDiagramState } from '../state/hooks'
|
|
7
7
|
import * as css from './DiagramTitlePanel.css'
|
|
8
8
|
|
|
9
9
|
const selector = (s: DiagramState) => ({
|
|
@@ -39,16 +39,18 @@ export default function DiagramTitlePanel() {
|
|
|
39
39
|
<Card
|
|
40
40
|
radius="sm"
|
|
41
41
|
className={css.card}
|
|
42
|
+
withBorder
|
|
42
43
|
p={'md'}
|
|
44
|
+
pb={isCollapsed ? 'sm' : 'md'}
|
|
43
45
|
onDoubleClick={e => e.stopPropagation()}>
|
|
44
|
-
<CardSection mb={
|
|
46
|
+
<CardSection mb={isCollapsed ? 10 : 'sm'}>
|
|
45
47
|
<Button
|
|
46
48
|
fullWidth
|
|
47
49
|
size="xs"
|
|
48
50
|
h={'sm'}
|
|
49
51
|
py={2}
|
|
50
52
|
radius={'0'}
|
|
51
|
-
variant=
|
|
53
|
+
variant={isCollapsed ? 'light' : 'subtle'}
|
|
52
54
|
color="gray"
|
|
53
55
|
onClick={toggle}
|
|
54
56
|
>
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Box, Button } from '@mantine/core'
|
|
1
|
+
import { Box, Button, ButtonGroup } from '@mantine/core'
|
|
2
2
|
import { useHotkeys } from '@mantine/hooks'
|
|
3
3
|
import {
|
|
4
4
|
IconPlayerPlayFilled,
|
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
} from '@tabler/icons-react'
|
|
9
9
|
import clsx from 'clsx'
|
|
10
10
|
import { isNullish, isNumber } from 'remeda'
|
|
11
|
-
import { useDiagramState } from '../state/
|
|
11
|
+
import { useDiagramState } from '../state/hooks'
|
|
12
12
|
import { useXYStore } from '../xyflow/hooks/useXYFlow'
|
|
13
13
|
import * as css from './DynamicViewWalkthrough.css'
|
|
14
14
|
|
|
@@ -58,6 +58,8 @@ export function DynamicViewWalkthrough() {
|
|
|
58
58
|
return (
|
|
59
59
|
<Box
|
|
60
60
|
className={clsx('react-flow__panel', css.container)}
|
|
61
|
+
onClick={(e) => e.stopPropagation()}
|
|
62
|
+
onDoubleClick={(e) => e.stopPropagation()}
|
|
61
63
|
>
|
|
62
64
|
{isNullish(activeDynamicViewStep)
|
|
63
65
|
&& (
|
|
@@ -71,7 +73,7 @@ export function DynamicViewWalkthrough() {
|
|
|
71
73
|
</Button>
|
|
72
74
|
)}
|
|
73
75
|
{isNumber(activeDynamicViewStep) && (
|
|
74
|
-
<
|
|
76
|
+
<ButtonGroup className={css.buttons}>
|
|
75
77
|
<Button
|
|
76
78
|
{...buttonProps}
|
|
77
79
|
pl={'lg'}
|
|
@@ -98,7 +100,7 @@ export function DynamicViewWalkthrough() {
|
|
|
98
100
|
<IconPlayerSkipForwardFilled />
|
|
99
101
|
</Button>
|
|
100
102
|
)}
|
|
101
|
-
</
|
|
103
|
+
</ButtonGroup>
|
|
102
104
|
)}
|
|
103
105
|
</Box>
|
|
104
106
|
)
|
package/src/ui/OptionsPanel.tsx
CHANGED
|
@@ -1,38 +1,42 @@
|
|
|
1
1
|
import { Card } from '@mantine/core'
|
|
2
|
+
import { useOnSelectionChange } from '@xyflow/react'
|
|
2
3
|
import clsx from 'clsx'
|
|
3
|
-
import {
|
|
4
|
-
import { shallowEqual } from 'fast-equals'
|
|
4
|
+
import { deepEqual as eq } from 'fast-equals'
|
|
5
5
|
import { AnimatePresence, m } from 'framer-motion'
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import useTilg from 'tilg'
|
|
9
|
-
import { useDiagramState } from '../state'
|
|
10
|
-
import { useXYStore } from '../xyflow/hooks'
|
|
6
|
+
import { useState } from 'react'
|
|
7
|
+
import { useDiagramState } from '../state/hooks'
|
|
11
8
|
import { NodeOptions } from './options/NodeOptions'
|
|
12
9
|
import * as styles from './OptionsPanel.css'
|
|
13
10
|
|
|
14
|
-
const Empty = [] as string[]
|
|
15
11
|
export default function OptionsPanel() {
|
|
16
|
-
const {
|
|
12
|
+
const { isFocused, viewId } = useDiagramState(s => ({
|
|
17
13
|
viewId: s.view.id,
|
|
18
|
-
|
|
14
|
+
isFocused: s.focusedNodeId !== null || s.activeDynamicViewStep !== null
|
|
19
15
|
}))
|
|
20
|
-
const
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
16
|
+
const [selected, setSelected] = useState<{
|
|
17
|
+
nodes: string[]
|
|
18
|
+
edges: string[]
|
|
19
|
+
}>({
|
|
20
|
+
nodes: [],
|
|
21
|
+
edges: []
|
|
22
|
+
})
|
|
23
|
+
useOnSelectionChange({
|
|
24
|
+
onChange: ({ nodes, edges }) => {
|
|
25
|
+
const newSelected = {
|
|
26
|
+
nodes: nodes.map(n => n.id).sort(),
|
|
27
|
+
edges: edges.map(e => e.id).sort()
|
|
28
|
+
}
|
|
29
|
+
if (!eq(selected, newSelected)) {
|
|
30
|
+
setSelected(newSelected)
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
})
|
|
34
|
+
const { nodes } = selected
|
|
35
|
+
// const hasAnySelection = !isFocused && (nodes.length > 0 || edges.length > 0)
|
|
36
|
+
const hasAnySelection = !isFocused && nodes.length > 0
|
|
33
37
|
return (
|
|
34
38
|
<AnimatePresence mode="wait">
|
|
35
|
-
{
|
|
39
|
+
{hasAnySelection && (
|
|
36
40
|
<m.div
|
|
37
41
|
key={viewId}
|
|
38
42
|
initial={{ opacity: 0, scale: 0.85 }}
|
|
@@ -44,11 +48,17 @@ export default function OptionsPanel() {
|
|
|
44
48
|
transition={{ duration: 0.14 }}
|
|
45
49
|
className={clsx('react-flow__panel', styles.panel)}
|
|
46
50
|
style={{
|
|
47
|
-
transformOrigin: 'center right'
|
|
51
|
+
transformOrigin: 'center right',
|
|
52
|
+
maxWidth: '320px'
|
|
48
53
|
}}
|
|
49
54
|
>
|
|
50
|
-
<Card shadow="
|
|
51
|
-
<NodeOptions selectedNodeIds={
|
|
55
|
+
<Card shadow="md">
|
|
56
|
+
{nodes.length > 0 && <NodeOptions selectedNodeIds={nodes} />}
|
|
57
|
+
{
|
|
58
|
+
/* nodes.length === 0 && edges.length > 0 && (
|
|
59
|
+
<EdgeOptions selectedEdgeIds={edges} />
|
|
60
|
+
) */
|
|
61
|
+
}
|
|
52
62
|
</Card>
|
|
53
63
|
</m.div>
|
|
54
64
|
)}
|