@dxos/plugin-deck 0.8.2-main.f11618f → 0.8.2-staging.7ac8446

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 (88) hide show
  1. package/dist/lib/browser/{check-app-scheme-O7JPE4TM.mjs → check-app-scheme-SEYECDHI.mjs} +3 -2
  2. package/dist/lib/browser/check-app-scheme-SEYECDHI.mjs.map +7 -0
  3. package/dist/lib/browser/{chunk-6HJZL3WT.mjs → chunk-6ZSOFCPP.mjs} +7 -8
  4. package/dist/lib/browser/{chunk-6HJZL3WT.mjs.map → chunk-6ZSOFCPP.mjs.map} +3 -3
  5. package/dist/lib/browser/chunk-B4LOJUWW.mjs +24 -0
  6. package/dist/lib/browser/{chunk-RBJ6DLAC.mjs.map → chunk-B4LOJUWW.mjs.map} +3 -3
  7. package/dist/lib/browser/{chunk-RDFJGGGX.mjs → chunk-FJBMNSUC.mjs} +162 -207
  8. package/dist/lib/browser/chunk-FJBMNSUC.mjs.map +7 -0
  9. package/dist/lib/browser/{chunk-ZMJMCN7O.mjs → chunk-RJNCG4ND.mjs} +6 -9
  10. package/dist/lib/browser/chunk-RJNCG4ND.mjs.map +7 -0
  11. package/dist/lib/browser/index.mjs +3 -5
  12. package/dist/lib/browser/index.mjs.map +2 -2
  13. package/dist/lib/browser/{intent-resolver-JKWXWUV6.mjs → intent-resolver-UDYKO2QW.mjs} +34 -59
  14. package/dist/lib/browser/intent-resolver-UDYKO2QW.mjs.map +7 -0
  15. package/dist/lib/browser/meta.json +1 -1
  16. package/dist/lib/browser/{react-root-S6ZAKNZA.mjs → react-root-XLXN2VEW.mjs} +5 -5
  17. package/dist/lib/browser/{react-surface-I7WZBOGM.mjs → react-surface-WNGMZL7I.mjs} +5 -5
  18. package/dist/lib/browser/{settings-6NU7CF2B.mjs → settings-HMDGSBGO.mjs} +4 -4
  19. package/dist/lib/browser/settings-HMDGSBGO.mjs.map +7 -0
  20. package/dist/lib/browser/{state-Z6UY2Z3M.mjs → state-7TN26M42.mjs} +5 -6
  21. package/dist/lib/browser/state-7TN26M42.mjs.map +7 -0
  22. package/dist/lib/browser/{tools-VDVQTJMD.mjs → tools-SC6QEN7R.mjs} +2 -2
  23. package/dist/lib/browser/types.mjs +1 -1
  24. package/dist/lib/browser/{url-handler-3CARFXQK.mjs → url-handler-ODG4B6NX.mjs} +2 -2
  25. package/dist/types/src/capabilities/capabilities.d.ts +6 -8
  26. package/dist/types/src/capabilities/capabilities.d.ts.map +1 -1
  27. package/dist/types/src/capabilities/check-app-scheme.d.ts.map +1 -1
  28. package/dist/types/src/capabilities/index.d.ts +2 -2
  29. package/dist/types/src/capabilities/index.d.ts.map +1 -1
  30. package/dist/types/src/capabilities/intent-resolver.d.ts.map +1 -1
  31. package/dist/types/src/capabilities/state.d.ts +4 -5
  32. package/dist/types/src/capabilities/state.d.ts.map +1 -1
  33. package/dist/types/src/components/DeckLayout/ContentEmpty.d.ts.map +1 -1
  34. package/dist/types/src/components/DeckLayout/DeckLayout.d.ts.map +1 -1
  35. package/dist/types/src/components/DeckLayout/Fullscreen.d.ts +5 -0
  36. package/dist/types/src/components/DeckLayout/Fullscreen.d.ts.map +1 -0
  37. package/dist/types/src/components/Plank/Plank.d.ts.map +1 -1
  38. package/dist/types/src/components/Plank/PlankControls.d.ts +2 -2
  39. package/dist/types/src/components/Plank/PlankControls.d.ts.map +1 -1
  40. package/dist/types/src/components/Plank/PlankError.d.ts.map +1 -1
  41. package/dist/types/src/components/Plank/PlankHeading.d.ts +2 -3
  42. package/dist/types/src/components/Plank/PlankHeading.d.ts.map +1 -1
  43. package/dist/types/src/components/Sidebar/ComplementarySidebar.d.ts.map +1 -1
  44. package/dist/types/src/components/Sidebar/Sidebar.d.ts.map +1 -1
  45. package/dist/types/src/translations.d.ts +0 -2
  46. package/dist/types/src/translations.d.ts.map +1 -1
  47. package/dist/types/src/types.d.ts +9 -11
  48. package/dist/types/src/types.d.ts.map +1 -1
  49. package/dist/types/src/util/layoutAppliesTopbar.d.ts +1 -2
  50. package/dist/types/src/util/layoutAppliesTopbar.d.ts.map +1 -1
  51. package/dist/types/src/util/useHoistStatusbar.d.ts +1 -2
  52. package/dist/types/src/util/useHoistStatusbar.d.ts.map +1 -1
  53. package/package.json +28 -29
  54. package/src/capabilities/check-app-scheme.ts +5 -3
  55. package/src/capabilities/index.ts +2 -2
  56. package/src/capabilities/intent-resolver.ts +65 -89
  57. package/src/capabilities/settings.ts +2 -2
  58. package/src/capabilities/state.ts +2 -3
  59. package/src/components/DeckLayout/ContentEmpty.tsx +2 -6
  60. package/src/components/DeckLayout/DeckLayout.tsx +181 -114
  61. package/src/components/DeckLayout/Fullscreen.tsx +31 -0
  62. package/src/components/Plank/Plank.tsx +3 -6
  63. package/src/components/Plank/PlankControls.tsx +34 -40
  64. package/src/components/Plank/PlankError.tsx +6 -2
  65. package/src/components/Plank/PlankHeading.tsx +5 -12
  66. package/src/components/Sidebar/ComplementarySidebar.tsx +20 -30
  67. package/src/components/Sidebar/Sidebar.tsx +3 -5
  68. package/src/translations.ts +0 -2
  69. package/src/types.ts +6 -9
  70. package/src/util/layoutAppliesTopbar.ts +2 -8
  71. package/src/util/useHoistStatusbar.ts +4 -9
  72. package/dist/lib/browser/check-app-scheme-O7JPE4TM.mjs.map +0 -7
  73. package/dist/lib/browser/chunk-RBJ6DLAC.mjs +0 -24
  74. package/dist/lib/browser/chunk-RDFJGGGX.mjs.map +0 -7
  75. package/dist/lib/browser/chunk-ZMJMCN7O.mjs.map +0 -7
  76. package/dist/lib/browser/intent-resolver-JKWXWUV6.mjs.map +0 -7
  77. package/dist/lib/browser/settings-6NU7CF2B.mjs.map +0 -7
  78. package/dist/lib/browser/state-Z6UY2Z3M.mjs.map +0 -7
  79. package/dist/types/src/components/DeckLayout/Dialog.d.ts +0 -3
  80. package/dist/types/src/components/DeckLayout/Dialog.d.ts.map +0 -1
  81. package/dist/types/src/components/DeckLayout/Popover.d.ts +0 -5
  82. package/dist/types/src/components/DeckLayout/Popover.d.ts.map +0 -1
  83. package/src/components/DeckLayout/Dialog.tsx +0 -36
  84. package/src/components/DeckLayout/Popover.tsx +0 -104
  85. /package/dist/lib/browser/{react-root-S6ZAKNZA.mjs.map → react-root-XLXN2VEW.mjs.map} +0 -0
  86. /package/dist/lib/browser/{react-surface-I7WZBOGM.mjs.map → react-surface-WNGMZL7I.mjs.map} +0 -0
  87. /package/dist/lib/browser/{tools-VDVQTJMD.mjs.map → tools-SC6QEN7R.mjs.map} +0 -0
  88. /package/dist/lib/browser/{url-handler-3CARFXQK.mjs.map → url-handler-ODG4B6NX.mjs.map} +0 -0
@@ -3,7 +3,7 @@
3
3
  //
4
4
 
5
5
  import { batch } from '@preact/signals-core';
6
- import { Effect, pipe } from 'effect';
6
+ import { pipe } from 'effect';
7
7
 
8
8
  import {
9
9
  Capabilities,
@@ -17,7 +17,7 @@ import {
17
17
  } from '@dxos/app-framework';
18
18
  import { getTypename, S } from '@dxos/echo-schema';
19
19
  import { invariant } from '@dxos/invariant';
20
- import { isLiveObject } from '@dxos/live-object';
20
+ import { isReactiveObject } from '@dxos/live-object';
21
21
  import { log } from '@dxos/log';
22
22
  import { AttentionCapabilities } from '@dxos/plugin-attention';
23
23
  import { type Node } from '@dxos/plugin-graph';
@@ -121,14 +121,9 @@ export default (context: PluginsContext) =>
121
121
  resolve: ({ subject, options }) => {
122
122
  const layout = context.requestCapability(DeckCapabilities.MutableDeckState);
123
123
  layout.popoverOpen = options.state ?? Boolean(subject);
124
- layout.popoverContent =
125
- typeof subject === 'string' ? { component: subject, props: options.props } : subject ? { subject } : null;
124
+ layout.popoverContent = subject ? { component: subject, props: options.props } : null;
125
+ layout.popoverAnchorId = options.anchorId;
126
126
  layout.popoverSide = options.side;
127
- if (options.variant === 'virtual') {
128
- layout.popoverAnchor = options.anchor;
129
- } else {
130
- layout.popoverAnchorId = options.anchorId;
131
- }
132
127
  },
133
128
  }),
134
129
  createResolver({
@@ -179,8 +174,10 @@ export default (context: PluginsContext) =>
179
174
  deck.initialized = true;
180
175
  }
181
176
 
182
- if (mode === 'solo--fullscreen') {
183
- deck.fullscreen = !deck.fullscreen;
177
+ if (mode === 'fullscreen' && !deck.fullscreen) {
178
+ deck.fullscreen = true;
179
+ } else if (mode !== 'fullscreen' && deck.fullscreen) {
180
+ deck.fullscreen = false;
184
181
  }
185
182
  };
186
183
 
@@ -241,63 +238,55 @@ export default (context: PluginsContext) =>
241
238
  intent: LayoutAction.UpdateLayout,
242
239
  filter: (data): data is S.Schema.Type<typeof LayoutAction.Open.fields.input> =>
243
240
  S.is(LayoutAction.Open.fields.input)(data),
244
- resolve: ({ subject, options }) =>
245
- Effect.gen(function* () {
246
- const { graph } = context.requestCapability(Capabilities.AppGraph);
247
- const state = context.requestCapability(DeckCapabilities.MutableDeckState);
248
- const attention = context.requestCapability(AttentionCapabilities.Attention);
249
- const settings = context
250
- .requestCapabilities(Capabilities.SettingsStore)[0]
251
- ?.getStore<DeckSettingsProps>(DECK_PLUGIN)?.value;
252
-
253
- if (options?.workspace && state.activeDeck !== options?.workspace) {
254
- const { dispatch } = context.requestCapability(Capabilities.IntentDispatcher);
255
- yield* dispatch(
256
- createIntent(LayoutAction.SwitchWorkspace, { part: 'workspace', subject: options.workspace }),
257
- );
258
- }
241
+ resolve: ({ subject, options }) => {
242
+ const { graph } = context.requestCapability(Capabilities.AppGraph);
243
+ const state = context.requestCapability(DeckCapabilities.MutableDeckState);
244
+ const attention = context.requestCapability(AttentionCapabilities.Attention);
245
+ const settings = context
246
+ .requestCapabilities(Capabilities.SettingsStore)[0]
247
+ ?.getStore<DeckSettingsProps>(DECK_PLUGIN)?.value;
259
248
 
260
- const previouslyOpenIds = new Set<string>(state.deck.solo ? [state.deck.solo] : state.deck.active);
261
- batch(() => {
262
- const next = state.deck.solo
263
- ? (subject as string[]).map((id) => createEntryId(id, options?.variant))
264
- : subject.reduce(
265
- (acc, entryId) =>
266
- openEntry(acc, entryId, {
267
- key: options?.key,
268
- positioning: options?.positioning ?? settings?.newPlankPositioning,
269
- pivotId: options?.pivotId,
270
- variant: options?.variant,
271
- }),
272
- state.deck.active,
273
- );
249
+ const previouslyOpenIds = new Set<string>(state.deck.solo ? [state.deck.solo] : state.deck.active);
250
+ batch(() => {
251
+ const next = state.deck.solo
252
+ ? (subject as string[]).map((id) => createEntryId(id, options?.variant))
253
+ : subject.reduce(
254
+ (acc, entryId) =>
255
+ openEntry(acc, entryId, {
256
+ key: options?.key,
257
+ positioning: options?.positioning ?? settings?.newPlankPositioning,
258
+ pivotId: options?.pivotId,
259
+ variant: options?.variant,
260
+ }),
261
+ state.deck.active,
262
+ );
274
263
 
275
- return setActive({ next, state, attention });
276
- });
264
+ return setActive({ next, state, attention });
265
+ });
277
266
 
278
- const ids = state.deck.solo ? [state.deck.solo] : state.deck.active;
279
- const newlyOpen = ids.filter((i) => !previouslyOpenIds.has(i));
267
+ const ids = state.deck.solo ? [state.deck.solo] : state.deck.active;
268
+ const newlyOpen = ids.filter((i) => !previouslyOpenIds.has(i));
280
269
 
281
- return {
282
- intents: [
283
- ...(options?.scrollIntoView !== false
284
- ? [createIntent(LayoutAction.ScrollIntoView, { part: 'current', subject: newlyOpen[0] ?? subject[0] })]
285
- : []),
286
- createIntent(LayoutAction.Expose, { part: 'navigation', subject: newlyOpen[0] ?? subject[0] }),
287
- ...newlyOpen.map((subjectId) => {
288
- const active = graph?.findNode(subjectId)?.data;
289
- const typename = isLiveObject(active) ? getTypename(active) : undefined;
290
- return createIntent(ObservabilityAction.SendEvent, {
291
- name: 'navigation.activate',
292
- properties: {
293
- subjectId,
294
- typename,
295
- },
296
- });
297
- }),
298
- ],
299
- };
300
- }),
270
+ return {
271
+ intents: [
272
+ ...(options?.scrollIntoView !== false
273
+ ? [createIntent(LayoutAction.ScrollIntoView, { part: 'current', subject: newlyOpen[0] ?? subject[0] })]
274
+ : []),
275
+ createIntent(LayoutAction.Expose, { part: 'navigation', subject: newlyOpen[0] ?? subject[0] }),
276
+ ...newlyOpen.map((subjectId) => {
277
+ const active = graph?.findNode(subjectId)?.data;
278
+ const typename = isReactiveObject(active) ? getTypename(active) : undefined;
279
+ return createIntent(ObservabilityAction.SendEvent, {
280
+ name: 'navigation.activate',
281
+ properties: {
282
+ subjectId,
283
+ typename,
284
+ },
285
+ });
286
+ }),
287
+ ],
288
+ };
289
+ },
301
290
  }),
302
291
  createResolver({
303
292
  intent: LayoutAction.UpdateLayout,
@@ -401,7 +390,7 @@ export default (context: PluginsContext) =>
401
390
  }
402
391
  }
403
392
 
404
- if (adjustment.type.startsWith('solo')) {
393
+ if (adjustment.type === 'solo') {
405
394
  const entryId = adjustment.id;
406
395
  if (!state.deck.solo) {
407
396
  // Solo the entry.
@@ -410,34 +399,21 @@ export default (context: PluginsContext) =>
410
399
  createIntent(LayoutAction.SetLayoutMode, {
411
400
  part: 'mode',
412
401
  subject: entryId,
413
- options: { mode: adjustment.type },
402
+ options: { mode: 'solo' },
414
403
  }),
415
404
  ],
416
405
  };
417
406
  } else {
418
- if (adjustment.type === 'solo--fullscreen') {
419
- // Toggle fullscreen on the current entry.
420
- return {
421
- intents: [
422
- createIntent(LayoutAction.SetLayoutMode, {
423
- part: 'mode',
424
- subject: entryId,
425
- options: { mode: 'solo--fullscreen' },
426
- }),
427
- ],
428
- };
429
- } else if (adjustment.type === 'solo') {
430
- // Un-solo the current entry.
431
- return {
432
- intents: [
433
- // NOTE: The order of these is important.
434
- pipe(
435
- createIntent(LayoutAction.SetLayoutMode, { part: 'mode', options: { mode: 'deck' } }),
436
- chain(LayoutAction.Open, { part: 'main', subject: [entryId] }),
437
- ),
438
- ],
439
- };
440
- }
407
+ // Un-solo the current entry.
408
+ return {
409
+ intents: [
410
+ // NOTE: The order of these is important.
411
+ pipe(
412
+ createIntent(LayoutAction.SetLayoutMode, { part: 'mode', options: { mode: 'deck' } }),
413
+ chain(LayoutAction.Open, { part: 'main', subject: [entryId] }),
414
+ ),
415
+ ],
416
+ };
441
417
  }
442
418
  }
443
419
  });
@@ -3,13 +3,13 @@
3
3
  //
4
4
 
5
5
  import { Capabilities, contributes } from '@dxos/app-framework';
6
- import { live } from '@dxos/live-object';
6
+ import { create } from '@dxos/live-object';
7
7
 
8
8
  import { DECK_PLUGIN } from '../meta';
9
9
  import { DeckSettingsSchema, type DeckSettingsProps } from '../types';
10
10
 
11
11
  export default () => {
12
- const settings = live<DeckSettingsProps>({
12
+ const settings = create<DeckSettingsProps>({
13
13
  showHints: false,
14
14
  enableDeck: true,
15
15
  enableNativeRedirect: false,
@@ -4,7 +4,7 @@
4
4
 
5
5
  import { Capabilities, contributes } from '@dxos/app-framework';
6
6
  import { invariant } from '@dxos/invariant';
7
- import { live } from '@dxos/live-object';
7
+ import { create } from '@dxos/live-object';
8
8
  import { LocalStorageStore } from '@dxos/local-storage';
9
9
  import { type SidebarState } from '@dxos/react-ui';
10
10
 
@@ -41,7 +41,6 @@ export default () => {
41
41
  dialogBlockAlign: undefined,
42
42
  dialogType: undefined,
43
43
  popoverContent: null,
44
- popoverAnchor: undefined,
45
44
  popoverAnchorId: undefined,
46
45
  popoverOpen: false,
47
46
  toasts: [],
@@ -68,7 +67,7 @@ export default () => {
68
67
  .prop({ key: 'activeDeck', type: LocalStorageStore.string() })
69
68
  .prop({ key: 'previousDeck', type: LocalStorageStore.string() });
70
69
 
71
- const layout = live<Capabilities.Layout>({
70
+ const layout = create<Capabilities.Layout>({
72
71
  get mode() {
73
72
  return getMode(state.values.deck);
74
73
  },
@@ -4,19 +4,15 @@
4
4
 
5
5
  import React from 'react';
6
6
 
7
- import { Surface, useCapability } from '@dxos/app-framework';
7
+ import { Surface } from '@dxos/app-framework';
8
8
 
9
- import { DeckCapabilities } from '../../capabilities';
10
- import { getMode } from '../../types';
11
9
  import { layoutAppliesTopbar, useBreakpoints } from '../../util';
12
10
  import { ToggleSidebarButton } from '../Sidebar';
13
11
  import { fixedSidebarToggleStyles } from '../fragments';
14
12
 
15
13
  export const ContentEmpty = () => {
16
14
  const breakpoint = useBreakpoints();
17
- const { deck } = useCapability(DeckCapabilities.MutableDeckState);
18
- const layoutMode = getMode(deck);
19
- const topbar = layoutAppliesTopbar(breakpoint, layoutMode);
15
+ const topbar = layoutAppliesTopbar(breakpoint);
20
16
  return (
21
17
  <div
22
18
  role='none'
@@ -3,25 +3,33 @@
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
  Capabilities,
10
10
  LayoutAction,
11
+ Surface,
11
12
  createIntent,
12
13
  useCapability,
13
14
  useIntentDispatcher,
14
15
  usePluginManager,
15
16
  } from '@dxos/app-framework';
16
17
  import { AttentionCapabilities } from '@dxos/plugin-attention';
17
- import { Main, type MainProps, useMediaQuery, useOnTransition } from '@dxos/react-ui';
18
+ import {
19
+ AlertDialog,
20
+ Dialog as NaturalDialog,
21
+ Main,
22
+ Popover,
23
+ type MainProps,
24
+ useMediaQuery,
25
+ useOnTransition,
26
+ } from '@dxos/react-ui';
18
27
  import { Stack, StackContext, DEFAULT_HORIZONTAL_SIZE } from '@dxos/react-ui-stack';
19
28
  import { mainPaddingTransitions } from '@dxos/react-ui-theme';
20
29
 
21
30
  import { ActiveNode } from './ActiveNode';
22
31
  import { ContentEmpty } from './ContentEmpty';
23
- import { Dialog } from './Dialog';
24
- import { PopoverContent, PopoverRoot } from './Popover';
32
+ import { Fullscreen } from './Fullscreen';
25
33
  import { StatusBar } from './StatusBar';
26
34
  import { Toast } from './Toast';
27
35
  import { Topbar } from './Topbar';
@@ -29,7 +37,7 @@ import { DeckCapabilities } from '../../capabilities';
29
37
  import { DECK_PLUGIN } from '../../meta';
30
38
  import { type DeckSettingsProps, getMode } from '../../types';
31
39
  import { calculateOverscroll, layoutAppliesTopbar, useBreakpoints, useHoistStatusbar } from '../../util';
32
- import { Plank } from '../Plank';
40
+ import { Plank, PlankContentError } from '../Plank';
33
41
  import { ComplementarySidebar, Sidebar, ToggleComplementarySidebarButton, ToggleSidebarButton } from '../Sidebar';
34
42
  import { fixedComplementarySidebarToggleStyles, fixedSidebarToggleStyles } from '../fragments';
35
43
 
@@ -44,17 +52,36 @@ export const DeckLayout = ({ onDismissToast }: DeckLayoutProps) => {
44
52
  const { dispatchPromise: dispatch } = useIntentDispatcher();
45
53
  const settings = useCapability(Capabilities.SettingsStore).getStore<DeckSettingsProps>(DECK_PLUGIN)!.value;
46
54
  const context = useCapability(DeckCapabilities.MutableDeckState);
47
- const { sidebarState, complementarySidebarState, complementarySidebarPanel, deck, toasts } = context;
55
+ const {
56
+ sidebarState,
57
+ complementarySidebarState,
58
+ complementarySidebarPanel,
59
+ dialogOpen,
60
+ dialogContent,
61
+ dialogBlockAlign,
62
+ dialogType,
63
+ popoverOpen,
64
+ popoverContent,
65
+ popoverAnchorId,
66
+ deck,
67
+ toasts,
68
+ } = context;
48
69
  const { active, activeCompanions, fullscreen, solo, plankSizing } = deck;
49
70
  const breakpoint = useBreakpoints();
50
- const layoutMode = getMode(deck);
51
- const topbar = layoutAppliesTopbar(breakpoint, layoutMode);
52
- const hoistStatusbar = useHoistStatusbar(breakpoint, layoutMode);
71
+ const topbar = layoutAppliesTopbar(breakpoint);
72
+ const hoistStatusbar = useHoistStatusbar(breakpoint);
53
73
  const pluginManager = usePluginManager();
54
74
 
55
75
  const scrollLeftRef = useRef<number | null>();
56
76
  const deckRef = useRef<HTMLDivElement>(null);
57
77
 
78
+ // TODO(thure): This is a workaround for the difference in `React`ion time between displaying a Popover and rendering
79
+ // the anchor further down the tree. Refactor to use VirtualTrigger or some other approach which does not cause a lag.
80
+ const [delayedPopoverVisibility, setDelayedPopoverVisibility] = useState(false);
81
+ useEffect(() => {
82
+ popoverOpen ? setTimeout(() => setDelayedPopoverVisibility(true), 40) : setDelayedPopoverVisibility(false);
83
+ }, [popoverOpen]);
84
+
58
85
  // Ensure the first plank is attended when the deck is first rendered.
59
86
  useEffect(() => {
60
87
  // NOTE: Not `useAttended` so that the layout component is not re-rendered when the attended list changes.
@@ -117,6 +144,8 @@ export const DeckLayout = ({ onDismissToast }: DeckLayoutProps) => {
117
144
  deckRef.current.scrollLeft = scrollLeftRef.current;
118
145
  }
119
146
  }, []);
147
+
148
+ const layoutMode = getMode(deck);
120
149
  useOnTransition(layoutMode, (mode) => mode !== 'deck', 'deck', restoreScroll);
121
150
 
122
151
  /**
@@ -149,6 +178,22 @@ export const DeckLayout = ({ onDismissToast }: DeckLayoutProps) => {
149
178
  [topbar, hoistStatusbar],
150
179
  );
151
180
 
181
+ const Dialog = dialogType === 'alert' ? AlertDialog : NaturalDialog;
182
+
183
+ const handlePopoverOpenChange = useCallback(
184
+ (nextOpen: boolean) => {
185
+ if (nextOpen && popoverAnchorId) {
186
+ context.popoverOpen = true;
187
+ } else {
188
+ context.popoverOpen = false;
189
+ context.popoverAnchorId = undefined;
190
+ context.popoverSide = undefined;
191
+ }
192
+ },
193
+ [context],
194
+ );
195
+ const handlePopoverClose = useCallback(() => handlePopoverOpenChange(false), [handlePopoverOpenChange]);
196
+
152
197
  const { order, itemsCount }: { order: Record<string, number>; itemsCount: number } = useMemo(() => {
153
198
  return active.reduce(
154
199
  (acc: { order: Record<string, number>; itemsCount: number }, entryId) => {
@@ -161,124 +206,146 @@ export const DeckLayout = ({ onDismissToast }: DeckLayoutProps) => {
161
206
  }, [active, activeCompanions]);
162
207
 
163
208
  return (
164
- <PopoverRoot>
209
+ <Popover.Root modal open={!!(popoverAnchorId && delayedPopoverVisibility)} onOpenChange={handlePopoverOpenChange}>
165
210
  <ActiveNode />
166
211
 
167
- <Main.Root
168
- navigationSidebarState={fullscreen ? 'closed' : context.sidebarState}
169
- onNavigationSidebarStateChange={(next) => (context.sidebarState = next)}
170
- complementarySidebarState={fullscreen ? 'closed' : context.complementarySidebarState}
171
- onComplementarySidebarStateChange={(next) => (context.complementarySidebarState = next)}
172
- >
173
- {/* Left sidebar. */}
174
- <Sidebar />
212
+ {fullscreen && <Fullscreen id={solo} />}
175
213
 
176
- {/* Right sidebar. */}
177
- <ComplementarySidebar current={complementarySidebarPanel} />
214
+ {!fullscreen && (
215
+ <Main.Root
216
+ navigationSidebarState={context.sidebarState}
217
+ onNavigationSidebarStateChange={(next) => (context.sidebarState = next)}
218
+ complementarySidebarState={context.complementarySidebarState}
219
+ onComplementarySidebarStateChange={(next) => (context.complementarySidebarState = next)}
220
+ >
221
+ {/* Left sidebar. */}
222
+ <Sidebar />
178
223
 
179
- {/* Dialog overlay to dismiss dialogs. */}
180
- <Main.Overlay />
224
+ {/* Right sidebar. */}
225
+ <ComplementarySidebar current={complementarySidebarPanel} />
181
226
 
182
- {/* No content. */}
183
- {isEmpty && (
184
- <Main.Content bounce handlesFocus classNames={mainPosition}>
185
- <ContentEmpty />
186
- </Main.Content>
187
- )}
227
+ {/* Dialog overlay to dismiss dialogs. */}
228
+ <Main.Overlay />
188
229
 
189
- {/* Solo/deck mode. */}
190
- {!isEmpty && (
191
- <Main.Content
192
- bounce
193
- classNames={mainPosition}
194
- handlesFocus
195
- style={
196
- {
197
- '--dx-main-sidebarWidth':
198
- sidebarState === 'expanded'
199
- ? 'var(--nav-sidebar-size)'
200
- : sidebarState === 'collapsed'
201
- ? 'var(--l0-size)'
202
- : '0',
203
- '--dx-main-complementaryWidth':
204
- complementarySidebarState === 'expanded'
205
- ? 'var(--complementary-sidebar-size)'
206
- : complementarySidebarState === 'collapsed'
207
- ? 'var(--rail-size)'
208
- : '0',
209
- '--dx-main-contentFirstWidth': `${plankSizing[active[0] ?? 'never'] ?? DEFAULT_HORIZONTAL_SIZE}rem`,
210
- '--dx-main-contentLastWidth': `${plankSizing[active[(active.length ?? 1) - 1] ?? 'never'] ?? DEFAULT_HORIZONTAL_SIZE}rem`,
211
- } as MainProps['style']
212
- }
213
- >
214
- <div
215
- role='none'
216
- className={!solo ? 'relative bg-deck overflow-hidden' : 'sr-only'}
217
- {...(solo && { inert: '' })}
230
+ {/* No content. */}
231
+ {isEmpty && (
232
+ <Main.Content bounce handlesFocus classNames={mainPosition}>
233
+ <ContentEmpty />
234
+ </Main.Content>
235
+ )}
236
+
237
+ {/* Solo/deck mode. */}
238
+ {!isEmpty && (
239
+ <Main.Content
240
+ bounce
241
+ classNames={mainPosition}
242
+ handlesFocus
243
+ style={
244
+ {
245
+ '--dx-main-sidebarWidth':
246
+ sidebarState === 'expanded'
247
+ ? 'var(--nav-sidebar-size)'
248
+ : sidebarState === 'collapsed'
249
+ ? 'var(--l0-size)'
250
+ : '0',
251
+ '--dx-main-complementaryWidth':
252
+ complementarySidebarState === 'expanded'
253
+ ? 'var(--complementary-sidebar-size)'
254
+ : complementarySidebarState === 'collapsed'
255
+ ? 'var(--rail-size)'
256
+ : '0',
257
+ '--dx-main-contentFirstWidth': `${plankSizing[active[0] ?? 'never'] ?? DEFAULT_HORIZONTAL_SIZE}rem`,
258
+ '--dx-main-contentLastWidth': `${plankSizing[active[(active.length ?? 1) - 1] ?? 'never'] ?? DEFAULT_HORIZONTAL_SIZE}rem`,
259
+ } as MainProps['style']
260
+ }
218
261
  >
219
- {!topbar && !fullscreen && <ToggleSidebarButton classNames={fixedSidebarToggleStyles} />}
220
- {!topbar && !fullscreen && (
221
- <ToggleComplementarySidebarButton classNames={fixedComplementarySidebarToggleStyles} />
222
- )}
223
- <Stack
224
- ref={deckRef}
225
- orientation='horizontal'
226
- size='contain'
227
- classNames={['absolute inset-block-0 -inset-inline-px', mainPaddingTransitions]}
228
- itemsCount={itemsCount - 1}
229
- style={padding}
230
- onScroll={handleScroll}
262
+ <div
263
+ role='none'
264
+ className={!solo ? 'relative bg-deck overflow-hidden' : 'sr-only'}
265
+ {...(solo && { inert: '' })}
231
266
  >
232
- {active.map((entryId) => (
233
- <Fragment key={entryId}>
234
- <PlankSeparator order={order[entryId] - 1} />
235
- <Plank
236
- id={entryId}
237
- companionId={activeCompanions?.[entryId]}
238
- part='deck'
239
- order={order[entryId]}
240
- active={active}
241
- layoutMode={layoutMode}
242
- settings={settings}
243
- />
244
- </Fragment>
245
- ))}
246
- </Stack>
247
- </div>
248
- <div
249
- role='none'
250
- className={solo ? 'relative bg-deck overflow-hidden' : 'sr-only'}
251
- {...(!solo && { inert: '' })}
252
- >
253
- {!topbar && !fullscreen && <ToggleSidebarButton classNames={fixedSidebarToggleStyles} />}
254
- {!topbar && !fullscreen && (
255
- <ToggleComplementarySidebarButton classNames={fixedComplementarySidebarToggleStyles} />
256
- )}
257
- <StackContext.Provider value={{ size: 'contain', orientation: 'horizontal', rail: true }}>
258
- <Plank
259
- id={solo}
260
- companionId={solo ? activeCompanions?.[solo] : undefined}
261
- part='solo'
262
- layoutMode={layoutMode}
263
- settings={settings}
264
- />
265
- </StackContext.Provider>
266
- </div>
267
- </Main.Content>
268
- )}
267
+ {!topbar && <ToggleSidebarButton classNames={fixedSidebarToggleStyles} />}
268
+ {!topbar && <ToggleComplementarySidebarButton classNames={fixedComplementarySidebarToggleStyles} />}
269
+ <Stack
270
+ ref={deckRef}
271
+ orientation='horizontal'
272
+ size='contain'
273
+ classNames={['absolute inset-block-0 -inset-inline-px', mainPaddingTransitions]}
274
+ itemsCount={itemsCount - 1}
275
+ style={padding}
276
+ onScroll={handleScroll}
277
+ >
278
+ {active.map((entryId) => (
279
+ <Fragment key={entryId}>
280
+ <PlankSeparator order={order[entryId] - 1} />
281
+ <Plank
282
+ id={entryId}
283
+ companionId={activeCompanions?.[entryId]}
284
+ part='deck'
285
+ order={order[entryId]}
286
+ active={active}
287
+ layoutMode={layoutMode}
288
+ settings={settings}
289
+ />
290
+ </Fragment>
291
+ ))}
292
+ </Stack>
293
+ </div>
294
+ <div
295
+ role='none'
296
+ className={solo ? 'relative bg-deck overflow-hidden' : 'sr-only'}
297
+ {...(!solo && { inert: '' })}
298
+ >
299
+ {!topbar && <ToggleSidebarButton classNames={fixedSidebarToggleStyles} />}
300
+ {!topbar && <ToggleComplementarySidebarButton classNames={fixedComplementarySidebarToggleStyles} />}
301
+ <StackContext.Provider value={{ size: 'contain', orientation: 'horizontal', rail: true }}>
302
+ <Plank
303
+ id={solo}
304
+ companionId={solo ? activeCompanions?.[solo] : undefined}
305
+ part='solo'
306
+ layoutMode={layoutMode}
307
+ settings={settings}
308
+ />
309
+ </StackContext.Provider>
310
+ </div>
311
+ </Main.Content>
312
+ )}
269
313
 
270
- {/* Topbar. */}
271
- {topbar && <Topbar />}
314
+ {/* Topbar. */}
315
+ {topbar && <Topbar />}
272
316
 
273
- {/* Status bar. */}
274
- {hoistStatusbar && <StatusBar showHints={settings.showHints} />}
275
- </Main.Root>
317
+ {/* Status bar. */}
318
+ {hoistStatusbar && <StatusBar showHints={settings.showHints} />}
319
+ </Main.Root>
320
+ )}
276
321
 
277
322
  {/* Global popovers. */}
278
- <PopoverContent />
323
+ <Popover.Portal>
324
+ <Popover.Content side={context.popoverSide} onEscapeKeyDown={handlePopoverClose}>
325
+ <Popover.Viewport>
326
+ <Surface role='popover' data={popoverContent} limit={1} />
327
+ </Popover.Viewport>
328
+ <Popover.Arrow />
329
+ </Popover.Content>
330
+ </Popover.Portal>
279
331
 
280
332
  {/* Global dialog. */}
281
- <Dialog />
333
+ {/* TODO(thure): End block alignment affecting `modal` and whether the surface renders in an overlay is tailored
334
+ to the needs of the ambient chat dialog. As the feature matures, consider separating concerns. */}
335
+ <Dialog.Root
336
+ modal={dialogBlockAlign !== 'end'}
337
+ open={dialogOpen}
338
+ onOpenChange={(nextOpen) => (context.dialogOpen = nextOpen)}
339
+ >
340
+ {dialogBlockAlign === 'end' ? (
341
+ // TODO(burdon): Placeholder creates a suspense boundary; replace with defaults.
342
+ <Surface role='dialog' data={dialogContent} limit={1} fallback={PlankContentError} placeholder={<div />} />
343
+ ) : (
344
+ <Dialog.Overlay blockAlign={dialogBlockAlign}>
345
+ <Surface role='dialog' data={dialogContent} limit={1} fallback={PlankContentError} />
346
+ </Dialog.Overlay>
347
+ )}
348
+ </Dialog.Root>
282
349
 
283
350
  {/* Global toasts. */}
284
351
  {toasts?.map((toast) => (
@@ -294,6 +361,6 @@ export const DeckLayout = ({ onDismissToast }: DeckLayoutProps) => {
294
361
  }}
295
362
  />
296
363
  ))}
297
- </PopoverRoot>
364
+ </Popover.Root>
298
365
  );
299
366
  };