@dxos/plugin-deck 0.6.13 → 0.6.14-main.1366248
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/{chunk-YVHGFQQR.mjs → chunk-GVOGPULO.mjs} +1 -1
- package/dist/lib/browser/chunk-GVOGPULO.mjs.map +7 -0
- package/dist/lib/browser/chunk-NIRHDTX4.mjs +17 -0
- package/dist/lib/browser/chunk-NIRHDTX4.mjs.map +7 -0
- package/dist/lib/browser/index.mjs +334 -314
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/browser/meta.mjs +1 -1
- package/dist/lib/browser/types.mjs +11 -0
- package/dist/lib/browser/types.mjs.map +7 -0
- package/dist/types/src/DeckPlugin.d.ts.map +1 -1
- package/dist/types/src/components/DeckLayout/ActiveNode.d.ts +1 -3
- package/dist/types/src/components/DeckLayout/ActiveNode.d.ts.map +1 -1
- package/dist/types/src/components/DeckLayout/ComplementarySidebar.d.ts +4 -4
- package/dist/types/src/components/DeckLayout/ComplementarySidebar.d.ts.map +1 -1
- package/dist/types/src/components/DeckLayout/DeckLayout.d.ts +5 -5
- package/dist/types/src/components/DeckLayout/DeckLayout.d.ts.map +1 -1
- package/dist/types/src/components/DeckLayout/NodePlankHeading.d.ts +9 -7
- package/dist/types/src/components/DeckLayout/NodePlankHeading.d.ts.map +1 -1
- package/dist/types/src/components/DeckLayout/Plank.d.ts +2 -2
- package/dist/types/src/components/DeckLayout/Plank.d.ts.map +1 -1
- package/dist/types/src/components/DeckLayout/PlankError.d.ts +1 -2
- package/dist/types/src/components/DeckLayout/PlankError.d.ts.map +1 -1
- package/dist/types/src/components/DeckLayout/Sidebar.d.ts +2 -3
- package/dist/types/src/components/DeckLayout/Sidebar.d.ts.map +1 -1
- package/dist/types/src/components/DeckLayout/StatusBar.d.ts +3 -1
- 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/LayoutSettings.d.ts.map +1 -1
- package/dist/types/src/hooks/useNode.d.ts.map +1 -1
- package/dist/types/src/layout.d.ts.map +1 -1
- package/dist/types/src/meta.d.ts.map +1 -1
- package/dist/types/src/translations.d.ts +1 -3
- package/dist/types/src/translations.d.ts.map +1 -1
- package/dist/types/src/types.d.ts +14 -2
- package/dist/types/src/types.d.ts.map +1 -1
- package/dist/types/src/util/overscroll.d.ts +1 -1
- package/dist/types/src/util/overscroll.d.ts.map +1 -1
- package/package.json +42 -33
- package/src/DeckPlugin.tsx +104 -80
- package/src/components/DeckLayout/ActiveNode.tsx +4 -1
- package/src/components/DeckLayout/ComplementarySidebar.tsx +59 -28
- package/src/components/DeckLayout/DeckLayout.tsx +67 -98
- package/src/components/DeckLayout/NodePlankHeading.tsx +130 -127
- package/src/components/DeckLayout/Plank.tsx +48 -32
- package/src/components/DeckLayout/PlankError.tsx +1 -9
- package/src/components/DeckLayout/Sidebar.tsx +7 -8
- package/src/components/DeckLayout/StatusBar.tsx +12 -3
- package/src/components/DeckLayout/Toast.tsx +3 -3
- package/src/components/LayoutSettings.tsx +17 -20
- package/src/hooks/useNode.ts +5 -1
- package/src/layout.ts +1 -0
- package/src/meta.ts +3 -1
- package/src/translations.ts +1 -3
- package/src/types.ts +16 -1
- package/src/util/overscroll.ts +5 -5
- package/dist/lib/browser/chunk-YVHGFQQR.mjs.map +0 -7
|
@@ -3,24 +3,17 @@
|
|
|
3
3
|
//
|
|
4
4
|
|
|
5
5
|
import { Sidebar as MenuIcon } from '@phosphor-icons/react';
|
|
6
|
-
import
|
|
6
|
+
import { untracked } from '@preact/signals-core';
|
|
7
|
+
import React, { useCallback, useEffect, useMemo, useRef, type UIEvent } from 'react';
|
|
7
8
|
|
|
8
|
-
import {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
type LayoutEntry,
|
|
12
|
-
type LayoutParts,
|
|
13
|
-
Surface,
|
|
14
|
-
type Toast as ToastSchema,
|
|
15
|
-
firstIdInPart,
|
|
16
|
-
usePlugin,
|
|
17
|
-
} from '@dxos/app-framework';
|
|
18
|
-
import { Button, Dialog, Main, Popover, useTranslation } from '@dxos/react-ui';
|
|
9
|
+
import { type LayoutParts, Surface, type Toast as ToastSchema, firstIdInPart, usePlugin } from '@dxos/app-framework';
|
|
10
|
+
import { type AttentionPluginProvides } from '@dxos/plugin-attention';
|
|
11
|
+
import { Button, Dialog, Main, Popover, useOnTransition, useTranslation } from '@dxos/react-ui';
|
|
19
12
|
import { Deck } from '@dxos/react-ui-deck';
|
|
20
|
-
import { getSize } from '@dxos/react-ui-theme';
|
|
13
|
+
import { getSize, mainPaddingTransitions } from '@dxos/react-ui-theme';
|
|
21
14
|
|
|
22
15
|
import { ActiveNode } from './ActiveNode';
|
|
23
|
-
import { ComplementarySidebar } from './ComplementarySidebar';
|
|
16
|
+
import { ComplementarySidebar, type ComplementarySidebarProps } from './ComplementarySidebar';
|
|
24
17
|
import { ContentEmpty } from './ContentEmpty';
|
|
25
18
|
import { Fullscreen } from './Fullscreen';
|
|
26
19
|
import { Plank } from './Plank';
|
|
@@ -35,25 +28,24 @@ import { useLayout } from '../LayoutContext';
|
|
|
35
28
|
|
|
36
29
|
export type DeckLayoutProps = {
|
|
37
30
|
layoutParts: LayoutParts;
|
|
38
|
-
attention: Attention;
|
|
39
31
|
toasts: ToastSchema[];
|
|
40
32
|
flatDeck?: boolean;
|
|
41
33
|
overscroll: Overscroll;
|
|
42
|
-
|
|
34
|
+
showHints: boolean;
|
|
43
35
|
slots?: {
|
|
44
36
|
wallpaper?: { classNames?: string };
|
|
45
37
|
};
|
|
46
38
|
onDismissToast: (id: string) => void;
|
|
47
|
-
}
|
|
39
|
+
} & Pick<ComplementarySidebarProps, 'panels'>;
|
|
48
40
|
|
|
49
41
|
export const DeckLayout = ({
|
|
50
42
|
layoutParts,
|
|
51
|
-
attention,
|
|
52
43
|
toasts,
|
|
53
44
|
flatDeck,
|
|
54
45
|
overscroll,
|
|
55
|
-
|
|
46
|
+
showHints,
|
|
56
47
|
slots,
|
|
48
|
+
panels,
|
|
57
49
|
onDismissToast,
|
|
58
50
|
}: DeckLayoutProps) => {
|
|
59
51
|
const context = useLayout();
|
|
@@ -70,36 +62,44 @@ export const DeckLayout = ({
|
|
|
70
62
|
} = context;
|
|
71
63
|
const { t } = useTranslation(DECK_PLUGIN);
|
|
72
64
|
const { plankSizing } = useDeckContext();
|
|
65
|
+
// NOTE: Not `useAttended` so that the layout component is not re-rendered when the attended list changes.
|
|
66
|
+
const attentionPlugin = usePlugin<AttentionPluginProvides>('dxos.org/plugin/attention');
|
|
73
67
|
const searchPlugin = usePlugin('dxos.org/plugin/search');
|
|
74
68
|
const fullScreenSlug = useMemo(() => firstIdInPart(layoutParts, 'fullScreen'), [layoutParts]);
|
|
75
69
|
|
|
76
|
-
const
|
|
77
|
-
const deckRef = useRef<HTMLDivElement
|
|
78
|
-
|
|
70
|
+
const scrollLeftRef = useRef<number | null>();
|
|
71
|
+
const deckRef = useRef<HTMLDivElement>(null);
|
|
72
|
+
|
|
73
|
+
// Ensure the first plank is attended when the deck is first rendered.
|
|
74
|
+
useEffect(() => {
|
|
75
|
+
const attended = untracked(() => attentionPlugin?.provides.attention.attended ?? []);
|
|
76
|
+
const firstId = layoutMode === 'solo' ? firstIdInPart(layoutParts, 'solo') : firstIdInPart(layoutParts, 'main');
|
|
77
|
+
if (attended.length === 0 && firstId) {
|
|
78
|
+
// TODO(wittjosiah): Focusing the type button is a workaround.
|
|
79
|
+
// If the plank is directly focused on first load the focus ring appears.
|
|
80
|
+
document.querySelector<HTMLElement>(`article[data-attendable-id="${firstId}"] button`)?.focus();
|
|
81
|
+
}
|
|
82
|
+
}, []);
|
|
79
83
|
|
|
80
84
|
/**
|
|
81
85
|
* Clear scroll restoration state if the window is resized
|
|
82
86
|
*/
|
|
83
87
|
const handleResize = useCallback(() => {
|
|
84
|
-
|
|
88
|
+
scrollLeftRef.current = null;
|
|
85
89
|
}, []);
|
|
90
|
+
|
|
86
91
|
useEffect(() => {
|
|
87
92
|
window.addEventListener('resize', handleResize);
|
|
88
93
|
return () => window.removeEventListener('resize', handleResize);
|
|
89
94
|
}, [handleResize]);
|
|
90
95
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
useLayoutEffect(() => {
|
|
95
|
-
if (layoutMode !== 'deck') {
|
|
96
|
-
restoreScrollRef.current = true;
|
|
97
|
-
} else if (restoreScrollRef.current && deckRef.current && scrollLeft) {
|
|
98
|
-
// console.log('[restoring scrollLeft]', scrollLeft);
|
|
99
|
-
deckRef.current.scrollLeft = scrollLeft;
|
|
100
|
-
restoreScrollRef.current = false;
|
|
96
|
+
const restoreScroll = useCallback(() => {
|
|
97
|
+
if (deckRef.current && scrollLeftRef.current != null) {
|
|
98
|
+
deckRef.current.scrollLeft = scrollLeftRef.current;
|
|
101
99
|
}
|
|
102
|
-
}, [
|
|
100
|
+
}, []);
|
|
101
|
+
|
|
102
|
+
useOnTransition(layoutMode, (mode) => mode !== 'deck', 'deck', restoreScroll);
|
|
103
103
|
|
|
104
104
|
/**
|
|
105
105
|
* Save scroll position as the user scrolls
|
|
@@ -107,48 +107,20 @@ export const DeckLayout = ({
|
|
|
107
107
|
const handleScroll = useCallback(
|
|
108
108
|
(event: UIEvent) => {
|
|
109
109
|
if (layoutMode === 'deck' && event.currentTarget === event.target) {
|
|
110
|
-
|
|
111
|
-
setScrollLeft((event.target as HTMLDivElement).scrollLeft);
|
|
110
|
+
scrollLeftRef.current = (event.target as HTMLDivElement).scrollLeft;
|
|
112
111
|
}
|
|
113
112
|
},
|
|
114
113
|
[layoutMode],
|
|
115
114
|
);
|
|
116
115
|
|
|
117
|
-
const
|
|
118
|
-
const entry = layoutParts.complementary?.at(0);
|
|
119
|
-
if (entry) {
|
|
120
|
-
return entry.path ? `${entry.id}${SLUG_PATH_SEPARATOR}${entry.path}` : entry.id;
|
|
121
|
-
}
|
|
122
|
-
}, [layoutParts]);
|
|
123
|
-
|
|
124
|
-
const firstAttendedId = useMemo(() => Array.from(attention.attended ?? [])[0], [attention.attended]);
|
|
116
|
+
const isEmpty = layoutParts.main?.length === 0 && layoutParts.solo?.length === 0;
|
|
125
117
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
if (layoutMode === 'deck' && firstAttendedId) {
|
|
130
|
-
// setTimeout(() => {
|
|
131
|
-
// const el = deckRef.current?.querySelector(`article[data-attendable-id="${firstAttendedId}"]`);
|
|
132
|
-
// el?.scrollIntoView({ behavior: 'smooth', inline: 'center' });
|
|
133
|
-
// }, 0);
|
|
134
|
-
}
|
|
135
|
-
}, [layoutMode, firstAttendedId]);
|
|
136
|
-
|
|
137
|
-
// TODO(burdon): Needs cleaning up.
|
|
138
|
-
const parts: LayoutEntry[] = useMemo(() => {
|
|
139
|
-
const parts = [...(layoutParts.main ?? [])];
|
|
140
|
-
for (const part of layoutParts.solo ?? []) {
|
|
141
|
-
if (!parts.find((entry) => entry.id === part.id)) {
|
|
142
|
-
parts.push(part);
|
|
143
|
-
}
|
|
118
|
+
const padding = useMemo(() => {
|
|
119
|
+
if (layoutMode === 'deck' && overscroll === 'centering') {
|
|
120
|
+
return calculateOverscroll(layoutParts.main, plankSizing, sidebarOpen, complementarySidebarOpen);
|
|
144
121
|
}
|
|
145
|
-
return
|
|
146
|
-
}, [layoutParts.main,
|
|
147
|
-
|
|
148
|
-
const padding =
|
|
149
|
-
layoutMode === 'deck' && overscroll === 'centering'
|
|
150
|
-
? calculateOverscroll(layoutParts.main, plankSizing, sidebarOpen, complementarySidebarOpen)
|
|
151
|
-
: {};
|
|
122
|
+
return {};
|
|
123
|
+
}, [layoutMode, overscroll, layoutParts.main, plankSizing, sidebarOpen, complementarySidebarOpen]);
|
|
152
124
|
|
|
153
125
|
if (layoutMode === 'fullscreen') {
|
|
154
126
|
return <Fullscreen id={fullScreenSlug} />;
|
|
@@ -167,31 +139,22 @@ export const DeckLayout = ({
|
|
|
167
139
|
}
|
|
168
140
|
}}
|
|
169
141
|
>
|
|
170
|
-
|
|
171
|
-
<ActiveNode id={firstAttendedId} />
|
|
142
|
+
<ActiveNode />
|
|
172
143
|
|
|
173
144
|
<Main.Root
|
|
174
145
|
navigationSidebarOpen={context.sidebarOpen}
|
|
175
146
|
onNavigationSidebarOpenChange={(next) => (context.sidebarOpen = next)}
|
|
176
|
-
{
|
|
177
|
-
|
|
178
|
-
onComplementarySidebarOpenChange: (next) => (context.complementarySidebarOpen = next),
|
|
179
|
-
})}
|
|
147
|
+
complementarySidebarOpen={context.complementarySidebarOpen}
|
|
148
|
+
onComplementarySidebarOpenChange={(next) => (context.complementarySidebarOpen = next)}
|
|
180
149
|
>
|
|
181
150
|
{/* Notch */}
|
|
182
151
|
<Main.Notch classNames='z-[21]'>
|
|
183
152
|
<Surface role='notch-start' />
|
|
184
|
-
<Button
|
|
185
|
-
// disabled={!sidebarAvailable}
|
|
186
|
-
onClick={() => (context.sidebarOpen = !context.sidebarOpen)}
|
|
187
|
-
variant='ghost'
|
|
188
|
-
classNames='p-1'
|
|
189
|
-
>
|
|
153
|
+
<Button onClick={() => (context.sidebarOpen = !context.sidebarOpen)} variant='ghost' classNames='p-1'>
|
|
190
154
|
<span className='sr-only'>{t('open navigation sidebar label')}</span>
|
|
191
155
|
<MenuIcon weight='light' className={getSize(5)} />
|
|
192
156
|
</Button>
|
|
193
157
|
<Button
|
|
194
|
-
// disabled={!complementaryAvailable}
|
|
195
158
|
onClick={() => (context.complementarySidebarOpen = !context.complementarySidebarOpen)}
|
|
196
159
|
variant='ghost'
|
|
197
160
|
classNames='p-1'
|
|
@@ -203,38 +166,51 @@ export const DeckLayout = ({
|
|
|
203
166
|
</Main.Notch>
|
|
204
167
|
|
|
205
168
|
{/* Left sidebar. */}
|
|
206
|
-
<Sidebar
|
|
169
|
+
<Sidebar layoutParts={layoutParts} />
|
|
207
170
|
|
|
208
171
|
{/* Right sidebar. */}
|
|
209
|
-
<ComplementarySidebar
|
|
172
|
+
<ComplementarySidebar panels={panels} current={layoutParts.complementary?.[0].id} flatDeck={flatDeck} />
|
|
210
173
|
|
|
211
174
|
{/* Dialog overlay to dismiss dialogs. */}
|
|
212
175
|
<Main.Overlay />
|
|
213
176
|
|
|
214
177
|
{/* No content. */}
|
|
215
|
-
{
|
|
178
|
+
{isEmpty && (
|
|
216
179
|
<Main.Content handlesFocus>
|
|
217
180
|
<ContentEmpty />
|
|
218
181
|
</Main.Content>
|
|
219
182
|
)}
|
|
220
183
|
|
|
221
184
|
{/* Solo/deck mode. */}
|
|
222
|
-
{
|
|
185
|
+
{!isEmpty && (
|
|
223
186
|
<Main.Content bounce classNames='grid block-end-[--statusbar-size]' handlesFocus>
|
|
224
187
|
<div role='none' className='relative'>
|
|
225
188
|
<Deck.Root
|
|
226
189
|
style={padding}
|
|
227
|
-
classNames={[
|
|
190
|
+
classNames={[
|
|
191
|
+
!flatDeck && 'bg-deck',
|
|
192
|
+
mainPaddingTransitions,
|
|
193
|
+
'absolute inset-0',
|
|
194
|
+
slots?.wallpaper?.classNames,
|
|
195
|
+
]}
|
|
228
196
|
solo={layoutMode === 'solo'}
|
|
229
197
|
onScroll={handleScroll}
|
|
230
198
|
ref={deckRef}
|
|
231
199
|
>
|
|
232
|
-
|
|
200
|
+
<Plank
|
|
201
|
+
entry={layoutParts.solo?.[0]}
|
|
202
|
+
layoutParts={layoutParts}
|
|
203
|
+
part='solo'
|
|
204
|
+
layoutMode={layoutMode}
|
|
205
|
+
flatDeck={flatDeck}
|
|
206
|
+
searchEnabled={!!searchPlugin}
|
|
207
|
+
/>
|
|
208
|
+
{layoutParts.main?.map((layoutEntry) => (
|
|
233
209
|
<Plank
|
|
234
210
|
key={layoutEntry.id}
|
|
235
211
|
entry={layoutEntry}
|
|
236
212
|
layoutParts={layoutParts}
|
|
237
|
-
part=
|
|
213
|
+
part='main'
|
|
238
214
|
layoutMode={layoutMode}
|
|
239
215
|
flatDeck={flatDeck}
|
|
240
216
|
searchEnabled={!!searchPlugin}
|
|
@@ -245,15 +221,8 @@ export const DeckLayout = ({
|
|
|
245
221
|
</Main.Content>
|
|
246
222
|
)}
|
|
247
223
|
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
{/* Help hints. */}
|
|
251
|
-
{/* TODO(burdon): Need to make room for this in status bar. */}
|
|
252
|
-
{showHintsFooter && (
|
|
253
|
-
<div className='fixed bottom-0 left-0 right-0 h-[32px] z-[1] flex justify-center'>
|
|
254
|
-
<Surface role='hints' limit={1} />
|
|
255
|
-
</div>
|
|
256
|
-
)}
|
|
224
|
+
{/* Footer status. */}
|
|
225
|
+
<StatusBar showHints={showHints} />
|
|
257
226
|
|
|
258
227
|
{/* Global popovers. */}
|
|
259
228
|
<Popover.Portal>
|
|
@@ -2,157 +2,160 @@
|
|
|
2
2
|
// Copyright 2024 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import {
|
|
6
|
-
import React, { Fragment, useEffect } from 'react';
|
|
5
|
+
import React, { Fragment, memo, useEffect, useMemo } from 'react';
|
|
7
6
|
|
|
8
7
|
import {
|
|
9
8
|
LayoutAction,
|
|
10
9
|
NavigationAction,
|
|
11
|
-
SLUG_COLLECTION_INDICATOR,
|
|
12
10
|
SLUG_PATH_SEPARATOR,
|
|
13
11
|
Surface,
|
|
14
12
|
useIntentDispatcher,
|
|
15
|
-
|
|
16
|
-
partLength,
|
|
17
|
-
type LayoutParts,
|
|
18
|
-
type LayoutPart,
|
|
19
|
-
type LayoutEntry,
|
|
13
|
+
type LayoutCoordinate,
|
|
20
14
|
} from '@dxos/app-framework';
|
|
21
15
|
import { type Node, useGraph } from '@dxos/plugin-graph';
|
|
22
|
-
import { Popover, toLocalizedString, useMediaQuery, useTranslation } from '@dxos/react-ui';
|
|
23
|
-
import { PlankHeading,
|
|
16
|
+
import { Icon, Popover, toLocalizedString, useMediaQuery, useTranslation } from '@dxos/react-ui';
|
|
17
|
+
import { PlankHeading, type PlankHeadingAction } from '@dxos/react-ui-deck';
|
|
24
18
|
import { TextTooltip } from '@dxos/react-ui-text-tooltip';
|
|
25
19
|
|
|
26
20
|
import { DECK_PLUGIN } from '../../meta';
|
|
27
21
|
|
|
28
|
-
export
|
|
29
|
-
|
|
30
|
-
id,
|
|
31
|
-
layoutParts,
|
|
32
|
-
layoutPart,
|
|
33
|
-
// TODO(wittjosiah): Unused?
|
|
34
|
-
layoutEntry,
|
|
35
|
-
popoverAnchorId,
|
|
36
|
-
pending,
|
|
37
|
-
flatDeck,
|
|
38
|
-
}: {
|
|
22
|
+
export type NodePlankHeadingProps = {
|
|
23
|
+
coordinate: LayoutCoordinate;
|
|
39
24
|
node?: Node;
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
layoutPart?: LayoutPart;
|
|
43
|
-
layoutEntry?: LayoutEntry;
|
|
25
|
+
canIncrementStart?: boolean;
|
|
26
|
+
canIncrementEnd?: boolean;
|
|
44
27
|
popoverAnchorId?: string;
|
|
45
28
|
pending?: boolean;
|
|
46
29
|
flatDeck?: boolean;
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
const { graph } = useGraph();
|
|
50
|
-
const Icon = node?.properties?.icon ?? Placeholder;
|
|
51
|
-
const label = pending
|
|
52
|
-
? t('pending heading')
|
|
53
|
-
: toLocalizedString(node?.properties?.label ?? ['plank heading fallback label', { ns: DECK_PLUGIN }], t);
|
|
54
|
-
const dispatch = useIntentDispatcher();
|
|
55
|
-
const ActionRoot = node && popoverAnchorId === `dxos.org/ui/${DECK_PLUGIN}/${node.id}` ? Popover.Anchor : Fragment;
|
|
56
|
-
const [isNotMobile] = useMediaQuery('md');
|
|
57
|
-
|
|
58
|
-
useEffect(() => {
|
|
59
|
-
const frame = requestAnimationFrame(() => {
|
|
60
|
-
// Load actions for the node.
|
|
61
|
-
node && graph.actions(node);
|
|
62
|
-
});
|
|
30
|
+
actions?: PlankHeadingAction[];
|
|
31
|
+
};
|
|
63
32
|
|
|
64
|
-
|
|
65
|
-
|
|
33
|
+
export const NodePlankHeading = memo(
|
|
34
|
+
({
|
|
35
|
+
coordinate,
|
|
36
|
+
node,
|
|
37
|
+
canIncrementStart,
|
|
38
|
+
canIncrementEnd,
|
|
39
|
+
popoverAnchorId,
|
|
40
|
+
pending,
|
|
41
|
+
flatDeck,
|
|
42
|
+
actions = [],
|
|
43
|
+
}: NodePlankHeadingProps) => {
|
|
44
|
+
const { t } = useTranslation(DECK_PLUGIN);
|
|
45
|
+
const { graph } = useGraph();
|
|
46
|
+
const icon = node?.properties?.icon ?? 'ph--placeholder--regular';
|
|
47
|
+
const label = pending
|
|
48
|
+
? t('pending heading')
|
|
49
|
+
: toLocalizedString(node?.properties?.label ?? ['plank heading fallback label', { ns: DECK_PLUGIN }], t);
|
|
50
|
+
const dispatch = useIntentDispatcher();
|
|
51
|
+
const ActionRoot = node && popoverAnchorId === `dxos.org/ui/${DECK_PLUGIN}/${node.id}` ? Popover.Anchor : Fragment;
|
|
52
|
+
const [isNotMobile] = useMediaQuery('md');
|
|
66
53
|
|
|
67
|
-
|
|
68
|
-
|
|
54
|
+
useEffect(() => {
|
|
55
|
+
const frame = requestAnimationFrame(() => {
|
|
56
|
+
// Load actions for the node.
|
|
57
|
+
node && graph.actions(node);
|
|
58
|
+
});
|
|
69
59
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
const length = partLength(layoutParts, layoutPart);
|
|
60
|
+
return () => cancelAnimationFrame(frame);
|
|
61
|
+
}, [node]);
|
|
73
62
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
63
|
+
const layoutPart = coordinate.part;
|
|
64
|
+
// NOTE(Zan): Node ids may now contain a path like `${space}:${id}~comments`
|
|
65
|
+
const attendableId = coordinate.entryId.split(SLUG_PATH_SEPARATOR).at(0);
|
|
66
|
+
const capabilities = useMemo(
|
|
67
|
+
() => ({
|
|
68
|
+
solo: (layoutPart === 'solo' || layoutPart === 'main') && isNotMobile,
|
|
69
|
+
incrementStart: canIncrementStart,
|
|
70
|
+
incrementEnd: canIncrementEnd,
|
|
71
|
+
}),
|
|
72
|
+
[isNotMobile, layoutPart, canIncrementStart, canIncrementEnd],
|
|
73
|
+
);
|
|
77
74
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
75
|
+
return (
|
|
76
|
+
<PlankHeading.Root
|
|
77
|
+
{...((layoutPart !== 'main' || !flatDeck) && { classNames: 'pie-1 border-b border-separator' })}
|
|
78
|
+
>
|
|
79
|
+
<ActionRoot>
|
|
80
|
+
{node ? (
|
|
81
|
+
<PlankHeading.ActionsMenu
|
|
82
|
+
icon={icon}
|
|
83
|
+
related={layoutPart === 'complementary'}
|
|
84
|
+
attendableId={attendableId}
|
|
85
|
+
triggerLabel={t('actions menu label')}
|
|
86
|
+
actions={[actions, graph.actions(node)].filter((a) => a.length > 0)}
|
|
87
|
+
onAction={(action) =>
|
|
88
|
+
typeof action.data === 'function' && action.data?.({ node: action as Node, caller: DECK_PLUGIN })
|
|
89
|
+
}
|
|
90
|
+
>
|
|
91
|
+
<Surface role='menu-footer' data={{ object: node.data }} />
|
|
92
|
+
</PlankHeading.ActionsMenu>
|
|
93
|
+
) : (
|
|
94
|
+
<PlankHeading.Button>
|
|
95
|
+
<span className='sr-only'>{label}</span>
|
|
96
|
+
<Icon icon={icon} size={5} />
|
|
97
|
+
</PlankHeading.Button>
|
|
98
|
+
)}
|
|
99
|
+
</ActionRoot>
|
|
100
|
+
<TextTooltip text={label} onlyWhenTruncating>
|
|
101
|
+
<PlankHeading.Label
|
|
84
102
|
attendableId={attendableId}
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
onAction={(action) =>
|
|
88
|
-
typeof action.data === 'function' && action.data?.({ node: action as Node, caller: DECK_PLUGIN })
|
|
89
|
-
}
|
|
103
|
+
related={layoutPart === 'complementary'}
|
|
104
|
+
{...(pending && { classNames: 'text-description' })}
|
|
90
105
|
>
|
|
91
|
-
|
|
92
|
-
</PlankHeading.
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
</PlankHeading.Button>
|
|
106
|
+
{label}
|
|
107
|
+
</PlankHeading.Label>
|
|
108
|
+
</TextTooltip>
|
|
109
|
+
{node && layoutPart !== 'complementary' && (
|
|
110
|
+
// TODO(Zan): What are we doing with layout coordinate here?
|
|
111
|
+
<Surface role='navbar-end' direction='inline-reverse' data={{ object: node.data }} />
|
|
98
112
|
)}
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
{
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
<Surface role='navbar-end' direction='inline-reverse' data={{ object: node.data }} />
|
|
108
|
-
)}
|
|
109
|
-
{/* NOTE(thure): Pinning & unpinning are temporarily disabled */}
|
|
110
|
-
<PlankHeading.Controls
|
|
111
|
-
capabilities={{
|
|
112
|
-
solo: (layoutPart === 'solo' || layoutPart === 'main') && isNotMobile,
|
|
113
|
-
incrementStart: canIncrementStart,
|
|
114
|
-
incrementEnd: canIncrementEnd,
|
|
115
|
-
}}
|
|
116
|
-
isSolo={layoutPart === 'solo'}
|
|
117
|
-
onClick={(eventType) => {
|
|
118
|
-
if (!layoutPart) {
|
|
119
|
-
return;
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
if (eventType === 'solo') {
|
|
123
|
-
return dispatch([
|
|
124
|
-
{
|
|
125
|
-
action: NavigationAction.ADJUST,
|
|
126
|
-
data: { type: eventType, layoutCoordinate: { part: 'main', entryId: id } },
|
|
127
|
-
},
|
|
128
|
-
]);
|
|
129
|
-
}
|
|
113
|
+
{/* NOTE(thure): Pinning & unpinning are temporarily disabled */}
|
|
114
|
+
<PlankHeading.Controls
|
|
115
|
+
capabilities={capabilities}
|
|
116
|
+
isSolo={layoutPart === 'solo'}
|
|
117
|
+
onClick={(eventType) => {
|
|
118
|
+
if (!layoutPart) {
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
130
121
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
122
|
+
// TODO(Zan): Update this to use the new layout actions.
|
|
123
|
+
if (eventType === 'solo') {
|
|
124
|
+
return dispatch([
|
|
125
|
+
{
|
|
126
|
+
action: NavigationAction.ADJUST,
|
|
127
|
+
data: { type: eventType, layoutCoordinate: { part: 'main', entryId: coordinate.entryId } },
|
|
128
|
+
},
|
|
129
|
+
]);
|
|
130
|
+
} else if (eventType === 'close') {
|
|
131
|
+
if (layoutPart === 'complementary') {
|
|
132
|
+
return dispatch({
|
|
133
|
+
action: LayoutAction.SET_LAYOUT,
|
|
134
|
+
data: {
|
|
135
|
+
element: 'complementary',
|
|
136
|
+
state: false,
|
|
137
|
+
},
|
|
138
|
+
});
|
|
139
|
+
} else {
|
|
140
|
+
return dispatch({
|
|
141
|
+
action: NavigationAction.CLOSE,
|
|
142
|
+
data: {
|
|
143
|
+
activeParts: {
|
|
144
|
+
[layoutPart]: [coordinate.entryId],
|
|
149
145
|
},
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
}
|
|
146
|
+
},
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
} else {
|
|
150
|
+
return dispatch({
|
|
151
|
+
action: NavigationAction.ADJUST,
|
|
152
|
+
data: { type: eventType, layoutCoordinate: coordinate },
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
}}
|
|
156
|
+
close={layoutPart === 'complementary' ? 'minify-end' : true}
|
|
157
|
+
/>
|
|
158
|
+
</PlankHeading.Root>
|
|
159
|
+
);
|
|
160
|
+
},
|
|
161
|
+
);
|