@dxos/plugin-deck 0.8.0 → 0.8.1-main.013e445
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-IYHAGFA3.mjs → app-graph-builder-K4KVSHNT.mjs} +3 -3
- package/dist/lib/browser/{check-app-scheme-S3EYUPMF.mjs → check-app-scheme-6SS6I3RN.mjs} +2 -2
- package/dist/lib/browser/{chunk-N7TEPFVR.mjs → chunk-2WTHB3TG.mjs} +1 -1
- package/dist/lib/browser/{chunk-N7TEPFVR.mjs.map → chunk-2WTHB3TG.mjs.map} +2 -2
- package/dist/lib/browser/chunk-7A5DLPQ3.mjs +24 -0
- package/dist/lib/browser/{chunk-FT33W5CI.mjs → chunk-7X43JKZG.mjs} +34 -4
- package/dist/lib/browser/chunk-7X43JKZG.mjs.map +7 -0
- package/dist/lib/browser/{chunk-OEDK54N2.mjs → chunk-7YPIAXSW.mjs} +179 -72
- package/dist/lib/browser/chunk-7YPIAXSW.mjs.map +7 -0
- package/dist/lib/browser/{chunk-KANJBSIX.mjs → chunk-RZLH5F56.mjs} +5 -5
- package/dist/lib/browser/{chunk-KANJBSIX.mjs.map → chunk-RZLH5F56.mjs.map} +3 -3
- package/dist/lib/browser/{chunk-22AQ5IVX.mjs → chunk-WCNPMAR4.mjs} +2 -2
- package/dist/lib/browser/index.mjs +8 -7
- package/dist/lib/browser/index.mjs.map +2 -2
- package/dist/lib/browser/{intent-resolver-ZD67BRUI.mjs → intent-resolver-MEBOMCYI.mjs} +46 -17
- package/dist/lib/browser/intent-resolver-MEBOMCYI.mjs.map +7 -0
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/browser/{react-root-2T6BCRK4.mjs → react-root-PPKFVPO7.mjs} +7 -7
- package/dist/lib/browser/{react-surface-GFMOJSIA.mjs → react-surface-7JBPPUHM.mjs} +7 -7
- package/dist/lib/browser/{settings-H35U6NHE.mjs → settings-DYS3FFMN.mjs} +4 -4
- package/dist/lib/browser/{settings-H35U6NHE.mjs.map → settings-DYS3FFMN.mjs.map} +3 -3
- package/dist/lib/browser/{state-U4SHOPJW.mjs → state-DRRCGMU2.mjs} +7 -11
- package/dist/lib/browser/state-DRRCGMU2.mjs.map +7 -0
- package/dist/lib/browser/tools-NDEUSO4R.mjs +78 -0
- package/dist/lib/browser/tools-NDEUSO4R.mjs.map +7 -0
- package/dist/lib/browser/types.mjs +10 -4
- package/dist/lib/browser/{url-handler-MVHTKUYA.mjs → url-handler-4BCN7AYC.mjs} +7 -9
- package/dist/lib/browser/url-handler-4BCN7AYC.mjs.map +7 -0
- package/dist/types/src/capabilities/app-graph-builder.d.ts.map +1 -1
- package/dist/types/src/capabilities/capabilities.d.ts +26 -2
- package/dist/types/src/capabilities/capabilities.d.ts.map +1 -1
- package/dist/types/src/capabilities/index.d.ts.map +1 -1
- package/dist/types/src/capabilities/intent-resolver.d.ts.map +1 -1
- package/dist/types/src/capabilities/state.d.ts +13 -1
- package/dist/types/src/capabilities/state.d.ts.map +1 -1
- package/dist/types/src/capabilities/tools.d.ts +1 -0
- package/dist/types/src/capabilities/tools.d.ts.map +1 -1
- package/dist/types/src/capabilities/url-handler.d.ts.map +1 -1
- package/dist/types/src/components/DeckLayout/ActiveNode.d.ts +2 -1
- package/dist/types/src/components/DeckLayout/ActiveNode.d.ts.map +1 -1
- package/dist/types/src/components/DeckLayout/Banner.d.ts +2 -1
- package/dist/types/src/components/DeckLayout/Banner.d.ts.map +1 -1
- package/dist/types/src/components/DeckLayout/ComplementarySidebar.d.ts +2 -1
- package/dist/types/src/components/DeckLayout/ComplementarySidebar.d.ts.map +1 -1
- package/dist/types/src/components/DeckLayout/ContentEmpty.d.ts +2 -1
- package/dist/types/src/components/DeckLayout/ContentEmpty.d.ts.map +1 -1
- package/dist/types/src/components/DeckLayout/DeckLayout.d.ts +2 -1
- package/dist/types/src/components/DeckLayout/DeckLayout.d.ts.map +1 -1
- package/dist/types/src/components/DeckLayout/Fallback.d.ts +2 -1
- package/dist/types/src/components/DeckLayout/Fallback.d.ts.map +1 -1
- package/dist/types/src/components/DeckLayout/Fullscreen.d.ts +2 -1
- package/dist/types/src/components/DeckLayout/Fullscreen.d.ts.map +1 -1
- package/dist/types/src/components/DeckLayout/NodePlankHeading.d.ts +6 -2
- package/dist/types/src/components/DeckLayout/NodePlankHeading.d.ts.map +1 -1
- package/dist/types/src/components/DeckLayout/Plank.d.ts +4 -4
- package/dist/types/src/components/DeckLayout/Plank.d.ts.map +1 -1
- package/dist/types/src/components/DeckLayout/PlankControls.d.ts +6 -1
- package/dist/types/src/components/DeckLayout/PlankControls.d.ts.map +1 -1
- package/dist/types/src/components/DeckLayout/PlankError.d.ts +3 -2
- package/dist/types/src/components/DeckLayout/PlankError.d.ts.map +1 -1
- package/dist/types/src/components/DeckLayout/PlankLoading.d.ts +2 -1
- package/dist/types/src/components/DeckLayout/PlankLoading.d.ts.map +1 -1
- package/dist/types/src/components/DeckLayout/Sidebar.d.ts +2 -1
- package/dist/types/src/components/DeckLayout/Sidebar.d.ts.map +1 -1
- package/dist/types/src/components/DeckLayout/SidebarButton.d.ts +4 -3
- package/dist/types/src/components/DeckLayout/SidebarButton.d.ts.map +1 -1
- package/dist/types/src/components/DeckLayout/StatusBar.d.ts +2 -1
- package/dist/types/src/components/DeckLayout/StatusBar.d.ts.map +1 -1
- package/dist/types/src/components/DeckLayout/Toast.d.ts +2 -1
- package/dist/types/src/components/DeckLayout/Toast.d.ts.map +1 -1
- package/dist/types/src/components/DeckLayout/Topbar.d.ts +2 -1
- package/dist/types/src/components/DeckLayout/Topbar.d.ts.map +1 -1
- package/dist/types/src/components/LayoutSettings.d.ts +2 -1
- package/dist/types/src/components/LayoutSettings.d.ts.map +1 -1
- package/dist/types/src/meta.d.ts +2 -5
- package/dist/types/src/meta.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 +42 -11
- package/dist/types/src/types.d.ts.map +1 -1
- package/package.json +31 -31
- package/src/capabilities/intent-resolver.ts +29 -3
- package/src/capabilities/settings.ts +1 -1
- package/src/capabilities/state.ts +2 -9
- package/src/capabilities/tools.ts +34 -22
- package/src/capabilities/url-handler.ts +2 -8
- package/src/components/DeckLayout/DeckLayout.tsx +36 -11
- package/src/components/DeckLayout/NodePlankHeading.tsx +49 -29
- package/src/components/DeckLayout/Plank.tsx +181 -111
- package/src/components/DeckLayout/PlankControls.tsx +34 -3
- package/src/components/LayoutSettings.tsx +3 -3
- package/src/meta.ts +2 -2
- package/src/translations.ts +4 -3
- package/src/types.ts +30 -1
- package/src/util/useHoistStatusbar.ts +4 -4
- package/dist/lib/browser/chunk-FT33W5CI.mjs.map +0 -7
- package/dist/lib/browser/chunk-J65MNI4S.mjs +0 -24
- package/dist/lib/browser/chunk-OEDK54N2.mjs.map +0 -7
- package/dist/lib/browser/intent-resolver-ZD67BRUI.mjs.map +0 -7
- package/dist/lib/browser/state-U4SHOPJW.mjs.map +0 -7
- package/dist/lib/browser/tools-64LXGLYR.mjs +0 -59
- package/dist/lib/browser/tools-64LXGLYR.mjs.map +0 -7
- package/dist/lib/browser/url-handler-MVHTKUYA.mjs.map +0 -7
- /package/dist/lib/browser/{app-graph-builder-IYHAGFA3.mjs.map → app-graph-builder-K4KVSHNT.mjs.map} +0 -0
- /package/dist/lib/browser/{check-app-scheme-S3EYUPMF.mjs.map → check-app-scheme-6SS6I3RN.mjs.map} +0 -0
- /package/dist/lib/browser/{chunk-J65MNI4S.mjs.map → chunk-7A5DLPQ3.mjs.map} +0 -0
- /package/dist/lib/browser/{chunk-22AQ5IVX.mjs.map → chunk-WCNPMAR4.mjs.map} +0 -0
- /package/dist/lib/browser/{react-root-2T6BCRK4.mjs.map → react-root-PPKFVPO7.mjs.map} +0 -0
- /package/dist/lib/browser/{react-surface-GFMOJSIA.mjs.map → react-surface-7JBPPUHM.mjs.map} +0 -0
|
@@ -19,9 +19,9 @@ import {
|
|
|
19
19
|
Dialog as NaturalDialog,
|
|
20
20
|
Main,
|
|
21
21
|
Popover,
|
|
22
|
-
useOnTransition,
|
|
23
22
|
type MainProps,
|
|
24
23
|
useMediaQuery,
|
|
24
|
+
useOnTransition,
|
|
25
25
|
} from '@dxos/react-ui';
|
|
26
26
|
import { Stack, StackContext, DEFAULT_HORIZONTAL_SIZE } from '@dxos/react-ui-stack';
|
|
27
27
|
import { mainPaddingTransitions } from '@dxos/react-ui-theme';
|
|
@@ -31,6 +31,7 @@ import { ComplementarySidebar } from './ComplementarySidebar';
|
|
|
31
31
|
import { ContentEmpty } from './ContentEmpty';
|
|
32
32
|
import { Fullscreen } from './Fullscreen';
|
|
33
33
|
import { Plank } from './Plank';
|
|
34
|
+
import { PlankContentError } from './PlankError';
|
|
34
35
|
import { Sidebar } from './Sidebar';
|
|
35
36
|
import { ToggleComplementarySidebarButton, ToggleSidebarButton } from './SidebarButton';
|
|
36
37
|
import { StatusBar } from './StatusBar';
|
|
@@ -47,8 +48,8 @@ export type DeckLayoutProps = {
|
|
|
47
48
|
onDismissToast: (id: string) => void;
|
|
48
49
|
};
|
|
49
50
|
|
|
50
|
-
const PlankSeparator = ({
|
|
51
|
-
|
|
51
|
+
const PlankSeparator = ({ order }: { order: number }) =>
|
|
52
|
+
order > 0 ? <span role='separator' className='row-span-2 bg-deck is-4' style={{ gridColumn: order }} /> : null;
|
|
52
53
|
|
|
53
54
|
export const DeckLayout = ({ overscroll, showHints, onDismissToast }: DeckLayoutProps) => {
|
|
54
55
|
const { dispatchPromise: dispatch } = useIntentDispatcher();
|
|
@@ -67,7 +68,7 @@ export const DeckLayout = ({ overscroll, showHints, onDismissToast }: DeckLayout
|
|
|
67
68
|
deck,
|
|
68
69
|
toasts,
|
|
69
70
|
} = context;
|
|
70
|
-
const { active, fullscreen, solo, plankSizing } = deck;
|
|
71
|
+
const { active, activeCompanions, fullscreen, solo, plankSizing } = deck;
|
|
71
72
|
const breakpoint = useBreakpoints();
|
|
72
73
|
const topbar = layoutAppliesTopbar(breakpoint);
|
|
73
74
|
const hoistStatusbar = useHoistStatusbar(breakpoint);
|
|
@@ -186,6 +187,17 @@ export const DeckLayout = ({ overscroll, showHints, onDismissToast }: DeckLayout
|
|
|
186
187
|
);
|
|
187
188
|
const handlePopoverClose = useCallback(() => handlePopoverOpenChange(false), [handlePopoverOpenChange]);
|
|
188
189
|
|
|
190
|
+
const { order, itemsCount }: { order: Record<string, number>; itemsCount: number } = useMemo(() => {
|
|
191
|
+
return active.reduce(
|
|
192
|
+
(acc: { order: Record<string, number>; itemsCount: number }, entryId) => {
|
|
193
|
+
acc.order[entryId] = acc.itemsCount + 1;
|
|
194
|
+
acc.itemsCount += activeCompanions?.[entryId] ? 3 : 2;
|
|
195
|
+
return acc;
|
|
196
|
+
},
|
|
197
|
+
{ order: {}, itemsCount: 0 },
|
|
198
|
+
);
|
|
199
|
+
}, [active, activeCompanions]);
|
|
200
|
+
|
|
189
201
|
return (
|
|
190
202
|
<Popover.Root modal open={!!(popoverAnchorId && delayedPopoverVisibility)} onOpenChange={handlePopoverOpenChange}>
|
|
191
203
|
<ActiveNode />
|
|
@@ -252,14 +264,21 @@ export const DeckLayout = ({ overscroll, showHints, onDismissToast }: DeckLayout
|
|
|
252
264
|
size='contain'
|
|
253
265
|
classNames={['absolute inset-block-0 -inset-inline-px', mainPaddingTransitions]}
|
|
254
266
|
onScroll={handleScroll}
|
|
255
|
-
itemsCount={
|
|
267
|
+
itemsCount={itemsCount - 1}
|
|
256
268
|
style={padding}
|
|
257
269
|
ref={deckRef}
|
|
258
270
|
>
|
|
259
|
-
{active.map((entryId
|
|
271
|
+
{active.map((entryId) => (
|
|
260
272
|
<Fragment key={entryId}>
|
|
261
|
-
<PlankSeparator
|
|
262
|
-
<Plank
|
|
273
|
+
<PlankSeparator order={order[entryId] - 1} />
|
|
274
|
+
<Plank
|
|
275
|
+
id={entryId}
|
|
276
|
+
companionId={activeCompanions?.[entryId]}
|
|
277
|
+
part='deck'
|
|
278
|
+
order={order[entryId]}
|
|
279
|
+
active={active}
|
|
280
|
+
layoutMode={layoutMode}
|
|
281
|
+
/>
|
|
263
282
|
</Fragment>
|
|
264
283
|
))}
|
|
265
284
|
</Stack>
|
|
@@ -272,7 +291,12 @@ export const DeckLayout = ({ overscroll, showHints, onDismissToast }: DeckLayout
|
|
|
272
291
|
{!topbar && <ToggleSidebarButton classNames={fixedSidebarToggleStyles} />}
|
|
273
292
|
{!topbar && <ToggleComplementarySidebarButton classNames={fixedComplementarySidebarToggleStyles} />}
|
|
274
293
|
<StackContext.Provider value={{ size: 'contain', orientation: 'horizontal', rail: true }}>
|
|
275
|
-
<Plank
|
|
294
|
+
<Plank
|
|
295
|
+
id={solo}
|
|
296
|
+
companionId={solo ? activeCompanions?.[solo] : undefined}
|
|
297
|
+
part='solo'
|
|
298
|
+
layoutMode={layoutMode}
|
|
299
|
+
/>
|
|
276
300
|
</StackContext.Provider>
|
|
277
301
|
</div>
|
|
278
302
|
</Main.Content>
|
|
@@ -303,10 +327,11 @@ export const DeckLayout = ({ overscroll, showHints, onDismissToast }: DeckLayout
|
|
|
303
327
|
onOpenChange={(nextOpen) => (context.dialogOpen = nextOpen)}
|
|
304
328
|
>
|
|
305
329
|
{dialogBlockAlign === 'end' ? (
|
|
306
|
-
|
|
330
|
+
// TODO(burdon): Placeholder creates a suspense boundary; replace with defaults.
|
|
331
|
+
<Surface role='dialog' data={dialogContent} limit={1} fallback={PlankContentError} placeholder={<div />} />
|
|
307
332
|
) : (
|
|
308
333
|
<Dialog.Overlay blockAlign={dialogBlockAlign}>
|
|
309
|
-
<Surface role='dialog' data={dialogContent} limit={1} />
|
|
334
|
+
<Surface role='dialog' data={dialogContent} limit={1} fallback={PlankContentError} />
|
|
310
335
|
</Dialog.Overlay>
|
|
311
336
|
)}
|
|
312
337
|
</Dialog.Root>
|
|
@@ -10,21 +10,24 @@ import { Icon, Popover, toLocalizedString, useTranslation } from '@dxos/react-ui
|
|
|
10
10
|
import { StackItem, type StackItemSigilAction } from '@dxos/react-ui-stack';
|
|
11
11
|
import { TextTooltip } from '@dxos/react-ui-text-tooltip';
|
|
12
12
|
|
|
13
|
-
import { PlankControls } from './PlankControls';
|
|
13
|
+
import { PlankCompanionControls, PlankControls } from './PlankControls';
|
|
14
14
|
import { DECK_PLUGIN } from '../../meta';
|
|
15
|
-
import { DeckAction, SLUG_PATH_SEPARATOR } from '../../types';
|
|
15
|
+
import { DeckAction, type ResolvedPart, SLUG_PATH_SEPARATOR } from '../../types';
|
|
16
16
|
import { useBreakpoints } from '../../util';
|
|
17
17
|
import { soloInlinePadding } from '../fragments';
|
|
18
18
|
|
|
19
19
|
export type NodePlankHeadingProps = {
|
|
20
20
|
id: string;
|
|
21
|
-
part:
|
|
21
|
+
part: ResolvedPart;
|
|
22
22
|
node?: Node;
|
|
23
23
|
canIncrementStart?: boolean;
|
|
24
24
|
canIncrementEnd?: boolean;
|
|
25
25
|
popoverAnchorId?: string;
|
|
26
26
|
pending?: boolean;
|
|
27
27
|
actions?: StackItemSigilAction[];
|
|
28
|
+
companioned?: 'primary' | 'companion';
|
|
29
|
+
primaryId?: string;
|
|
30
|
+
surfaceVariant?: string;
|
|
28
31
|
};
|
|
29
32
|
|
|
30
33
|
export const NodePlankHeading = memo(
|
|
@@ -37,6 +40,9 @@ export const NodePlankHeading = memo(
|
|
|
37
40
|
popoverAnchorId,
|
|
38
41
|
pending,
|
|
39
42
|
actions = [],
|
|
43
|
+
companioned,
|
|
44
|
+
primaryId,
|
|
45
|
+
surfaceVariant,
|
|
40
46
|
}: NodePlankHeadingProps) => {
|
|
41
47
|
const { t } = useTranslation(DECK_PLUGIN);
|
|
42
48
|
const { graph } = useAppGraph();
|
|
@@ -44,7 +50,14 @@ export const NodePlankHeading = memo(
|
|
|
44
50
|
const icon = node?.properties?.icon ?? 'ph--placeholder--regular';
|
|
45
51
|
const label = pending
|
|
46
52
|
? t('pending heading')
|
|
47
|
-
: toLocalizedString(
|
|
53
|
+
: toLocalizedString(
|
|
54
|
+
(surfaceVariant
|
|
55
|
+
? Array.isArray(node?.properties?.label)
|
|
56
|
+
? [`${surfaceVariant} plank heading`, node.properties.label[1]]
|
|
57
|
+
: ['companion plank heading fallback label', { ns: DECK_PLUGIN }]
|
|
58
|
+
: node?.properties?.label) ?? ['plank heading fallback label', { ns: DECK_PLUGIN }],
|
|
59
|
+
t,
|
|
60
|
+
);
|
|
48
61
|
const { dispatchPromise: dispatch } = useIntentDispatcher();
|
|
49
62
|
const ActionRoot = node && popoverAnchorId === `dxos.org/ui/${DECK_PLUGIN}/${node.id}` ? Popover.Anchor : Fragment;
|
|
50
63
|
|
|
@@ -105,27 +118,30 @@ export const NodePlankHeading = memo(
|
|
|
105
118
|
classNames={[
|
|
106
119
|
'plb-1 border-be border-separator items-stretch gap-1 sticky inline-start-12 app-drag',
|
|
107
120
|
part === 'solo' ? soloInlinePadding : 'pli-1',
|
|
121
|
+
surfaceVariant && 'pis-3',
|
|
108
122
|
]}
|
|
109
123
|
>
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
<
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
124
|
+
{!surfaceVariant && (
|
|
125
|
+
<ActionRoot>
|
|
126
|
+
{node && sigilActions ? (
|
|
127
|
+
<StackItem.Sigil
|
|
128
|
+
icon={icon}
|
|
129
|
+
related={part === 'complementary'}
|
|
130
|
+
attendableId={attendableId}
|
|
131
|
+
triggerLabel={t('actions menu label')}
|
|
132
|
+
actions={sigilActions}
|
|
133
|
+
onAction={handleAction}
|
|
134
|
+
>
|
|
135
|
+
<Surface role='menu-footer' data={{ subject: node.data }} />
|
|
136
|
+
</StackItem.Sigil>
|
|
137
|
+
) : (
|
|
138
|
+
<StackItem.SigilButton>
|
|
139
|
+
<span className='sr-only'>{label}</span>
|
|
140
|
+
<Icon icon={icon} size={5} />
|
|
141
|
+
</StackItem.SigilButton>
|
|
142
|
+
)}
|
|
143
|
+
</ActionRoot>
|
|
144
|
+
)}
|
|
129
145
|
<TextTooltip text={label} onlyWhenTruncating>
|
|
130
146
|
<StackItem.HeadingLabel
|
|
131
147
|
attendableId={attendableId}
|
|
@@ -136,12 +152,16 @@ export const NodePlankHeading = memo(
|
|
|
136
152
|
</StackItem.HeadingLabel>
|
|
137
153
|
</TextTooltip>
|
|
138
154
|
{node && part !== 'complementary' && <Surface role='navbar-end' data={{ subject: node.data }} />}
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
155
|
+
{companioned === 'companion' ? (
|
|
156
|
+
<PlankCompanionControls primary={surfaceVariant ? id : primaryId} />
|
|
157
|
+
) : (
|
|
158
|
+
<PlankControls
|
|
159
|
+
capabilities={capabilities}
|
|
160
|
+
isSolo={part === 'solo'}
|
|
161
|
+
onClick={handlePlankAction}
|
|
162
|
+
close={part === 'complementary' ? 'minify-end' : true}
|
|
163
|
+
/>
|
|
164
|
+
)}
|
|
145
165
|
</StackItem.Heading>
|
|
146
166
|
);
|
|
147
167
|
},
|
|
@@ -2,7 +2,16 @@
|
|
|
2
2
|
// Copyright 2024 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import React, {
|
|
5
|
+
import React, {
|
|
6
|
+
Fragment,
|
|
7
|
+
type KeyboardEvent,
|
|
8
|
+
memo,
|
|
9
|
+
type PropsWithChildren,
|
|
10
|
+
useCallback,
|
|
11
|
+
useLayoutEffect,
|
|
12
|
+
useMemo,
|
|
13
|
+
useRef,
|
|
14
|
+
} from 'react';
|
|
6
15
|
|
|
7
16
|
import {
|
|
8
17
|
createIntent,
|
|
@@ -17,133 +26,194 @@ import { useAttendableAttributes } from '@dxos/react-ui-attention';
|
|
|
17
26
|
import { StackItem, railGridHorizontal } from '@dxos/react-ui-stack';
|
|
18
27
|
import { mainIntrinsicSize, mx } from '@dxos/react-ui-theme';
|
|
19
28
|
|
|
20
|
-
import { NodePlankHeading
|
|
29
|
+
import { NodePlankHeading } from './NodePlankHeading';
|
|
21
30
|
import { PlankContentError, PlankError } from './PlankError';
|
|
22
31
|
import { PlankLoading } from './PlankLoading';
|
|
23
32
|
import { DeckCapabilities } from '../../capabilities';
|
|
24
33
|
import { useNode, useMainSize } from '../../hooks';
|
|
25
|
-
import { DeckAction, type LayoutMode } from '../../types';
|
|
34
|
+
import { DeckAction, type LayoutMode, type Part, type ResolvedPart, surfaceVariantSeparator } from '../../types';
|
|
26
35
|
|
|
27
36
|
const UNKNOWN_ID = 'unknown_id';
|
|
28
37
|
|
|
29
38
|
export type PlankProps = {
|
|
30
39
|
id?: string;
|
|
31
|
-
|
|
40
|
+
companionId?: string;
|
|
41
|
+
part: Part;
|
|
32
42
|
path?: string[];
|
|
33
43
|
order?: number;
|
|
34
44
|
active?: string[];
|
|
35
45
|
layoutMode: LayoutMode;
|
|
36
46
|
};
|
|
37
47
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
const canResize = layoutMode === 'deck';
|
|
45
|
-
const Root = part === 'solo' ? 'article' : StackItem.Root;
|
|
46
|
-
|
|
47
|
-
const attendableAttrs = useAttendableAttributes(id);
|
|
48
|
-
const index = active ? active.findIndex((entryId) => entryId === id) : 0;
|
|
49
|
-
const length = active?.length ?? 1;
|
|
50
|
-
const canIncrementStart = active && index !== undefined && index > 0 && length !== undefined && length > 1;
|
|
51
|
-
const canIncrementEnd = active && index !== undefined && index < length - 1 && length !== undefined;
|
|
52
|
-
|
|
53
|
-
const key = id.split('+')[0];
|
|
54
|
-
const size = deck.plankSizing[key] as number | undefined;
|
|
55
|
-
const setSize = useCallback(
|
|
56
|
-
debounce((nextSize: number) => {
|
|
57
|
-
return dispatch(createIntent(DeckAction.UpdatePlankSize, { id: key, size: nextSize }));
|
|
58
|
-
}, 200),
|
|
59
|
-
[dispatch, key],
|
|
60
|
-
);
|
|
61
|
-
|
|
62
|
-
// TODO(thure): Tabster’s focus group should handle moving focus to Main, but something is blocking it.
|
|
63
|
-
const handleKeyDown = useCallback((event: KeyboardEvent) => {
|
|
64
|
-
if (event.target === event.currentTarget && event.key === 'Escape') {
|
|
65
|
-
rootElement.current?.closest('main')?.focus();
|
|
66
|
-
}
|
|
67
|
-
}, []);
|
|
68
|
-
|
|
69
|
-
useLayoutEffect(() => {
|
|
70
|
-
if (scrollIntoView === id) {
|
|
71
|
-
// TODO(wittjosiah): When focused on page load, the focus is always visible.
|
|
72
|
-
// Forcing focus to something smaller than the plank prevents large focus ring in the interim.
|
|
73
|
-
const focusable = rootElement.current?.querySelector('button') || rootElement.current;
|
|
74
|
-
focusable?.focus({ preventScroll: true });
|
|
75
|
-
layoutMode === 'deck' && focusable?.scrollIntoView({ behavior: 'smooth', inline: 'center' });
|
|
76
|
-
// Clear the scroll into view state once it has been actioned.
|
|
77
|
-
void dispatch(createIntent(LayoutAction.ScrollIntoView, { part: 'current', subject: undefined }));
|
|
78
|
-
}
|
|
79
|
-
}, [id, scrollIntoView, layoutMode]);
|
|
80
|
-
|
|
81
|
-
const isSolo = layoutMode === 'solo' && part === 'solo';
|
|
82
|
-
const isAttendable = isSolo || (layoutMode === 'deck' && part === 'deck');
|
|
48
|
+
type PlankImplProps = Omit<PlankProps, 'companionId' | 'part'> & {
|
|
49
|
+
part: ResolvedPart;
|
|
50
|
+
surfaceVariant?: string;
|
|
51
|
+
companioned?: 'primary' | 'companion';
|
|
52
|
+
primaryId?: string;
|
|
53
|
+
};
|
|
83
54
|
|
|
55
|
+
const PlankImpl = memo(
|
|
56
|
+
({
|
|
57
|
+
id = UNKNOWN_ID,
|
|
58
|
+
part,
|
|
59
|
+
path,
|
|
60
|
+
order,
|
|
61
|
+
active,
|
|
62
|
+
layoutMode,
|
|
63
|
+
surfaceVariant,
|
|
64
|
+
companioned,
|
|
65
|
+
primaryId,
|
|
66
|
+
}: PlankImplProps) => {
|
|
67
|
+
const { dispatchPromise: dispatch } = useIntentDispatcher();
|
|
68
|
+
const { deck, popoverAnchorId, scrollIntoView } = useCapability(DeckCapabilities.DeckState);
|
|
69
|
+
const { graph } = useAppGraph();
|
|
70
|
+
const node = useNode(graph, id);
|
|
71
|
+
const rootElement = useRef<HTMLDivElement | null>(null);
|
|
72
|
+
const canResize = layoutMode === 'deck';
|
|
73
|
+
const Root = part.startsWith('solo') ? 'article' : StackItem.Root;
|
|
74
|
+
|
|
75
|
+
const attendableAttrs = useAttendableAttributes(id);
|
|
76
|
+
const index = active ? active.findIndex((entryId) => entryId === id) : 0;
|
|
77
|
+
const length = active?.length ?? 1;
|
|
78
|
+
const canIncrementStart = active && index !== undefined && index > 0 && length !== undefined && length > 1;
|
|
79
|
+
const canIncrementEnd = active && index !== undefined && index < length - 1 && length !== undefined;
|
|
80
|
+
|
|
81
|
+
const sizeKey = `${id.split('+')[0]}${surfaceVariant ? `${surfaceVariantSeparator}${surfaceVariant}` : ''}`;
|
|
82
|
+
const size = deck.plankSizing[sizeKey] as number | undefined;
|
|
83
|
+
const setSize = useCallback(
|
|
84
|
+
debounce((nextSize: number) => {
|
|
85
|
+
return dispatch(createIntent(DeckAction.UpdatePlankSize, { id: sizeKey, size: nextSize }));
|
|
86
|
+
}, 200),
|
|
87
|
+
[dispatch, sizeKey],
|
|
88
|
+
);
|
|
89
|
+
|
|
90
|
+
// TODO(thure): Tabster’s focus group should handle moving focus to Main, but something is blocking it.
|
|
91
|
+
const handleKeyDown = useCallback((event: KeyboardEvent) => {
|
|
92
|
+
if (event.target === event.currentTarget && event.key === 'Escape') {
|
|
93
|
+
rootElement.current?.closest('main')?.focus();
|
|
94
|
+
}
|
|
95
|
+
}, []);
|
|
96
|
+
|
|
97
|
+
useLayoutEffect(() => {
|
|
98
|
+
if (scrollIntoView === id) {
|
|
99
|
+
// TODO(wittjosiah): When focused on page load, the focus is always visible.
|
|
100
|
+
// Forcing focus to something smaller than the plank prevents large focus ring in the interim.
|
|
101
|
+
const focusable = rootElement.current?.querySelector('button') || rootElement.current;
|
|
102
|
+
focusable?.focus({ preventScroll: true });
|
|
103
|
+
layoutMode === 'deck' && focusable?.scrollIntoView({ behavior: 'smooth', inline: 'center' });
|
|
104
|
+
// Clear the scroll into view state once it has been actioned.
|
|
105
|
+
void dispatch(createIntent(LayoutAction.ScrollIntoView, { part: 'current', subject: undefined }));
|
|
106
|
+
}
|
|
107
|
+
}, [id, scrollIntoView, layoutMode]);
|
|
108
|
+
|
|
109
|
+
const isSolo = layoutMode === 'solo' && part === 'solo';
|
|
110
|
+
const isAttendable = isSolo || (layoutMode === 'deck' && part === 'deck');
|
|
111
|
+
|
|
112
|
+
const sizeAttrs = useMainSize();
|
|
113
|
+
|
|
114
|
+
const data = useMemo(
|
|
115
|
+
() =>
|
|
116
|
+
node && {
|
|
117
|
+
subject: node.data,
|
|
118
|
+
variant: surfaceVariant,
|
|
119
|
+
path,
|
|
120
|
+
popoverAnchorId,
|
|
121
|
+
},
|
|
122
|
+
[node, node?.data, path, popoverAnchorId, surfaceVariant],
|
|
123
|
+
);
|
|
124
|
+
|
|
125
|
+
// TODO(wittjosiah): Change prop to accept a component.
|
|
126
|
+
const placeholder = useMemo(() => <PlankLoading />, []);
|
|
127
|
+
|
|
128
|
+
const className = mx(
|
|
129
|
+
'attention-surface relative',
|
|
130
|
+
isSolo && mainIntrinsicSize,
|
|
131
|
+
isSolo && railGridHorizontal,
|
|
132
|
+
isSolo && 'grid absolute inset-0',
|
|
133
|
+
part === 'deck' && (companioned === 'companion' ? '!border-separator border-ie' : '!border-separator border-li'),
|
|
134
|
+
part.startsWith('solo-') && 'row-span-2 min-is-0',
|
|
135
|
+
part === 'solo-companion' && '!border-separator border-is',
|
|
136
|
+
);
|
|
137
|
+
|
|
138
|
+
return (
|
|
139
|
+
<Root
|
|
140
|
+
ref={rootElement}
|
|
141
|
+
data-testid='deck.plank'
|
|
142
|
+
tabIndex={0}
|
|
143
|
+
{...(part.startsWith('solo')
|
|
144
|
+
? ({ ...sizeAttrs, className } as any)
|
|
145
|
+
: {
|
|
146
|
+
item: { id },
|
|
147
|
+
size,
|
|
148
|
+
onSizeChange: setSize,
|
|
149
|
+
classNames: className,
|
|
150
|
+
order,
|
|
151
|
+
role: 'article',
|
|
152
|
+
})}
|
|
153
|
+
{...(isAttendable ? attendableAttrs : {})}
|
|
154
|
+
onKeyDown={handleKeyDown}
|
|
155
|
+
>
|
|
156
|
+
{node ? (
|
|
157
|
+
<>
|
|
158
|
+
<NodePlankHeading
|
|
159
|
+
id={id}
|
|
160
|
+
part={part.startsWith('solo-') ? 'solo' : part}
|
|
161
|
+
node={node}
|
|
162
|
+
canIncrementStart={canIncrementStart}
|
|
163
|
+
canIncrementEnd={canIncrementEnd}
|
|
164
|
+
popoverAnchorId={popoverAnchorId}
|
|
165
|
+
companioned={companioned}
|
|
166
|
+
primaryId={primaryId}
|
|
167
|
+
surfaceVariant={surfaceVariant}
|
|
168
|
+
/>
|
|
169
|
+
<Surface
|
|
170
|
+
key={node.id}
|
|
171
|
+
role='article'
|
|
172
|
+
data={data}
|
|
173
|
+
limit={1}
|
|
174
|
+
fallback={PlankContentError}
|
|
175
|
+
placeholder={placeholder}
|
|
176
|
+
/>
|
|
177
|
+
</>
|
|
178
|
+
) : (
|
|
179
|
+
<PlankError id={id} part={part} />
|
|
180
|
+
)}
|
|
181
|
+
{canResize && <StackItem.ResizeHandle />}
|
|
182
|
+
</Root>
|
|
183
|
+
);
|
|
184
|
+
},
|
|
185
|
+
);
|
|
186
|
+
|
|
187
|
+
const SplitFrame = ({ children }: PropsWithChildren<{}>) => {
|
|
84
188
|
const sizeAttrs = useMainSize();
|
|
85
|
-
|
|
86
|
-
const data = useMemo(
|
|
87
|
-
() =>
|
|
88
|
-
node && {
|
|
89
|
-
subject: node.data,
|
|
90
|
-
path,
|
|
91
|
-
popoverAnchorId,
|
|
92
|
-
},
|
|
93
|
-
[node, node?.data, path, popoverAnchorId],
|
|
94
|
-
);
|
|
95
|
-
|
|
96
|
-
// TODO(wittjosiah): Change prop to accept a component.
|
|
97
|
-
const placeholder = useMemo(() => <PlankLoading />, []);
|
|
98
|
-
|
|
99
|
-
const className = mx(
|
|
100
|
-
'attention-surface relative',
|
|
101
|
-
isSolo && mainIntrinsicSize,
|
|
102
|
-
isSolo && railGridHorizontal,
|
|
103
|
-
isSolo ? 'grid absolute inset-0' : '!border-separator border-li',
|
|
104
|
-
);
|
|
105
|
-
|
|
106
189
|
return (
|
|
107
|
-
<
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
{...(part === 'solo'
|
|
112
|
-
? ({ ...sizeAttrs, className } as any)
|
|
113
|
-
: {
|
|
114
|
-
item: { id },
|
|
115
|
-
size,
|
|
116
|
-
onSizeChange: setSize,
|
|
117
|
-
classNames: className,
|
|
118
|
-
order,
|
|
119
|
-
role: 'article',
|
|
120
|
-
})}
|
|
121
|
-
{...(isAttendable ? attendableAttrs : {})}
|
|
122
|
-
onKeyDown={handleKeyDown}
|
|
190
|
+
<div
|
|
191
|
+
role='none'
|
|
192
|
+
className={mx('grid grid-cols-[1fr_1fr] absolute inset-0', railGridHorizontal, mainIntrinsicSize)}
|
|
193
|
+
{...sizeAttrs}
|
|
123
194
|
>
|
|
124
|
-
{
|
|
125
|
-
|
|
126
|
-
<NodePlankHeading
|
|
127
|
-
id={id}
|
|
128
|
-
part={part}
|
|
129
|
-
node={node}
|
|
130
|
-
canIncrementStart={canIncrementStart}
|
|
131
|
-
canIncrementEnd={canIncrementEnd}
|
|
132
|
-
popoverAnchorId={popoverAnchorId}
|
|
133
|
-
/>
|
|
134
|
-
<Surface
|
|
135
|
-
key={node.id}
|
|
136
|
-
role='article'
|
|
137
|
-
data={data}
|
|
138
|
-
limit={1}
|
|
139
|
-
fallback={PlankContentError}
|
|
140
|
-
placeholder={placeholder}
|
|
141
|
-
/>
|
|
142
|
-
</>
|
|
143
|
-
) : (
|
|
144
|
-
<PlankError id={id} part={part} />
|
|
145
|
-
)}
|
|
146
|
-
{canResize && <StackItem.ResizeHandle />}
|
|
147
|
-
</Root>
|
|
195
|
+
{children}
|
|
196
|
+
</div>
|
|
148
197
|
);
|
|
149
|
-
}
|
|
198
|
+
};
|
|
199
|
+
|
|
200
|
+
export const Plank = (props: PlankProps) => {
|
|
201
|
+
if (props.companionId) {
|
|
202
|
+
const Root = props.part === 'solo' ? SplitFrame : Fragment;
|
|
203
|
+
return (
|
|
204
|
+
<Root>
|
|
205
|
+
<PlankImpl {...props} {...(props.part === 'solo' ? { part: 'solo-primary' } : {})} companioned='primary' />
|
|
206
|
+
<PlankImpl
|
|
207
|
+
{...props}
|
|
208
|
+
{...(props.companionId.startsWith(surfaceVariantSeparator)
|
|
209
|
+
? { surfaceVariant: props.companionId.substring(2) }
|
|
210
|
+
: { id: props.companionId, primaryId: props.id })}
|
|
211
|
+
{...(props.part === 'solo' ? { part: 'solo-companion' } : { order: props.order! + 1 })}
|
|
212
|
+
companioned='companion'
|
|
213
|
+
/>
|
|
214
|
+
</Root>
|
|
215
|
+
);
|
|
216
|
+
} else {
|
|
217
|
+
return <PlankImpl {...props} />;
|
|
218
|
+
}
|
|
219
|
+
};
|
|
@@ -2,8 +2,10 @@
|
|
|
2
2
|
// Copyright 2024 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import React, { forwardRef } from 'react';
|
|
5
|
+
import React, { forwardRef, useCallback } from 'react';
|
|
6
6
|
|
|
7
|
+
import { createIntent, useIntentDispatcher } from '@dxos/app-framework';
|
|
8
|
+
import { invariant } from '@dxos/invariant';
|
|
7
9
|
import {
|
|
8
10
|
Button,
|
|
9
11
|
ButtonGroup,
|
|
@@ -15,7 +17,7 @@ import {
|
|
|
15
17
|
} from '@dxos/react-ui';
|
|
16
18
|
|
|
17
19
|
import { DECK_PLUGIN } from '../../meta';
|
|
18
|
-
import {
|
|
20
|
+
import { DeckAction } from '../../types';
|
|
19
21
|
|
|
20
22
|
export type PlankControlHandler = (event: DeckAction.PartAdjustment) => void;
|
|
21
23
|
|
|
@@ -50,6 +52,34 @@ const PlankControl = ({ icon, label, ...props }: Omit<ButtonProps, 'children'> &
|
|
|
50
52
|
);
|
|
51
53
|
};
|
|
52
54
|
|
|
55
|
+
const plankControlSpacing = 'pli-2 plb-3';
|
|
56
|
+
|
|
57
|
+
type PlankComplimentControlsProps = {
|
|
58
|
+
primary?: string;
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
export const PlankCompanionControls = forwardRef<HTMLDivElement, PlankComplimentControlsProps>(
|
|
62
|
+
({ primary }, forwardedRef) => {
|
|
63
|
+
const { t } = useTranslation(DECK_PLUGIN);
|
|
64
|
+
const { dispatchPromise: dispatch } = useIntentDispatcher();
|
|
65
|
+
const handleCloseCompanion = useCallback(() => {
|
|
66
|
+
invariant(primary);
|
|
67
|
+
return dispatch(createIntent(DeckAction.ChangeCompanion, { primary, companion: null }));
|
|
68
|
+
}, []);
|
|
69
|
+
return (
|
|
70
|
+
<div ref={forwardedRef} className='contents app-no-drag'>
|
|
71
|
+
<PlankControl
|
|
72
|
+
label={t('close companion label')}
|
|
73
|
+
variant='ghost'
|
|
74
|
+
icon='ph--minus--regular'
|
|
75
|
+
onClick={handleCloseCompanion}
|
|
76
|
+
classNames={plankControlSpacing}
|
|
77
|
+
/>
|
|
78
|
+
</div>
|
|
79
|
+
);
|
|
80
|
+
},
|
|
81
|
+
);
|
|
82
|
+
|
|
53
83
|
// TODO(wittjosiah): Duplicate of stack LayoutControls?
|
|
54
84
|
// Translations were to be duplicated between packages.
|
|
55
85
|
// NOTE(thure): Pinning & unpinning are disabled indefinitely.
|
|
@@ -59,7 +89,8 @@ export const PlankControls = forwardRef<HTMLDivElement, PlankControlsProps>(
|
|
|
59
89
|
forwardedRef,
|
|
60
90
|
) => {
|
|
61
91
|
const { t } = useTranslation(DECK_PLUGIN);
|
|
62
|
-
const buttonClassNames =
|
|
92
|
+
const buttonClassNames =
|
|
93
|
+
variant === 'hide-disabled' ? `disabled:hidden ${plankControlSpacing}` : plankControlSpacing;
|
|
63
94
|
|
|
64
95
|
return (
|
|
65
96
|
<ButtonGroup {...props} classNames={['app-no-drag', classNames]} ref={forwardedRef}>
|
|
@@ -72,10 +72,10 @@ export const LayoutSettings = ({ settings }: { settings: DeckSettingsProps }) =>
|
|
|
72
72
|
/>
|
|
73
73
|
</DeprecatedFormInput>
|
|
74
74
|
)}
|
|
75
|
-
<DeprecatedFormInput label={t('settings enable
|
|
75
|
+
<DeprecatedFormInput label={t('settings enable statusbar label')}>
|
|
76
76
|
<Input.Switch
|
|
77
|
-
checked={settings.
|
|
78
|
-
onCheckedChange={(checked) => (settings.
|
|
77
|
+
checked={settings.enableStatusbar}
|
|
78
|
+
onCheckedChange={(checked) => (settings.enableStatusbar = checked)}
|
|
79
79
|
/>
|
|
80
80
|
</DeprecatedFormInput>
|
|
81
81
|
</DeprecatedFormContainer>
|
package/src/meta.ts
CHANGED
|
@@ -6,8 +6,8 @@ import { type PluginMeta } from '@dxos/app-framework';
|
|
|
6
6
|
|
|
7
7
|
export const DECK_PLUGIN = 'dxos.org/plugin/deck' as const;
|
|
8
8
|
|
|
9
|
-
export const meta = {
|
|
9
|
+
export const meta: PluginMeta = {
|
|
10
10
|
id: DECK_PLUGIN,
|
|
11
11
|
name: 'Deck',
|
|
12
12
|
icon: 'ph--columns--regular',
|
|
13
|
-
}
|
|
13
|
+
};
|
package/src/translations.ts
CHANGED
|
@@ -47,14 +47,15 @@ export default [
|
|
|
47
47
|
'show solo plank label': 'Maximize',
|
|
48
48
|
'close label': 'Close',
|
|
49
49
|
'minify label': 'Minify',
|
|
50
|
-
'settings overscroll label': 'Plank
|
|
51
|
-
'select overscroll placeholder': 'Select plank
|
|
50
|
+
'settings overscroll label': 'Plank scrolling',
|
|
51
|
+
'select overscroll placeholder': 'Select plank scrolling behavior',
|
|
52
52
|
'settings overscroll centering label': 'Centering',
|
|
53
53
|
'settings overscroll none label': 'None',
|
|
54
|
-
'settings enable
|
|
54
|
+
'settings enable statusbar label': 'Show status bar',
|
|
55
55
|
'close current label': 'Close current plank',
|
|
56
56
|
'close others label': 'Close other planks',
|
|
57
57
|
'close all label': 'Close all planks',
|
|
58
|
+
'companion plank heading fallback label': 'Related',
|
|
58
59
|
},
|
|
59
60
|
},
|
|
60
61
|
},
|