@likec4/diagram 1.32.0 → 1.32.2

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 (54) hide show
  1. package/README.md +12 -5
  2. package/bundle/{chunk-BvTQaUYl.js → chunk-DlHyKoeo.js} +236 -210
  3. package/bundle/custom.js +50 -49
  4. package/bundle/index.js +144 -121
  5. package/dist/LikeC4Diagram.js +1 -1
  6. package/dist/LikeC4Diagram.props.d.ts +14 -3
  7. package/dist/bundle/LikeC4View.js +16 -15
  8. package/dist/bundle/ReactLikeC4.js +7 -6
  9. package/dist/bundle/index.js +1 -1
  10. package/dist/custom/customNodes.d.ts +5 -0
  11. package/dist/custom/hooks.d.ts +1 -1
  12. package/dist/index.js +1 -1
  13. package/dist/likec4diagram/DiagramXYFlow.js +3 -3
  14. package/dist/likec4diagram/custom/nodes/toolbar/_shared.d.ts +5 -5
  15. package/dist/likec4model/index.d.ts +1 -1
  16. package/dist/likec4model/useLikeC4Model.d.ts +2 -1
  17. package/dist/likec4model/useLikeC4Model.js +13 -2
  18. package/dist/overlays/overlay/Overlay.js +24 -4
  19. package/dist/search/Search.d.ts +2 -3
  20. package/dist/search/Search.js +27 -46
  21. package/dist/search/components/ElementsColumn.js +12 -13
  22. package/dist/search/components/PickView.js +6 -6
  23. package/dist/search/components/SearchByTags.js +5 -2
  24. package/dist/search/components/SearchInput.js +16 -14
  25. package/dist/search/components/ViewsColum.js +16 -9
  26. package/dist/search/components/utils.d.ts +3 -2
  27. package/dist/search/components/utils.js +37 -5
  28. package/dist/search/hooks.d.ts +2 -8
  29. package/dist/search/hooks.js +4 -4
  30. package/dist/state/DiagramActorProvider.js +1 -1
  31. package/dist/state/diagram-machine.d.ts +216 -8
  32. package/dist/state/diagram-machine.js +8 -6
  33. package/package.json +17 -16
  34. package/panda.buildinfo.json +1 -1
  35. package/src/LikeC4Diagram.props.ts +14 -3
  36. package/src/LikeC4Diagram.tsx +1 -1
  37. package/src/bundle/LikeC4View.tsx +12 -19
  38. package/src/bundle/ReactLikeC4.tsx +2 -10
  39. package/src/custom/customNodes.tsx +5 -0
  40. package/src/custom/hooks.ts +1 -0
  41. package/src/likec4diagram/DiagramXYFlow.tsx +3 -3
  42. package/src/likec4model/index.ts +2 -0
  43. package/src/likec4model/useLikeC4Model.ts +16 -2
  44. package/src/overlays/overlay/Overlay.tsx +20 -5
  45. package/src/search/Search.tsx +34 -51
  46. package/src/search/components/ElementsColumn.tsx +12 -15
  47. package/src/search/components/PickView.tsx +41 -47
  48. package/src/search/components/SearchByTags.tsx +5 -2
  49. package/src/search/components/SearchInput.tsx +17 -14
  50. package/src/search/components/ViewsColum.tsx +18 -10
  51. package/src/search/components/utils.ts +41 -4
  52. package/src/search/hooks.ts +5 -8
  53. package/src/state/DiagramActorProvider.tsx +2 -1
  54. package/src/state/diagram-machine.ts +8 -6
@@ -33,7 +33,7 @@ function LikeC4Diagram({
33
33
  pannable = true,
34
34
  zoomable = true,
35
35
  background = "dots",
36
- enableElementTags = true,
36
+ enableElementTags = false,
37
37
  enableFocusMode = false,
38
38
  enableElementDetails = false,
39
39
  enableRelationshipDetails = enableElementDetails,
@@ -73,7 +73,15 @@ export interface LikeC4DiagramProperties<A extends aux.Any> {
73
73
  * @default false if readonly
74
74
  */
75
75
  nodesDraggable?: boolean | undefined;
76
+ /**
77
+ * Initial width of the diagram
78
+ * (supposed to be used during SSR)
79
+ */
76
80
  initialWidth?: number | undefined;
81
+ /**
82
+ * Initial height of the diagram
83
+ * (supposed to be used during SSR)
84
+ */
77
85
  initialHeight?: number | undefined;
78
86
  /**
79
87
  * Background pattern
@@ -91,7 +99,9 @@ export interface LikeC4DiagramProperties<A extends aux.Any> {
91
99
  */
92
100
  showNavigationButtons?: undefined | boolean;
93
101
  /**
94
- * Display notations of the view
102
+ * Display notations in the bottom right corner
103
+ * (Active if only notations are present)
104
+ *
95
105
  * @default true
96
106
  */
97
107
  showNotations?: boolean | undefined;
@@ -128,7 +138,7 @@ export interface LikeC4DiagramProperties<A extends aux.Any> {
128
138
  enableDynamicViewWalkthrough?: boolean | undefined;
129
139
  /**
130
140
  * Display element tags in the bottom left corner
131
- * @default true
141
+ * @default false
132
142
  */
133
143
  enableElementTags?: boolean | undefined;
134
144
  /**
@@ -138,8 +148,9 @@ export interface LikeC4DiagramProperties<A extends aux.Any> {
138
148
  experimentalEdgeEditing?: boolean | undefined;
139
149
  /**
140
150
  * Improve performance by hiding certain elements and reducing visual effects (disable mix-blend, shadows, animations)
151
+ * Enable it if you have a large or static view
141
152
  *
142
- * @default 'auto' - will be set to true if view has more then 3000 * 2000 pixels
153
+ * @default 'auto' - will be `true` if view has more then 3000 * 2000 pixels
143
154
  */
144
155
  reduceGraphics?: 'auto' | boolean | undefined;
145
156
  /**
@@ -11,23 +11,24 @@ import { useLikeC4Model } from '../likec4model/useLikeC4Model.js';
11
11
  import { Overlay } from '../overlays/overlay/Overlay.js';
12
12
  import { ShadowRoot } from './ShadowRoot.js';
13
13
  import { useColorScheme, useShadowRootStyle, cssInteractive } from './styles.css.js';
14
- import { ErrorMessage, ViewNotFound } from './ViewNotFound.js';
14
+ import { ViewNotFound, ErrorMessage } from './ViewNotFound.js';
15
15
 
16
16
  function LikeC4View({
17
17
  viewId,
18
18
  ...props
19
19
  }) {
20
20
  const likec4model = useLikeC4Model();
21
- const view = likec4model?.findView(viewId);
22
- if (!likec4model) {
23
- return /* @__PURE__ */ jsx(ErrorMessage, { children: "LikeC4Model not found. Make sure you have LikeC4ModelProvider." });
24
- }
25
- if (likec4model.stage !== "layouted") {
26
- return /* @__PURE__ */ jsx(ErrorMessage, { children: "LikeC4Model is not layouted. Make sure you have LikeC4ModelProvider with layouted model." });
27
- }
28
- if (!view || !view.isDiagram()) {
21
+ const view = likec4model.findView(viewId);
22
+ if (!view) {
29
23
  return /* @__PURE__ */ jsx(ViewNotFound, { viewId });
30
24
  }
25
+ if (!view.isDiagram()) {
26
+ return /* @__PURE__ */ jsxs(ErrorMessage, { children: [
27
+ 'LikeC4 View "$',
28
+ viewId,
29
+ '" is not layouted. Make sure you have LikeC4ModelProvider with layouted model.'
30
+ ] });
31
+ }
31
32
  return /* @__PURE__ */ jsx(LikeC4ViewInner, { view: view.$view, ...props });
32
33
  }
33
34
  const LikeC4ViewInner = memo(({
@@ -40,7 +41,7 @@ const LikeC4ViewInner = memo(({
40
41
  injectFontCss = true,
41
42
  controls = false,
42
43
  fitView = true,
43
- fitViewPadding = "8px",
44
+ fitViewPadding = "16px",
44
45
  background = "transparent",
45
46
  browser = true,
46
47
  showDiagramTitle = false,
@@ -134,13 +135,13 @@ const LikeC4ViewInner = memo(({
134
135
  ...props
135
136
  }
136
137
  ),
137
- browserView && /* @__PURE__ */ jsxs(Overlay, { onClose: () => onNavigateTo(null), children: [
138
+ browserView && /* @__PURE__ */ jsxs(Overlay, { openDelay: 0, onClose: () => onNavigateTo(null), children: [
138
139
  /* @__PURE__ */ jsx(
139
140
  LikeC4Diagram,
140
141
  {
141
142
  view: browserView,
142
143
  background: "dots",
143
- onNavigateTo: (to) => onNavigateTo(to),
144
+ onNavigateTo,
144
145
  enableDynamicViewWalkthrough: true,
145
146
  enableFocusMode: true,
146
147
  enableRelationshipBrowser: true,
@@ -151,11 +152,11 @@ const LikeC4ViewInner = memo(({
151
152
  controls: true,
152
153
  readonly: true,
153
154
  fitView: true,
154
- fitViewPadding: "16px",
155
- renderNodes,
155
+ fitViewPadding: "32px",
156
156
  ...props,
157
157
  ...browserProps,
158
- showNotations: (browserProps.showNotations ?? true) && (browserView.notation?.nodes.length ?? 0) > 0
158
+ showNotations: (browserProps.showNotations ?? true) && (browserView.notation?.nodes.length ?? 0) > 0,
159
+ renderNodes
159
160
  }
160
161
  ),
161
162
  /* @__PURE__ */ jsx(Box, { pos: "absolute", top: "1rem", right: "1rem", children: /* @__PURE__ */ jsx(
@@ -5,22 +5,23 @@ import { LikeC4Diagram } from '../LikeC4Diagram.js';
5
5
  import { useLikeC4Model } from '../likec4model/useLikeC4Model.js';
6
6
  import { ShadowRoot } from './ShadowRoot.js';
7
7
  import { useColorScheme, useShadowRootStyle } from './styles.css.js';
8
- import { ErrorMessage, ViewNotFound } from './ViewNotFound.js';
8
+ import { ViewNotFound, ErrorMessage } from './ViewNotFound.js';
9
9
 
10
10
  function ReactLikeC4({
11
11
  viewId,
12
12
  ...props
13
13
  }) {
14
14
  const likec4model = useLikeC4Model();
15
- const view = likec4model?.findView(viewId);
16
- if (!likec4model) {
17
- return /* @__PURE__ */ jsx(ErrorMessage, { children: "LikeC4Model not found. Make sure you have LikeC4ModelProvider." });
18
- }
15
+ const view = likec4model.findView(viewId);
19
16
  if (!view) {
20
17
  return /* @__PURE__ */ jsx(ViewNotFound, { viewId });
21
18
  }
22
19
  if (!view.isDiagram()) {
23
- return /* @__PURE__ */ jsx(ErrorMessage, { children: "LikeC4Model is not layouted. Make sure you have LikeC4ModelProvider with layouted model." });
20
+ return /* @__PURE__ */ jsxs(ErrorMessage, { children: [
21
+ 'LikeC4 View "$',
22
+ viewId,
23
+ '" is not layouted. Make sure you have LikeC4ModelProvider with layouted model.'
24
+ ] });
24
25
  }
25
26
  return /* @__PURE__ */ jsx(ReactLikeC4Inner, { view: view.$view, ...props });
26
27
  }
@@ -7,7 +7,7 @@ export { useEnabledFeatures } from '../context/DiagramFeatures.js';
7
7
  export { useCurrentViewId } from '../hooks/useCurrentViewId.js';
8
8
  export { useDiagram, useDiagramContext } from '../hooks/useDiagram.js';
9
9
  export { useXYFlow, useXYInternalNode, useXYStore, useXYStoreApi } from '../hooks/useXYFlow.js';
10
- export { useLikeC4Model, useLikeC4Specification } from '../likec4model/useLikeC4Model.js';
10
+ export { useLikeC4Model, useLikeC4Specification, useLikeC4ViewModel } from '../likec4model/useLikeC4Model.js';
11
11
  export { useMantinePortalProps } from '../hooks/useMantinePortalProps.js';
12
12
  export { useSetState } from '../hooks/useSetState.js';
13
13
  export { useUpdateEffect } from '../hooks/useUpdateEffect.js';
@@ -26,15 +26,20 @@ export type CustomElementNodeProps<M extends Any = Any> = {
26
26
  * ElementTitle,
27
27
  * ElementActions,
28
28
  * ElementDetailsButtonWithHandler,
29
+ * IfEnabled,
29
30
  * IfNotReadOnly,
30
31
  * ElementToolbar,
31
32
  * DefaultHandles,
33
+ * ElementTags
32
34
  * } from '@likec4/diagram/custom'
33
35
  *
34
36
  * const CustomElementNode = elementNode(({ nodeProps, nodeModel }) => (
35
37
  * <ElementNodeContainer nodeProps={nodeProps}>
36
38
  * <ElementShape {...nodeProps} />
37
39
  * <ElementTitle {...nodeProps} />
40
+ * <IfEnabled feature="ElementTags">
41
+ * <ElementTags {...nodeProps} />
42
+ * </IfEnabled>
38
43
  * <ElementActions
39
44
  * {...nodeProps}
40
45
  * extraButtons={[
@@ -4,7 +4,7 @@ export { type EnabledFeatures, useEnabledFeatures, } from '../context/DiagramFea
4
4
  export { useCurrentViewId, } from '../hooks/useCurrentViewId';
5
5
  export { useDiagram, useDiagramContext, } from '../hooks/useDiagram';
6
6
  export { useXYFlow, useXYInternalNode, useXYStore, useXYStoreApi, } from '../hooks/useXYFlow';
7
- export { useLikeC4Model, useLikeC4Specification, } from '../likec4model/useLikeC4Model';
7
+ export { useLikeC4Model, useLikeC4Specification, useLikeC4ViewModel, } from '../likec4model/useLikeC4Model';
8
8
  export { useMantinePortalProps } from '../hooks/useMantinePortalProps';
9
9
  export { useSetState } from '../hooks/useSetState';
10
10
  export { useUpdateEffect } from '../hooks/useUpdateEffect';
package/dist/index.js CHANGED
@@ -9,7 +9,7 @@ export { useEnabledFeatures } from './context/DiagramFeatures.js';
9
9
  export { useCurrentViewId } from './hooks/useCurrentViewId.js';
10
10
  export { useDiagram, useDiagramContext } from './hooks/useDiagram.js';
11
11
  export { useXYFlow, useXYInternalNode, useXYStore, useXYStoreApi } from './hooks/useXYFlow.js';
12
- export { useLikeC4Model, useLikeC4Specification } from './likec4model/useLikeC4Model.js';
12
+ export { useLikeC4Model, useLikeC4Specification, useLikeC4ViewModel } from './likec4model/useLikeC4Model.js';
13
13
  export { useMantinePortalProps } from './hooks/useMantinePortalProps.js';
14
14
  export { useSetState } from './hooks/useSetState.js';
15
15
  export { useUpdateEffect } from './hooks/useUpdateEffect.js';
@@ -59,12 +59,12 @@ function LikeC4DiagramXYFlow({
59
59
  } = useDiagramEventHandlers();
60
60
  const notReadOnly = !enableReadOnly, isReducedGraphics = useIsReducedGraphics(), layoutConstraints = useLayoutConstraints(), $isPanning = usePanningAtom(), isPanning = useTimeout(() => {
61
61
  $isPanning.set(true);
62
- }, isReducedGraphics ? 100 : 200), notPanning = useDebouncedCallback(() => {
62
+ }, isReducedGraphics ? 120 : 200), notPanning = useDebouncedCallback(() => {
63
63
  isPanning.clear();
64
64
  if ($isPanning.get()) {
65
65
  $isPanning.set(false);
66
66
  }
67
- }, 200), onMove = useCallbackRef((event) => {
67
+ }, isReducedGraphics ? 350 : 200), onMove = useCallbackRef((event) => {
68
68
  if (!event) {
69
69
  return;
70
70
  }
@@ -73,7 +73,7 @@ function LikeC4DiagramXYFlow({
73
73
  }
74
74
  notPanning();
75
75
  }), onMoveEnd = useCallbackRef((event, viewport) => {
76
- if (!!event) {
76
+ if (!!event && !isReducedGraphics) {
77
77
  notPanning.flush();
78
78
  }
79
79
  diagram.send({ type: "xyflow.viewportMoved", viewport, manually: !!event });
@@ -1,4 +1,4 @@
1
- import { DeploymentFqn, Fqn, ThemeColor, ViewChange } from '@likec4/core';
1
+ import { DeploymentFqn, Fqn, ThemeColor, ViewChange, ColorLiteral } from '@likec4/core';
2
2
  import { MergeExclusive } from 'type-fest';
3
3
  import { NodeProps } from '../../../../base';
4
4
  import { Types } from '../../../types';
@@ -7,17 +7,17 @@ import { TooltipProps } from '@mantine/core';
7
7
  export type OnStyleChange = (style: ViewChange.ChangeElementStyle['style']) => void;
8
8
  export declare const themedColors: ({
9
9
  key: "primary";
10
- value: `#${string}`;
10
+ value: ColorLiteral;
11
11
  } | {
12
12
  key: "secondary";
13
- value: `#${string}`;
13
+ value: ColorLiteral;
14
14
  } | {
15
15
  key: "muted";
16
- value: `#${string}`;
16
+ value: ColorLiteral;
17
17
  })[];
18
18
  export declare const colors: {
19
19
  key: "blue" | "gray" | "green" | "indigo" | "red" | "amber" | "slate" | "sky";
20
- value: `#${string}`;
20
+ value: ColorLiteral;
21
21
  }[];
22
22
  export type ThemeColorKey = typeof themedColors[0]['key'];
23
23
  export type ColorKey = typeof colors[0]['key'];
@@ -1,2 +1,2 @@
1
1
  export { useLikeC4ElementsTree, } from './useLikeC4ElementsTree';
2
- export { useLikeC4Model, } from './useLikeC4Model';
2
+ export { useLikeC4Model, useLikeC4Specification, useLikeC4ViewModel, } from './useLikeC4Model';
@@ -1,9 +1,10 @@
1
- import { aux, LikeC4Model } from '@likec4/core/model';
1
+ import { aux, LikeC4Model, LikeC4ViewModel } from '@likec4/core/model';
2
2
  import { TagSpecification, ElementSpecification, RelationshipSpecification, CustomColorDefinitions } from '@likec4/core';
3
3
  export declare function useLikeC4Model<A extends aux.Any = aux.Unknown>(): LikeC4Model<A>;
4
4
  export declare function useLikeC4Model<A extends aux.Any = aux.Unknown>(type: 'layouted'): LikeC4Model.Layouted<A>;
5
5
  export declare function useLikeC4Model<A extends aux.Any = aux.Unknown>(type: 'computed'): LikeC4Model.Computed<A>;
6
6
  export declare function useLikeC4Model<A extends aux.Any = aux.Unknown>(type: 'layouted' | 'computed' | undefined): LikeC4Model<A>;
7
+ export declare function useLikeC4ViewModel<A extends aux.Any = aux.Unknown>(viewId: aux.LooseViewId<A>): LikeC4ViewModel<aux.toLayouted<A>> | null;
7
8
  export declare function useLikeC4Specification(): {
8
9
  tags: {
9
10
  [x: string]: TagSpecification;
@@ -5,16 +5,27 @@ import { LikeC4ModelContext } from './LikeC4ModelContext.js';
5
5
  function useLikeC4Model(type) {
6
6
  const model = useContext(LikeC4ModelContext);
7
7
  if (!model) {
8
- throw new Error("No LikeC4Model found in context");
8
+ throw new Error("LikeC4Model not found. Make sure you have LikeC4ModelProvider.");
9
9
  }
10
10
  if (isString(type) && model.stage !== type) {
11
11
  throw new Error(`Invalid LikeC4ModelContext, expected "${type}" but got "${model.stage}" in context`);
12
12
  }
13
13
  return model;
14
14
  }
15
+ function useLikeC4ViewModel(viewId) {
16
+ const model = useLikeC4Model("layouted");
17
+ const view = model.findView(viewId);
18
+ if (!view) {
19
+ return null;
20
+ }
21
+ if (!view.isDiagram()) {
22
+ throw new Error(`View "${viewId}" is not layouted`);
23
+ }
24
+ return view;
25
+ }
15
26
  function useLikeC4Specification() {
16
27
  const model = useLikeC4Model();
17
28
  return model.$data.specification;
18
29
  }
19
30
 
20
- export { useLikeC4Model, useLikeC4Specification };
31
+ export { useLikeC4Model, useLikeC4Specification, useLikeC4ViewModel };
@@ -1,7 +1,7 @@
1
1
  import { jsx, Fragment } from 'react/jsx-runtime';
2
2
  import { cx } from '@likec4/styles/css';
3
3
  import { RemoveScroll } from '@mantine/core';
4
- import { useMergedRef } from '@mantine/hooks';
4
+ import { useFocusTrap, useMergedRef } from '@mantine/hooks';
5
5
  import { useDebouncedCallback, useTimeoutEffect } from '@react-hookz/web';
6
6
  import { useReducedMotionConfig, m } from 'motion/react';
7
7
  import { forwardRef, useState, useRef, useLayoutEffect } from 'react';
@@ -21,6 +21,7 @@ const Overlay = forwardRef(({
21
21
  ...rest
22
22
  }, ref) => {
23
23
  const [opened, setOpened] = useState(openDelay === 0);
24
+ const focusTrapRef = useFocusTrap(opened);
24
25
  const dialogRef = useRef(null);
25
26
  const isClosingRef = useRef(false);
26
27
  const motionNotReduced = useReducedMotionConfig() !== true;
@@ -54,8 +55,17 @@ const Overlay = forwardRef(({
54
55
  return /* @__PURE__ */ jsx(
55
56
  m.dialog,
56
57
  {
57
- ref: useMergedRef(dialogRef, ref),
58
- className: cx(RemoveScroll.classNames.fullWidth, classes?.dialog, className, styles.dialog),
58
+ ref: useMergedRef(
59
+ dialogRef,
60
+ focusTrapRef,
61
+ ref
62
+ ),
63
+ className: cx(
64
+ classes?.dialog,
65
+ className,
66
+ styles.dialog,
67
+ fullscreen && RemoveScroll.classNames.fullWidth
68
+ ),
59
69
  layout: true,
60
70
  style: {
61
71
  // @ts-ignore
@@ -115,7 +125,17 @@ const Overlay = forwardRef(({
115
125
  close();
116
126
  },
117
127
  ...rest,
118
- children: /* @__PURE__ */ jsx(RemoveScroll, { forwardProps: true, removeScrollBar: false, children: /* @__PURE__ */ jsx("div", { className: cx(classes?.body, styles.body, "overlay-body"), children: opened && /* @__PURE__ */ jsx(Fragment, { children }) }) })
128
+ children: /* @__PURE__ */ jsx(RemoveScroll, { forwardProps: true, children: /* @__PURE__ */ jsx(
129
+ "div",
130
+ {
131
+ className: cx(
132
+ classes?.body,
133
+ styles.body,
134
+ "overlay-body"
135
+ ),
136
+ children: opened && /* @__PURE__ */ jsx(Fragment, { children })
137
+ }
138
+ ) })
119
139
  }
120
140
  );
121
141
  });
@@ -1,5 +1,4 @@
1
1
  import { SearchActorRef } from './searchActor';
2
- import { NamedExoticComponent } from 'react';
3
- export declare const Search: NamedExoticComponent<{
2
+ export declare function Search({ searchActorRef }: {
4
3
  searchActorRef: SearchActorRef;
5
- }>;
4
+ }): import("react/jsx-runtime").JSX.Element;
@@ -1,12 +1,12 @@
1
1
  import { jsx, jsxs } from 'react/jsx-runtime';
2
2
  import { css } from '@likec4/styles/css';
3
- import { VStack } from '@likec4/styles/jsx';
4
- import { FocusTrap, Group, Grid, GridCol, Title, ScrollArea } from '@mantine/core';
3
+ import { Box, VStack } from '@likec4/styles/jsx';
4
+ import { Group, Grid, GridCol, Title, ScrollArea } from '@mantine/core';
5
5
  import { useCallbackRef, useHotkeys } from '@mantine/hooks';
6
6
  import { useTimeoutEffect } from '@react-hookz/web';
7
7
  import { useSelector } from '@xstate/react';
8
- import { LayoutGroup, AnimatePresence } from 'motion/react';
9
- import { memo, useRef } from 'react';
8
+ import { AnimatePresence, LayoutGroup } from 'motion/react';
9
+ import { useRef } from 'react';
10
10
  import { ErrorBoundary } from 'react-error-boundary';
11
11
  import { isTruthy } from 'remeda';
12
12
  import { ErrorFallback } from '../components/ErrorFallback.js';
@@ -57,24 +57,13 @@ const body = css({
57
57
  // },
58
58
  });
59
59
  const selectIsOpened = (s) => !s.matches("inactive");
60
- const Search = memo(({ searchActorRef }) => {
60
+ function Search({ searchActorRef }) {
61
61
  const isOpened = useSelector(searchActorRef, selectIsOpened);
62
62
  const openSearch = () => {
63
63
  searchActorRef.send({ type: "open" });
64
64
  };
65
- const sendClose = () => {
65
+ const close = useCallbackRef(() => {
66
66
  searchActorRef.send({ type: "close" });
67
- };
68
- const afterCloseCbRef = useRef(null);
69
- const close = useCallbackRef((cb) => {
70
- afterCloseCbRef.current = cb ?? null;
71
- sendClose();
72
- });
73
- const onExitComplete = useCallbackRef(() => {
74
- if (afterCloseCbRef.current) {
75
- afterCloseCbRef.current();
76
- afterCloseCbRef.current = null;
77
- }
78
67
  });
79
68
  useHotkeys([
80
69
  ["mod+k", openSearch, {
@@ -84,34 +73,25 @@ const Search = memo(({ searchActorRef }) => {
84
73
  preventDefault: true
85
74
  }]
86
75
  ]);
87
- return /* @__PURE__ */ jsx(
88
- SearchActorContext,
76
+ return /* @__PURE__ */ jsx(SearchActorContext, { value: searchActorRef, children: /* @__PURE__ */ jsx(DiagramFeatures.Overlays, { children: /* @__PURE__ */ jsx(ErrorBoundary, { FallbackComponent: ErrorFallback, onReset: close, children: /* @__PURE__ */ jsx(AnimatePresence, { children: isOpened && /* @__PURE__ */ jsx(
77
+ Overlay,
89
78
  {
90
- value: {
91
- searchActorRef,
92
- close
79
+ fullscreen: true,
80
+ withBackdrop: false,
81
+ backdrop: {
82
+ opacity: 0.9
93
83
  },
94
- children: /* @__PURE__ */ jsx(DiagramFeatures.Overlays, { children: /* @__PURE__ */ jsx(ErrorBoundary, { FallbackComponent: ErrorFallback, onReset: sendClose, children: /* @__PURE__ */ jsx(LayoutGroup, { children: /* @__PURE__ */ jsx(AnimatePresence, { onExitComplete, children: isOpened && /* @__PURE__ */ jsx(
95
- Overlay,
96
- {
97
- fullscreen: true,
98
- withBackdrop: false,
99
- backdrop: {
100
- opacity: 0.9
101
- },
102
- classes: {
103
- dialog,
104
- body
105
- },
106
- openDelay: 0,
107
- onClose: close,
108
- "data-likec4-search": "true",
109
- children: /* @__PURE__ */ jsx(SearchOverlay, { close, searchActorRef })
110
- }
111
- ) }) }) }) })
84
+ classes: {
85
+ dialog,
86
+ body
87
+ },
88
+ openDelay: 0,
89
+ onClose: close,
90
+ "data-likec4-search": "true",
91
+ children: /* @__PURE__ */ jsx(SearchOverlay, { searchActorRef })
112
92
  }
113
- );
114
- });
93
+ ) }) }) }) });
94
+ }
115
95
  const scrollArea = css({
116
96
  height: [
117
97
  "100%",
@@ -125,14 +105,15 @@ const scrollArea = css({
125
105
  }
126
106
  }
127
107
  });
128
- const SearchOverlay = ({ close, searchActorRef }) => {
108
+ const SearchOverlay = ({ searchActorRef }) => {
109
+ const ref = useRef(null);
129
110
  const pickViewFor = usePickViewFor();
130
111
  useTimeoutEffect(() => {
131
112
  if (isTruthy(searchActorRef.getSnapshot().context.openedWithSearch)) {
132
- focusToFirstFoundElement();
113
+ focusToFirstFoundElement(ref.current);
133
114
  }
134
115
  }, 150);
135
- return /* @__PURE__ */ jsxs(FocusTrap, { children: [
116
+ return /* @__PURE__ */ jsxs(Box, { ref, display: "contents", children: [
136
117
  /* @__PURE__ */ jsx(
137
118
  Group,
138
119
  {
@@ -140,7 +121,7 @@ const SearchOverlay = ({ close, searchActorRef }) => {
140
121
  wrap: "nowrap",
141
122
  onClick: (e) => {
142
123
  e.stopPropagation();
143
- moveFocusToSearchInput();
124
+ moveFocusToSearchInput(ref.current);
144
125
  },
145
126
  children: /* @__PURE__ */ jsxs(VStack, { flex: 1, px: "sm", children: [
146
127
  /* @__PURE__ */ jsx(LikeC4SearchInput, {}),
@@ -5,7 +5,7 @@ import { useTree, Tree, ActionIcon, UnstyledButton, Box, Group, Highlight, Toolt
5
5
  import { useCallbackRef } from '@mantine/hooks';
6
6
  import { IconChevronRight } from '@tabler/icons-react';
7
7
  import * as m from 'motion/react-m';
8
- import { memo, useMemo, useCallback, useEffect } from 'react';
8
+ import { memo, useMemo, useEffect } from 'react';
9
9
  import { isEmpty, pipe, reduce, only, first } from 'remeda';
10
10
  import { IconOrShapeRenderer } from '../../context/IconRenderer.js';
11
11
  import { useDiagram } from '../../hooks/useDiagram.js';
@@ -14,7 +14,7 @@ import { useLikeC4Model } from '../../likec4model/useLikeC4Model.js';
14
14
  import { useNormalizedSearch, useSearchActor } from '../hooks.js';
15
15
  import { focusable, buttonsva } from './_shared.css.js';
16
16
  import { treeSubtree, treeLabel, treeRoot, treeNode, elementIcon, elementExpandIcon, elementTitleAndId, elementId, elementViewsCount } from './ElementsColumn.css.js';
17
- import { stopAndPrevent, moveFocusToSearchInput, centerY } from './utils.js';
17
+ import { stopAndPrevent, moveFocusToSearchInput, queryAllFocusable, centerY } from './utils.js';
18
18
  import { NothingFound } from './ViewsColum.js';
19
19
 
20
20
  const btn = buttonsva();
@@ -108,7 +108,7 @@ function ElementsTree({
108
108
  if (e.key === "ArrowUp") {
109
109
  if (id === roots[0]?.value) {
110
110
  stopAndPrevent(e);
111
- moveFocusToSearchInput();
111
+ moveFocusToSearchInput(target);
112
112
  }
113
113
  return;
114
114
  }
@@ -119,9 +119,7 @@ function ElementsTree({
119
119
  }
120
120
  const label = e.target.querySelector(".mantine-Tree-label") ?? target;
121
121
  const maxY = label.getBoundingClientRect().y;
122
- const viewButtons = [...document.querySelectorAll(
123
- `[data-likec4-search-views] .${focusable}`
124
- )];
122
+ const viewButtons = queryAllFocusable(target, "views");
125
123
  let view = viewButtons.length > 1 ? viewButtons.find((el, i, all2) => centerY(el) > maxY || i === all2.length - 1) : null;
126
124
  view ??= first(viewButtons);
127
125
  if (view) {
@@ -240,23 +238,24 @@ function ElementTreeNode({ node, elementProps, hasChildren, expanded }) {
240
238
  }
241
239
  function useHandleElementSelection() {
242
240
  const diagram = useDiagram();
243
- const { searchActorRef } = useSearchActor();
244
- return useCallback((element) => {
241
+ const searchActorRef = useSearchActor();
242
+ return useCallbackRef((element) => {
245
243
  const views = [...element.views()];
246
244
  if (views.length === 0) {
247
245
  return;
248
246
  }
249
247
  const singleView = only(views);
250
248
  if (singleView) {
251
- if (singleView.id === diagram.currentView.id) {
252
- searchActorRef.send({ type: "close" });
253
- } else {
254
- diagram.navigateTo(singleView.id);
249
+ searchActorRef.send({ type: "close" });
250
+ if (singleView.id !== diagram.currentView.id) {
251
+ setTimeout(() => {
252
+ diagram.navigateTo(singleView.id);
253
+ }, 100);
255
254
  }
256
255
  return;
257
256
  }
258
257
  searchActorRef.send({ type: "pickview.open", elementFqn: element.id });
259
- }, []);
258
+ });
260
259
  }
261
260
 
262
261
  export { ElementsColumn };
@@ -1,5 +1,5 @@
1
1
  import { jsxs, Fragment, jsx } from 'react/jsx-runtime';
2
- import { Group, Title, ActionIcon, FocusTrap, ScrollAreaAutosize, Stack } from '@mantine/core';
2
+ import { FocusTrap, Group, Title, ActionIcon, ScrollAreaAutosize, Stack } from '@mantine/core';
3
3
  import { useWindowEvent } from '@mantine/hooks';
4
4
  import { IconX } from '@tabler/icons-react';
5
5
  import * as m from 'motion/react-m';
@@ -9,7 +9,7 @@ import { ViewButton } from './ViewsColum.js';
9
9
  import { useLikeC4Model } from '../../likec4model/useLikeC4Model.js';
10
10
 
11
11
  function PickView({ elementFqn }) {
12
- const { searchActorRef } = useSearchActor();
12
+ const searchActorRef = useSearchActor();
13
13
  const element = useLikeC4Model().element(elementFqn);
14
14
  const scoped = [];
15
15
  const others = [];
@@ -52,7 +52,7 @@ function PickView({ elementFqn }) {
52
52
  },
53
53
  "pickview-backdrop"
54
54
  ),
55
- /* @__PURE__ */ jsxs(
55
+ /* @__PURE__ */ jsx(FocusTrap, { children: /* @__PURE__ */ jsxs(
56
56
  m.div,
57
57
  {
58
58
  initial: {
@@ -93,7 +93,7 @@ function PickView({ elementFqn }) {
93
93
  }
94
94
  )
95
95
  ] }),
96
- /* @__PURE__ */ jsx(FocusTrap, { children: /* @__PURE__ */ jsxs(ScrollAreaAutosize, { mah: "calc(100vh - 8rem - 50px)", type: "never", children: [
96
+ /* @__PURE__ */ jsxs(ScrollAreaAutosize, { mah: "calc(100vh - 110px)", type: "never", children: [
97
97
  scoped.length > 0 && /* @__PURE__ */ jsxs(Stack, { gap: "sm", px: "sm", className: pickviewGroup, children: [
98
98
  /* @__PURE__ */ jsx(Title, { order: 6, c: "dimmed", children: "scoped views of the element" }),
99
99
  scoped.map((view, i) => /* @__PURE__ */ jsx(
@@ -124,11 +124,11 @@ function PickView({ elementFqn }) {
124
124
  view.id
125
125
  ))
126
126
  ] })
127
- ] }) })
127
+ ] })
128
128
  ]
129
129
  },
130
130
  "pickview"
131
- )
131
+ ) })
132
132
  ] });
133
133
  }
134
134