@dxos/plugin-simple-layout 0.8.4-main.abd8ff62ef → 0.8.4-main.bc2380dfbc

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 (126) hide show
  1. package/LICENSE +102 -5
  2. package/dist/lib/{browser/SimpleLayoutPlugin-Q5BZE6KD.mjs → neutral/SimpleLayoutPlugin.mjs} +3 -1
  3. package/dist/lib/neutral/app-graph-builder-EYQKLRRP.mjs +21 -0
  4. package/dist/lib/neutral/app-graph-builder-EYQKLRRP.mjs.map +7 -0
  5. package/dist/lib/neutral/capabilities/index.mjs +21 -0
  6. package/dist/lib/neutral/capabilities/index.mjs.map +7 -0
  7. package/dist/lib/neutral/chunk-7UDV3JDT.mjs +22 -0
  8. package/dist/lib/neutral/chunk-7UDV3JDT.mjs.map +7 -0
  9. package/dist/lib/{browser/index.mjs → neutral/chunk-AMTEDJHG.mjs} +5 -8
  10. package/dist/lib/neutral/chunk-AMTEDJHG.mjs.map +7 -0
  11. package/dist/lib/neutral/chunk-FD2CAY4Q.mjs +26 -0
  12. package/dist/lib/neutral/chunk-FD2CAY4Q.mjs.map +7 -0
  13. package/dist/lib/neutral/chunk-J5LGTIGS.mjs +10 -0
  14. package/dist/lib/neutral/chunk-J5LGTIGS.mjs.map +7 -0
  15. package/dist/lib/neutral/chunk-XVUAQHKU.mjs +8 -0
  16. package/dist/lib/neutral/chunk-XVUAQHKU.mjs.map +7 -0
  17. package/dist/lib/neutral/close-WKMURGUB.mjs +35 -0
  18. package/dist/lib/neutral/close-WKMURGUB.mjs.map +7 -0
  19. package/dist/lib/neutral/components/index.mjs +926 -0
  20. package/dist/lib/neutral/components/index.mjs.map +7 -0
  21. package/dist/lib/neutral/hooks/index.mjs +332 -0
  22. package/dist/lib/neutral/hooks/index.mjs.map +7 -0
  23. package/dist/lib/neutral/index.mjs +14 -0
  24. package/dist/lib/neutral/index.mjs.map +7 -0
  25. package/dist/lib/neutral/meta.json +1 -0
  26. package/dist/lib/neutral/meta.mjs +8 -0
  27. package/dist/lib/neutral/meta.mjs.map +7 -0
  28. package/dist/lib/neutral/open-XI2T7D5O.mjs +49 -0
  29. package/dist/lib/neutral/open-XI2T7D5O.mjs.map +7 -0
  30. package/dist/lib/neutral/operation-handler-EAIE7KPR.mjs +13 -0
  31. package/dist/lib/neutral/operation-handler-EAIE7KPR.mjs.map +7 -0
  32. package/dist/lib/neutral/operations/index.mjs +8 -0
  33. package/dist/lib/neutral/operations/index.mjs.map +7 -0
  34. package/dist/lib/neutral/plugin.mjs +16 -0
  35. package/dist/lib/neutral/plugin.mjs.map +7 -0
  36. package/dist/lib/neutral/react-root-VE265VX4.mjs +18 -0
  37. package/dist/lib/neutral/react-root-VE265VX4.mjs.map +7 -0
  38. package/dist/lib/neutral/react-surface-REZMYKQV.mjs +46 -0
  39. package/dist/lib/neutral/react-surface-REZMYKQV.mjs.map +7 -0
  40. package/dist/lib/neutral/revert-workspace-ST6NZUNG.mjs +22 -0
  41. package/dist/lib/neutral/revert-workspace-ST6NZUNG.mjs.map +7 -0
  42. package/dist/lib/neutral/set-6ZRLWPJS.mjs +22 -0
  43. package/dist/lib/neutral/set-6ZRLWPJS.mjs.map +7 -0
  44. package/dist/lib/neutral/set-layout-mode-L22HRCKS.mjs +13 -0
  45. package/dist/lib/neutral/set-layout-mode-L22HRCKS.mjs.map +7 -0
  46. package/dist/lib/neutral/spotlight-dismiss-EIYW5E7M.mjs +58 -0
  47. package/dist/lib/neutral/spotlight-dismiss-EIYW5E7M.mjs.map +7 -0
  48. package/dist/lib/neutral/state-7NXKBLPY.mjs +47 -0
  49. package/dist/lib/neutral/state-7NXKBLPY.mjs.map +7 -0
  50. package/dist/lib/neutral/switch-workspace-PYWPTMFO.mjs +25 -0
  51. package/dist/lib/neutral/switch-workspace-PYWPTMFO.mjs.map +7 -0
  52. package/dist/lib/{browser → neutral}/translations.mjs +2 -0
  53. package/dist/lib/neutral/types/index.mjs +10 -0
  54. package/dist/lib/neutral/types/index.mjs.map +7 -0
  55. package/dist/lib/neutral/update-complementary-HKWF5OXT.mjs +33 -0
  56. package/dist/lib/neutral/update-complementary-HKWF5OXT.mjs.map +7 -0
  57. package/dist/lib/neutral/update-dialog-P4ASXCE7.mjs +30 -0
  58. package/dist/lib/neutral/update-dialog-P4ASXCE7.mjs.map +7 -0
  59. package/dist/lib/neutral/update-popover-REAKC2GN.mjs +34 -0
  60. package/dist/lib/neutral/update-popover-REAKC2GN.mjs.map +7 -0
  61. package/dist/lib/neutral/update-sidebar-O5SQPR6Q.mjs +12 -0
  62. package/dist/lib/neutral/update-sidebar-O5SQPR6Q.mjs.map +7 -0
  63. package/dist/lib/neutral/url-handler-GZXUUAHD.mjs +129 -0
  64. package/dist/lib/neutral/url-handler-GZXUUAHD.mjs.map +7 -0
  65. package/dist/types/src/capabilities/index.d.ts +1 -1
  66. package/dist/types/src/capabilities/state.d.ts +3 -3
  67. package/dist/types/src/capabilities/state.d.ts.map +1 -1
  68. package/dist/types/src/components/MobileLayout/MobileLayout.d.ts.map +1 -1
  69. package/dist/types/src/components/Popover/Popover.d.ts.map +1 -1
  70. package/dist/types/src/components/SimpleLayout/AppBar.d.ts.map +1 -1
  71. package/dist/types/src/hooks/actions.d.ts +3 -3
  72. package/dist/types/src/hooks/actions.d.ts.map +1 -1
  73. package/dist/types/src/hooks/useSimpleLayoutState.d.ts +3 -3
  74. package/dist/types/src/hooks/useSimpleLayoutState.d.ts.map +1 -1
  75. package/dist/types/src/index.d.ts +1 -2
  76. package/dist/types/src/index.d.ts.map +1 -1
  77. package/dist/types/src/meta.d.ts.map +1 -1
  78. package/dist/types/src/operations/state-access.d.ts +3 -3
  79. package/dist/types/src/operations/state-access.d.ts.map +1 -1
  80. package/dist/types/src/plugin.d.ts +4 -0
  81. package/dist/types/src/plugin.d.ts.map +1 -0
  82. package/dist/types/src/types/{capabilities.d.ts → SimpleLayoutCapabilities.d.ts} +2 -8
  83. package/dist/types/src/types/SimpleLayoutCapabilities.d.ts.map +1 -0
  84. package/dist/types/src/types/SimpleLayoutEvents.d.ts +4 -0
  85. package/dist/types/src/types/SimpleLayoutEvents.d.ts.map +1 -0
  86. package/dist/types/src/types/index.d.ts +2 -2
  87. package/dist/types/src/types/index.d.ts.map +1 -1
  88. package/dist/types/tsconfig.tsbuildinfo +1 -1
  89. package/package.json +81 -45
  90. package/src/capabilities/state.tsx +5 -6
  91. package/src/capabilities/url-handler.ts +16 -13
  92. package/src/components/Home/Home.tsx +1 -1
  93. package/src/components/Loading/Loading.tsx +1 -1
  94. package/src/components/MobileLayout/MobileLayout.tsx +0 -2
  95. package/src/components/NavBranch/NavBranch.tsx +1 -1
  96. package/src/components/Popover/Popover.tsx +1 -0
  97. package/src/components/SimpleLayout/AppBar.tsx +42 -40
  98. package/src/components/SimpleLayout/SimpleLayout.stories.tsx +3 -3
  99. package/src/hooks/actions.ts +5 -3
  100. package/src/hooks/useAppBarProps.ts +2 -2
  101. package/src/hooks/useDrawerActions.ts +2 -2
  102. package/src/hooks/useNavbarActions.ts +2 -2
  103. package/src/hooks/useSimpleLayoutState.ts +7 -5
  104. package/src/index.ts +1 -6
  105. package/src/meta.ts +1 -0
  106. package/src/operations/open.ts +1 -1
  107. package/src/operations/state-access.ts +5 -3
  108. package/src/plugin.ts +11 -0
  109. package/src/types/{capabilities.ts → SimpleLayoutCapabilities.ts} +3 -15
  110. package/src/types/SimpleLayoutEvents.ts +15 -0
  111. package/src/types/index.ts +2 -2
  112. package/dist/lib/browser/SimpleLayoutPlugin-Q5BZE6KD.mjs.map +0 -7
  113. package/dist/lib/browser/index.mjs.map +0 -7
  114. package/dist/lib/browser/meta.json +0 -1
  115. package/dist/lib/browser/translations.mjs.map +0 -7
  116. package/dist/lib/node-esm/SimpleLayoutPlugin-OD45TNPO.mjs +0 -52
  117. package/dist/lib/node-esm/index.mjs +0 -24
  118. package/dist/lib/node-esm/index.mjs.map +0 -7
  119. package/dist/lib/node-esm/meta.json +0 -1
  120. package/dist/lib/node-esm/translations.mjs +0 -36
  121. package/dist/types/src/types/capabilities.d.ts.map +0 -1
  122. package/dist/types/src/types/events.d.ts +0 -6
  123. package/dist/types/src/types/events.d.ts.map +0 -1
  124. package/src/types/events.ts +0 -15
  125. /package/dist/lib/{node-esm/SimpleLayoutPlugin-OD45TNPO.mjs.map → neutral/SimpleLayoutPlugin.mjs.map} +0 -0
  126. /package/dist/lib/{node-esm → neutral}/translations.mjs.map +0 -0
@@ -0,0 +1,926 @@
1
+ import "../chunk-J5LGTIGS.mjs";
2
+
3
+ // src/components/DebugOverlay/DebugOverlay.tsx
4
+ import { createContext } from "@radix-ui/react-context";
5
+ import React, { useCallback, useRef } from "react";
6
+ var DEBUG_OVERLAY_NAME = "DebugOverlay";
7
+ var [DebugOverlayProvider, useDebugLog] = createContext(DEBUG_OVERLAY_NAME, {
8
+ dbg: () => {
9
+ }
10
+ });
11
+ var DebugOverlayRoot = ({ children, enabled = true, maxLines = 10 }) => {
12
+ const overlayRef = useRef(null);
13
+ const dbg = useCallback((msg) => {
14
+ if (!overlayRef.current) {
15
+ return;
16
+ }
17
+ const line = document.createElement("pre");
18
+ line.textContent = `${(performance.now() / 1e3).toFixed(2).padStart(8, " ")} ${msg}`;
19
+ overlayRef.current.prepend(line);
20
+ while (overlayRef.current.children.length > maxLines) {
21
+ overlayRef.current.lastChild?.remove();
22
+ }
23
+ }, []);
24
+ return /* @__PURE__ */ React.createElement(DebugOverlayProvider, {
25
+ dbg
26
+ }, children, enabled && /* @__PURE__ */ React.createElement("div", {
27
+ ref: overlayRef,
28
+ style: {
29
+ position: "fixed",
30
+ bottom: "calc(var(--kb-height, 0px) + 8px)",
31
+ left: 8,
32
+ right: 8,
33
+ background: "rgba(0,0,0,0.8)",
34
+ color: "#0f0",
35
+ fontSize: 10,
36
+ fontFamily: "monospace",
37
+ padding: 6,
38
+ borderRadius: 4,
39
+ zIndex: 9999,
40
+ pointerEvents: "none"
41
+ }
42
+ }));
43
+ };
44
+ var DebugOverlay = {
45
+ Root: DebugOverlayRoot
46
+ };
47
+
48
+ // src/components/Home/Home.tsx
49
+ import React2, { useCallback as useCallback2, useEffect as useEffect2, useMemo, useRef as useRef2 } from "react";
50
+ import { useOperationInvoker } from "@dxos/app-framework/ui";
51
+ import { LayoutOperation } from "@dxos/app-toolkit";
52
+ import { useAppGraph as useAppGraph2 } from "@dxos/app-toolkit/ui";
53
+ import { Node, useConnections } from "@dxos/plugin-graph";
54
+ import { Avatar, Icon, ScrollArea, toLocalizedString, useTranslation } from "@dxos/react-ui";
55
+ import { Card } from "@dxos/react-ui";
56
+ import { Mosaic } from "@dxos/react-ui-mosaic";
57
+ import { SearchPanel, useSearchListItem, useSearchListResults } from "@dxos/react-ui-search";
58
+ import { mx } from "@dxos/ui-theme";
59
+ import { byPosition, getHostPlatform, isTauri } from "@dxos/util";
60
+ import { meta } from "#meta";
61
+
62
+ // src/components/hooks.ts
63
+ import { useEffect } from "react";
64
+ import { useAppGraph } from "@dxos/app-toolkit/ui";
65
+ import { Graph } from "@dxos/plugin-graph";
66
+ import { expandAttendableId } from "@dxos/react-ui-attention";
67
+ var useExpandPath = (nodeId) => {
68
+ const { graph } = useAppGraph();
69
+ useEffect(() => {
70
+ if (nodeId) {
71
+ for (const prefix of expandAttendableId(nodeId)) {
72
+ Graph.expand(graph, prefix, "child");
73
+ }
74
+ }
75
+ }, [
76
+ nodeId,
77
+ graph
78
+ ]);
79
+ };
80
+
81
+ // src/components/Home/Home.tsx
82
+ var Home = (_) => {
83
+ const { t } = useTranslation(meta.id);
84
+ const userAccountItem = useItemsByDisposition("user-account")[0];
85
+ const pinnedItems = useItemsByDisposition("pin-end", true);
86
+ const workspaceItems = useItemsByDisposition("workspace");
87
+ useExpandPath(Node.RootId);
88
+ const items = useMemo(() => [
89
+ ...userAccountItem ? [
90
+ userAccountItem
91
+ ] : [],
92
+ ...pinnedItems,
93
+ ...workspaceItems
94
+ ], [
95
+ userAccountItem,
96
+ pinnedItems,
97
+ workspaceItems
98
+ ]);
99
+ const { results, handleSearch } = useSearchListResults({
100
+ items,
101
+ extract: (node) => toLocalizedString(node.properties.label, t)
102
+ });
103
+ const autoFocus = !isTauri() || getHostPlatform() !== "ios";
104
+ return /* @__PURE__ */ React2.createElement(SearchPanel, {
105
+ onSearch: handleSearch
106
+ }, /* @__PURE__ */ React2.createElement(Mosaic.Container, {
107
+ asChild: true
108
+ }, /* @__PURE__ */ React2.createElement(ScrollArea.Root, {
109
+ centered: true,
110
+ padding: true,
111
+ thin: true
112
+ }, /* @__PURE__ */ React2.createElement(ScrollArea.Viewport, null, /* @__PURE__ */ React2.createElement(Mosaic.Stack, {
113
+ classNames: "gap-1",
114
+ draggable: false,
115
+ items: results,
116
+ getId: (item) => item.id,
117
+ Tile: WorkspaceTile
118
+ })))));
119
+ };
120
+ var WorkspaceTile = (props) => {
121
+ const data = props.data;
122
+ const { t } = useTranslation(meta.id);
123
+ const { invokePromise } = useOperationInvoker();
124
+ const { selectedValue, registerItem, unregisterItem } = useSearchListItem();
125
+ const name = toLocalizedString(data.properties.label, t);
126
+ const isSelected = selectedValue === data.id;
127
+ const cardRef = useRef2(null);
128
+ useExpandPath(data.id);
129
+ const handleSelect = useCallback2(() => invokePromise(LayoutOperation.SwitchWorkspace, {
130
+ subject: data.id
131
+ }), [
132
+ invokePromise,
133
+ data.id
134
+ ]);
135
+ useEffect2(() => {
136
+ if (cardRef.current) {
137
+ registerItem(data.id, cardRef.current, handleSelect);
138
+ }
139
+ return () => unregisterItem(data.id);
140
+ }, [
141
+ data.id,
142
+ handleSelect,
143
+ registerItem,
144
+ unregisterItem
145
+ ]);
146
+ useEffect2(() => {
147
+ if (isSelected && cardRef.current) {
148
+ cardRef.current.scrollIntoView({
149
+ block: "nearest",
150
+ behavior: "smooth"
151
+ });
152
+ }
153
+ }, [
154
+ isSelected
155
+ ]);
156
+ return /* @__PURE__ */ React2.createElement(Card.Root, {
157
+ role: "button",
158
+ fullWidth: true,
159
+ tabIndex: -1,
160
+ "data-selected": isSelected,
161
+ classNames: mx("dx-focus-ring", isSelected && "bg-selected-surface"),
162
+ onClick: handleSelect,
163
+ ref: cardRef
164
+ }, /* @__PURE__ */ React2.createElement(Card.Toolbar, {
165
+ density: "fine"
166
+ }, /* @__PURE__ */ React2.createElement(Avatar.Root, null, /* @__PURE__ */ React2.createElement(Avatar.Content, {
167
+ icon: data.properties.icon,
168
+ hue: data.properties.hue,
169
+ hueVariant: "transparent",
170
+ variant: "square",
171
+ size: 8,
172
+ fallback: name
173
+ }), /* @__PURE__ */ React2.createElement(Avatar.Label, {
174
+ classNames: "cursor-pointer"
175
+ }, name), /* @__PURE__ */ React2.createElement(Icon, {
176
+ icon: "ph--caret-right--regular"
177
+ }))));
178
+ };
179
+ var filterItems = (node, disposition) => {
180
+ return node.properties.disposition === disposition;
181
+ };
182
+ var useItemsByDisposition = (disposition, sort = false) => {
183
+ const { graph } = useAppGraph2();
184
+ const connections = useConnections(graph, Node.RootId, "child");
185
+ return useMemo(() => {
186
+ const filtered = connections.filter((node) => filterItems(node, disposition));
187
+ return sort ? filtered.toSorted((a, b) => byPosition(a.properties, b.properties)) : filtered;
188
+ }, [
189
+ connections,
190
+ disposition,
191
+ sort
192
+ ]);
193
+ };
194
+
195
+ // src/components/Loading/Loading.tsx
196
+ import React3 from "react";
197
+ var Loading = () => {
198
+ return /* @__PURE__ */ React3.createElement("div", {
199
+ className: "grid place-items-center dx-attention-surface"
200
+ });
201
+ };
202
+
203
+ // src/components/MobileLayout/MobileLayout.tsx
204
+ import { createContext as createContext2 } from "@radix-ui/react-context";
205
+ import React4, { forwardRef, useEffect as useEffect3, useLayoutEffect, useState } from "react";
206
+ import { addEventListener, combine } from "@dxos/async";
207
+ import { log } from "@dxos/log";
208
+ import { mx as mx2 } from "@dxos/ui-theme";
209
+ var __dxlog_file = "/__w/dxos/dxos/packages/plugins/plugin-simple-layout/src/components/MobileLayout/MobileLayout.tsx";
210
+ var MOBILE_LAYOUT_NAME = "MobileLayout";
211
+ var MOBILE_LAYOUT_ROOT_NAME = "MobileLayout.Root";
212
+ var MOBILE_LAYOUT_PANEL_NAME = "MobileLayout.Panel";
213
+ var [MobileLayoutProvider, useMobileLayout] = createContext2(MOBILE_LAYOUT_NAME);
214
+ var MobileLayoutRoot = /* @__PURE__ */ forwardRef(({ classNames, children, transition = 500, onKeyboardOpenChange, ...props }, forwardedRef) => {
215
+ const { open: keyboardOpen } = useIOSKeyboard();
216
+ useLockBodyScroll(keyboardOpen);
217
+ useAutoScroll();
218
+ useLayoutEffect(() => onKeyboardOpenChange?.(keyboardOpen), [
219
+ keyboardOpen,
220
+ onKeyboardOpenChange
221
+ ]);
222
+ return /* @__PURE__ */ React4.createElement(MobileLayoutProvider, {
223
+ keyboardOpen
224
+ }, /* @__PURE__ */ React4.createElement("div", {
225
+ ...props,
226
+ style: {
227
+ height: "calc(100vh - var(--kb-height, 0px))",
228
+ transition: `height ${keyboardOpen ? 0 : transition}ms ease-out`
229
+ },
230
+ className: mx2("fixed top-0 left-0 right-0 grid overflow-hidden", classNames),
231
+ ref: forwardedRef
232
+ }, children));
233
+ });
234
+ MobileLayoutRoot.displayName = MOBILE_LAYOUT_ROOT_NAME;
235
+ var MobileLayoutPanel = /* @__PURE__ */ forwardRef(({ classNames, children, safe, ...props }, forwardedRef) => {
236
+ return /* @__PURE__ */ React4.createElement("div", {
237
+ ...props,
238
+ style: {
239
+ paddingTop: safe?.top ? "env(safe-area-inset-top)" : void 0,
240
+ paddingBottom: safe?.bottom ? `calc((1 - var(--kb-open, 0)) * env(safe-area-inset-bottom))` : void 0
241
+ },
242
+ className: mx2(classNames),
243
+ ref: forwardedRef
244
+ }, children);
245
+ });
246
+ MobileLayoutPanel.displayName = MOBILE_LAYOUT_PANEL_NAME;
247
+ var MobileLayout = {
248
+ Root: MobileLayoutRoot,
249
+ Panel: MobileLayoutPanel
250
+ };
251
+ var useAutoScroll = () => {
252
+ const { dbg } = useDebugLog("useAutoScroll");
253
+ useEffect3(() => {
254
+ const resetScroll = () => {
255
+ if (window.scrollX !== 0 || window.scrollY !== 0) {
256
+ window.scrollTo(0, 0);
257
+ }
258
+ };
259
+ const detectContainerScroll = (event) => {
260
+ const el = event.target;
261
+ if (el === document.documentElement || el === document.body) {
262
+ return;
263
+ }
264
+ dbg(`scroll: ${el.tagName}.${Array.from(el.classList).slice(0, 2).join(".")} top=${el.scrollTop.toFixed(0)}`);
265
+ };
266
+ return combine(
267
+ addEventListener(window, "scroll", resetScroll),
268
+ window.visualViewport ? addEventListener(window.visualViewport, "scroll", resetScroll) : () => {
269
+ },
270
+ // TODO(burdon): Remove debug logging.
271
+ addEventListener(document, "scroll", detectContainerScroll, {
272
+ capture: true
273
+ }),
274
+ // Prevent focus-triggered scroll-into-view on inputs.
275
+ (() => {
276
+ let focusingWithPreventScroll = false;
277
+ return addEventListener(document, "focus", (event) => {
278
+ if (focusingWithPreventScroll) {
279
+ return;
280
+ }
281
+ const target = event.target;
282
+ if (target.tagName === "INPUT" || target.tagName === "TEXTAREA" || target.isContentEditable) {
283
+ focusingWithPreventScroll = true;
284
+ target.focus({
285
+ preventScroll: true
286
+ });
287
+ focusingWithPreventScroll = false;
288
+ }
289
+ }, {
290
+ capture: true
291
+ });
292
+ })()
293
+ );
294
+ }, [
295
+ dbg
296
+ ]);
297
+ };
298
+ var useLockBodyScroll = (enabled) => {
299
+ useEffect3(() => {
300
+ if (!enabled) {
301
+ return;
302
+ }
303
+ const isScrollable = (el, axis) => {
304
+ while (el && el !== document.body) {
305
+ const style = getComputedStyle(el);
306
+ if (axis === "y") {
307
+ const overflow = style.overflowY;
308
+ if ((overflow === "auto" || overflow === "scroll") && el.scrollHeight > el.clientHeight) {
309
+ return true;
310
+ }
311
+ } else {
312
+ const overflow = style.overflowX;
313
+ if ((overflow === "auto" || overflow === "scroll") && el.scrollWidth > el.clientWidth) {
314
+ return true;
315
+ }
316
+ }
317
+ el = el.parentElement;
318
+ }
319
+ return false;
320
+ };
321
+ let touchStartX = 0;
322
+ let touchStartY = 0;
323
+ return combine(
324
+ // Record initial touch position.
325
+ addEventListener(document, "touchstart", (event) => {
326
+ const touch = event.touches[0];
327
+ touchStartX = touch.clientX;
328
+ touchStartY = touch.clientY;
329
+ }, {
330
+ passive: true
331
+ }),
332
+ // Prevent scrolling the viewport.
333
+ addEventListener(document, "touchmove", (event) => {
334
+ const touch = event.touches[0];
335
+ const dx = Math.abs(touch.clientX - touchStartX);
336
+ const dy = Math.abs(touch.clientY - touchStartY);
337
+ if (!isScrollable(event.target, dx > dy ? "x" : "y")) {
338
+ event.preventDefault();
339
+ }
340
+ }, {
341
+ passive: false
342
+ })
343
+ );
344
+ }, [
345
+ enabled
346
+ ]);
347
+ };
348
+ var useIOSKeyboard = () => {
349
+ const { dbg } = useDebugLog("useIOSKeyboard");
350
+ const [open, setOpen] = useState(false);
351
+ const [height, setHeight] = useState(0);
352
+ const [duration, setDuration] = useState(void 0);
353
+ useEffect3(() => {
354
+ const viewport = window.visualViewport;
355
+ if (!viewport) {
356
+ return;
357
+ }
358
+ const initialHeight = viewport.height ?? window.innerHeight;
359
+ const updateState = (keyboardHeight, keyboardOpen, animationDuration) => {
360
+ setOpen(keyboardOpen);
361
+ setHeight(keyboardHeight);
362
+ setDuration(animationDuration);
363
+ const vvh = initialHeight - keyboardHeight;
364
+ document.documentElement.style.setProperty("--vvh", `${vvh}px`);
365
+ document.documentElement.style.setProperty("--kb-height", `${keyboardHeight}px`);
366
+ document.documentElement.style.setProperty("--kb-open", keyboardOpen ? "1" : "0");
367
+ log.info("viewport size", {
368
+ initialHeight,
369
+ vvh,
370
+ keyboardHeight,
371
+ keyboardOpen,
372
+ animationDuration
373
+ }, { "~LogMeta": "~LogMeta", F: __dxlog_file, L: 211, S: void 0 });
374
+ };
375
+ let rafId;
376
+ return combine(
377
+ // Handler for native iOS keyboard events (from KeyboardObserver.swift).
378
+ addEventListener(window, "keyboard", (event) => {
379
+ const { type, height: height2, duration: duration2 } = event.detail;
380
+ const durationMs = duration2 < 1 ? duration2 * 1e3 : duration2;
381
+ const vp = window.visualViewport;
382
+ dbg(`kb:${type} h=${height2} dur=${duration2} scrollY=${window.scrollY} vpOffset=${vp?.offsetTop?.toFixed(0) ?? "?"}`);
383
+ log.info("keyboard event", {
384
+ type,
385
+ height: height2,
386
+ duration: duration2
387
+ }, { "~LogMeta": "~LogMeta", F: __dxlog_file, L: 228, S: void 0 });
388
+ updateState(height2, type === "show", durationMs);
389
+ const end = performance.now() + durationMs + 300;
390
+ let prevOffsetTop = vp?.offsetTop ?? 0;
391
+ let prevScrollY = window.scrollY;
392
+ const monitorFrame = () => {
393
+ const offsetTop = vp?.offsetTop ?? 0;
394
+ const scrollY = window.scrollY;
395
+ if (offsetTop !== prevOffsetTop || scrollY !== prevScrollY) {
396
+ dbg(`\u0394 vpOffset=${offsetTop.toFixed(0)} scrollY=${scrollY.toFixed(0)}`);
397
+ prevOffsetTop = offsetTop;
398
+ prevScrollY = scrollY;
399
+ }
400
+ if (scrollY !== 0) {
401
+ window.scrollTo(0, 0);
402
+ }
403
+ if (performance.now() < end) {
404
+ rafId = requestAnimationFrame(monitorFrame);
405
+ }
406
+ };
407
+ rafId = requestAnimationFrame(monitorFrame);
408
+ }),
409
+ () => {
410
+ if (rafId !== void 0) {
411
+ cancelAnimationFrame(rafId);
412
+ }
413
+ }
414
+ );
415
+ }, [
416
+ dbg
417
+ ]);
418
+ return {
419
+ open,
420
+ height,
421
+ duration
422
+ };
423
+ };
424
+
425
+ // src/components/Popover/Popover.tsx
426
+ import { createContext as createContext3 } from "@radix-ui/react-context";
427
+ import React5, { useCallback as useCallback3, useEffect as useEffect4, useMemo as useMemo2, useRef as useRef3, useState as useState2 } from "react";
428
+ import { Surface } from "@dxos/app-framework/ui";
429
+ import { AppSurface } from "@dxos/app-toolkit/ui";
430
+ import { Popover, toLocalizedString as toLocalizedString2, useTranslation as useTranslation2 } from "@dxos/react-ui";
431
+ import { Card as Card2 } from "@dxos/react-ui";
432
+ import { useSimpleLayoutState } from "#hooks";
433
+ import { meta as meta2 } from "#meta";
434
+ var DEBOUNCE_DELAY = 40;
435
+ var [LayoutPopoverProvider, useLayoutPopoverContext] = createContext3("LayoutPopover");
436
+ var PopoverRoot = ({ children }) => {
437
+ const { state } = useSimpleLayoutState();
438
+ const [open, setOpen] = useState2(false);
439
+ const virtualRef = useRef3(null);
440
+ const [virtualIter, setVirtualIter] = useState2(0);
441
+ const debounceRef = useRef3(null);
442
+ useEffect4(() => {
443
+ setOpen(false);
444
+ if (state.popoverOpen) {
445
+ if (debounceRef.current) {
446
+ clearTimeout(debounceRef.current);
447
+ }
448
+ if (state.popoverAnchor && virtualRef.current !== state.popoverAnchor) {
449
+ virtualRef.current = state.popoverAnchor ?? null;
450
+ setVirtualIter((iter) => iter + 1);
451
+ }
452
+ debounceRef.current = setTimeout(() => setOpen(true), DEBOUNCE_DELAY);
453
+ }
454
+ }, [
455
+ state.popoverOpen,
456
+ state.popoverAnchorId,
457
+ state.popoverAnchor,
458
+ state.popoverContent
459
+ ]);
460
+ return /* @__PURE__ */ React5.createElement(LayoutPopoverProvider, {
461
+ setOpen
462
+ }, /* @__PURE__ */ React5.createElement(Popover.Root, {
463
+ modal: false,
464
+ open
465
+ }, state.popoverAnchor && /* @__PURE__ */ React5.createElement(Popover.VirtualTrigger, {
466
+ key: virtualIter,
467
+ virtualRef
468
+ }), children));
469
+ };
470
+ var PopoverContent = () => {
471
+ const { t } = useTranslation2(meta2.id);
472
+ const { state, updateState } = useSimpleLayoutState();
473
+ const { setOpen } = useLayoutPopoverContext("PopoverContent");
474
+ const handleClose = useCallback3(() => {
475
+ setOpen(false);
476
+ updateState((s) => ({
477
+ ...s,
478
+ popoverOpen: false,
479
+ popoverAnchor: void 0,
480
+ popoverAnchorId: void 0,
481
+ popoverSide: void 0
482
+ }));
483
+ }, [
484
+ setOpen,
485
+ updateState
486
+ ]);
487
+ const handleInteractOutside = useCallback3((event) => {
488
+ if (
489
+ // TODO(thure): CodeMirror should not focus itself when it updates.
490
+ event.type === "dismissableLayer.focusOutside" && event.currentTarget?.classList.contains("cm-content")
491
+ ) {
492
+ event.preventDefault();
493
+ } else {
494
+ handleClose();
495
+ }
496
+ }, [
497
+ handleClose
498
+ ]);
499
+ const collisionBoundaries = useMemo2(() => {
500
+ const closest = state.popoverAnchor?.closest("[data-popover-collision-boundary]");
501
+ return closest ? [
502
+ closest
503
+ ] : [];
504
+ }, [
505
+ state.popoverAnchor
506
+ ]);
507
+ return /* @__PURE__ */ React5.createElement(Popover.Portal, null, /* @__PURE__ */ React5.createElement(Popover.Content, {
508
+ side: state.popoverSide,
509
+ sticky: "always",
510
+ hideWhenDetached: true,
511
+ collisionBoundary: collisionBoundaries,
512
+ onInteractOutside: handleInteractOutside,
513
+ onEscapeKeyDown: handleInteractOutside
514
+ }, /* @__PURE__ */ React5.createElement(Popover.Viewport, null, state.popoverKind === "base" && state.popoverContent && "component" in state.popoverContent && /* @__PURE__ */ React5.createElement(Surface.Surface, {
515
+ type: AppSurface.Popover,
516
+ data: state.popoverContent,
517
+ limit: 1
518
+ }), state.popoverKind === "card" && /* @__PURE__ */ React5.createElement(Card2.Root, {
519
+ border: false,
520
+ classNames: "dx-card-popover"
521
+ }, /* @__PURE__ */ React5.createElement(Card2.Toolbar, null, /* @__PURE__ */ React5.createElement("span", null), state.popoverTitle ? /* @__PURE__ */ React5.createElement(Card2.Title, null, toLocalizedString2(state.popoverTitle, t)) : /* @__PURE__ */ React5.createElement("span", null), /* @__PURE__ */ React5.createElement(Card2.CloseIconButton, {
522
+ onClick: handleClose
523
+ })), state.popoverContent && "subject" in state.popoverContent && /* @__PURE__ */ React5.createElement(Surface.Surface, {
524
+ type: AppSurface.Card,
525
+ data: state.popoverContent,
526
+ limit: 1
527
+ }))), /* @__PURE__ */ React5.createElement(Popover.Arrow, null)));
528
+ };
529
+
530
+ // src/components/SimpleLayout/AppBar.tsx
531
+ import { useAtomValue } from "@effect-atom/atom-react";
532
+ import React6, { Fragment } from "react";
533
+ import { DensityProvider, IconButton, Popover as Popover2, Toolbar, useTranslation as useTranslation3 } from "@dxos/react-ui";
534
+ import { Menu, useMenuActions } from "@dxos/react-ui-menu";
535
+ import { composable, composableProps, osTranslations } from "@dxos/ui-theme";
536
+ import { meta as meta3 } from "#meta";
537
+ var APP_BAR_NAME = "SimpleLayout.AppBar";
538
+ var AppBar = composable(({ classNames, title, actions, showBackButton, popoverAnchorId, onAction, onBack, ...props }, forwardedRef) => {
539
+ const { t } = useTranslation3(meta3.id);
540
+ const menuActions = useMenuActions(actions);
541
+ const actionsValue = useAtomValue(actions);
542
+ const hasActions = actionsValue.nodes.length > 0;
543
+ const { keyboardOpen } = useMobileLayout(APP_BAR_NAME);
544
+ const displayTitle = title ?? t("current-app.name", {
545
+ ns: osTranslations
546
+ });
547
+ const AnchorRoot = popoverAnchorId ? Popover2.Anchor : Fragment;
548
+ return /* @__PURE__ */ React6.createElement(DensityProvider, {
549
+ density: "fine"
550
+ }, /* @__PURE__ */ React6.createElement(Toolbar.Root, {
551
+ ...composableProps(props, {
552
+ role: "banner",
553
+ classNames: "grid grid-cols-[var(--dx-rail-size)_1fr_var(--dx-rail-size)] items-center dx-density-fine"
554
+ }),
555
+ ref: forwardedRef
556
+ }, keyboardOpen ? /* @__PURE__ */ React6.createElement(IconButton, {
557
+ variant: "ghost",
558
+ icon: "ph--x--regular",
559
+ iconOnly: true,
560
+ label: t("done.label")
561
+ }) : showBackButton ? /* @__PURE__ */ React6.createElement(IconButton, {
562
+ variant: "ghost",
563
+ icon: "ph--caret-left--regular",
564
+ iconOnly: true,
565
+ label: t("back.label"),
566
+ onClick: onBack
567
+ }) : /* @__PURE__ */ React6.createElement("div", null), /* @__PURE__ */ React6.createElement("h1", {
568
+ className: "text-center truncate font-thin uppercase"
569
+ }, displayTitle), hasActions ? /* @__PURE__ */ React6.createElement(AnchorRoot, null, /* @__PURE__ */ React6.createElement(Menu.Root, {
570
+ ...menuActions,
571
+ caller: meta3.id,
572
+ onAction
573
+ }, /* @__PURE__ */ React6.createElement(Menu.Trigger, {
574
+ asChild: true
575
+ }, /* @__PURE__ */ React6.createElement(IconButton, {
576
+ variant: "ghost",
577
+ icon: "ph--dots-three-vertical--regular",
578
+ iconOnly: true,
579
+ label: t("actions-menu.label")
580
+ })), /* @__PURE__ */ React6.createElement(Menu.Content, null))) : /* @__PURE__ */ React6.createElement("span", null)));
581
+ });
582
+ AppBar.displayName = APP_BAR_NAME;
583
+
584
+ // src/components/SimpleLayout/Main.tsx
585
+ import React8, { useMemo as useMemo3 } from "react";
586
+ import { Surface as Surface2 } from "@dxos/app-framework/ui";
587
+ import { AppSurface as AppSurface2, useAppGraph as useAppGraph3 } from "@dxos/app-toolkit/ui";
588
+ import { useNode } from "@dxos/plugin-graph";
589
+ import { ErrorFallback, Panel } from "@dxos/react-ui";
590
+ import { useAttentionAttributes } from "@dxos/react-ui-attention";
591
+ import { useAppBarProps, useNavbarActions, useSimpleLayoutState as useSimpleLayoutState2 } from "#hooks";
592
+
593
+ // src/components/SimpleLayout/NavBar.tsx
594
+ import React7 from "react";
595
+ import { Menu as Menu2, useMenuActions as useMenuActions2 } from "@dxos/react-ui-menu";
596
+ import { composable as composable2, composableProps as composableProps2 } from "@dxos/ui-theme";
597
+ var NAVBAR_NAME = "SimpleLayout.NavBar";
598
+ var NavBar = composable2(({ actions, onAction, ...props }, forwardedRef) => {
599
+ const menuActions = useMenuActions2(actions);
600
+ return /* @__PURE__ */ React7.createElement(Menu2.Root, {
601
+ ...menuActions,
602
+ alwaysActive: true,
603
+ onAction
604
+ }, /* @__PURE__ */ React7.createElement(Menu2.Toolbar, {
605
+ ...composableProps2(props),
606
+ ref: forwardedRef
607
+ }));
608
+ });
609
+ NavBar.displayName = NAVBAR_NAME;
610
+
611
+ // src/components/SimpleLayout/Main.tsx
612
+ var MAIN_NAME = "SimpleLayout.Main";
613
+ var Main = () => {
614
+ const { state } = useSimpleLayoutState2();
615
+ const id = state.active ?? state.workspace;
616
+ const attentionAttrs = useAttentionAttributes(id);
617
+ const { keyboardOpen } = useMobileLayout(MAIN_NAME);
618
+ const { actions, onAction } = useNavbarActions();
619
+ const appBarProps = useAppBarProps();
620
+ const placeholder = useMemo3(() => /* @__PURE__ */ React8.createElement(Loading, null), []);
621
+ const { graph } = useAppGraph3();
622
+ const node = useNode(graph, id);
623
+ const data = useMemo3(() => {
624
+ return node && {
625
+ attendableId: id,
626
+ subject: node.data,
627
+ properties: node.properties,
628
+ popoverAnchorId: state.popoverAnchorId
629
+ };
630
+ }, [
631
+ id,
632
+ node,
633
+ node?.data,
634
+ node?.properties,
635
+ state.popoverAnchorId
636
+ ]);
637
+ useExpandPath(id);
638
+ const showNavBar = !keyboardOpen && !state.isPopover && state.drawerState === "closed";
639
+ return /* @__PURE__ */ React8.createElement(Panel.Root, {
640
+ ...attentionAttrs,
641
+ className: "dx-document"
642
+ }, /* @__PURE__ */ React8.createElement(Panel.Toolbar, {
643
+ asChild: true
644
+ }, /* @__PURE__ */ React8.createElement(AppBar, appBarProps)), /* @__PURE__ */ React8.createElement(Panel.Content, {
645
+ role: "article",
646
+ className: "bg-base-surface"
647
+ }, /* @__PURE__ */ React8.createElement(Surface2.Surface, {
648
+ key: id,
649
+ type: AppSurface2.Article,
650
+ data,
651
+ limit: 1,
652
+ fallback: ErrorFallback,
653
+ placeholder
654
+ })), showNavBar && /* @__PURE__ */ React8.createElement(Panel.Statusbar, {
655
+ asChild: true
656
+ }, /* @__PURE__ */ React8.createElement(NavBar, {
657
+ actions,
658
+ onAction
659
+ })));
660
+ };
661
+ Main.displayName = MAIN_NAME;
662
+
663
+ // src/components/SimpleLayout/SimpleLayout.tsx
664
+ import React11, { useLayoutEffect as useLayoutEffect2, useRef as useRef4, useState as useState3 } from "react";
665
+ import { Splitter } from "@dxos/react-ui";
666
+ import { Mosaic as Mosaic2 } from "@dxos/react-ui-mosaic";
667
+ import { useSimpleLayoutState as useSimpleLayoutState5 } from "#hooks";
668
+
669
+ // src/components/Dialog/Dialog.tsx
670
+ import React9 from "react";
671
+ import { Surface as Surface3 } from "@dxos/app-framework/ui";
672
+ import { AppSurface as AppSurface3 } from "@dxos/app-toolkit/ui";
673
+ import { AlertDialog, Dialog as NaturalDialog } from "@dxos/react-ui";
674
+ import { ErrorFallback as ErrorFallback2 } from "@dxos/react-ui";
675
+ import { useSimpleLayoutState as useSimpleLayoutState3 } from "#hooks";
676
+ var Dialog = () => {
677
+ const { state, updateState } = useSimpleLayoutState3();
678
+ const DialogRoot = state.dialogType === "alert" ? AlertDialog.Root : NaturalDialog.Root;
679
+ const DialogOverlay = state.dialogType === "alert" ? AlertDialog.Overlay : NaturalDialog.Overlay;
680
+ return /* @__PURE__ */ React9.createElement(DialogRoot, {
681
+ modal: state.dialogBlockAlign !== "end",
682
+ open: state.dialogOpen,
683
+ onOpenChange: (nextOpen) => updateState((state2) => ({
684
+ ...state2,
685
+ dialogOpen: nextOpen
686
+ }))
687
+ }, state.dialogBlockAlign === "end" ? /* @__PURE__ */ React9.createElement(Surface3.Surface, {
688
+ type: AppSurface3.Dialog,
689
+ data: state.dialogContent ?? void 0,
690
+ limit: 1,
691
+ fallback: ErrorFallback2
692
+ }) : /* @__PURE__ */ React9.createElement(DialogOverlay, {
693
+ blockAlign: state.dialogBlockAlign,
694
+ classNames: state.dialogOverlayClasses,
695
+ style: state.dialogOverlayStyle
696
+ }, /* @__PURE__ */ React9.createElement(Surface3.Surface, {
697
+ type: AppSurface3.Dialog,
698
+ data: state.dialogContent ?? void 0,
699
+ limit: 1,
700
+ fallback: ErrorFallback2
701
+ })));
702
+ };
703
+
704
+ // src/components/SimpleLayout/Drawer.tsx
705
+ import React10, { useMemo as useMemo4 } from "react";
706
+ import { Surface as Surface4 } from "@dxos/app-framework/ui";
707
+ import { AppSurface as AppSurface4, useAppGraph as useAppGraph4 } from "@dxos/app-toolkit/ui";
708
+ import { useNode as useNode2 } from "@dxos/plugin-graph";
709
+ import { ErrorFallback as ErrorFallback3, Panel as Panel2 } from "@dxos/react-ui";
710
+ import { getLinkedVariant } from "@dxos/react-ui-attention";
711
+ import { Menu as Menu3, useMenuActions as useMenuActions3 } from "@dxos/react-ui-menu";
712
+ import { useCompanions, useDrawerActions, useSimpleLayoutState as useSimpleLayoutState4 } from "#hooks";
713
+ var DRAWER_NAME = "SimpleLayout.Drawer";
714
+ var Drawer = () => {
715
+ const { graph } = useAppGraph4();
716
+ const { state: layoutState } = useSimpleLayoutState4();
717
+ const placeholder = useMemo4(() => /* @__PURE__ */ React10.createElement(Loading, null), []);
718
+ const activeId = layoutState.active ?? layoutState.workspace;
719
+ const companions = useCompanions(activeId);
720
+ const { companionId, variant } = useSelectedCompanion(companions, layoutState.companionVariant);
721
+ const node = useNode2(graph, companionId);
722
+ const parentNode = useNode2(graph, activeId);
723
+ const data = useMemo4(() => {
724
+ if (!node || !companionId) {
725
+ return void 0;
726
+ }
727
+ return {
728
+ attendableId: companionId,
729
+ subject: node.data,
730
+ companionTo: parentNode?.data,
731
+ properties: node.properties,
732
+ variant
733
+ };
734
+ }, [
735
+ companionId,
736
+ node,
737
+ parentNode,
738
+ variant
739
+ ]);
740
+ const { actions, onAction } = useDrawerActions(DRAWER_NAME);
741
+ const menuActions = useMenuActions3(actions);
742
+ return /* @__PURE__ */ React10.createElement(Panel2.Root, null, /* @__PURE__ */ React10.createElement(Panel2.Toolbar, null, /* @__PURE__ */ React10.createElement(Menu3.Root, {
743
+ ...menuActions,
744
+ alwaysActive: true,
745
+ onAction
746
+ }, /* @__PURE__ */ React10.createElement(Menu3.Toolbar, null))), /* @__PURE__ */ React10.createElement(Panel2.Content, null, /* @__PURE__ */ React10.createElement(Surface4.Surface, {
747
+ type: AppSurface4.Article,
748
+ data,
749
+ limit: 1,
750
+ fallback: ErrorFallback3,
751
+ placeholder
752
+ })));
753
+ };
754
+ Drawer.displayName = DRAWER_NAME;
755
+ var useSelectedCompanion = (companions, preferredVariant) => {
756
+ const selectedCompanion = useMemo4(() => {
757
+ if (companions.length === 0) {
758
+ return void 0;
759
+ }
760
+ if (preferredVariant) {
761
+ const preferred = companions.find((c) => getLinkedVariant(c.id) === preferredVariant);
762
+ if (preferred) {
763
+ return preferred;
764
+ }
765
+ }
766
+ return companions[0];
767
+ }, [
768
+ companions,
769
+ preferredVariant
770
+ ]);
771
+ const companionId = selectedCompanion?.id;
772
+ const variant = companionId ? getLinkedVariant(companionId) : void 0;
773
+ return {
774
+ selectedCompanion,
775
+ companionId,
776
+ variant
777
+ };
778
+ };
779
+
780
+ // src/components/SimpleLayout/SimpleLayout.tsx
781
+ var SimpleLayout = () => {
782
+ const { state } = useSimpleLayoutState5();
783
+ const [keyboardOpen, setKeyboardOpen] = useState3(false);
784
+ const [splitterMode, setSplitterMode] = useState3("top");
785
+ const drawerRef = useRef4(null);
786
+ useLayoutEffect2(() => {
787
+ if (!keyboardOpen) {
788
+ setSplitterMode(state.drawerState === "closed" ? "top" : state.drawerState === "open" ? "split" : "bottom");
789
+ }
790
+ }, [
791
+ state.drawerState,
792
+ keyboardOpen
793
+ ]);
794
+ return /* @__PURE__ */ React11.createElement(DebugOverlay.Root, {
795
+ enabled: false
796
+ }, /* @__PURE__ */ React11.createElement(PopoverRoot, null, /* @__PURE__ */ React11.createElement(Mosaic2.Root, null, /* @__PURE__ */ React11.createElement(MobileLayout.Root, {
797
+ classNames: "dx-container grid relative bg-toolbar-surface",
798
+ onKeyboardOpenChange: (nextKeyboardOpen) => setKeyboardOpen(nextKeyboardOpen)
799
+ }, /* @__PURE__ */ React11.createElement(MobileLayout.Panel, {
800
+ safe: {
801
+ top: true,
802
+ bottom: splitterMode === "top"
803
+ }
804
+ }, /* @__PURE__ */ React11.createElement(Splitter.Root, {
805
+ mode: splitterMode,
806
+ ratio: 0.55
807
+ }, /* @__PURE__ */ React11.createElement(Splitter.Panel, {
808
+ position: "top"
809
+ }, /* @__PURE__ */ React11.createElement(Main, null)), /* @__PURE__ */ React11.createElement(Splitter.Panel, {
810
+ position: "bottom",
811
+ ref: drawerRef
812
+ }, /* @__PURE__ */ React11.createElement(Drawer, null))), /* @__PURE__ */ React11.createElement(Dialog, null), /* @__PURE__ */ React11.createElement(PopoverContent, null))))));
813
+ };
814
+
815
+ // src/components/NavBranch/NavBranch.tsx
816
+ import React12, { useCallback as useCallback4, useEffect as useEffect5, useMemo as useMemo5, useRef as useRef5 } from "react";
817
+ import { useOperationInvoker as useOperationInvoker2 } from "@dxos/app-framework/ui";
818
+ import { LayoutOperation as LayoutOperation2 } from "@dxos/app-toolkit";
819
+ import { useAppGraph as useAppGraph5 } from "@dxos/app-toolkit/ui";
820
+ import { useConnections as useConnections2 } from "@dxos/plugin-graph";
821
+ import { Avatar as Avatar2, Icon as Icon2, ScrollArea as ScrollArea2, toLocalizedString as toLocalizedString3, useTranslation as useTranslation4 } from "@dxos/react-ui";
822
+ import { Card as Card3 } from "@dxos/react-ui";
823
+ import { Mosaic as Mosaic3 } from "@dxos/react-ui-mosaic";
824
+ import { SearchPanel as SearchPanel2, useSearchListItem as useSearchListItem2, useSearchListResults as useSearchListResults2 } from "@dxos/react-ui-search";
825
+ import { mx as mx3 } from "@dxos/ui-theme";
826
+ import { meta as meta4 } from "#meta";
827
+ var NavBranch = ({ id }) => {
828
+ const { t } = useTranslation4(meta4.id);
829
+ const { graph } = useAppGraph5();
830
+ useExpandPath(id);
831
+ const children = useConnections2(graph, id, "child");
832
+ const visibleChildren = useMemo5(() => children.filter((node) => node.properties.disposition !== "hidden"), [
833
+ children
834
+ ]);
835
+ const { results, handleSearch } = useSearchListResults2({
836
+ items: visibleChildren,
837
+ extract: (child) => toLocalizedString3(child.properties.label, t)
838
+ });
839
+ return /* @__PURE__ */ React12.createElement(SearchPanel2, {
840
+ onSearch: handleSearch
841
+ }, /* @__PURE__ */ React12.createElement(Mosaic3.Container, {
842
+ asChild: true
843
+ }, /* @__PURE__ */ React12.createElement(ScrollArea2.Root, {
844
+ centered: true,
845
+ padding: true,
846
+ thin: true
847
+ }, /* @__PURE__ */ React12.createElement(ScrollArea2.Viewport, null, /* @__PURE__ */ React12.createElement(Mosaic3.Stack, {
848
+ classNames: "gap-1",
849
+ draggable: false,
850
+ items: results,
851
+ getId: (item) => item.id,
852
+ Tile: NavBranchTile
853
+ })))));
854
+ };
855
+ var NavBranchTile = (props) => {
856
+ const data = props.data;
857
+ const { t } = useTranslation4(meta4.id);
858
+ const { invokePromise } = useOperationInvoker2();
859
+ const ref = useRef5(null);
860
+ const { selectedValue, registerItem, unregisterItem } = useSearchListItem2();
861
+ const isSelected = selectedValue === data.id;
862
+ const name = toLocalizedString3(data.properties.label, t);
863
+ const handleSelect = useCallback4(() => void invokePromise(LayoutOperation2.Open, {
864
+ subject: [
865
+ data.id
866
+ ]
867
+ }), [
868
+ invokePromise,
869
+ data.id
870
+ ]);
871
+ useEffect5(() => {
872
+ if (ref.current) {
873
+ registerItem(data.id, ref.current, handleSelect);
874
+ }
875
+ return () => unregisterItem(data.id);
876
+ }, [
877
+ data.id,
878
+ handleSelect,
879
+ registerItem,
880
+ unregisterItem
881
+ ]);
882
+ useEffect5(() => {
883
+ if (isSelected && ref.current) {
884
+ ref.current.scrollIntoView({
885
+ block: "nearest",
886
+ behavior: "smooth"
887
+ });
888
+ }
889
+ }, [
890
+ isSelected
891
+ ]);
892
+ return /* @__PURE__ */ React12.createElement(Card3.Root, {
893
+ ref,
894
+ role: "button",
895
+ fullWidth: true,
896
+ tabIndex: -1,
897
+ "data-selected": isSelected,
898
+ classNames: mx3("dx-focus-ring cursor-pointer", isSelected && "bg-selected-surface"),
899
+ onClick: handleSelect
900
+ }, /* @__PURE__ */ React12.createElement(Card3.Toolbar, null, /* @__PURE__ */ React12.createElement(Avatar2.Root, null, /* @__PURE__ */ React12.createElement(Avatar2.Content, {
901
+ hue: data.properties.hue,
902
+ icon: data.properties.icon,
903
+ hueVariant: "transparent",
904
+ variant: "square",
905
+ size: 8,
906
+ fallback: name
907
+ }), /* @__PURE__ */ React12.createElement(Avatar2.Label, null, name), /* @__PURE__ */ React12.createElement(Icon2, {
908
+ icon: "ph--caret-right--regular"
909
+ }))));
910
+ };
911
+ export {
912
+ AppBar,
913
+ DebugOverlay,
914
+ Home,
915
+ Loading,
916
+ Main,
917
+ MobileLayout,
918
+ NavBar,
919
+ NavBranch,
920
+ PopoverContent,
921
+ PopoverRoot,
922
+ SimpleLayout,
923
+ useDebugLog,
924
+ useMobileLayout
925
+ };
926
+ //# sourceMappingURL=index.mjs.map