@dxos/plugin-deck 0.7.5-labs.c0e040f → 0.7.5-labs.d199c0f

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 (113) hide show
  1. package/dist/lib/browser/{app-graph-builder-CI6ZFMNL.mjs → app-graph-builder-IYHAGFA3.mjs} +6 -2
  2. package/dist/lib/browser/{app-graph-builder-CI6ZFMNL.mjs.map → app-graph-builder-IYHAGFA3.mjs.map} +3 -3
  3. package/dist/lib/browser/{chunk-YQ2GWTDU.mjs → chunk-22AQ5IVX.mjs} +1 -1
  4. package/dist/lib/browser/{chunk-YQ2GWTDU.mjs.map → chunk-22AQ5IVX.mjs.map} +2 -2
  5. package/dist/lib/browser/{chunk-PTLNGUND.mjs → chunk-FT33W5CI.mjs} +3 -1
  6. package/dist/lib/browser/chunk-FT33W5CI.mjs.map +7 -0
  7. package/dist/lib/browser/chunk-G2X3ZDCE.mjs +24 -0
  8. package/dist/lib/browser/chunk-KANJBSIX.mjs +97 -0
  9. package/dist/lib/browser/chunk-KANJBSIX.mjs.map +7 -0
  10. package/dist/lib/browser/{chunk-23KS5L3I.mjs → chunk-O4RFYYQ6.mjs} +114 -124
  11. package/dist/lib/browser/chunk-O4RFYYQ6.mjs.map +7 -0
  12. package/dist/lib/browser/index.mjs +13 -6
  13. package/dist/lib/browser/index.mjs.map +3 -3
  14. package/dist/lib/browser/{intent-resolver-WAYXVAFN.mjs → intent-resolver-ZD67BRUI.mjs} +37 -46
  15. package/dist/lib/browser/intent-resolver-ZD67BRUI.mjs.map +7 -0
  16. package/dist/lib/browser/meta.json +1 -1
  17. package/dist/lib/browser/{react-root-YATKEIAZ.mjs → react-root-6ILKHD5J.mjs} +7 -8
  18. package/dist/lib/browser/react-root-6ILKHD5J.mjs.map +7 -0
  19. package/dist/lib/browser/{react-surface-SS2BX6FS.mjs → react-surface-O75FKXAI.mjs} +6 -5
  20. package/dist/lib/browser/{react-surface-SS2BX6FS.mjs.map → react-surface-O75FKXAI.mjs.map} +1 -1
  21. package/dist/lib/browser/{settings-CRQTVMN3.mjs → settings-H35U6NHE.mjs} +2 -2
  22. package/dist/lib/browser/{state-YEQA3IIB.mjs → state-U4SHOPJW.mjs} +16 -4
  23. package/dist/lib/browser/state-U4SHOPJW.mjs.map +7 -0
  24. package/dist/lib/browser/{tools-5LDJNU56.mjs → tools-64LXGLYR.mjs} +12 -4
  25. package/dist/lib/browser/tools-64LXGLYR.mjs.map +7 -0
  26. package/dist/lib/browser/types.mjs +1 -1
  27. package/dist/lib/browser/{url-handler-HLF42IHP.mjs → url-handler-MVHTKUYA.mjs} +8 -6
  28. package/dist/lib/browser/{url-handler-HLF42IHP.mjs.map → url-handler-MVHTKUYA.mjs.map} +3 -3
  29. package/dist/types/src/DeckPlugin.d.ts.map +1 -1
  30. package/dist/types/src/capabilities/app-graph-builder.d.ts +110 -110
  31. package/dist/types/src/capabilities/app-graph-builder.d.ts.map +1 -1
  32. package/dist/types/src/capabilities/capabilities.d.ts +6 -6
  33. package/dist/types/src/capabilities/capabilities.d.ts.map +1 -1
  34. package/dist/types/src/capabilities/index.d.ts +111 -111
  35. package/dist/types/src/capabilities/index.d.ts.map +1 -1
  36. package/dist/types/src/capabilities/intent-resolver.d.ts.map +1 -1
  37. package/dist/types/src/capabilities/react-root.d.ts.map +1 -1
  38. package/dist/types/src/capabilities/state.d.ts +4 -1
  39. package/dist/types/src/capabilities/state.d.ts.map +1 -1
  40. package/dist/types/src/capabilities/tools.d.ts.map +1 -1
  41. package/dist/types/src/capabilities/url-handler.d.ts.map +1 -1
  42. package/dist/types/src/components/DeckLayout/ActiveNode.d.ts +1 -2
  43. package/dist/types/src/components/DeckLayout/ActiveNode.d.ts.map +1 -1
  44. package/dist/types/src/components/DeckLayout/Banner.d.ts +1 -2
  45. package/dist/types/src/components/DeckLayout/Banner.d.ts.map +1 -1
  46. package/dist/types/src/components/DeckLayout/ComplementarySidebar.d.ts +1 -4
  47. package/dist/types/src/components/DeckLayout/ComplementarySidebar.d.ts.map +1 -1
  48. package/dist/types/src/components/DeckLayout/ContentEmpty.d.ts +1 -2
  49. package/dist/types/src/components/DeckLayout/ContentEmpty.d.ts.map +1 -1
  50. package/dist/types/src/components/DeckLayout/DeckLayout.d.ts +2 -4
  51. package/dist/types/src/components/DeckLayout/DeckLayout.d.ts.map +1 -1
  52. package/dist/types/src/components/DeckLayout/Fallback.d.ts +1 -2
  53. package/dist/types/src/components/DeckLayout/Fallback.d.ts.map +1 -1
  54. package/dist/types/src/components/DeckLayout/Fullscreen.d.ts +1 -2
  55. package/dist/types/src/components/DeckLayout/Fullscreen.d.ts.map +1 -1
  56. package/dist/types/src/components/DeckLayout/NodePlankHeading.d.ts +1 -1
  57. package/dist/types/src/components/DeckLayout/NodePlankHeading.d.ts.map +1 -1
  58. package/dist/types/src/components/DeckLayout/Plank.d.ts +1 -1
  59. package/dist/types/src/components/DeckLayout/Plank.d.ts.map +1 -1
  60. package/dist/types/src/components/DeckLayout/PlankError.d.ts +2 -3
  61. package/dist/types/src/components/DeckLayout/PlankError.d.ts.map +1 -1
  62. package/dist/types/src/components/DeckLayout/PlankLoading.d.ts +1 -2
  63. package/dist/types/src/components/DeckLayout/PlankLoading.d.ts.map +1 -1
  64. package/dist/types/src/components/DeckLayout/Sidebar.d.ts +1 -2
  65. package/dist/types/src/components/DeckLayout/Sidebar.d.ts.map +1 -1
  66. package/dist/types/src/components/DeckLayout/SidebarButton.d.ts +3 -4
  67. package/dist/types/src/components/DeckLayout/SidebarButton.d.ts.map +1 -1
  68. package/dist/types/src/components/DeckLayout/StatusBar.d.ts +1 -2
  69. package/dist/types/src/components/DeckLayout/StatusBar.d.ts.map +1 -1
  70. package/dist/types/src/components/DeckLayout/Toast.d.ts +1 -2
  71. package/dist/types/src/components/DeckLayout/Toast.d.ts.map +1 -1
  72. package/dist/types/src/components/DeckLayout/Topbar.d.ts +1 -2
  73. package/dist/types/src/components/DeckLayout/Topbar.d.ts.map +1 -1
  74. package/dist/types/src/components/LayoutSettings.d.ts +1 -2
  75. package/dist/types/src/components/LayoutSettings.d.ts.map +1 -1
  76. package/dist/types/src/events.d.ts +1 -0
  77. package/dist/types/src/events.d.ts.map +1 -1
  78. package/dist/types/src/types.d.ts +10 -0
  79. package/dist/types/src/types.d.ts.map +1 -1
  80. package/dist/types/src/util/index.d.ts +1 -0
  81. package/dist/types/src/util/index.d.ts.map +1 -1
  82. package/dist/types/src/util/set-active.d.ts.map +1 -0
  83. package/dist/types/src/util/useHoistStatusbar.d.ts.map +1 -1
  84. package/package.json +30 -29
  85. package/src/DeckPlugin.ts +8 -4
  86. package/src/capabilities/app-graph-builder.ts +4 -0
  87. package/src/capabilities/capabilities.ts +3 -6
  88. package/src/capabilities/intent-resolver.ts +21 -3
  89. package/src/capabilities/react-root.tsx +1 -3
  90. package/src/capabilities/state.ts +7 -1
  91. package/src/capabilities/tools.ts +8 -3
  92. package/src/capabilities/url-handler.ts +7 -5
  93. package/src/components/DeckLayout/ComplementarySidebar.tsx +107 -50
  94. package/src/components/DeckLayout/DeckLayout.tsx +28 -23
  95. package/src/components/DeckLayout/Plank.tsx +4 -3
  96. package/src/components/DeckLayout/PlankControls.tsx +1 -1
  97. package/src/events.ts +1 -0
  98. package/src/layout.ts +1 -1
  99. package/src/types.ts +15 -2
  100. package/src/util/index.ts +1 -0
  101. package/src/util/useHoistStatusbar.ts +4 -8
  102. package/dist/lib/browser/chunk-23KS5L3I.mjs.map +0 -7
  103. package/dist/lib/browser/chunk-4URQJVGI.mjs +0 -24
  104. package/dist/lib/browser/chunk-PTLNGUND.mjs.map +0 -7
  105. package/dist/lib/browser/intent-resolver-WAYXVAFN.mjs.map +0 -7
  106. package/dist/lib/browser/react-root-YATKEIAZ.mjs.map +0 -7
  107. package/dist/lib/browser/state-YEQA3IIB.mjs.map +0 -7
  108. package/dist/lib/browser/tools-5LDJNU56.mjs.map +0 -7
  109. package/dist/types/src/capabilities/set-active.d.ts.map +0 -1
  110. /package/dist/lib/browser/{chunk-4URQJVGI.mjs.map → chunk-G2X3ZDCE.mjs.map} +0 -0
  111. /package/dist/lib/browser/{settings-CRQTVMN3.mjs.map → settings-H35U6NHE.mjs.map} +0 -0
  112. /package/dist/types/src/{capabilities → util}/set-active.d.ts +0 -0
  113. /package/src/{capabilities → util}/set-active.ts +0 -0
@@ -2,49 +2,65 @@
2
2
  // Copyright 2024 DXOS.org
3
3
  //
4
4
 
5
- import React, { useCallback, useEffect, useMemo, useState, type MouseEvent } from 'react';
5
+ import React, {
6
+ type PropsWithChildren,
7
+ useCallback,
8
+ useEffect,
9
+ useMemo,
10
+ useState,
11
+ type MouseEvent,
12
+ Fragment,
13
+ } from 'react';
6
14
 
7
15
  import {
8
16
  createIntent,
9
17
  LayoutAction,
10
18
  Surface,
11
19
  useAppGraph,
20
+ useCapabilities,
12
21
  useCapability,
13
22
  useIntentDispatcher,
14
23
  } from '@dxos/app-framework';
15
- import { Main, useTranslation, toLocalizedString, IconButton, ScrollArea } from '@dxos/react-ui';
24
+ import { Main, useTranslation, toLocalizedString, IconButton, ScrollArea as NaturalScrollArea } from '@dxos/react-ui';
16
25
  import { useAttended } from '@dxos/react-ui-attention';
17
26
  import { Tabs } from '@dxos/react-ui-tabs';
27
+ import { byPosition } from '@dxos/util';
18
28
 
19
29
  import { PlankContentError } from './PlankError';
20
30
  import { PlankLoading } from './PlankLoading';
21
31
  import { ToggleComplementarySidebarButton } from './SidebarButton';
22
32
  import { DeckCapabilities } from '../../capabilities';
23
- import { useNode, useNodeActionExpander } from '../../hooks';
33
+ import { useNode } from '../../hooks';
24
34
  import { DECK_PLUGIN } from '../../meta';
25
- import { SLUG_PATH_SEPARATOR, type Panel } from '../../types';
35
+ import { type Panel } from '../../types';
26
36
  import { layoutAppliesTopbar, useBreakpoints, useHoistStatusbar } from '../../util';
27
37
 
28
38
  export type ComplementarySidebarProps = {
29
- panels: Panel[];
30
39
  current?: string;
31
40
  };
32
41
 
33
- export const ComplementarySidebar = ({ panels, current }: ComplementarySidebarProps) => {
42
+ export const ComplementarySidebar = ({ current }: ComplementarySidebarProps) => {
43
+ const { t } = useTranslation(DECK_PLUGIN);
44
+ const { dispatchPromise: dispatch } = useIntentDispatcher();
34
45
  const layout = useCapability(DeckCapabilities.MutableDeckState);
35
46
  const attended = useAttended();
36
- const panelIds = useMemo(() => panels.map((panel) => panel.id), [panels]);
37
- const activePanelId = panelIds.find((panelId) => panelId === current) ?? panels[0].id;
38
- const activeEntryId = attended[0] ? `${attended[0]}${SLUG_PATH_SEPARATOR}${activePanelId}` : undefined;
39
47
  const { graph } = useAppGraph();
40
- const node = useNode(graph, activeEntryId);
41
- const { t } = useTranslation(DECK_PLUGIN);
42
- const { dispatchPromise: dispatch } = useIntentDispatcher();
43
- useNodeActionExpander(node);
48
+ const node = useNode(graph, attended[0]);
44
49
  const breakpoint = useBreakpoints();
45
50
  const topbar = layoutAppliesTopbar(breakpoint);
46
51
  const hoistStatusbar = useHoistStatusbar(breakpoint);
47
52
 
53
+ const panels = useCapabilities(DeckCapabilities.ComplementaryPanel);
54
+ const availablePanels = panels
55
+ .filter((panel) => {
56
+ if (!node || !panel.filter) {
57
+ return true;
58
+ }
59
+
60
+ return panel.filter(node);
61
+ })
62
+ .toSorted(byPosition);
63
+ const activePanelId = availablePanels.find((panel) => panel.id === current)?.id ?? availablePanels[0]?.id;
48
64
  const [internalValue, setInternalValue] = useState(activePanelId);
49
65
 
50
66
  useEffect(() => {
@@ -65,6 +81,17 @@ export const ComplementarySidebar = ({ panels, current }: ComplementarySidebarPr
65
81
  [layout, activePanelId, dispatch],
66
82
  );
67
83
 
84
+ const data = useMemo(
85
+ () =>
86
+ node && {
87
+ id: node.id,
88
+ subject: node.data,
89
+ workspace: layout.activeDeck,
90
+ popoverAnchorId: layout.popoverAnchorId,
91
+ },
92
+ [node, layout.popoverAnchorId],
93
+ );
94
+
68
95
  // TODO(burdon): Scroll area should be controlled by surface.
69
96
  return (
70
97
  <Main.ComplementarySidebar
@@ -82,10 +109,10 @@ export const ComplementarySidebar = ({ panels, current }: ComplementarySidebarPr
82
109
  >
83
110
  <div
84
111
  role='none'
85
- className='absolute z-[1] inset-block-0 inline-end-0 !is-[--r0-size] border-is border-separator grid grid-cols-1 grid-rows-[1fr_min-content] bg-baseSurface contain-layout app-drag'
112
+ className='absolute z-[1] inset-block-0 inline-end-0 !is-[--r0-size] pbs-[env(safe-area-inset-top)] pbe-[env(safe-area-inset-bottom)] border-is border-separator grid grid-cols-1 grid-rows-[1fr_min-content] bg-baseSurface contain-layout app-drag'
86
113
  >
87
114
  <Tabs.Tablist classNames='grid grid-cols-1 auto-rows-[--rail-action] p-1 gap-1 !overflow-y-auto'>
88
- {panels.map((panel) => (
115
+ {availablePanels.map((panel) => (
89
116
  <Tabs.Tab key={panel.id} value={panel.id} asChild>
90
117
  <IconButton
91
118
  label={toLocalizedString(panel.label, t)}
@@ -115,49 +142,79 @@ export const ComplementarySidebar = ({ panels, current }: ComplementarySidebarPr
115
142
  <ToggleComplementarySidebarButton />
116
143
  </div>
117
144
  </div>
118
- {panels.map((panel) => (
145
+ {availablePanels.map((panel) => (
119
146
  <Tabs.Tabpanel
120
147
  key={panel.id}
121
148
  value={panel.id}
122
- classNames='absolute data-[state="inactive"]:-z-[1] inset-block-0 inline-start-0 is-[calc(100%-var(--r0-size))] lg:is-[--r1-size] grid grid-cols-1 grid-rows-[var(--rail-size)_1fr_min-content]'
149
+ classNames='absolute data-[state="inactive"]:-z-[1] inset-block-0 inline-start-0 is-[calc(100%-var(--r0-size))] lg:is-[--r1-size] grid grid-cols-1 grid-rows-[var(--rail-size)_1fr_min-content] pbs-[env(safe-area-inset-top)]'
123
150
  {...(layout.complementarySidebarState !== 'expanded' && { inert: 'true' })}
124
151
  >
125
- {panel.id === activePanelId && node && (
126
- <>
127
- <h2 className='flex items-center pli-2 border-separator border-be'>
128
- {toLocalizedString(panel.label, t)}
129
- </h2>
130
- <ScrollArea.Root>
131
- <ScrollArea.Viewport>
132
- <Surface
133
- key={activeEntryId}
134
- role={`complementary--${activePanelId}`}
135
- data={{
136
- id: activeEntryId,
137
- subject: node.properties.object ?? node.properties.space,
138
- popoverAnchorId: layout.popoverAnchorId,
139
- }}
140
- fallback={PlankContentError}
141
- placeholder={<PlankLoading />}
142
- />
143
- </ScrollArea.Viewport>
144
- <ScrollArea.Scrollbar orientation='vertical'>
145
- <ScrollArea.Thumb />
146
- </ScrollArea.Scrollbar>
147
- </ScrollArea.Root>
148
- {!hoistStatusbar && (
149
- <div
150
- role='contentinfo'
151
- className='flex flex-wrap justify-center items-center border-bs border-separator plb-1'
152
- >
153
- <Surface role='status-bar--r1-footer' limit={1} />
154
- </div>
155
- )}
156
- </>
157
- )}
152
+ <ComplementarySidebarPanel
153
+ panel={panel}
154
+ activePanelId={activePanelId}
155
+ data={data}
156
+ hoistStatusbar={hoistStatusbar}
157
+ />
158
158
  </Tabs.Tabpanel>
159
159
  ))}
160
160
  </Tabs.Root>
161
161
  </Main.ComplementarySidebar>
162
162
  );
163
163
  };
164
+
165
+ type ComplementarySidebarPanelProps = {
166
+ panel: Panel;
167
+ activePanelId: string;
168
+ data?: {
169
+ id: string;
170
+ subject: any;
171
+ workspace: string;
172
+ popoverAnchorId?: string;
173
+ };
174
+ hoistStatusbar: boolean;
175
+ };
176
+
177
+ const ScrollArea = ({ children }: PropsWithChildren) => {
178
+ return (
179
+ <NaturalScrollArea.Root>
180
+ <NaturalScrollArea.Viewport>{children}</NaturalScrollArea.Viewport>
181
+ <NaturalScrollArea.Scrollbar orientation='vertical'>
182
+ <NaturalScrollArea.Thumb />
183
+ </NaturalScrollArea.Scrollbar>
184
+ </NaturalScrollArea.Root>
185
+ );
186
+ };
187
+
188
+ const ComplementarySidebarPanel = ({ panel, activePanelId, data, hoistStatusbar }: ComplementarySidebarPanelProps) => {
189
+ const { t } = useTranslation(DECK_PLUGIN);
190
+
191
+ if (panel.id !== activePanelId || !data) {
192
+ return null;
193
+ }
194
+
195
+ const Wrapper = panel.fixed ? Fragment : ScrollArea;
196
+
197
+ return (
198
+ <>
199
+ <h2 className='flex items-center pli-2 border-separator border-be font-medium'>
200
+ {toLocalizedString(panel.label, t)}
201
+ </h2>
202
+ <Wrapper>
203
+ <Surface
204
+ role={`complementary--${activePanelId}`}
205
+ data={data}
206
+ fallback={PlankContentError}
207
+ placeholder={<PlankLoading />}
208
+ />
209
+ </Wrapper>
210
+ {!hoistStatusbar && (
211
+ <div
212
+ role='contentinfo'
213
+ className='flex flex-wrap justify-center items-center border-bs border-separator pbs-1 pbe-[max(env(safe-area-inset-bottom),0.25rem)]'
214
+ >
215
+ <Surface role='status-bar--r1-footer' limit={1} />
216
+ </div>
217
+ )}
218
+ </>
219
+ );
220
+ };
@@ -3,7 +3,7 @@
3
3
  //
4
4
 
5
5
  import { untracked } from '@preact/signals-core';
6
- import React, { useCallback, useEffect, useMemo, useRef, type UIEvent, Fragment } from 'react';
6
+ import React, { useCallback, useEffect, useMemo, useRef, type UIEvent, Fragment, useState } from 'react';
7
7
 
8
8
  import {
9
9
  LayoutAction,
@@ -27,7 +27,7 @@ import { Stack, StackContext, DEFAULT_HORIZONTAL_SIZE } from '@dxos/react-ui-sta
27
27
  import { mainPaddingTransitions } from '@dxos/react-ui-theme';
28
28
 
29
29
  import { ActiveNode } from './ActiveNode';
30
- import { ComplementarySidebar, type ComplementarySidebarProps } from './ComplementarySidebar';
30
+ import { ComplementarySidebar } from './ComplementarySidebar';
31
31
  import { ContentEmpty } from './ContentEmpty';
32
32
  import { Fullscreen } from './Fullscreen';
33
33
  import { Plank } from './Plank';
@@ -45,12 +45,12 @@ export type DeckLayoutProps = {
45
45
  overscroll: Overscroll;
46
46
  showHints: boolean;
47
47
  onDismissToast: (id: string) => void;
48
- } & Pick<ComplementarySidebarProps, 'panels'>;
48
+ };
49
49
 
50
50
  const PlankSeparator = ({ index }: { index: number }) =>
51
51
  index > 0 ? <span role='separator' className='row-span-2 bg-deck is-4' style={{ gridColumn: index * 2 }} /> : null;
52
52
 
53
- export const DeckLayout = ({ overscroll, showHints, panels, onDismissToast }: DeckLayoutProps) => {
53
+ export const DeckLayout = ({ overscroll, showHints, onDismissToast }: DeckLayoutProps) => {
54
54
  const { dispatchPromise: dispatch } = useIntentDispatcher();
55
55
  const context = useCapability(DeckCapabilities.MutableDeckState);
56
56
  const {
@@ -76,6 +76,13 @@ export const DeckLayout = ({ overscroll, showHints, panels, onDismissToast }: De
76
76
  const scrollLeftRef = useRef<number | null>();
77
77
  const deckRef = useRef<HTMLDivElement>(null);
78
78
 
79
+ // TODO(thure): This is a workaround for the difference in `React`ion time between displaying a Popover and rendering
80
+ // the anchor further down the tree. Refactor to use VirtualTrigger or some other approach which does not cause a lag.
81
+ const [delayedPopoverVisibility, setDelayedPopoverVisibility] = useState(false);
82
+ useEffect(() => {
83
+ popoverOpen ? setTimeout(() => setDelayedPopoverVisibility(true), 40) : setDelayedPopoverVisibility(false);
84
+ }, [popoverOpen]);
85
+
79
86
  // Ensure the first plank is attended when the deck is first rendered.
80
87
  useEffect(() => {
81
88
  // NOTE: Not `useAttended` so that the layout component is not re-rendered when the attended list changes.
@@ -165,19 +172,22 @@ export const DeckLayout = ({ overscroll, showHints, panels, onDismissToast }: De
165
172
 
166
173
  const Dialog = dialogType === 'alert' ? AlertDialog : NaturalDialog;
167
174
 
175
+ const handlePopoverOpenChange = useCallback(
176
+ (nextOpen: boolean) => {
177
+ if (nextOpen && popoverAnchorId) {
178
+ context.popoverOpen = true;
179
+ } else {
180
+ context.popoverOpen = false;
181
+ context.popoverAnchorId = undefined;
182
+ context.popoverSide = undefined;
183
+ }
184
+ },
185
+ [context],
186
+ );
187
+ const handlePopoverClose = useCallback(() => handlePopoverOpenChange(false), [handlePopoverOpenChange]);
188
+
168
189
  return (
169
- <Popover.Root
170
- modal
171
- open={!!(popoverAnchorId && popoverOpen)}
172
- onOpenChange={(nextOpen) => {
173
- if (nextOpen && popoverAnchorId) {
174
- context.popoverOpen = true;
175
- } else {
176
- context.popoverOpen = false;
177
- context.popoverAnchorId = undefined;
178
- }
179
- }}
180
- >
190
+ <Popover.Root modal open={!!(popoverAnchorId && delayedPopoverVisibility)} onOpenChange={handlePopoverOpenChange}>
181
191
  <ActiveNode />
182
192
 
183
193
  {fullscreen && <Fullscreen id={solo} />}
@@ -193,7 +203,7 @@ export const DeckLayout = ({ overscroll, showHints, panels, onDismissToast }: De
193
203
  <Sidebar />
194
204
 
195
205
  {/* Right sidebar. */}
196
- <ComplementarySidebar panels={panels} current={complementarySidebarPanel} />
206
+ <ComplementarySidebar current={complementarySidebarPanel} />
197
207
 
198
208
  {/* Dialog overlay to dismiss dialogs. */}
199
209
  <Main.Overlay />
@@ -276,12 +286,7 @@ export const DeckLayout = ({ overscroll, showHints, panels, onDismissToast }: De
276
286
 
277
287
  {/* Global popovers. */}
278
288
  <Popover.Portal>
279
- <Popover.Content
280
- onEscapeKeyDown={() => {
281
- context.popoverOpen = false;
282
- context.popoverAnchorId = undefined;
283
- }}
284
- >
289
+ <Popover.Content side={context.popoverSide} onEscapeKeyDown={handlePopoverClose}>
285
290
  <Popover.Viewport>
286
291
  <Surface role='popover' data={popoverContent} limit={1} />
287
292
  </Popover.Viewport>
@@ -50,12 +50,13 @@ export const Plank = memo(({ id = UNKNOWN_ID, part, path, order, active, layoutM
50
50
  const canIncrementStart = active && index !== undefined && index > 0 && length !== undefined && length > 1;
51
51
  const canIncrementEnd = active && index !== undefined && index < length - 1 && length !== undefined;
52
52
 
53
- const size = deck.plankSizing[id] as number | undefined;
53
+ const key = id.split('+')[0];
54
+ const size = deck.plankSizing[key] as number | undefined;
54
55
  const setSize = useCallback(
55
56
  debounce((nextSize: number) => {
56
- return dispatch(createIntent(DeckAction.UpdatePlankSize, { id, size: nextSize }));
57
+ return dispatch(createIntent(DeckAction.UpdatePlankSize, { id: key, size: nextSize }));
57
58
  }, 200),
58
- [dispatch, id],
59
+ [dispatch, key],
59
60
  );
60
61
 
61
62
  // TODO(thure): Tabster’s focus group should handle moving focus to Main, but something is blocking it.
@@ -40,7 +40,7 @@ const PlankControl = ({ icon, label, ...props }: Omit<ButtonProps, 'children'> &
40
40
  <Tooltip.Trigger asChild>
41
41
  <Button variant='ghost' {...props}>
42
42
  <span className='sr-only'>{label}</span>
43
- <Icon icon={icon} size={4} />
43
+ <Icon icon={icon} size={5} />
44
44
  </Button>
45
45
  </Tooltip.Trigger>
46
46
  <Tooltip.Portal>
package/src/events.ts CHANGED
@@ -7,5 +7,6 @@ import { Events } from '@dxos/app-framework';
7
7
  import { DECK_PLUGIN } from './meta';
8
8
 
9
9
  export namespace DeckEvents {
10
+ export const SetupComplementaryPanels = Events.createStateEvent(`${DECK_PLUGIN}/setup-complementary-panels`);
10
11
  export const StateReady = Events.createStateEvent(`${DECK_PLUGIN}/state-ready`);
11
12
  }
package/src/layout.ts CHANGED
@@ -20,7 +20,7 @@ export const openEntry = (deck: string[], entryId: string, options?: OpenLayoutE
20
20
  const pivotId = options?.pivotId;
21
21
 
22
22
  if (key) {
23
- const index = draft.findIndex((id) => id.startsWith(key));
23
+ const index = draft.findIndex((id) => id.split('+')[0] === key);
24
24
  if (index !== -1) {
25
25
  draft.splice(index, 1, entryId);
26
26
  return;
package/src/types.ts CHANGED
@@ -4,7 +4,9 @@
4
4
 
5
5
  import { LayoutAction } from '@dxos/app-framework';
6
6
  import { S } from '@dxos/echo-schema';
7
+ import { type Node } from '@dxos/plugin-graph';
7
8
  import { type Label } from '@dxos/react-ui';
9
+ import { type Position } from '@dxos/util';
8
10
 
9
11
  import { DECK_PLUGIN } from './meta';
10
12
 
@@ -15,8 +17,17 @@ export type NewPlankPositioning = (typeof NewPlankPositions)[number];
15
17
  export const OverscrollOptions = ['none', 'centering'] as const;
16
18
  export type Overscroll = (typeof OverscrollOptions)[number];
17
19
 
18
- // TODO(wittjosiah): Include a predicate for whether the panel is visible for the current subject.
19
- export type Panel = { id: string; label: Label; icon: string };
20
+ export type Panel = {
21
+ id: string;
22
+ label: Label;
23
+ icon: string;
24
+ position?: Position;
25
+ /**
26
+ * If true, the panel will now be wrapped in a scroll area.
27
+ */
28
+ fixed?: boolean;
29
+ filter?: (node: Node) => boolean;
30
+ };
20
31
 
21
32
  export const DeckSettingsSchema = S.mutable(
22
33
  S.Struct({
@@ -64,6 +75,7 @@ export const DeckState = S.mutable(
64
75
  dialogType: S.optional(S.Literal('default', 'alert')),
65
76
 
66
77
  popoverOpen: S.Boolean,
78
+ popoverSide: S.optional(S.Literal('top', 'right', 'bottom', 'left')),
67
79
  /**
68
80
  * Data to be passed to the popover Surface.
69
81
  */
@@ -74,6 +86,7 @@ export const DeckState = S.mutable(
74
86
  currentUndoId: S.optional(S.String),
75
87
 
76
88
  activeDeck: S.String,
89
+ previousDeck: S.String,
77
90
  decks: S.mutable(S.Record({ key: S.String, value: S.mutable(Deck) })),
78
91
  previousMode: S.mutable(S.Record({ key: S.String, value: LayoutMode })),
79
92
  deck: S.mutable(Deck),
package/src/util/index.ts CHANGED
@@ -3,6 +3,7 @@
3
3
  //
4
4
 
5
5
  export * from './overscroll';
6
+ export * from './set-active';
6
7
  export * from './useBreakpoints';
7
8
  export * from './layoutAppliesTopbar';
8
9
  export * from './useHoistStatusbar';
@@ -5,6 +5,7 @@
5
5
  import { useMemo } from 'react';
6
6
 
7
7
  import { Capabilities, useCapability } from '@dxos/app-framework';
8
+ import { useThemeContext } from '@dxos/react-ui';
8
9
 
9
10
  import { DECK_PLUGIN } from '../meta';
10
11
  import type { DeckSettingsProps } from '../types';
@@ -12,13 +13,8 @@ import type { DeckSettingsProps } from '../types';
12
13
  export const useHoistStatusbar = (breakpoint: string) => {
13
14
  const enableIdeStyleStatusbar = useCapability(Capabilities.SettingsStore).getStore<DeckSettingsProps>(DECK_PLUGIN)!
14
15
  .value.enableIdeStyleStatusbar;
16
+ const { safeAreaPadding } = useThemeContext();
15
17
  return useMemo(() => {
16
- return (
17
- breakpoint === 'desktop' &&
18
- enableIdeStyleStatusbar &&
19
- // NOTE(thure): this last predicate depends on a head script that measures `env(safe-area-bottom)` on resize;
20
- // see that of composer-app for an example.
21
- document.body.getAttribute('data-safe-area-bottom') === '0'
22
- );
23
- }, [enableIdeStyleStatusbar, breakpoint]);
18
+ return breakpoint === 'desktop' && enableIdeStyleStatusbar && safeAreaPadding?.bottom === 0;
19
+ }, [enableIdeStyleStatusbar, breakpoint, safeAreaPadding?.bottom]);
24
20
  };