@likec4/diagram 1.3.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.
Files changed (95) hide show
  1. package/dist/LikeC4Diagram.css.d.ts +0 -1
  2. package/dist/LikeC4Diagram.d.ts +1 -1
  3. package/dist/LikeC4Diagram.props.d.ts +7 -2
  4. package/dist/index.d.ts +1 -1
  5. package/dist/index.mjs +1580 -1194
  6. package/dist/state/DiagramContext.d.ts +97 -4169
  7. package/dist/state/diagramStore.d.ts +98 -4172
  8. package/dist/state/hooks.d.ts +4 -0
  9. package/dist/state/index.d.ts +1 -3
  10. package/dist/state/useDiagramStore.d.ts +3 -4622
  11. package/dist/state/useNavigationHistory.d.ts +5 -0
  12. package/dist/style.css +217 -159
  13. package/dist/theme.css.d.ts +5 -0
  14. package/dist/ui/BackwardForwardPanel.css.d.ts +1 -0
  15. package/dist/ui/BackwardForwardPanel.d.ts +1 -0
  16. package/dist/ui/DiagramTitlePanel.css.d.ts +1 -1
  17. package/dist/ui/OptionsPanel.d.ts +1 -3
  18. package/dist/ui/options/EdgeOptions.d.ts +19 -0
  19. package/dist/ui/options/NodeOptions.d.ts +2 -2
  20. package/dist/xyflow/EdgeMarkers.d.ts +11 -0
  21. package/dist/xyflow/XYFlowEvents.d.ts +12 -9
  22. package/dist/xyflow/XYFlowInner.d.ts +1 -0
  23. package/dist/xyflow/const.d.ts +3 -3
  24. package/dist/xyflow/diagram-to-xyflow.d.ts +1 -1
  25. package/dist/xyflow/edges/RelationshipEdge.d.ts +4 -3
  26. package/dist/xyflow/edges/edges.css.d.ts +1 -0
  27. package/dist/xyflow/edges/utils.d.ts +1 -0
  28. package/dist/xyflow/hooks/index.d.ts +3 -2
  29. package/dist/xyflow/hooks/useXYFlow.d.ts +22 -21
  30. package/dist/xyflow/nodes/compound/CompoundNode.css.d.ts +1 -0
  31. package/dist/xyflow/nodes/element/ElementIcon.d.ts +1 -4
  32. package/dist/xyflow/nodes/element/element.css.d.ts +3 -1
  33. package/dist/xyflow/types.d.ts +2 -32
  34. package/package.json +21 -21
  35. package/src/LikeC4Diagram.css.ts +10 -8
  36. package/src/LikeC4Diagram.props.ts +12 -5
  37. package/src/LikeC4Diagram.tsx +43 -26
  38. package/src/globals.css.ts +11 -4
  39. package/src/index.ts +1 -1
  40. package/src/mantine.css.ts +2 -2
  41. package/src/state/DiagramContext.tsx +47 -16
  42. package/src/state/diagramStore.ts +89 -80
  43. package/src/state/hooks.ts +5 -0
  44. package/src/state/index.ts +1 -3
  45. package/src/state/useDiagramStore.ts +6 -13
  46. package/src/state/useNavigationHistory.ts +39 -0
  47. package/src/theme.css.ts +5 -0
  48. package/src/ui/BackwardForwardPanel.css.ts +22 -0
  49. package/src/ui/BackwardForwardPanel.tsx +59 -0
  50. package/src/ui/DiagramTitlePanel.css.ts +17 -6
  51. package/src/ui/DiagramTitlePanel.tsx +108 -94
  52. package/src/ui/DynamicViewWalkthrough.tsx +6 -4
  53. package/src/ui/OptionsPanel.tsx +37 -18
  54. package/src/ui/options/EdgeOptions.tsx +157 -0
  55. package/src/ui/options/NodeOptions.tsx +37 -29
  56. package/src/xyflow/EdgeMarkers.tsx +148 -0
  57. package/src/xyflow/FitviewOnDiagramChange.tsx +8 -4
  58. package/src/xyflow/SelectEdgesOnNodeFocus.tsx +59 -55
  59. package/src/xyflow/SyncWithDiagram.tsx +13 -20
  60. package/src/xyflow/XYFlow.tsx +2 -4
  61. package/src/xyflow/XYFlowEvents.tsx +48 -33
  62. package/src/xyflow/XYFlowInner.tsx +6 -3
  63. package/src/xyflow/const.ts +3 -3
  64. package/src/xyflow/diagram-to-xyflow.ts +18 -53
  65. package/src/xyflow/edges/RelationshipEdge.tsx +218 -126
  66. package/src/xyflow/edges/edges.css.ts +44 -26
  67. package/src/xyflow/edges/utils.ts +10 -0
  68. package/src/xyflow/hooks/index.ts +4 -2
  69. package/src/xyflow/hooks/useLayoutConstraints.ts +5 -5
  70. package/src/xyflow/hooks/useXYFlow.ts +28 -1
  71. package/src/xyflow/nodes/compound/CompoundNode.css.ts +41 -31
  72. package/src/xyflow/nodes/compound/CompoundNode.tsx +52 -53
  73. package/src/xyflow/nodes/element/ElementIcon.tsx +11 -22
  74. package/src/xyflow/nodes/element/ElementLink.tsx +11 -16
  75. package/src/xyflow/nodes/element/ElementNode.tsx +69 -114
  76. package/src/xyflow/nodes/element/element.css.ts +48 -15
  77. package/src/xyflow/nodes/shared/NavigateToBtn.tsx +1 -1
  78. package/src/xyflow/types.ts +2 -4
  79. package/src/xyflow/utils.ts +3 -6
  80. package/dist/KeepAspectRatioContainer.d.ts +0 -9
  81. package/dist/icons/Clipboard.d.ts +0 -3
  82. package/dist/icons/Link.d.ts +0 -3
  83. package/dist/icons/ZoomIn.d.ts +0 -3
  84. package/dist/icons/index.d.ts +0 -3
  85. package/dist/mantine/theme.d.ts +0 -3
  86. package/dist/state/WhenInitialized.d.ts +0 -3
  87. package/dist/ui/Camera.d.ts +0 -3
  88. package/src/KeepAspectRatioContainer.tsx +0 -36
  89. package/src/icons/Clipboard.tsx +0 -24
  90. package/src/icons/Link.tsx +0 -23
  91. package/src/icons/ZoomIn.tsx +0 -25
  92. package/src/icons/index.ts +0 -3
  93. package/src/mantine/theme.ts +0 -20
  94. package/src/state/WhenInitialized.tsx +0 -24
  95. package/src/ui/Camera.tsx +0 -174
@@ -14,16 +14,17 @@ import type { XYPosition } from '@xyflow/system'
14
14
  import type { MouseEvent as ReactMouseEvent } from 'react'
15
15
  import type { SetRequired, Simplify } from 'type-fest'
16
16
  import type { XYFlowEdge, XYFlowNode } from './xyflow/types'
17
- import type { XYBackground, XYBackgroundProps } from './xyflow/XYFlowBackground'
17
+ import type { XYBackground } from './xyflow/XYFlowBackground'
18
18
 
19
19
  export type DiagramNodeWithNavigate = Simplify<SetRequired<DiagramNode, 'navigateTo'>>
20
20
 
21
21
  export type OnNavigateTo = {
22
22
  (
23
23
  to: ViewID,
24
- event: ReactMouseEvent,
25
- element: DiagramNodeWithNavigate,
26
- xynode: XYFlowNode
24
+ // These fields present if navigateTo triggered by a node click
25
+ event?: ReactMouseEvent,
26
+ element?: DiagramNodeWithNavigate,
27
+ xynode?: XYFlowNode
27
28
  ): void
28
29
  }
29
30
  export type OnNodeClick = (
@@ -35,7 +36,7 @@ export type OnNodeClick = (
35
36
  ) => void
36
37
  export type OnEdgeClick = (
37
38
  event: {
38
- relation: DiagramEdge
39
+ edge: DiagramEdge
39
40
  xyedge: XYFlowEdge
40
41
  event: ReactMouseEvent
41
42
  }
@@ -159,6 +160,12 @@ export interface LikeC4DiagramProperties {
159
160
  */
160
161
  showDiagramTitle?: boolean | undefined
161
162
 
163
+ /**
164
+ * Show back/forward navigation buttons
165
+ * @default false
166
+ */
167
+ showNavigationButtons?: undefined | boolean
168
+
162
169
  /**
163
170
  * If Walkthrough for dynamic views should be enabled
164
171
  * @default false
@@ -4,15 +4,14 @@ import clsx from 'clsx'
4
4
  import { DEV } from 'esm-env'
5
5
  import { shallowEqual } from 'fast-equals'
6
6
  import { domAnimation, LazyMotion } from 'framer-motion'
7
- import { memo, useRef } from 'react'
7
+ import { memo, useEffect, useRef, useState } from 'react'
8
8
  import useTilg from 'tilg'
9
9
  import { rootClassName } from './globals.css'
10
- import { KeepAspectRatioContainer } from './KeepAspectRatioContainer'
11
10
  import { cssDisablePan, cssNoControls, cssReactFlow, cssTransparentBg } from './LikeC4Diagram.css'
12
11
  import { type LikeC4DiagramEventHandlers, type LikeC4DiagramProperties } from './LikeC4Diagram.props'
13
12
  import { EnsureMantine } from './mantine/EnsureMantine'
14
13
  import { DiagramContextProvider } from './state/DiagramContext'
15
- import { WhenInitialized } from './state/WhenInitialized'
14
+ import { useDiagramStoreApi } from './state/useDiagramStore'
16
15
  import { diagramViewToXYFlowData } from './xyflow/diagram-to-xyflow'
17
16
  import { FitViewOnDiagramChange } from './xyflow/FitviewOnDiagramChange'
18
17
  import { SelectEdgesOnNodeFocus } from './xyflow/SelectEdgesOnNodeFocus'
@@ -36,6 +35,7 @@ export function LikeC4Diagram({
36
35
  controls = false,
37
36
  showElementLinks = true,
38
37
  showDiagramTitle = true,
38
+ showNavigationButtons = false,
39
39
  enableDynamicViewWalkthrough = true,
40
40
  initialWidth,
41
41
  initialHeight,
@@ -78,6 +78,8 @@ export function LikeC4Diagram({
78
78
  >
79
79
  <DiagramContextProvider
80
80
  view={view}
81
+ keepAspectRatio={keepAspectRatio}
82
+ className={clsx(rootClassName, className)}
81
83
  readonly={readonly}
82
84
  pannable={pannable}
83
85
  zoomable={zoomable}
@@ -98,24 +100,18 @@ export function LikeC4Diagram({
98
100
  onCanvasDblClick={onCanvasDblClick}
99
101
  >
100
102
  <LazyMotion features={domAnimation} strict>
101
- <KeepAspectRatioContainer
102
- className={clsx(rootClassName, className)}
103
- enabled={keepAspectRatio}
104
- width={view.width}
105
- height={view.height}
106
- >
107
- <LikeC4DiagramInnerMemo
108
- defaultNodes={initialRef.current.defaultNodes}
109
- defaultEdges={initialRef.current.defaultEdges}
110
- fitView={fitView}
111
- zoomable={zoomable}
112
- background={background}
113
- controls={controls}
114
- pannable={pannable}
115
- showDiagramTitle={showDiagramTitle}
116
- enableDynamicViewWalkthrough={enableDynamicViewWalkthrough}
117
- />
118
- </KeepAspectRatioContainer>
103
+ <LikeC4DiagramInnerMemo
104
+ defaultNodes={initialRef.current.defaultNodes}
105
+ defaultEdges={initialRef.current.defaultEdges}
106
+ fitView={fitView}
107
+ zoomable={zoomable}
108
+ background={background}
109
+ controls={controls}
110
+ pannable={pannable}
111
+ showDiagramTitle={showDiagramTitle}
112
+ showNavigationButtons={showNavigationButtons}
113
+ enableDynamicViewWalkthrough={enableDynamicViewWalkthrough}
114
+ />
119
115
  </LazyMotion>
120
116
  </DiagramContextProvider>
121
117
  </XYFlowProvider>
@@ -132,6 +128,7 @@ type LikeC4DiagramInnerProps = {
132
128
  defaultNodes: XYFlowNode[]
133
129
  defaultEdges: XYFlowEdge[]
134
130
  showDiagramTitle: boolean
131
+ showNavigationButtons: boolean
135
132
  enableDynamicViewWalkthrough: boolean
136
133
  }
137
134
  const LikeC4DiagramInnerMemo = memo<LikeC4DiagramInnerProps>(function LikeC4DiagramInner({
@@ -143,9 +140,26 @@ const LikeC4DiagramInnerMemo = memo<LikeC4DiagramInnerProps>(function LikeC4Diag
143
140
  defaultNodes,
144
141
  defaultEdges,
145
142
  showDiagramTitle,
143
+ showNavigationButtons,
146
144
  enableDynamicViewWalkthrough
147
145
  }) {
148
146
  DEV && useTilg()
147
+ const diagramApi = useDiagramStoreApi()
148
+ const [isInitialized, setIsInitialized] = useState(diagramApi.getState().initialized)
149
+
150
+ useEffect(() => {
151
+ if (isInitialized) {
152
+ return
153
+ }
154
+ return diagramApi.subscribe(
155
+ s => s.initialized,
156
+ setIsInitialized,
157
+ {
158
+ fireImmediately: true
159
+ }
160
+ )
161
+ }, [isInitialized])
162
+
149
163
  return (
150
164
  <>
151
165
  <XYFlow
@@ -160,17 +174,20 @@ const LikeC4DiagramInnerMemo = memo<LikeC4DiagramInnerProps>(function LikeC4Diag
160
174
  )}
161
175
  >
162
176
  <XYFlowInner
177
+ showNavigationButtons={showNavigationButtons}
163
178
  showDiagramTitle={showDiagramTitle}
164
179
  enableDynamicViewWalkthrough={enableDynamicViewWalkthrough}
165
180
  background={background}
166
181
  controls={controls}
167
182
  />
168
183
  </XYFlow>
169
- <WhenInitialized>
170
- <SyncWithDiagram />
171
- {fitView && <FitViewOnDiagramChange />}
172
- {fitView && zoomable && <SelectEdgesOnNodeFocus />}
173
- </WhenInitialized>
184
+ {isInitialized && (
185
+ <>
186
+ <SyncWithDiagram />
187
+ {fitView && <FitViewOnDiagramChange />}
188
+ {fitView && zoomable && <SelectEdgesOnNodeFocus />}
189
+ </>
190
+ )}
174
191
  </>
175
192
  )
176
193
  }, shallowEqual)
@@ -8,12 +8,19 @@ import { vars } from './theme.css'
8
8
  export const rootClassName = 'likec4-diagram-root'
9
9
 
10
10
  globalStyle(`.${rootClassName}`, {
11
+ overflow: 'hidden',
12
+ position: 'relative',
13
+ width: '100%',
14
+ height: '100%',
15
+ padding: 0,
16
+ margin: 0,
17
+ boxSizing: 'border-box',
11
18
  WebkitFontSmoothing: mantine.webkitFontSmoothing,
12
19
  MozOsxFontSmoothing: mantine.mozFontSmoothing
13
20
  })
14
21
 
15
22
  createGlobalTheme(`.${rootClassName}`, {
16
- ...omit(vars, ['optionsPanel', 'default'])
23
+ ...omit(vars, ['optionsPanel', 'navigationPanel', 'safariAnimationHook', 'default'])
17
24
  }, {
18
25
  likec4: {
19
26
  font:
@@ -21,7 +28,7 @@ createGlobalTheme(`.${rootClassName}`, {
21
28
  background: {
22
29
  color: mantine.colors.body,
23
30
  pattern: {
24
- color: mantine.colors.dimmed
31
+ color: mantine.colors.dark[3]
25
32
  }
26
33
  }
27
34
  },
@@ -74,13 +81,13 @@ createGlobalTheme(`:where([data-mantine-color-scheme='light']) .${rootClassName}
74
81
  likec4: {
75
82
  background: {
76
83
  pattern: {
77
- color: mantine.colors.dark[3]
84
+ color: mantine.colors.gray[6]
78
85
  }
79
86
  }
80
87
  }
81
88
  })
82
89
 
83
- for (const color of keys.strict(defaultTheme.elements)) {
90
+ for (const color of keys(defaultTheme.elements)) {
84
91
  createGlobalTheme(`:where([data-likec4-color='${color}'])`, {
85
92
  element: {
86
93
  fill: vars.element.fill,
package/src/index.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  export { LikeC4Diagram } from './LikeC4Diagram'
2
2
  export { StaticLikeC4Diagram } from './StaticLikeC4Diagram'
3
3
 
4
- export * from './hooks'
4
+ export { useSetState, useUpdateEffect } from './hooks'
5
5
  export type * from './LikeC4Diagram'
6
6
  export type * from './LikeC4Diagram.props'
7
7
  export type * from './StaticLikeC4Diagram'
@@ -1,4 +1,4 @@
1
1
  import { themeToVars } from '@mantine/vanilla-extract'
2
- import { theme } from './mantine/theme'
3
2
 
4
- export const mantine = themeToVars(theme)
3
+ // we need only variables names
4
+ export const mantine = themeToVars({})
@@ -1,31 +1,51 @@
1
+ import { shallowEqual } from 'fast-equals'
1
2
  import { createContext, type PropsWithChildren, useEffect, useRef } from 'react'
2
- import { hasSubObject, isIncludedIn } from 'remeda'
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
- type DiagramContextValue = ReturnType<typeof createDiagramStore>
8
- export const DiagramContext = createContext<DiagramContextValue | null>(null)
8
+ export type DiagramZustandStore = ReturnType<typeof createDiagramStore>
9
+ export const DiagramContext = createContext<DiagramZustandStore | null>(null)
9
10
 
10
- export function DiagramContextProvider({ children, view, ...props }: PropsWithChildren<DiagramInitialState>) {
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
- const store = useRef<DiagramContextValue>()
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?.getState().xyflow !== xyflow) {
25
- store.current?.setState({ xyflow }, false, 'update xyflow')
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
- if (!hasSubObject(state, props)) {
39
- store.current.setState(props, false, 'update incoming props')
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
- <DiagramContext.Provider value={store.current}>
47
- {children}
48
- </DiagramContext.Provider>
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
  }