@likec4/diagram 1.56.0 → 1.57.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 (82) hide show
  1. package/dist/LikeC4Diagram.js +3 -1
  2. package/dist/LikeC4View.js +47 -55
  3. package/dist/adhoc-editor/ElementsTree.js +2 -6
  4. package/dist/components/RootContainer.js +19 -18
  5. package/dist/context/DiagramFeatures.d.ts +1 -1
  6. package/dist/context/DiagramFeatures.js +5 -0
  7. package/dist/context/RootContainerContext.d.ts +10 -17
  8. package/dist/context/RootContainerContext.js +1 -13
  9. package/dist/custom/index.d.ts +1 -1
  10. package/dist/custom/index.js +1 -2
  11. package/dist/editor/LikeC4EditorCallbacks.d.ts +5 -0
  12. package/dist/editor/actor/actions.d.ts +115 -0
  13. package/dist/editor/actor/actions.js +122 -0
  14. package/dist/editor/{hotkeyActor.d.ts → actor/hotkey.d.ts} +1 -1
  15. package/dist/editor/{hotkeyActor.js → actor/hotkey.js} +4 -4
  16. package/dist/editor/actor/machine.d.ts +11 -0
  17. package/dist/editor/actor/machine.js +22 -0
  18. package/dist/editor/{editorActor.setup.d.ts → actor/setup.d.ts} +13 -49
  19. package/dist/editor/{editorActor.setup.js → actor/setup.js} +13 -10
  20. package/dist/editor/actor/state.editor.d.ts +97 -0
  21. package/dist/editor/actor/state.editor.js +55 -0
  22. package/dist/editor/actor/state.sync-queue.d.ts +420 -0
  23. package/dist/editor/actor/state.sync-queue.js +235 -0
  24. package/dist/editor/actor/types.d.ts +59 -0
  25. package/dist/editor/actor/types.js +5 -0
  26. package/dist/editor/applyChangesToManualLayout.js +1 -1
  27. package/dist/editor/index.d.ts +2 -1
  28. package/dist/editor/useEditorActorLogic.d.ts +8 -2
  29. package/dist/editor/useEditorActorLogic.js +26 -6
  30. package/dist/hooks/useDiagram.js +6 -1
  31. package/dist/hooks/useDiagramCompareLayout.js +1 -1
  32. package/dist/hooks/useEditorActor.d.ts +4 -0
  33. package/dist/hooks/useEditorActor.js +26 -0
  34. package/dist/hooks/useLikeC4ElementsTree.d.ts +3 -2
  35. package/dist/hooks/useLikeC4ElementsTree.js +3 -1
  36. package/dist/likec4diagram/DiagramUI.js +8 -4
  37. package/dist/likec4diagram/DiagramXYFlow.js +12 -12
  38. package/dist/likec4diagram/state/aligners.js +3 -0
  39. package/dist/likec4diagram/state/assign.js +1 -1
  40. package/dist/likec4diagram/state/diagram-api.d.ts +16 -0
  41. package/dist/likec4diagram/state/diagram-api.js +13 -7
  42. package/dist/likec4diagram/state/machine.actions.js +7 -8
  43. package/dist/likec4diagram/state/machine.d.ts +1 -1
  44. package/dist/likec4diagram/state/machine.setup.js +1 -1
  45. package/dist/likec4diagram/state/types.d.ts +1 -1
  46. package/dist/likec4diagram/state/utils.d.ts +1 -1
  47. package/dist/likec4diagram/useLayoutConstraints.js +2 -0
  48. package/dist/navigationpanel/controls/ToggleReadonly.js +4 -2
  49. package/dist/navigationpanel/editorpanel/ApplySemanticLayout.d.ts +4 -0
  50. package/dist/navigationpanel/editorpanel/ApplySemanticLayout.js +36 -0
  51. package/dist/navigationpanel/editorpanel/EditorPanel.js +45 -33
  52. package/dist/navigationpanel/walkthrough/DynamicViewControls.d.ts +61 -61
  53. package/dist/overlays/overlay/Overlay.d.ts +16 -0
  54. package/dist/overlays/overlay/Overlay.js +50 -52
  55. package/dist/overlays/overlaysActor.js +28 -27
  56. package/dist/overlays/relationship-details/custom/nodeTypes.d.ts +4 -5
  57. package/dist/overlays/relationship-details/custom/nodeTypes.js +25 -24
  58. package/dist/overlays/relationships-browser/SelectElement.js +2 -2
  59. package/dist/search/Search.d.ts +6 -3
  60. package/dist/search/Search.js +60 -71
  61. package/dist/search/components/ElementsColumn.js +12 -8
  62. package/dist/search/searchActor.js +30 -26
  63. package/dist/shadowroot/ShadowRoot.d.ts +7 -6
  64. package/dist/shadowroot/ShadowRoot.js +59 -20
  65. package/dist/shadowroot/styles.css.d.ts +10 -2
  66. package/dist/shadowroot/styles.css.js +33 -36
  67. package/dist/styles2.js +1 -1
  68. package/package.json +17 -23
  69. package/panda.buildinfo.json +15 -15
  70. package/src/styles-font.css +114 -0
  71. package/src/styles-min.css +2 -0
  72. package/src/styles-xyflow.css +1 -0
  73. package/src/styles.css +3 -0
  74. package/styles-min.css +1 -1
  75. package/styles.css +1 -1
  76. package/dist/bundle/index.d.ts +0 -1
  77. package/dist/editor/editorActor.actions.d.ts +0 -153
  78. package/dist/editor/editorActor.actions.js +0 -133
  79. package/dist/editor/editorActor.states.d.ts +0 -9
  80. package/dist/editor/editorActor.states.js +0 -219
  81. package/dist/hooks/useOverlaysActor.js +0 -11
  82. package/dist/hooks/useSearchActor.js +0 -11
@@ -1,7 +1,6 @@
1
1
  import "../_virtual/_rolldown/runtime.js";
2
- import { useCallbackRef as useCallbackRef$1 } from "../hooks/useCallbackRef.js";
2
+ import { useDiagram } from "../hooks/safeContext.js";
3
3
  import { Overlay } from "../overlays/overlay/Overlay.js";
4
- import { useSearchActorRef } from "../hooks/useSearchActor.js";
5
4
  import { useSearchContext } from "./SearchContext.js";
6
5
  import { focusToFirstFoundElement, moveFocusToSearchInput } from "./components/utils.js";
7
6
  import { ViewsColumn } from "./components/ViewsColum.js";
@@ -11,16 +10,15 @@ import { SearchByTags } from "./components/SearchByTags.js";
11
10
  import { LikeC4SearchInput } from "./components/SearchInput.js";
12
11
  import { XStateSearchAdapter } from "./XStateSearchAdapter.js";
13
12
  import { css } from "@likec4/styles/css";
14
- import { Suspense, memo, useEffect, useMemo, useRef } from "react";
13
+ import { forwardRef, memo, useRef } from "react";
15
14
  import { jsx, jsxs } from "react/jsx-runtime";
16
15
  import { useTimeoutEffect } from "@react-hookz/web";
17
16
  import { isTruthy } from "remeda";
18
17
  import { Grid, GridCol, Group, ScrollArea, Title } from "@mantine/core";
19
- import { AnimatePresence, LayoutGroup, usePresence } from "motion/react";
18
+ import { AnimatePresence, LayoutGroup } from "motion/react";
20
19
  import { useSelector } from "@xstate/react";
21
- import { useHotkeys } from "@mantine/hooks";
20
+ import { useHotkeys, useMergedRef } from "@mantine/hooks";
22
21
  import { Box as Box$1, VStack } from "@likec4/styles/jsx";
23
- import { createEmptyActor } from "xstate";
24
22
  //#region src/search/Search.tsx
25
23
  var dialogCss = css({
26
24
  backgroundColor: `[rgb(34 34 34 / var(--_opacity, 95%))]`,
@@ -61,52 +59,64 @@ var selectIsOpened = (s) => {
61
59
  return false;
62
60
  }
63
61
  };
64
- var Search = memo(() => {
65
- const searchActorRef = useSearchActorRef();
66
- const emptyActorRef = useRef(createEmptyActor());
67
- const isOpened = useSelector(searchActorRef ?? emptyActorRef.current, selectIsOpened);
68
- const close = useCallbackRef$1(() => {
69
- searchActorRef?.send({ type: "close" });
70
- });
71
- useHotkeys(useMemo(() => {
72
- const openSearch = () => searchActorRef?.send({ type: "open" });
73
- return searchActorRef ? [[
74
- "mod+k",
75
- openSearch,
76
- { preventDefault: true }
77
- ], [
78
- "mod+f",
79
- openSearch,
80
- { preventDefault: true }
81
- ]] : [];
82
- }, [searchActorRef]));
83
- return /* @__PURE__ */ jsx(AnimatePresence, { children: searchActorRef && isOpened && /* @__PURE__ */ jsx(Overlay, {
84
- fullscreen: true,
85
- withBackdrop: false,
86
- backdrop: { opacity: .9 },
87
- classes: {
88
- dialog: dialogCss,
89
- body: bodyCss
62
+ var Search = memo(({ searchActorRef }) => {
63
+ const diagram = useDiagram();
64
+ const isOpened = useSelector(searchActorRef, selectIsOpened);
65
+ const close = () => {
66
+ diagram.searchActor().send({ type: "close" });
67
+ };
68
+ const open = (event) => {
69
+ event.stopPropagation();
70
+ diagram.searchActor().send({ type: "open" });
71
+ };
72
+ useHotkeys(isOpened ? [] : [[
73
+ "mod+k",
74
+ open,
75
+ { preventDefault: true }
76
+ ], [
77
+ "mod+f",
78
+ open,
79
+ { preventDefault: true }
80
+ ]]);
81
+ return /* @__PURE__ */ jsx(AnimatePresence, {
82
+ onExitComplete: () => {
83
+ diagram.searchActor().send({ type: "animation.presence.end" });
90
84
  },
91
- openDelay: 0,
92
- onClose: close,
93
- "data-likec4-search": "true",
94
- children: /* @__PURE__ */ jsx(XStateSearchAdapter, {
95
- searchActorRef,
96
- children: /* @__PURE__ */ jsx(SearchOverlayBody, { searchActorRef })
85
+ children: isOpened && /* @__PURE__ */ jsx(Overlay, {
86
+ fullscreen: true,
87
+ withBackdrop: false,
88
+ backdrop: { opacity: .9 },
89
+ classes: {
90
+ dialog: dialogCss,
91
+ body: bodyCss
92
+ },
93
+ openDelay: 0,
94
+ onClose: close,
95
+ "data-likec4-search": "true",
96
+ children: /* @__PURE__ */ jsx(XStateSearchAdapter, {
97
+ searchActorRef,
98
+ children: /* @__PURE__ */ jsx(SearchOverlayBody, { searchActorRef })
99
+ })
97
100
  })
98
- }) });
101
+ });
99
102
  });
100
103
  Search.displayName = "Search";
104
+ function SearchOverlayBody({ searchActorRef }) {
105
+ const ref = useRef(null);
106
+ useTimeoutEffect(() => {
107
+ if (isTruthy(searchActorRef.getSnapshot().context.openedWithSearch)) focusToFirstFoundElement(ref.current);
108
+ }, 150);
109
+ return /* @__PURE__ */ jsx(SearchPanelContent, { ref });
110
+ }
101
111
  /**
102
112
  * The shared search panel content used by both diagram and overview search.
103
113
  * Must be rendered inside a SearchContext.Provider.
104
114
  */
105
- var SearchPanelContent = memo(() => {
106
- const ref = useRef(null);
115
+ var SearchPanelContent = forwardRef((_, ref) => {
116
+ const contentRef = useRef(null);
107
117
  const { pickViewFor } = useSearchContext();
108
118
  return /* @__PURE__ */ jsxs(Box$1, {
109
- ref,
119
+ ref: useMergedRef(ref, contentRef),
110
120
  display: "contents",
111
121
  children: [
112
122
  /* @__PURE__ */ jsx(Group, {
@@ -114,7 +124,7 @@ var SearchPanelContent = memo(() => {
114
124
  wrap: "nowrap",
115
125
  onClick: (e) => {
116
126
  e.stopPropagation();
117
- moveFocusToSearchInput(ref.current);
127
+ moveFocusToSearchInput(contentRef.current);
118
128
  },
119
129
  children: /* @__PURE__ */ jsxs(VStack, {
120
130
  flex: 1,
@@ -157,7 +167,11 @@ var SearchPanelContent = memo(() => {
157
167
  scrollbars: "y",
158
168
  children: /* @__PURE__ */ jsx(LayoutGroup, {
159
169
  id: "likec4-search-elements",
160
- children: /* @__PURE__ */ jsx(Suspense, { children: /* @__PURE__ */ jsx(ElementsColumn, {}) })
170
+ children: /* @__PURE__ */ jsx(AnimatePresence, {
171
+ mode: "popLayout",
172
+ anchorY: "top",
173
+ children: /* @__PURE__ */ jsx(ElementsColumn, {})
174
+ })
161
175
  })
162
176
  })
163
177
  }), /* @__PURE__ */ jsx(GridCol, {
@@ -167,10 +181,10 @@ var SearchPanelContent = memo(() => {
167
181
  className: scrollAreaCss,
168
182
  pr: "xs",
169
183
  scrollbars: "y",
170
- children: /* @__PURE__ */ jsx(Suspense, { children: /* @__PURE__ */ jsx(LayoutGroup, {
184
+ children: /* @__PURE__ */ jsx(LayoutGroup, {
171
185
  id: "likec4-search-views",
172
186
  children: /* @__PURE__ */ jsx(ViewsColumn, {})
173
- }) })
187
+ })
174
188
  })
175
189
  })]
176
190
  }),
@@ -179,30 +193,5 @@ var SearchPanelContent = memo(() => {
179
193
  });
180
194
  });
181
195
  SearchPanelContent.displayName = "SearchPanelContent";
182
- var SearchOverlayBody = memo(function SearchOverlayBody({ searchActorRef }) {
183
- const ref = useRef(null);
184
- useTimeoutEffect(() => {
185
- if (isTruthy(searchActorRef.getSnapshot().context.openedWithSearch)) focusToFirstFoundElement(ref.current);
186
- }, 150);
187
- const [isPresent, safeToRemove] = usePresence();
188
- useEffect(() => {
189
- if (isPresent) return;
190
- safeToRemove();
191
- try {
192
- searchActorRef.send({ type: "animation.presence.end" });
193
- } catch (e) {
194
- console.debug("SearchOverlayBody: animation.presence.end failed", e);
195
- }
196
- }, [
197
- isPresent,
198
- searchActorRef,
199
- safeToRemove
200
- ]);
201
- return /* @__PURE__ */ jsx(Box$1, {
202
- ref,
203
- display: "contents",
204
- children: /* @__PURE__ */ jsx(SearchPanelContent, {})
205
- });
206
- });
207
196
  //#endregion
208
197
  export { Search, SearchPanelContent, bodyCss, dialogCss };
@@ -25,7 +25,8 @@ function useElementsColumnData() {
25
25
  value: element.id,
26
26
  element,
27
27
  viewsCount: [...element.views()].length,
28
- children: []
28
+ children: [],
29
+ hasChildren: false
29
30
  })), toArray()), [model]);
30
31
  const search = useNormalizedSearch();
31
32
  return useMemo(() => {
@@ -61,10 +62,13 @@ function useElementsColumnData() {
61
62
  const byid = {};
62
63
  const { hash, all, roots } = pipe(elements, reduce((acc, treeItem) => {
63
64
  treeItem.children = [];
65
+ treeItem.hasChildren = false;
64
66
  byid[treeItem.value] = treeItem;
65
67
  const parent = acc.all.findLast((root) => isAncestor(root.value, treeItem.value));
66
- if (parent) parent.children.push(treeItem);
67
- else acc.roots.push(treeItem);
68
+ if (parent) {
69
+ parent.children.push(treeItem);
70
+ parent.hasChildren = true;
71
+ } else acc.roots.push(treeItem);
68
72
  acc.all.push(treeItem);
69
73
  acc.hash = stringHash(acc.hash + treeItem.value);
70
74
  return acc;
@@ -111,7 +115,7 @@ function ElementsTree({ data: { searchTerms, all, byid, hash, roots }, handleCli
111
115
  return;
112
116
  }
113
117
  if (e.key === "ArrowRight") {
114
- if (node.children.length > 0 && tree.expandedState[id] === false) return;
118
+ if (node.children.length > 0 && tree.expandedState[id] !== true) return;
115
119
  const maxY = (e.target.querySelector(".mantine-Tree-label") ?? target).getBoundingClientRect().y;
116
120
  const viewButtons = queryAllFocusable(target, "views");
117
121
  let view = viewButtons.length > 1 ? viewButtons.find((el, i, all) => centerY(el) > maxY || i === all.length - 1) : null;
@@ -135,7 +139,7 @@ function ElementsTree({ data: { searchTerms, all, byid, hash, roots }, handleCli
135
139
  selectOnClick: false,
136
140
  tree,
137
141
  data: roots,
138
- levelOffset: "lg",
142
+ levelOffset: "md",
139
143
  classNames: {
140
144
  root: treeRoot,
141
145
  node: cx(focusable, treeNode),
@@ -150,7 +154,7 @@ function ElementsTree({ data: { searchTerms, all, byid, hash, roots }, handleCli
150
154
  })
151
155
  });
152
156
  }
153
- function ElementTreeNode({ node, elementProps, hasChildren, expanded, handleClick, searchTerms }) {
157
+ function ElementTreeNode({ node, elementProps, expanded, handleClick, searchTerms }) {
154
158
  const { label, element, viewsCount } = node;
155
159
  const elementIcon$1 = IconOrShapeRenderer({
156
160
  element: {
@@ -170,7 +174,7 @@ function ElementTreeNode({ node, elementProps, hasChildren, expanded, handleClic
170
174
  size: 16,
171
175
  tabIndex: -1,
172
176
  className: cx(elementExpandIcon),
173
- style: { visibility: hasChildren ? "visible" : "hidden" },
177
+ style: { visibility: node.hasChildren ? "visible" : "hidden" },
174
178
  children: /* @__PURE__ */ jsx(IconChevronRight, {
175
179
  stroke: 3.5,
176
180
  style: {
@@ -186,7 +190,7 @@ function ElementTreeNode({ node, elementProps, hasChildren, expanded, handleClic
186
190
  "data-value": element.id,
187
191
  className: cx(btn.root, "group", "likec4-element-button"),
188
192
  ...viewsCount > 0 && { onClick: (e) => {
189
- if (!hasChildren || expanded) {
193
+ if (!node.hasChildren || expanded) {
190
194
  e.stopPropagation();
191
195
  handleClick(element);
192
196
  }
@@ -6,8 +6,9 @@ var searchActorLogic = setup({
6
6
  context: {},
7
7
  events: {}
8
8
  },
9
+ delays: { "MAX_WAIT_ANIMATION_END": 1e3 },
9
10
  actions: {
10
- "reset navigateTo": assign({ navigateTo: () => null }),
11
+ "reset navigateTo": assign(() => ({ navigateTo: null })),
11
12
  "assign navigateTo": assign(({ event }) => {
12
13
  assertEvent(event, ["navigate.to"]);
13
14
  return { navigateTo: {
@@ -19,7 +20,7 @@ var searchActorLogic = setup({
19
20
  assertEvent(event, ["change.search", "open"]);
20
21
  return event.search ?? context.searchValue;
21
22
  } }),
22
- "reset pickViewFor": assign({ pickViewFor: () => null })
23
+ "reset pickViewFor": assign(() => ({ pickViewFor: null }))
23
24
  }
24
25
  }).createMachine({
25
26
  id: "search",
@@ -30,7 +31,6 @@ var searchActorLogic = setup({
30
31
  navigateTo: null
31
32
  },
32
33
  initial: "inactive",
33
- on: { "close": { target: ".inactive" } },
34
34
  states: {
35
35
  inactive: {
36
36
  entry: ["reset navigateTo", "reset pickViewFor"],
@@ -52,7 +52,8 @@ var searchActorLogic = setup({
52
52
  "navigate.to": {
53
53
  target: "waitAnimationEnd",
54
54
  actions: "assign navigateTo"
55
- }
55
+ },
56
+ "close": { target: "inactive" }
56
57
  } },
57
58
  pickView: { on: {
58
59
  "pickview.close": {
@@ -62,34 +63,37 @@ var searchActorLogic = setup({
62
63
  "navigate.to": {
63
64
  target: "waitAnimationEnd",
64
65
  actions: "assign navigateTo"
65
- }
66
+ },
67
+ "close": { target: "inactive" }
66
68
  } },
67
69
  /**
68
70
  * Wait for animation end before triggering navigation
69
71
  * Otherwise, there could be weird artifacts when navigating to large diagrams.
70
72
  */
71
- waitAnimationEnd: { on: { "animation.presence.end": {
72
- target: "inactive",
73
- actions: enqueueActions(({ context, system, enqueue }) => {
74
- const navigateTo = context.navigateTo;
75
- if (!navigateTo) return;
76
- enqueue("reset navigateTo");
77
- const diagramActor = typedSystem(system).diagramActorRef;
78
- if (navigateTo.focusOnElement) {
79
- if (diagramActor.getSnapshot().context.view.id === navigateTo.viewId) {
80
- enqueue.sendTo(diagramActor, {
81
- type: "focus.node",
82
- nodeId: navigateTo.focusOnElement
83
- });
84
- return;
73
+ waitAnimationEnd: {
74
+ on: { "animation.presence.end": {
75
+ target: "inactive",
76
+ actions: enqueueActions(({ context, system, enqueue }) => {
77
+ const navigateTo = context.navigateTo;
78
+ if (!navigateTo) return;
79
+ const diagramActor = typedSystem(system).diagramActorRef;
80
+ if (navigateTo.focusOnElement) {
81
+ if (diagramActor.getSnapshot().context.view.id === navigateTo.viewId) {
82
+ enqueue.sendTo(diagramActor, {
83
+ type: "focus.node",
84
+ nodeId: navigateTo.focusOnElement
85
+ });
86
+ return;
87
+ }
85
88
  }
86
- }
87
- enqueue.sendTo(diagramActor, {
88
- type: "navigate.to",
89
- ...navigateTo
90
- });
91
- })
92
- } } }
89
+ enqueue.sendTo(diagramActor, {
90
+ type: "navigate.to",
91
+ ...navigateTo
92
+ });
93
+ })
94
+ } },
95
+ after: { MAX_WAIT_ANIMATION_END: { target: "inactive" } }
96
+ }
93
97
  }
94
98
  });
95
99
  //#endregion
@@ -1,10 +1,9 @@
1
- import { HTMLAttributes, ForwardRefExoticComponent, RefAttributes } from 'react';
2
- export declare const ShadowRoot: ForwardRefExoticComponent<HTMLAttributes<HTMLDivElement> & {
1
+ import { HTMLAttributes } from 'react';
2
+ export declare function useShadowRoot(): ShadowRoot;
3
+ type ShadowRootProps = HTMLAttributes<HTMLDivElement> & {
3
4
  injectFontCss?: boolean | undefined;
4
5
  styleNonce?: string | (() => string) | undefined;
5
- mode?: "open" | "closed";
6
- delegatesFocus?: boolean;
7
- colorScheme?: "light" | "dark" | undefined;
6
+ colorScheme?: 'light' | 'dark' | undefined;
8
7
  keepAspectRatio?: false | undefined | {
9
8
  width: number;
10
9
  height: number;
@@ -14,4 +13,6 @@ export declare const ShadowRoot: ForwardRefExoticComponent<HTMLAttributes<HTMLDi
14
13
  * @see https://mantine.dev/theming/mantine-provider/#theme-prop
15
14
  */
16
15
  theme?: any;
17
- } & RefAttributes<HTMLDivElement>>;
16
+ };
17
+ export declare function ShadowRoot({ children, theme, injectFontCss, styleNonce, colorScheme: explicitColorScheme, keepAspectRatio, ...props }: ShadowRootProps): import("react/jsx-runtime").JSX.Element;
18
+ export {};
@@ -1,16 +1,15 @@
1
1
  import "../_virtual/_rolldown/runtime.js";
2
- import { useCallbackRef as useCallbackRef$1 } from "../hooks/useCallbackRef.js";
2
+ import { useCallbackRef } from "../hooks/useCallbackRef.js";
3
3
  import { DefaultMantineProvider } from "../context/DefaultMantineProvider.js";
4
4
  import { FramerMotionConfig } from "../context/FramerMotionConfig.js";
5
- import { useId as useId$1 } from "../hooks/useId.js";
6
- import { useBundledStyleSheet, useColorScheme as useColorScheme$1 } from "./styles.css.js";
7
- import { forwardRef, memo, useRef, useState } from "react";
5
+ import { useId } from "../hooks/useId.js";
6
+ import { appendFontToDocument, createShadowRootStyles, createShadowRootStylesheets, useColorScheme } from "./styles.css.js";
7
+ import { createContext, memo, useLayoutEffect, useRef, useState } from "react";
8
8
  import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
9
+ import { useIsomorphicLayoutEffect } from "@react-hookz/web";
9
10
  import { isDefined } from "remeda";
10
- import { useMergedRef } from "@mantine/hooks";
11
- import root from "react-shadow";
11
+ import { createPortal } from "react-dom";
12
12
  //#region src/shadowroot/ShadowRoot.tsx
13
- var Root = root["div"];
14
13
  function useShadowRootStyle(instanceId, keepAspectRatio = false) {
15
14
  if (keepAspectRatio === false) return `
16
15
  :where([data-likec4-instance="${instanceId}"]) {
@@ -50,14 +49,15 @@ function useShadowRootStyle(instanceId, keepAspectRatio = false) {
50
49
  }
51
50
  `.trim();
52
51
  }
53
- var ShadowRoot = forwardRef(({ children, theme, injectFontCss = true, styleNonce, colorScheme: explicitColorScheme, keepAspectRatio = false, ...props }, ref) => {
54
- const colorScheme = useColorScheme$1(explicitColorScheme);
55
- const id = useId$1();
52
+ var ShadowRootContext = createContext(null);
53
+ function ShadowRoot({ children, theme, injectFontCss = true, styleNonce, colorScheme: explicitColorScheme, keepAspectRatio = false, ...props }) {
54
+ const colorScheme = useColorScheme(explicitColorScheme);
55
+ const id = useId();
56
56
  const cssstyle = useShadowRootStyle(id, keepAspectRatio);
57
- const rootRef = useRef(null);
58
- const styleSheets = useBundledStyleSheet(injectFontCss, styleNonce);
59
- const getRootElement = useCallbackRef$1(() => rootRef.current ?? void 0);
60
- const getStyleNonce = useCallbackRef$1(() => {
57
+ const mantineRootRef = useRef(null);
58
+ useIsomorphicLayoutEffect(() => appendFontToDocument(injectFontCss, styleNonce), []);
59
+ const getRootElement = useCallbackRef(() => mantineRootRef.current ?? void 0);
60
+ const getStyleNonce = useCallbackRef(() => {
61
61
  if (isDefined(styleNonce)) {
62
62
  if (typeof styleNonce === "string") return styleNonce;
63
63
  else if (typeof styleNonce === "function") return styleNonce();
@@ -68,13 +68,11 @@ var ShadowRoot = forwardRef(({ children, theme, injectFontCss = true, styleNonce
68
68
  return /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx(MemoizedStyle, {
69
69
  nonce,
70
70
  cssstyle
71
- }), /* @__PURE__ */ jsx(Root, {
72
- ssr: false,
71
+ }), /* @__PURE__ */ jsx(ShadowRootHost, {
73
72
  ...props,
74
- styleSheets,
75
73
  "data-likec4-instance": id,
76
74
  children: /* @__PURE__ */ jsx("div", {
77
- ref: useMergedRef(rootRef, ref),
75
+ ref: mantineRootRef,
78
76
  "data-mantine-color-scheme": colorScheme,
79
77
  className: "likec4-shadow-root",
80
78
  children: /* @__PURE__ */ jsx(DefaultMantineProvider, {
@@ -89,15 +87,56 @@ var ShadowRoot = forwardRef(({ children, theme, injectFontCss = true, styleNonce
89
87
  })
90
88
  })
91
89
  })] });
92
- });
90
+ }
93
91
  /**
94
92
  * @internal Memoized styles gives a performance boost during development
95
93
  */
96
94
  var MemoizedStyle = memo(({ nonce, cssstyle }) => /* @__PURE__ */ jsx("style", {
97
95
  type: "text/css",
98
- nonce: nonce || void 0,
96
+ nonce,
99
97
  dangerouslySetInnerHTML: { __html: cssstyle }
100
98
  }));
101
99
  MemoizedStyle.displayName = "MemoizedStyle";
100
+ var ShadowRootHostPortal = ({ children, root }) => {
101
+ return createPortal(children, root, "likec4-shadow-root");
102
+ };
103
+ var ShadowRootHost = ({ children, ...props }) => {
104
+ const hostRef = useRef(null);
105
+ const [root, setRoot] = useState(null);
106
+ useLayoutEffect(() => {
107
+ const host = hostRef.current;
108
+ if (!host) {
109
+ setRoot(null);
110
+ return;
111
+ }
112
+ setRoot((current) => {
113
+ try {
114
+ if (host.shadowRoot) {
115
+ host.shadowRoot.adoptedStyleSheets[0].replaceSync(createShadowRootStyles());
116
+ return host.shadowRoot;
117
+ }
118
+ const shadowRoot = host.attachShadow({
119
+ mode: "open",
120
+ delegatesFocus: false
121
+ });
122
+ shadowRoot.adoptedStyleSheets = createShadowRootStylesheets();
123
+ return shadowRoot;
124
+ } catch (error) {
125
+ return current;
126
+ }
127
+ });
128
+ }, []);
129
+ return /* @__PURE__ */ jsx("div", {
130
+ ...props,
131
+ ref: hostRef,
132
+ children: root && /* @__PURE__ */ jsx(ShadowRootHostPortal, {
133
+ root,
134
+ children: /* @__PURE__ */ jsx(ShadowRootContext.Provider, {
135
+ value: root,
136
+ children
137
+ })
138
+ })
139
+ });
140
+ };
102
141
  //#endregion
103
142
  export { ShadowRoot };
@@ -1,4 +1,12 @@
1
- export declare const cssInteractive: string;
2
- export declare function useBundledStyleSheet(injectFontCss: boolean, styleNonce?: string | (() => string) | undefined): CSSStyleSheet[];
1
+ export declare function scopeStylesToShadowRoot(styles: string): string;
2
+ export declare function appendFontToDocument(injectFontCss: boolean, styleNonce?: string | (() => string) | undefined): void;
3
+ /**
4
+ * Creates a CSS string with styles scoped to the shadow root
5
+ */
6
+ export declare function createShadowRootStyles(): string;
7
+ /**
8
+ * Creates a CSSStyleSheet with styles scoped to the shadow root
9
+ */
10
+ export declare function createShadowRootStylesheets(): [CSSStyleSheet];
3
11
  export type ColorScheme = 'light' | 'dark';
4
12
  export declare function useColorScheme(explicit?: ColorScheme): ColorScheme;
@@ -2,43 +2,39 @@ import { __name } from "../_virtual/_rolldown/runtime.js";
2
2
  import { useCallbackRef as useCallbackRef$1 } from "../hooks/useCallbackRef.js";
3
3
  import styles_font_default from "../styles-font2.js";
4
4
  import styles_default from "../styles2.js";
5
- import { css } from "@likec4/styles/css";
6
5
  import { useState } from "react";
7
- import { useIsomorphicLayoutEffect } from "@react-hookz/web";
8
6
  import { first, isFunction, isString } from "remeda";
9
7
  import { useColorScheme, useMutationObserverTarget } from "@mantine/hooks";
10
- css({
11
- cursor: "pointer",
12
- ["--mantine-cursor-pointer"]: "pointer",
13
- "& :where(.likec4-diagram, .likec4-compound-node, .likec4-element-node)": { cursor: "pointer" }
14
- });
15
- function useBundledStyleSheet(injectFontCss, styleNonce) {
16
- const [styleSheets] = useState(() => {
17
- const css = new CSSStyleSheet();
18
- css.replaceSync(
19
- styles_default
20
- /**
21
- * replace only top-level body selectors, for example
22
- * `body { }` should be replaced with `.likec4-shadow-root { }`
23
- * but `.likec4-overlay-body { }` - not
24
- */
25
- );
26
- return [css];
27
- });
28
- useIsomorphicLayoutEffect(() => {
29
- if (injectFontCss && !document.querySelector(`style[data-likec4-font]`)) {
30
- const style = document.createElement("style");
31
- style.setAttribute("type", "text/css");
32
- style.setAttribute("data-likec4-font", "");
33
- let nonce;
34
- if (isString(styleNonce)) nonce = styleNonce;
35
- if (isFunction(styleNonce)) nonce = styleNonce();
36
- if (nonce) style.setAttribute("nonce", nonce);
37
- style.appendChild(document.createTextNode(styles_font_default));
38
- document.head.appendChild(style);
39
- }
40
- }, [injectFontCss]);
41
- return styleSheets;
8
+ //#region src/shadowroot/styles.css.ts
9
+ function scopeStylesToShadowRoot(styles) {
10
+ return styles.replaceAll(/:where\(\s*:root\s*,\s*:host\s*\)/g, `:where(.likec4-shadow-root)`).replaceAll(":root", `.likec4-shadow-root`).replaceAll(/(^|[{},;]|\*\/)(\s*)body(?=\s*[{,])/g, "$1$2.likec4-shadow-root");
11
+ }
12
+ function appendFontToDocument(injectFontCss, styleNonce) {
13
+ if (injectFontCss && !document.querySelector(`style[data-likec4-font]`)) {
14
+ const style = document.createElement("style");
15
+ style.setAttribute("type", "text/css");
16
+ style.setAttribute("data-likec4-font", "");
17
+ let nonce;
18
+ if (isString(styleNonce)) nonce = styleNonce;
19
+ if (isFunction(styleNonce)) nonce = styleNonce();
20
+ if (nonce) style.setAttribute("nonce", nonce);
21
+ style.appendChild(document.createTextNode(scopeStylesToShadowRoot(styles_font_default)));
22
+ document.head.appendChild(style);
23
+ }
24
+ }
25
+ /**
26
+ * Creates a CSS string with styles scoped to the shadow root
27
+ */
28
+ function createShadowRootStyles() {
29
+ return scopeStylesToShadowRoot(styles_default);
30
+ }
31
+ /**
32
+ * Creates a CSSStyleSheet with styles scoped to the shadow root
33
+ */
34
+ function createShadowRootStylesheets() {
35
+ const css = new CSSStyleSheet();
36
+ css.replaceSync(createShadowRootStyles());
37
+ return [css];
42
38
  }
43
39
  var getComputedColorScheme = () => {
44
40
  try {
@@ -47,6 +43,7 @@ var getComputedColorScheme = () => {
47
43
  } catch {}
48
44
  return null;
49
45
  };
46
+ var getDocumentElement = () => document.documentElement;
50
47
  function useColorScheme$1(explicit) {
51
48
  const preferred = useColorScheme();
52
49
  const [computed, setComputed] = useState(getComputedColorScheme);
@@ -54,9 +51,9 @@ function useColorScheme$1(explicit) {
54
51
  attributes: true,
55
52
  childList: false,
56
53
  subtree: false
57
- }, () => document.documentElement);
54
+ }, getDocumentElement);
58
55
  return explicit ?? computed ?? preferred;
59
56
  }
60
57
  __name(useColorScheme$1, "useColorScheme");
61
58
  //#endregion
62
- export { useBundledStyleSheet, useColorScheme$1 as useColorScheme };
59
+ export { appendFontToDocument, createShadowRootStyles, createShadowRootStylesheets, useColorScheme$1 as useColorScheme };