@dxos/plugin-deck 0.8.2-staging.7ac8446 → 0.8.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/lib/browser/{app-graph-builder-VYZ4IWI3.mjs → app-graph-builder-M5BT34YG.mjs} +17 -16
- package/dist/lib/browser/app-graph-builder-M5BT34YG.mjs.map +7 -0
- package/dist/lib/browser/{check-app-scheme-SEYECDHI.mjs → check-app-scheme-7AXGR6UT.mjs} +2 -3
- package/dist/lib/browser/check-app-scheme-7AXGR6UT.mjs.map +7 -0
- package/dist/lib/browser/{state-7TN26M42.mjs → chunk-FX44YX3G.mjs} +11 -8
- package/dist/lib/browser/chunk-FX44YX3G.mjs.map +7 -0
- package/dist/lib/browser/chunk-JE2ARGEB.mjs +1487 -0
- package/dist/lib/browser/chunk-JE2ARGEB.mjs.map +7 -0
- package/dist/lib/browser/{chunk-XMCG42ID.mjs → chunk-KLN73CM3.mjs} +2 -2
- package/dist/lib/browser/{chunk-XMCG42ID.mjs.map → chunk-KLN73CM3.mjs.map} +1 -1
- package/dist/lib/browser/chunk-SLQNOATN.mjs +127 -0
- package/dist/lib/browser/chunk-SLQNOATN.mjs.map +7 -0
- package/dist/lib/browser/chunk-TRFYUEBA.mjs +145 -0
- package/dist/lib/browser/chunk-TRFYUEBA.mjs.map +7 -0
- package/dist/lib/browser/chunk-YN5OZEGS.mjs +162 -0
- package/dist/lib/browser/chunk-YN5OZEGS.mjs.map +7 -0
- package/dist/lib/browser/index.mjs +8 -8
- package/dist/lib/browser/index.mjs.map +2 -2
- package/dist/lib/browser/{intent-resolver-UDYKO2QW.mjs → intent-resolver-3GAC57UA.mjs} +135 -92
- package/dist/lib/browser/intent-resolver-3GAC57UA.mjs.map +7 -0
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/browser/{react-root-XLXN2VEW.mjs → react-root-ISFFOJZX.mjs} +7 -7
- package/dist/lib/browser/{react-surface-WNGMZL7I.mjs → react-surface-A63RQB5N.mjs} +7 -7
- package/dist/lib/browser/{settings-HMDGSBGO.mjs → settings-X7GDEXU3.mjs} +6 -6
- package/dist/lib/browser/settings-X7GDEXU3.mjs.map +7 -0
- package/dist/lib/browser/state-VJ6E3ADY.mjs +10 -0
- package/dist/lib/browser/state-VJ6E3ADY.mjs.map +7 -0
- package/dist/lib/browser/{tools-SC6QEN7R.mjs → tools-N57NQ2LH.mjs} +28 -18
- package/dist/lib/browser/tools-N57NQ2LH.mjs.map +7 -0
- package/dist/lib/browser/types.mjs +1 -1
- package/dist/lib/browser/{url-handler-ODG4B6NX.mjs → url-handler-BUGI6XRE.mjs} +5 -5
- package/dist/lib/browser/url-handler-BUGI6XRE.mjs.map +7 -0
- package/dist/types/src/capabilities/app-graph-builder.d.ts +2 -179
- package/dist/types/src/capabilities/app-graph-builder.d.ts.map +1 -1
- package/dist/types/src/capabilities/capabilities.d.ts +18 -8
- package/dist/types/src/capabilities/capabilities.d.ts.map +1 -1
- package/dist/types/src/capabilities/check-app-scheme.d.ts +2 -2
- package/dist/types/src/capabilities/check-app-scheme.d.ts.map +1 -1
- package/dist/types/src/capabilities/index.d.ts +8 -183
- package/dist/types/src/capabilities/index.d.ts.map +1 -1
- package/dist/types/src/capabilities/intent-resolver.d.ts +2 -2
- package/dist/types/src/capabilities/intent-resolver.d.ts.map +1 -1
- package/dist/types/src/capabilities/state.d.ts +12 -7
- package/dist/types/src/capabilities/state.d.ts.map +1 -1
- package/dist/types/src/capabilities/tools.d.ts +1 -1
- package/dist/types/src/capabilities/tools.d.ts.map +1 -1
- package/dist/types/src/capabilities/url-handler.d.ts +2 -2
- package/dist/types/src/capabilities/url-handler.d.ts.map +1 -1
- package/dist/types/src/components/DeckLayout/Banner.d.ts.map +1 -1
- package/dist/types/src/components/DeckLayout/ContentEmpty.d.ts.map +1 -1
- package/dist/types/src/components/DeckLayout/DeckLayout.d.ts.map +1 -1
- package/dist/types/src/components/DeckLayout/Dialog.d.ts +3 -0
- package/dist/types/src/components/DeckLayout/Dialog.d.ts.map +1 -0
- package/dist/types/src/components/DeckLayout/Popover.d.ts +5 -0
- package/dist/types/src/components/DeckLayout/Popover.d.ts.map +1 -0
- package/dist/types/src/components/DeckLayout/StatusBar.d.ts.map +1 -1
- package/dist/types/src/components/DeckLayout/Toast.d.ts.map +1 -1
- package/dist/types/src/components/DeckSettings/DeckSettings.d.ts.map +1 -1
- package/dist/types/src/components/Plank/Plank.d.ts +18 -5
- package/dist/types/src/components/Plank/Plank.d.ts.map +1 -1
- package/dist/types/src/components/Plank/Plank.stories.d.ts +3 -3
- package/dist/types/src/components/Plank/Plank.stories.d.ts.map +1 -1
- package/dist/types/src/components/Plank/PlankControls.d.ts +3 -2
- package/dist/types/src/components/Plank/PlankControls.d.ts.map +1 -1
- package/dist/types/src/components/Plank/PlankError.d.ts.map +1 -1
- package/dist/types/src/components/Plank/PlankHeading.d.ts +3 -2
- package/dist/types/src/components/Plank/PlankHeading.d.ts.map +1 -1
- package/dist/types/src/components/Sidebar/ComplementarySidebar.d.ts.map +1 -1
- package/dist/types/src/components/Sidebar/Sidebar.d.ts.map +1 -1
- package/dist/types/src/components/Sidebar/SidebarButton.d.ts +2 -1
- package/dist/types/src/components/Sidebar/SidebarButton.d.ts.map +1 -1
- package/dist/types/src/hooks/index.d.ts +5 -1
- package/dist/types/src/hooks/index.d.ts.map +1 -1
- package/dist/types/src/hooks/useBreakpoints.d.ts.map +1 -0
- package/dist/types/src/hooks/useCompanions.d.ts.map +1 -0
- package/dist/types/src/hooks/useDeckCompanions.d.ts +13 -0
- package/dist/types/src/hooks/useDeckCompanions.d.ts.map +1 -0
- package/dist/types/src/hooks/useHoistStatusbar.d.ts +3 -0
- package/dist/types/src/hooks/useHoistStatusbar.d.ts.map +1 -0
- package/dist/types/src/hooks/useNodeActionExpander.d.ts.map +1 -1
- package/dist/types/src/index.d.ts +1 -1
- package/dist/types/src/index.d.ts.map +1 -1
- package/dist/types/src/layout.d.ts.map +1 -1
- package/dist/types/src/translations.d.ts +2 -1
- package/dist/types/src/translations.d.ts.map +1 -1
- package/dist/types/src/types.d.ts +108 -104
- package/dist/types/src/types.d.ts.map +1 -1
- package/dist/types/src/util/index.d.ts +1 -4
- package/dist/types/src/util/index.d.ts.map +1 -1
- package/dist/types/src/util/layoutAppliesTopbar.d.ts +2 -1
- package/dist/types/src/util/layoutAppliesTopbar.d.ts.map +1 -1
- package/dist/types/src/util/overscroll.d.ts.map +1 -1
- package/dist/types/src/util/set-active.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +39 -30
- package/src/capabilities/app-graph-builder.ts +120 -92
- package/src/capabilities/check-app-scheme.ts +3 -7
- package/src/capabilities/index.ts +3 -2
- package/src/capabilities/intent-resolver.ts +181 -135
- package/src/capabilities/settings.ts +4 -4
- package/src/capabilities/state.ts +7 -4
- package/src/capabilities/tools.ts +15 -12
- package/src/capabilities/url-handler.ts +4 -4
- package/src/components/DeckLayout/ContentEmpty.tsx +9 -4
- package/src/components/DeckLayout/DeckLayout.tsx +123 -188
- package/src/components/DeckLayout/Dialog.tsx +36 -0
- package/src/components/DeckLayout/Popover.tsx +104 -0
- package/src/components/Plank/Plank.stories.tsx +20 -8
- package/src/components/Plank/Plank.tsx +105 -69
- package/src/components/Plank/PlankControls.tsx +53 -57
- package/src/components/Plank/PlankError.tsx +2 -6
- package/src/components/Plank/PlankHeading.tsx +31 -12
- package/src/components/Sidebar/ComplementarySidebar.tsx +36 -57
- package/src/components/Sidebar/Sidebar.tsx +7 -4
- package/src/components/Sidebar/SidebarButton.tsx +26 -7
- package/src/components/fragments.ts +1 -1
- package/src/hooks/index.ts +5 -1
- package/src/{util → hooks}/useCompanions.ts +3 -3
- package/src/hooks/useDeckCompanions.ts +33 -0
- package/src/{util → hooks}/useHoistStatusbar.ts +9 -4
- package/src/hooks/useNodeActionExpander.ts +3 -8
- package/src/index.ts +1 -1
- package/src/translations.ts +2 -1
- package/src/types.ts +77 -71
- package/src/util/index.ts +1 -4
- package/src/util/layoutAppliesTopbar.ts +8 -2
- package/dist/lib/browser/app-graph-builder-VYZ4IWI3.mjs.map +0 -7
- package/dist/lib/browser/check-app-scheme-SEYECDHI.mjs.map +0 -7
- package/dist/lib/browser/chunk-6ZSOFCPP.mjs +0 -117
- package/dist/lib/browser/chunk-6ZSOFCPP.mjs.map +0 -7
- package/dist/lib/browser/chunk-B4LOJUWW.mjs +0 -24
- package/dist/lib/browser/chunk-B4LOJUWW.mjs.map +0 -7
- package/dist/lib/browser/chunk-FJBMNSUC.mjs +0 -1289
- package/dist/lib/browser/chunk-FJBMNSUC.mjs.map +0 -7
- package/dist/lib/browser/chunk-FLOVGNYB.mjs +0 -81
- package/dist/lib/browser/chunk-FLOVGNYB.mjs.map +0 -7
- package/dist/lib/browser/chunk-RJNCG4ND.mjs +0 -154
- package/dist/lib/browser/chunk-RJNCG4ND.mjs.map +0 -7
- package/dist/lib/browser/intent-resolver-UDYKO2QW.mjs.map +0 -7
- package/dist/lib/browser/settings-HMDGSBGO.mjs.map +0 -7
- package/dist/lib/browser/state-7TN26M42.mjs.map +0 -7
- package/dist/lib/browser/tools-SC6QEN7R.mjs.map +0 -7
- package/dist/lib/browser/url-handler-ODG4B6NX.mjs.map +0 -7
- package/dist/types/src/components/DeckLayout/Fullscreen.d.ts +0 -5
- package/dist/types/src/components/DeckLayout/Fullscreen.d.ts.map +0 -1
- package/dist/types/src/util/useBreakpoints.d.ts.map +0 -1
- package/dist/types/src/util/useCompanions.d.ts.map +0 -1
- package/dist/types/src/util/useHoistStatusbar.d.ts +0 -2
- package/dist/types/src/util/useHoistStatusbar.d.ts.map +0 -1
- package/src/components/DeckLayout/Fullscreen.tsx +0 -31
- /package/dist/lib/browser/{react-root-XLXN2VEW.mjs.map → react-root-ISFFOJZX.mjs.map} +0 -0
- /package/dist/lib/browser/{react-surface-WNGMZL7I.mjs.map → react-surface-A63RQB5N.mjs.map} +0 -0
- /package/dist/types/src/{util → hooks}/useBreakpoints.d.ts +0 -0
- /package/dist/types/src/{util → hooks}/useCompanions.d.ts +0 -0
- /package/src/{util → hooks}/useBreakpoints.ts +0 -0
|
@@ -2,16 +2,16 @@
|
|
|
2
2
|
// Copyright 2025 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import { Capabilities, contributes, createIntent, LayoutAction, type
|
|
5
|
+
import { Capabilities, contributes, createIntent, LayoutAction, type PluginContext } from '@dxos/app-framework';
|
|
6
6
|
import { scheduledEffect } from '@dxos/echo-signals/core';
|
|
7
7
|
|
|
8
8
|
import { DeckCapabilities } from './capabilities';
|
|
9
9
|
import { defaultDeck } from '../types';
|
|
10
10
|
|
|
11
11
|
// TODO(wittjosiah): Cleanup the url handling. May justify introducing routing capabilities.
|
|
12
|
-
export default async (context:
|
|
13
|
-
const { dispatchPromise: dispatch } = context.
|
|
14
|
-
const state = context.
|
|
12
|
+
export default async (context: PluginContext) => {
|
|
13
|
+
const { dispatchPromise: dispatch } = context.getCapability(Capabilities.IntentDispatcher);
|
|
14
|
+
const state = context.getCapability(DeckCapabilities.MutableDeckState);
|
|
15
15
|
|
|
16
16
|
const handleNavigation = async () => {
|
|
17
17
|
const pathname = window.location.pathname;
|
|
@@ -4,19 +4,24 @@
|
|
|
4
4
|
|
|
5
5
|
import React from 'react';
|
|
6
6
|
|
|
7
|
-
import { Surface } from '@dxos/app-framework';
|
|
7
|
+
import { Surface, useCapability } from '@dxos/app-framework';
|
|
8
8
|
|
|
9
|
-
import {
|
|
9
|
+
import { DeckCapabilities } from '../../capabilities';
|
|
10
|
+
import { useBreakpoints } from '../../hooks';
|
|
11
|
+
import { getMode } from '../../types';
|
|
12
|
+
import { layoutAppliesTopbar } from '../../util';
|
|
10
13
|
import { ToggleSidebarButton } from '../Sidebar';
|
|
11
14
|
import { fixedSidebarToggleStyles } from '../fragments';
|
|
12
15
|
|
|
13
16
|
export const ContentEmpty = () => {
|
|
14
17
|
const breakpoint = useBreakpoints();
|
|
15
|
-
const
|
|
18
|
+
const { deck } = useCapability(DeckCapabilities.MutableDeckState);
|
|
19
|
+
const layoutMode = getMode(deck);
|
|
20
|
+
const topbar = layoutAppliesTopbar(breakpoint, layoutMode);
|
|
16
21
|
return (
|
|
17
22
|
<div
|
|
18
23
|
role='none'
|
|
19
|
-
className='grid place-items-center p-8 relative bg-
|
|
24
|
+
className='grid place-items-center p-8 relative bg-deckSurface'
|
|
20
25
|
data-testid='layoutPlugin.firstRunMessage'
|
|
21
26
|
>
|
|
22
27
|
<Surface role='keyshortcuts' />
|
|
@@ -3,41 +3,34 @@
|
|
|
3
3
|
//
|
|
4
4
|
|
|
5
5
|
import { untracked } from '@preact/signals-core';
|
|
6
|
-
import React, { useCallback, useEffect, useMemo, useRef, type UIEvent, Fragment
|
|
6
|
+
import React, { useCallback, useEffect, useMemo, useRef, type UIEvent, Fragment } from 'react';
|
|
7
7
|
|
|
8
8
|
import {
|
|
9
9
|
Capabilities,
|
|
10
10
|
LayoutAction,
|
|
11
|
-
Surface,
|
|
12
11
|
createIntent,
|
|
13
12
|
useCapability,
|
|
14
13
|
useIntentDispatcher,
|
|
15
14
|
usePluginManager,
|
|
16
15
|
} from '@dxos/app-framework';
|
|
17
16
|
import { AttentionCapabilities } from '@dxos/plugin-attention';
|
|
18
|
-
import {
|
|
19
|
-
AlertDialog,
|
|
20
|
-
Dialog as NaturalDialog,
|
|
21
|
-
Main,
|
|
22
|
-
Popover,
|
|
23
|
-
type MainProps,
|
|
24
|
-
useMediaQuery,
|
|
25
|
-
useOnTransition,
|
|
26
|
-
} from '@dxos/react-ui';
|
|
17
|
+
import { Main, type MainProps, useMediaQuery, useOnTransition } from '@dxos/react-ui';
|
|
27
18
|
import { Stack, StackContext, DEFAULT_HORIZONTAL_SIZE } from '@dxos/react-ui-stack';
|
|
28
19
|
import { mainPaddingTransitions } from '@dxos/react-ui-theme';
|
|
29
20
|
|
|
30
21
|
import { ActiveNode } from './ActiveNode';
|
|
31
22
|
import { ContentEmpty } from './ContentEmpty';
|
|
32
|
-
import {
|
|
23
|
+
import { Dialog } from './Dialog';
|
|
24
|
+
import { PopoverContent, PopoverRoot } from './Popover';
|
|
33
25
|
import { StatusBar } from './StatusBar';
|
|
34
26
|
import { Toast } from './Toast';
|
|
35
27
|
import { Topbar } from './Topbar';
|
|
36
28
|
import { DeckCapabilities } from '../../capabilities';
|
|
29
|
+
import { useBreakpoints, useHoistStatusbar } from '../../hooks';
|
|
37
30
|
import { DECK_PLUGIN } from '../../meta';
|
|
38
31
|
import { type DeckSettingsProps, getMode } from '../../types';
|
|
39
|
-
import { calculateOverscroll, layoutAppliesTopbar
|
|
40
|
-
import { Plank
|
|
32
|
+
import { calculateOverscroll, layoutAppliesTopbar } from '../../util';
|
|
33
|
+
import { Plank } from '../Plank';
|
|
41
34
|
import { ComplementarySidebar, Sidebar, ToggleComplementarySidebarButton, ToggleSidebarButton } from '../Sidebar';
|
|
42
35
|
import { fixedComplementarySidebarToggleStyles, fixedSidebarToggleStyles } from '../fragments';
|
|
43
36
|
|
|
@@ -46,47 +39,28 @@ export type DeckLayoutProps = {
|
|
|
46
39
|
};
|
|
47
40
|
|
|
48
41
|
const PlankSeparator = ({ order }: { order: number }) =>
|
|
49
|
-
order > 0 ? <span role='separator' className='row-span-2 bg-
|
|
42
|
+
order > 0 ? <span role='separator' className='row-span-2 bg-deckSurface is-4' style={{ gridColumn: order }} /> : null;
|
|
50
43
|
|
|
51
44
|
export const DeckLayout = ({ onDismissToast }: DeckLayoutProps) => {
|
|
52
45
|
const { dispatchPromise: dispatch } = useIntentDispatcher();
|
|
53
46
|
const settings = useCapability(Capabilities.SettingsStore).getStore<DeckSettingsProps>(DECK_PLUGIN)!.value;
|
|
54
47
|
const context = useCapability(DeckCapabilities.MutableDeckState);
|
|
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
|
+
const { sidebarState, complementarySidebarState, complementarySidebarPanel, deck, toasts } = context;
|
|
69
49
|
const { active, activeCompanions, fullscreen, solo, plankSizing } = deck;
|
|
70
50
|
const breakpoint = useBreakpoints();
|
|
71
|
-
const
|
|
72
|
-
const
|
|
51
|
+
const layoutMode = getMode(deck);
|
|
52
|
+
const topbar = layoutAppliesTopbar(breakpoint, layoutMode);
|
|
53
|
+
const hoistStatusbar = useHoistStatusbar(breakpoint, layoutMode);
|
|
73
54
|
const pluginManager = usePluginManager();
|
|
74
55
|
|
|
75
56
|
const scrollLeftRef = useRef<number | null>();
|
|
76
57
|
const deckRef = useRef<HTMLDivElement>(null);
|
|
77
58
|
|
|
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
|
-
|
|
85
59
|
// Ensure the first plank is attended when the deck is first rendered.
|
|
86
60
|
useEffect(() => {
|
|
87
61
|
// NOTE: Not `useAttended` so that the layout component is not re-rendered when the attended list changes.
|
|
88
62
|
const attended = untracked(() => {
|
|
89
|
-
const attention = pluginManager.context.
|
|
63
|
+
const attention = pluginManager.context.getCapability(AttentionCapabilities.Attention);
|
|
90
64
|
return attention.current;
|
|
91
65
|
});
|
|
92
66
|
const firstId = solo ?? active[0];
|
|
@@ -105,7 +79,7 @@ export const DeckLayout = ({ onDismissToast }: DeckLayoutProps) => {
|
|
|
105
79
|
if (!isNotMobile && getMode(deck) === 'deck') {
|
|
106
80
|
// NOTE: Not `useAttended` so that the layout component is not re-rendered when the attended list changes.
|
|
107
81
|
const attended = untracked(() => {
|
|
108
|
-
const attention = pluginManager.context.
|
|
82
|
+
const attention = pluginManager.context.getCapability(AttentionCapabilities.Attention);
|
|
109
83
|
return attention.current;
|
|
110
84
|
});
|
|
111
85
|
|
|
@@ -118,14 +92,15 @@ export const DeckLayout = ({ onDismissToast }: DeckLayoutProps) => {
|
|
|
118
92
|
}
|
|
119
93
|
}, [isNotMobile, deck, dispatch]);
|
|
120
94
|
|
|
121
|
-
//
|
|
95
|
+
// When deck is disabled in settings, set to solo mode if the current layout mode is deck.
|
|
96
|
+
// TODO(thure): Applying this as an effect should be avoided over emitting the intent only when the setting changes.
|
|
122
97
|
useEffect(() => {
|
|
123
|
-
if (!settings.enableDeck) {
|
|
98
|
+
if (!settings.enableDeck && layoutMode === 'deck') {
|
|
124
99
|
void dispatch(
|
|
125
100
|
createIntent(LayoutAction.SetLayoutMode, { part: 'mode', subject: active[0], options: { mode: 'solo' } }),
|
|
126
101
|
);
|
|
127
102
|
}
|
|
128
|
-
}, [settings.enableDeck, dispatch, active]);
|
|
103
|
+
}, [settings.enableDeck, dispatch, active, layoutMode]);
|
|
129
104
|
|
|
130
105
|
/**
|
|
131
106
|
* Clear scroll restoration state if the window is resized
|
|
@@ -144,8 +119,6 @@ export const DeckLayout = ({ onDismissToast }: DeckLayoutProps) => {
|
|
|
144
119
|
deckRef.current.scrollLeft = scrollLeftRef.current;
|
|
145
120
|
}
|
|
146
121
|
}, []);
|
|
147
|
-
|
|
148
|
-
const layoutMode = getMode(deck);
|
|
149
122
|
useOnTransition(layoutMode, (mode) => mode !== 'deck', 'deck', restoreScroll);
|
|
150
123
|
|
|
151
124
|
/**
|
|
@@ -178,22 +151,6 @@ export const DeckLayout = ({ onDismissToast }: DeckLayoutProps) => {
|
|
|
178
151
|
[topbar, hoistStatusbar],
|
|
179
152
|
);
|
|
180
153
|
|
|
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
|
-
|
|
197
154
|
const { order, itemsCount }: { order: Record<string, number>; itemsCount: number } = useMemo(() => {
|
|
198
155
|
return active.reduce(
|
|
199
156
|
(acc: { order: Record<string, number>; itemsCount: number }, entryId) => {
|
|
@@ -206,146 +163,124 @@ export const DeckLayout = ({ onDismissToast }: DeckLayoutProps) => {
|
|
|
206
163
|
}, [active, activeCompanions]);
|
|
207
164
|
|
|
208
165
|
return (
|
|
209
|
-
<
|
|
166
|
+
<PopoverRoot>
|
|
210
167
|
<ActiveNode />
|
|
211
168
|
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
>
|
|
221
|
-
{/* Left sidebar. */}
|
|
222
|
-
<Sidebar />
|
|
169
|
+
<Main.Root
|
|
170
|
+
navigationSidebarState={fullscreen ? 'closed' : context.sidebarState}
|
|
171
|
+
onNavigationSidebarStateChange={(next) => (context.sidebarState = next)}
|
|
172
|
+
complementarySidebarState={fullscreen ? 'closed' : context.complementarySidebarState}
|
|
173
|
+
onComplementarySidebarStateChange={(next) => (context.complementarySidebarState = next)}
|
|
174
|
+
>
|
|
175
|
+
{/* Left sidebar. */}
|
|
176
|
+
<Sidebar />
|
|
223
177
|
|
|
224
|
-
|
|
225
|
-
|
|
178
|
+
{/* Right sidebar. */}
|
|
179
|
+
<ComplementarySidebar current={complementarySidebarPanel} />
|
|
226
180
|
|
|
227
|
-
|
|
228
|
-
|
|
181
|
+
{/* Dialog overlay to dismiss dialogs. */}
|
|
182
|
+
<Main.Overlay />
|
|
229
183
|
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
184
|
+
{/* No content. */}
|
|
185
|
+
{isEmpty && (
|
|
186
|
+
<Main.Content bounce handlesFocus classNames={mainPosition}>
|
|
187
|
+
<ContentEmpty />
|
|
188
|
+
</Main.Content>
|
|
189
|
+
)}
|
|
236
190
|
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
191
|
+
{/* Solo/deck mode. */}
|
|
192
|
+
{!isEmpty && (
|
|
193
|
+
<Main.Content
|
|
194
|
+
bounce
|
|
195
|
+
handlesFocus
|
|
196
|
+
classNames={mainPosition}
|
|
197
|
+
style={
|
|
198
|
+
{
|
|
199
|
+
'--dx-main-sidebarWidth':
|
|
200
|
+
sidebarState === 'expanded'
|
|
201
|
+
? 'var(--nav-sidebar-size)'
|
|
202
|
+
: sidebarState === 'collapsed'
|
|
203
|
+
? 'var(--l0-size)'
|
|
204
|
+
: '0',
|
|
205
|
+
'--dx-main-complementaryWidth':
|
|
206
|
+
complementarySidebarState === 'expanded'
|
|
207
|
+
? 'var(--complementary-sidebar-size)'
|
|
208
|
+
: complementarySidebarState === 'collapsed'
|
|
209
|
+
? 'var(--rail-size)'
|
|
210
|
+
: '0',
|
|
211
|
+
'--dx-main-contentFirstWidth': `${plankSizing[active[0] ?? 'never'] ?? DEFAULT_HORIZONTAL_SIZE}rem`,
|
|
212
|
+
'--dx-main-contentLastWidth': `${plankSizing[active[(active.length ?? 1) - 1] ?? 'never'] ?? DEFAULT_HORIZONTAL_SIZE}rem`,
|
|
213
|
+
} as MainProps['style']
|
|
214
|
+
}
|
|
215
|
+
>
|
|
216
|
+
<div
|
|
217
|
+
role='none'
|
|
218
|
+
className={!solo ? 'relative bg-deckSurface overflow-hidden' : 'sr-only'}
|
|
219
|
+
{...(solo && { inert: '' })}
|
|
261
220
|
>
|
|
262
|
-
<
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
221
|
+
{!topbar && !fullscreen && <ToggleSidebarButton classNames={fixedSidebarToggleStyles} />}
|
|
222
|
+
{!topbar && !fullscreen && (
|
|
223
|
+
<ToggleComplementarySidebarButton classNames={fixedComplementarySidebarToggleStyles} />
|
|
224
|
+
)}
|
|
225
|
+
<Stack
|
|
226
|
+
ref={deckRef}
|
|
227
|
+
orientation='horizontal'
|
|
228
|
+
size='contain'
|
|
229
|
+
classNames={['absolute inset-block-0 -inset-inline-px', mainPaddingTransitions]}
|
|
230
|
+
itemsCount={itemsCount - 1}
|
|
231
|
+
style={padding}
|
|
232
|
+
onScroll={handleScroll}
|
|
266
233
|
>
|
|
267
|
-
{
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
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
|
-
)}
|
|
234
|
+
{active.map((entryId) => (
|
|
235
|
+
<Fragment key={entryId}>
|
|
236
|
+
<PlankSeparator order={order[entryId] - 1} />
|
|
237
|
+
<Plank
|
|
238
|
+
id={entryId}
|
|
239
|
+
companionId={activeCompanions?.[entryId]}
|
|
240
|
+
part='deck'
|
|
241
|
+
order={order[entryId]}
|
|
242
|
+
active={active}
|
|
243
|
+
layoutMode={layoutMode}
|
|
244
|
+
settings={settings}
|
|
245
|
+
/>
|
|
246
|
+
</Fragment>
|
|
247
|
+
))}
|
|
248
|
+
</Stack>
|
|
249
|
+
</div>
|
|
250
|
+
<div
|
|
251
|
+
role='none'
|
|
252
|
+
className={solo ? 'relative bg-deckSurface overflow-hidden' : 'sr-only'}
|
|
253
|
+
{...(!solo && { inert: '' })}
|
|
254
|
+
>
|
|
255
|
+
{!topbar && !fullscreen && <ToggleSidebarButton classNames={fixedSidebarToggleStyles} />}
|
|
256
|
+
{!topbar && !fullscreen && (
|
|
257
|
+
<ToggleComplementarySidebarButton classNames={fixedComplementarySidebarToggleStyles} />
|
|
258
|
+
)}
|
|
259
|
+
<StackContext.Provider value={{ size: 'contain', orientation: 'horizontal', rail: true }}>
|
|
260
|
+
<Plank
|
|
261
|
+
id={solo}
|
|
262
|
+
companionId={solo ? activeCompanions?.[solo] : undefined}
|
|
263
|
+
part='solo'
|
|
264
|
+
layoutMode={layoutMode}
|
|
265
|
+
settings={settings}
|
|
266
|
+
/>
|
|
267
|
+
</StackContext.Provider>
|
|
268
|
+
</div>
|
|
269
|
+
</Main.Content>
|
|
270
|
+
)}
|
|
313
271
|
|
|
314
|
-
|
|
315
|
-
|
|
272
|
+
{/* Topbar. */}
|
|
273
|
+
{topbar && <Topbar />}
|
|
316
274
|
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
)}
|
|
275
|
+
{/* Status bar. */}
|
|
276
|
+
{hoistStatusbar && <StatusBar showHints={settings.showHints} />}
|
|
277
|
+
</Main.Root>
|
|
321
278
|
|
|
322
279
|
{/* Global popovers. */}
|
|
323
|
-
<
|
|
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>
|
|
280
|
+
<PopoverContent />
|
|
331
281
|
|
|
332
282
|
{/* Global dialog. */}
|
|
333
|
-
|
|
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>
|
|
283
|
+
<Dialog />
|
|
349
284
|
|
|
350
285
|
{/* Global toasts. */}
|
|
351
286
|
{toasts?.map((toast) => (
|
|
@@ -361,6 +296,6 @@ export const DeckLayout = ({ onDismissToast }: DeckLayoutProps) => {
|
|
|
361
296
|
}}
|
|
362
297
|
/>
|
|
363
298
|
))}
|
|
364
|
-
</
|
|
299
|
+
</PopoverRoot>
|
|
365
300
|
);
|
|
366
301
|
};
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2025 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import React from 'react';
|
|
6
|
+
|
|
7
|
+
import { Surface, useCapability } from '@dxos/app-framework';
|
|
8
|
+
import { AlertDialog, Dialog as NaturalDialog } from '@dxos/react-ui';
|
|
9
|
+
|
|
10
|
+
import { DeckCapabilities } from '../../capabilities';
|
|
11
|
+
import { PlankContentError } from '../Plank';
|
|
12
|
+
|
|
13
|
+
export const Dialog = () => {
|
|
14
|
+
const context = useCapability(DeckCapabilities.MutableDeckState);
|
|
15
|
+
const { dialogOpen, dialogType, dialogBlockAlign, dialogOverlayClasses, dialogOverlayStyle, dialogContent } = context;
|
|
16
|
+
const Root = dialogType === 'alert' ? AlertDialog.Root : NaturalDialog.Root;
|
|
17
|
+
const Overlay = dialogType === 'alert' ? AlertDialog.Overlay : NaturalDialog.Overlay;
|
|
18
|
+
|
|
19
|
+
// TODO(thure): End block alignment affecting `modal` and whether the surface renders in an overlay is tailored to the needs of the ambient chat dialog. As the feature matures, consider separating concerns.
|
|
20
|
+
return (
|
|
21
|
+
<Root
|
|
22
|
+
modal={dialogBlockAlign !== 'end'}
|
|
23
|
+
open={dialogOpen}
|
|
24
|
+
onOpenChange={(nextOpen) => (context.dialogOpen = nextOpen)}
|
|
25
|
+
>
|
|
26
|
+
{dialogBlockAlign === 'end' ? (
|
|
27
|
+
// TODO(burdon): Placeholder creates a suspense boundary; replace with defaults.
|
|
28
|
+
<Surface role='dialog' data={dialogContent} limit={1} fallback={PlankContentError} placeholder={<div />} />
|
|
29
|
+
) : (
|
|
30
|
+
<Overlay blockAlign={dialogBlockAlign} classNames={dialogOverlayClasses} style={dialogOverlayStyle}>
|
|
31
|
+
<Surface role='dialog' data={dialogContent} limit={1} fallback={PlankContentError} />
|
|
32
|
+
</Overlay>
|
|
33
|
+
)}
|
|
34
|
+
</Root>
|
|
35
|
+
);
|
|
36
|
+
};
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2025 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { createContext } from '@radix-ui/react-context';
|
|
6
|
+
import React, { type PropsWithChildren, useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
7
|
+
|
|
8
|
+
import { Surface, useCapability } from '@dxos/app-framework';
|
|
9
|
+
import { Popover, type PopoverContentInteractOutsideEvent } from '@dxos/react-ui';
|
|
10
|
+
|
|
11
|
+
import { DeckCapabilities } from '../../capabilities';
|
|
12
|
+
|
|
13
|
+
export type DeckPopoverRootProps = PropsWithChildren<{}>;
|
|
14
|
+
|
|
15
|
+
const DEBOUNCE_DELAY = 40;
|
|
16
|
+
|
|
17
|
+
type DeckPopoverContextValue = {
|
|
18
|
+
setOpen: (open: boolean) => void;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
const [DeckPopoverProvider, useDeckPopoverContext] = createContext<DeckPopoverContextValue>('DeckPopover');
|
|
22
|
+
|
|
23
|
+
export const PopoverRoot = ({ children }: DeckPopoverRootProps) => {
|
|
24
|
+
const layout = useCapability(DeckCapabilities.MutableDeckState);
|
|
25
|
+
const virtualRef = useRef<HTMLButtonElement | null>(null);
|
|
26
|
+
const [virtualIter, setVirtualIter] = useState(0);
|
|
27
|
+
const [open, setOpen] = useState(false);
|
|
28
|
+
const debounceRef = useRef<NodeJS.Timeout | null>(null);
|
|
29
|
+
|
|
30
|
+
// TODO(thure): This is a workaround for the race condition between displaying a Popover and either rendering
|
|
31
|
+
// the anchor further down the tree or measuring the virtual trigger’s client rect.
|
|
32
|
+
useEffect(() => {
|
|
33
|
+
setOpen(false);
|
|
34
|
+
if (layout.popoverOpen) {
|
|
35
|
+
if (debounceRef.current) {
|
|
36
|
+
clearTimeout(debounceRef.current);
|
|
37
|
+
}
|
|
38
|
+
if (layout.popoverAnchor && virtualRef.current !== layout.popoverAnchor) {
|
|
39
|
+
virtualRef.current = layout.popoverAnchor ?? null;
|
|
40
|
+
setVirtualIter((iter) => iter + 1);
|
|
41
|
+
}
|
|
42
|
+
debounceRef.current = setTimeout(() => setOpen(true), DEBOUNCE_DELAY);
|
|
43
|
+
}
|
|
44
|
+
}, [layout.popoverOpen, layout.popoverAnchorId, layout.popoverAnchor, layout.popoverContent]);
|
|
45
|
+
|
|
46
|
+
return (
|
|
47
|
+
<DeckPopoverProvider setOpen={setOpen}>
|
|
48
|
+
<Popover.Root modal={false} open={open}>
|
|
49
|
+
{layout.popoverAnchor && <Popover.VirtualTrigger key={virtualIter} virtualRef={virtualRef} />}
|
|
50
|
+
{children}
|
|
51
|
+
</Popover.Root>
|
|
52
|
+
</DeckPopoverProvider>
|
|
53
|
+
);
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
export const PopoverContent = () => {
|
|
57
|
+
const layout = useCapability(DeckCapabilities.MutableDeckState);
|
|
58
|
+
const { setOpen } = useDeckPopoverContext('PopoverContent');
|
|
59
|
+
|
|
60
|
+
const handleClose = useCallback(
|
|
61
|
+
(event: KeyboardEvent | PopoverContentInteractOutsideEvent) => {
|
|
62
|
+
if (
|
|
63
|
+
// TODO(thure): CodeMirror should not focus itself when it updates.
|
|
64
|
+
event.type === 'dismissableLayer.focusOutside' &&
|
|
65
|
+
(event.currentTarget as HTMLElement | undefined)?.classList.contains('cm-content')
|
|
66
|
+
) {
|
|
67
|
+
event.preventDefault();
|
|
68
|
+
} else {
|
|
69
|
+
setOpen(false);
|
|
70
|
+
layout.popoverOpen = false;
|
|
71
|
+
layout.popoverAnchor = undefined;
|
|
72
|
+
layout.popoverAnchorId = undefined;
|
|
73
|
+
layout.popoverSide = undefined;
|
|
74
|
+
}
|
|
75
|
+
},
|
|
76
|
+
[setOpen],
|
|
77
|
+
);
|
|
78
|
+
|
|
79
|
+
const collisionBoundaries: HTMLElement[] = useMemo(() => {
|
|
80
|
+
const closest = layout.popoverAnchor?.closest('[data-popover-collision-boundary]') as
|
|
81
|
+
| HTMLElement
|
|
82
|
+
| null
|
|
83
|
+
| undefined;
|
|
84
|
+
return closest ? [closest] : [];
|
|
85
|
+
}, [layout.popoverAnchor]);
|
|
86
|
+
|
|
87
|
+
return (
|
|
88
|
+
<Popover.Portal>
|
|
89
|
+
<Popover.Content
|
|
90
|
+
side={layout.popoverSide}
|
|
91
|
+
onInteractOutside={handleClose}
|
|
92
|
+
onEscapeKeyDown={handleClose}
|
|
93
|
+
collisionBoundary={collisionBoundaries}
|
|
94
|
+
sticky='always'
|
|
95
|
+
hideWhenDetached
|
|
96
|
+
>
|
|
97
|
+
<Popover.Viewport>
|
|
98
|
+
<Surface role='popover' data={layout.popoverContent} limit={1} />
|
|
99
|
+
</Popover.Viewport>
|
|
100
|
+
<Popover.Arrow />
|
|
101
|
+
</Popover.Content>
|
|
102
|
+
</Popover.Portal>
|
|
103
|
+
);
|
|
104
|
+
};
|