@dxos/plugin-deck 0.6.13-staging.1e988a3 → 0.6.14-main.7bd9c89
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/index.mjs +245 -189
- package/dist/lib/browser/index.mjs.map +3 -3
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/browser/meta.mjs +1 -1
- package/dist/types/src/DeckPlugin.d.ts.map +1 -1
- package/dist/types/src/components/DeckLayout/ComplementarySidebar.d.ts +2 -2
- package/dist/types/src/components/DeckLayout/ComplementarySidebar.d.ts.map +1 -1
- package/dist/types/src/components/DeckLayout/DeckLayout.d.ts +3 -4
- package/dist/types/src/components/DeckLayout/DeckLayout.d.ts.map +1 -1
- package/dist/types/src/components/DeckLayout/NodePlankHeading.d.ts +4 -3
- package/dist/types/src/components/DeckLayout/NodePlankHeading.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 +5 -3
- package/dist/types/src/translations.d.ts.map +1 -1
- package/dist/types/src/types.d.ts +1 -1
- 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 +30 -29
- package/src/DeckPlugin.tsx +93 -71
- package/src/components/DeckLayout/ComplementarySidebar.tsx +69 -27
- package/src/components/DeckLayout/DeckLayout.tsx +63 -84
- package/src/components/DeckLayout/NodePlankHeading.tsx +15 -15
- package/src/components/DeckLayout/Plank.tsx +3 -3
- package/src/components/DeckLayout/Sidebar.tsx +6 -5
- package/src/components/DeckLayout/StatusBar.tsx +12 -3
- package/src/components/DeckLayout/Toast.tsx +3 -3
- package/src/components/LayoutSettings.tsx +5 -8
- package/src/hooks/useNode.ts +5 -1
- package/src/layout.ts +1 -0
- package/src/meta.ts +3 -1
- package/src/translations.ts +7 -3
- package/src/types.ts +1 -1
- package/src/util/overscroll.ts +5 -5
- package/dist/lib/browser/chunk-YVHGFQQR.mjs.map +0 -7
|
@@ -3,21 +3,13 @@
|
|
|
3
3
|
//
|
|
4
4
|
|
|
5
5
|
import { Sidebar as MenuIcon } from '@phosphor-icons/react';
|
|
6
|
-
import React, { useCallback, useEffect, useMemo, useRef,
|
|
6
|
+
import React, { useCallback, useEffect, useMemo, useRef, type UIEvent } from 'react';
|
|
7
7
|
|
|
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';
|
|
8
|
+
import { type LayoutParts, Surface, type Toast as ToastSchema, firstIdInPart, usePlugin } from '@dxos/app-framework';
|
|
9
|
+
import { Button, Dialog, Main, Popover, useOnTransition, useTranslation } from '@dxos/react-ui';
|
|
10
|
+
import { useAttended } from '@dxos/react-ui-attention';
|
|
19
11
|
import { Deck } from '@dxos/react-ui-deck';
|
|
20
|
-
import { getSize } from '@dxos/react-ui-theme';
|
|
12
|
+
import { getSize, mainPaddingTransitions } from '@dxos/react-ui-theme';
|
|
21
13
|
|
|
22
14
|
import { ActiveNode } from './ActiveNode';
|
|
23
15
|
import { ComplementarySidebar } from './ComplementarySidebar';
|
|
@@ -35,11 +27,10 @@ import { useLayout } from '../LayoutContext';
|
|
|
35
27
|
|
|
36
28
|
export type DeckLayoutProps = {
|
|
37
29
|
layoutParts: LayoutParts;
|
|
38
|
-
attention: Attention;
|
|
39
30
|
toasts: ToastSchema[];
|
|
40
31
|
flatDeck?: boolean;
|
|
41
32
|
overscroll: Overscroll;
|
|
42
|
-
|
|
33
|
+
showHints: boolean;
|
|
43
34
|
slots?: {
|
|
44
35
|
wallpaper?: { classNames?: string };
|
|
45
36
|
};
|
|
@@ -48,11 +39,10 @@ export type DeckLayoutProps = {
|
|
|
48
39
|
|
|
49
40
|
export const DeckLayout = ({
|
|
50
41
|
layoutParts,
|
|
51
|
-
attention,
|
|
52
42
|
toasts,
|
|
53
43
|
flatDeck,
|
|
54
44
|
overscroll,
|
|
55
|
-
|
|
45
|
+
showHints,
|
|
56
46
|
slots,
|
|
57
47
|
onDismissToast,
|
|
58
48
|
}: DeckLayoutProps) => {
|
|
@@ -70,36 +60,42 @@ export const DeckLayout = ({
|
|
|
70
60
|
} = context;
|
|
71
61
|
const { t } = useTranslation(DECK_PLUGIN);
|
|
72
62
|
const { plankSizing } = useDeckContext();
|
|
63
|
+
const attended = useAttended();
|
|
73
64
|
const searchPlugin = usePlugin('dxos.org/plugin/search');
|
|
74
65
|
const fullScreenSlug = useMemo(() => firstIdInPart(layoutParts, 'fullScreen'), [layoutParts]);
|
|
75
66
|
|
|
76
|
-
const
|
|
77
|
-
const deckRef = useRef<HTMLDivElement
|
|
78
|
-
|
|
67
|
+
const scrollLeftRef = useRef<number | null>();
|
|
68
|
+
const deckRef = useRef<HTMLDivElement>(null);
|
|
69
|
+
|
|
70
|
+
// Ensure the first plank is attended when the deck is first rendered.
|
|
71
|
+
useEffect(() => {
|
|
72
|
+
const firstId = layoutMode === 'solo' ? firstIdInPart(layoutParts, 'solo') : firstIdInPart(layoutParts, 'main');
|
|
73
|
+
if (attended.length === 0 && firstId) {
|
|
74
|
+
// TODO(wittjosiah): Focusing the type button is a workaround.
|
|
75
|
+
// If the plank is directly focused on first load the focus ring appears.
|
|
76
|
+
document.querySelector<HTMLElement>(`article[data-attendable-id="${firstId}"] button`)?.focus();
|
|
77
|
+
}
|
|
78
|
+
}, []);
|
|
79
79
|
|
|
80
80
|
/**
|
|
81
81
|
* Clear scroll restoration state if the window is resized
|
|
82
82
|
*/
|
|
83
83
|
const handleResize = useCallback(() => {
|
|
84
|
-
|
|
84
|
+
scrollLeftRef.current = null;
|
|
85
85
|
}, []);
|
|
86
|
+
|
|
86
87
|
useEffect(() => {
|
|
87
88
|
window.addEventListener('resize', handleResize);
|
|
88
89
|
return () => window.removeEventListener('resize', handleResize);
|
|
89
90
|
}, [handleResize]);
|
|
90
91
|
|
|
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;
|
|
92
|
+
const restoreScroll = useCallback(() => {
|
|
93
|
+
if (deckRef.current && scrollLeftRef.current != null) {
|
|
94
|
+
deckRef.current.scrollLeft = scrollLeftRef.current;
|
|
101
95
|
}
|
|
102
|
-
}, [
|
|
96
|
+
}, []);
|
|
97
|
+
|
|
98
|
+
useOnTransition(layoutMode, (mode) => mode !== 'deck', 'deck', restoreScroll);
|
|
103
99
|
|
|
104
100
|
/**
|
|
105
101
|
* Save scroll position as the user scrolls
|
|
@@ -107,22 +103,13 @@ export const DeckLayout = ({
|
|
|
107
103
|
const handleScroll = useCallback(
|
|
108
104
|
(event: UIEvent) => {
|
|
109
105
|
if (layoutMode === 'deck' && event.currentTarget === event.target) {
|
|
110
|
-
|
|
111
|
-
setScrollLeft((event.target as HTMLDivElement).scrollLeft);
|
|
106
|
+
scrollLeftRef.current = (event.target as HTMLDivElement).scrollLeft;
|
|
112
107
|
}
|
|
113
108
|
},
|
|
114
109
|
[layoutMode],
|
|
115
110
|
);
|
|
116
111
|
|
|
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]);
|
|
125
|
-
|
|
112
|
+
const firstAttendedId = attended[0];
|
|
126
113
|
useEffect(() => {
|
|
127
114
|
// TODO(burdon): Can we prevent the need to re-scroll since the planks are preserved?
|
|
128
115
|
// E.g., hide the deck and just move the solo article?
|
|
@@ -134,21 +121,14 @@ export const DeckLayout = ({
|
|
|
134
121
|
}
|
|
135
122
|
}, [layoutMode, firstAttendedId]);
|
|
136
123
|
|
|
137
|
-
|
|
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
|
-
}
|
|
144
|
-
}
|
|
145
|
-
return parts;
|
|
146
|
-
}, [layoutParts.main, layoutParts.solo]);
|
|
124
|
+
const isEmpty = layoutParts.main?.length === 0 && layoutParts.solo?.length === 0;
|
|
147
125
|
|
|
148
|
-
const padding =
|
|
149
|
-
layoutMode === 'deck' && overscroll === 'centering'
|
|
150
|
-
|
|
151
|
-
|
|
126
|
+
const padding = useMemo(() => {
|
|
127
|
+
if (layoutMode === 'deck' && overscroll === 'centering') {
|
|
128
|
+
return calculateOverscroll(layoutParts.main, plankSizing, sidebarOpen, complementarySidebarOpen);
|
|
129
|
+
}
|
|
130
|
+
return {};
|
|
131
|
+
}, [layoutMode, overscroll, layoutParts.main, plankSizing, sidebarOpen, complementarySidebarOpen]);
|
|
152
132
|
|
|
153
133
|
if (layoutMode === 'fullscreen') {
|
|
154
134
|
return <Fullscreen id={fullScreenSlug} />;
|
|
@@ -173,25 +153,17 @@ export const DeckLayout = ({
|
|
|
173
153
|
<Main.Root
|
|
174
154
|
navigationSidebarOpen={context.sidebarOpen}
|
|
175
155
|
onNavigationSidebarOpenChange={(next) => (context.sidebarOpen = next)}
|
|
176
|
-
{
|
|
177
|
-
|
|
178
|
-
onComplementarySidebarOpenChange: (next) => (context.complementarySidebarOpen = next),
|
|
179
|
-
})}
|
|
156
|
+
complementarySidebarOpen={context.complementarySidebarOpen}
|
|
157
|
+
onComplementarySidebarOpenChange={(next) => (context.complementarySidebarOpen = next)}
|
|
180
158
|
>
|
|
181
159
|
{/* Notch */}
|
|
182
160
|
<Main.Notch classNames='z-[21]'>
|
|
183
161
|
<Surface role='notch-start' />
|
|
184
|
-
<Button
|
|
185
|
-
// disabled={!sidebarAvailable}
|
|
186
|
-
onClick={() => (context.sidebarOpen = !context.sidebarOpen)}
|
|
187
|
-
variant='ghost'
|
|
188
|
-
classNames='p-1'
|
|
189
|
-
>
|
|
162
|
+
<Button onClick={() => (context.sidebarOpen = !context.sidebarOpen)} variant='ghost' classNames='p-1'>
|
|
190
163
|
<span className='sr-only'>{t('open navigation sidebar label')}</span>
|
|
191
164
|
<MenuIcon weight='light' className={getSize(5)} />
|
|
192
165
|
</Button>
|
|
193
166
|
<Button
|
|
194
|
-
// disabled={!complementaryAvailable}
|
|
195
167
|
onClick={() => (context.complementarySidebarOpen = !context.complementarySidebarOpen)}
|
|
196
168
|
variant='ghost'
|
|
197
169
|
classNames='p-1'
|
|
@@ -203,38 +175,52 @@ export const DeckLayout = ({
|
|
|
203
175
|
</Main.Notch>
|
|
204
176
|
|
|
205
177
|
{/* Left sidebar. */}
|
|
206
|
-
<Sidebar
|
|
178
|
+
<Sidebar layoutParts={layoutParts} />
|
|
207
179
|
|
|
208
180
|
{/* Right sidebar. */}
|
|
209
|
-
|
|
181
|
+
{/* TODO(wittjosiah): Get context from layout parts. */}
|
|
182
|
+
<ComplementarySidebar context='comments' layoutParts={layoutParts} flatDeck={flatDeck} />
|
|
210
183
|
|
|
211
184
|
{/* Dialog overlay to dismiss dialogs. */}
|
|
212
185
|
<Main.Overlay />
|
|
213
186
|
|
|
214
187
|
{/* No content. */}
|
|
215
|
-
{
|
|
188
|
+
{isEmpty && (
|
|
216
189
|
<Main.Content handlesFocus>
|
|
217
190
|
<ContentEmpty />
|
|
218
191
|
</Main.Content>
|
|
219
192
|
)}
|
|
220
193
|
|
|
221
194
|
{/* Solo/deck mode. */}
|
|
222
|
-
{
|
|
195
|
+
{!isEmpty && (
|
|
223
196
|
<Main.Content bounce classNames='grid block-end-[--statusbar-size]' handlesFocus>
|
|
224
197
|
<div role='none' className='relative'>
|
|
225
198
|
<Deck.Root
|
|
226
199
|
style={padding}
|
|
227
|
-
classNames={[
|
|
200
|
+
classNames={[
|
|
201
|
+
!flatDeck && 'bg-deck',
|
|
202
|
+
mainPaddingTransitions,
|
|
203
|
+
'absolute inset-0',
|
|
204
|
+
slots?.wallpaper?.classNames,
|
|
205
|
+
]}
|
|
228
206
|
solo={layoutMode === 'solo'}
|
|
229
207
|
onScroll={handleScroll}
|
|
230
208
|
ref={deckRef}
|
|
231
209
|
>
|
|
232
|
-
|
|
210
|
+
<Plank
|
|
211
|
+
entry={layoutParts.solo?.[0] ?? { id: 'unknown-solo' }}
|
|
212
|
+
layoutParts={layoutParts}
|
|
213
|
+
part='solo'
|
|
214
|
+
layoutMode={layoutMode}
|
|
215
|
+
flatDeck={flatDeck}
|
|
216
|
+
searchEnabled={!!searchPlugin}
|
|
217
|
+
/>
|
|
218
|
+
{layoutParts.main?.map((layoutEntry) => (
|
|
233
219
|
<Plank
|
|
234
220
|
key={layoutEntry.id}
|
|
235
221
|
entry={layoutEntry}
|
|
236
222
|
layoutParts={layoutParts}
|
|
237
|
-
part=
|
|
223
|
+
part='main'
|
|
238
224
|
layoutMode={layoutMode}
|
|
239
225
|
flatDeck={flatDeck}
|
|
240
226
|
searchEnabled={!!searchPlugin}
|
|
@@ -245,15 +231,8 @@ export const DeckLayout = ({
|
|
|
245
231
|
</Main.Content>
|
|
246
232
|
)}
|
|
247
233
|
|
|
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
|
-
)}
|
|
234
|
+
{/* Footer status. */}
|
|
235
|
+
<StatusBar showHints={showHints} />
|
|
257
236
|
|
|
258
237
|
{/* Global popovers. */}
|
|
259
238
|
<Popover.Portal>
|
|
@@ -2,13 +2,11 @@
|
|
|
2
2
|
// Copyright 2024 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import { Placeholder } from '@phosphor-icons/react';
|
|
6
5
|
import React, { Fragment, useEffect } 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,
|
|
@@ -16,11 +14,10 @@ import {
|
|
|
16
14
|
partLength,
|
|
17
15
|
type LayoutParts,
|
|
18
16
|
type LayoutPart,
|
|
19
|
-
type LayoutEntry,
|
|
20
17
|
} from '@dxos/app-framework';
|
|
21
18
|
import { type Node, useGraph } from '@dxos/plugin-graph';
|
|
22
|
-
import { Popover, toLocalizedString, useMediaQuery, useTranslation } from '@dxos/react-ui';
|
|
23
|
-
import { PlankHeading,
|
|
19
|
+
import { Icon, Popover, toLocalizedString, useMediaQuery, useTranslation } from '@dxos/react-ui';
|
|
20
|
+
import { PlankHeading, type PlankHeadingAction } from '@dxos/react-ui-deck';
|
|
24
21
|
import { TextTooltip } from '@dxos/react-ui-text-tooltip';
|
|
25
22
|
|
|
26
23
|
import { DECK_PLUGIN } from '../../meta';
|
|
@@ -30,24 +27,23 @@ export const NodePlankHeading = ({
|
|
|
30
27
|
id,
|
|
31
28
|
layoutParts,
|
|
32
29
|
layoutPart,
|
|
33
|
-
// TODO(wittjosiah): Unused?
|
|
34
|
-
layoutEntry,
|
|
35
30
|
popoverAnchorId,
|
|
36
31
|
pending,
|
|
37
32
|
flatDeck,
|
|
33
|
+
actions = [],
|
|
38
34
|
}: {
|
|
39
35
|
node?: Node;
|
|
40
36
|
id?: string;
|
|
41
37
|
layoutParts?: LayoutParts;
|
|
42
38
|
layoutPart?: LayoutPart;
|
|
43
|
-
layoutEntry?: LayoutEntry;
|
|
44
39
|
popoverAnchorId?: string;
|
|
45
40
|
pending?: boolean;
|
|
46
41
|
flatDeck?: boolean;
|
|
42
|
+
actions?: PlankHeadingAction[];
|
|
47
43
|
}) => {
|
|
48
44
|
const { t } = useTranslation(DECK_PLUGIN);
|
|
49
45
|
const { graph } = useGraph();
|
|
50
|
-
const
|
|
46
|
+
const icon = node?.properties?.icon ?? 'ph--placeholder--regular';
|
|
51
47
|
const label = pending
|
|
52
48
|
? t('pending heading')
|
|
53
49
|
: toLocalizedString(node?.properties?.label ?? ['plank heading fallback label', { ns: DECK_PLUGIN }], t);
|
|
@@ -80,10 +76,11 @@ export const NodePlankHeading = ({
|
|
|
80
76
|
<ActionRoot>
|
|
81
77
|
{node ? (
|
|
82
78
|
<PlankHeading.ActionsMenu
|
|
83
|
-
|
|
79
|
+
icon={icon}
|
|
80
|
+
related={layoutPart === 'complementary'}
|
|
84
81
|
attendableId={attendableId}
|
|
85
82
|
triggerLabel={t('actions menu label')}
|
|
86
|
-
actions={graph.actions(node)}
|
|
83
|
+
actions={[actions, graph.actions(node)].filter((a) => a.length > 0)}
|
|
87
84
|
onAction={(action) =>
|
|
88
85
|
typeof action.data === 'function' && action.data?.({ node: action as Node, caller: DECK_PLUGIN })
|
|
89
86
|
}
|
|
@@ -93,12 +90,16 @@ export const NodePlankHeading = ({
|
|
|
93
90
|
) : (
|
|
94
91
|
<PlankHeading.Button>
|
|
95
92
|
<span className='sr-only'>{label}</span>
|
|
96
|
-
<Icon {
|
|
93
|
+
<Icon icon={icon} size={5} />
|
|
97
94
|
</PlankHeading.Button>
|
|
98
95
|
)}
|
|
99
96
|
</ActionRoot>
|
|
100
97
|
<TextTooltip text={label} onlyWhenTruncating>
|
|
101
|
-
<PlankHeading.Label
|
|
98
|
+
<PlankHeading.Label
|
|
99
|
+
attendableId={attendableId}
|
|
100
|
+
related={layoutPart === 'complementary'}
|
|
101
|
+
{...(pending && { classNames: 'text-description' })}
|
|
102
|
+
>
|
|
102
103
|
{label}
|
|
103
104
|
</PlankHeading.Label>
|
|
104
105
|
</TextTooltip>
|
|
@@ -143,7 +144,6 @@ export const NodePlankHeading = ({
|
|
|
143
144
|
action: NavigationAction.CLOSE,
|
|
144
145
|
data: {
|
|
145
146
|
activeParts: {
|
|
146
|
-
complementary: [`${id}${SLUG_PATH_SEPARATOR}comments${SLUG_COLLECTION_INDICATOR}`],
|
|
147
147
|
[layoutPart]: [id],
|
|
148
148
|
},
|
|
149
149
|
},
|
|
@@ -151,7 +151,7 @@ export const NodePlankHeading = ({
|
|
|
151
151
|
: { action: NavigationAction.ADJUST, data: { type: eventType, layoutCoordinate } },
|
|
152
152
|
);
|
|
153
153
|
}}
|
|
154
|
-
close={
|
|
154
|
+
close={layoutPart === 'complementary' ? 'minify-end' : true}
|
|
155
155
|
/>
|
|
156
156
|
</PlankHeading.Root>
|
|
157
157
|
);
|
|
@@ -19,7 +19,7 @@ import {
|
|
|
19
19
|
import { debounce } from '@dxos/async';
|
|
20
20
|
import { useGraph } from '@dxos/plugin-graph';
|
|
21
21
|
import { Button, Tooltip, useTranslation } from '@dxos/react-ui';
|
|
22
|
-
import {
|
|
22
|
+
import { useAttendableAttributes } from '@dxos/react-ui-attention';
|
|
23
23
|
import { Plank as NaturalPlank } from '@dxos/react-ui-deck';
|
|
24
24
|
import { mainIntrinsicSize } from '@dxos/react-ui-theme';
|
|
25
25
|
|
|
@@ -52,7 +52,7 @@ export const Plank = ({ entry, layoutParts, part, flatDeck, searchEnabled, layou
|
|
|
52
52
|
const rootElement = useRef<HTMLDivElement | null>(null);
|
|
53
53
|
const resizeable = layoutMode === 'deck';
|
|
54
54
|
|
|
55
|
-
const attendableAttrs =
|
|
55
|
+
const attendableAttrs = useAttendableAttributes(entry.id);
|
|
56
56
|
const coordinate: LayoutCoordinate = { part, entryId: entry.id };
|
|
57
57
|
|
|
58
58
|
const size = plankSizing?.[entry.id] as number | undefined;
|
|
@@ -78,7 +78,7 @@ export const Plank = ({ entry, layoutParts, part, flatDeck, searchEnabled, layou
|
|
|
78
78
|
}, [scrollIntoView, layoutMode]);
|
|
79
79
|
|
|
80
80
|
const isSolo = layoutMode === 'solo' && part === 'solo';
|
|
81
|
-
const isSuppressed = layoutMode === 'solo' && part !== 'solo';
|
|
81
|
+
const isSuppressed = (layoutMode === 'solo' && part !== 'solo') || (layoutMode === 'deck' && part === 'solo');
|
|
82
82
|
|
|
83
83
|
const sizeAttrs = useMainSize();
|
|
84
84
|
|
|
@@ -4,18 +4,19 @@
|
|
|
4
4
|
|
|
5
5
|
import React, { useMemo } from 'react';
|
|
6
6
|
|
|
7
|
-
import { type
|
|
7
|
+
import { type LayoutParts, openIds, Surface } from '@dxos/app-framework';
|
|
8
8
|
import { Main } from '@dxos/react-ui';
|
|
9
|
+
import { useAttended } from '@dxos/react-ui-attention';
|
|
9
10
|
|
|
10
11
|
import { useLayout } from '../LayoutContext';
|
|
11
12
|
|
|
12
13
|
export type SidebarProps = {
|
|
13
|
-
attention: Attention;
|
|
14
14
|
layoutParts: LayoutParts;
|
|
15
15
|
};
|
|
16
16
|
|
|
17
|
-
export const Sidebar = ({
|
|
17
|
+
export const Sidebar = ({ layoutParts }: SidebarProps) => {
|
|
18
18
|
const { layoutMode, popoverAnchorId } = useLayout();
|
|
19
|
+
const attended = useAttended();
|
|
19
20
|
|
|
20
21
|
const activeIds = useMemo(() => {
|
|
21
22
|
if (layoutMode === 'solo') {
|
|
@@ -31,9 +32,9 @@ export const Sidebar = ({ attention, layoutParts }: SidebarProps) => {
|
|
|
31
32
|
() => ({
|
|
32
33
|
popoverAnchorId,
|
|
33
34
|
activeIds,
|
|
34
|
-
attended
|
|
35
|
+
attended,
|
|
35
36
|
}),
|
|
36
|
-
[popoverAnchorId, activeIds,
|
|
37
|
+
[popoverAnchorId, activeIds, attended],
|
|
37
38
|
);
|
|
38
39
|
return (
|
|
39
40
|
<Main.NavigationSidebar>
|
|
@@ -5,14 +5,23 @@
|
|
|
5
5
|
import React from 'react';
|
|
6
6
|
|
|
7
7
|
import { Surface } from '@dxos/app-framework';
|
|
8
|
-
import { mainPadding, mx } from '@dxos/react-ui-theme';
|
|
8
|
+
import { mainPadding, mainPaddingTransitions, mx } from '@dxos/react-ui-theme';
|
|
9
9
|
|
|
10
10
|
import { useMainSize } from '../../hooks';
|
|
11
11
|
|
|
12
|
-
export const StatusBar = () => {
|
|
12
|
+
export const StatusBar = ({ showHints }: { showHints?: boolean }) => {
|
|
13
13
|
const sizeAttrs = useMainSize();
|
|
14
14
|
return (
|
|
15
|
-
<div
|
|
15
|
+
<div
|
|
16
|
+
role='none'
|
|
17
|
+
{...sizeAttrs}
|
|
18
|
+
className={mx(
|
|
19
|
+
'fixed flex justify-between block-end-0 inset-inline-0 items-center border-bs border-separator z-[2]',
|
|
20
|
+
mainPadding,
|
|
21
|
+
mainPaddingTransitions,
|
|
22
|
+
)}
|
|
23
|
+
>
|
|
24
|
+
<div role='none'>{showHints && <Surface role='hints' limit={1} />}</div>
|
|
16
25
|
<Surface role='status-bar' limit={1} />
|
|
17
26
|
</div>
|
|
18
27
|
);
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
import React from 'react';
|
|
6
6
|
|
|
7
7
|
import type { Toast as ToastProps } from '@dxos/app-framework';
|
|
8
|
-
import { Button, Toast as NaturalToast, type ToastRootProps } from '@dxos/react-ui';
|
|
8
|
+
import { Button, Icon, Toast as NaturalToast, type ToastRootProps } from '@dxos/react-ui';
|
|
9
9
|
|
|
10
10
|
// TODO(wittjosiah): Render remaining duration as a progress bar within the toast.
|
|
11
11
|
export const Toast = ({
|
|
@@ -23,8 +23,8 @@ export const Toast = ({
|
|
|
23
23
|
return (
|
|
24
24
|
<NaturalToast.Root data-testid={id} defaultOpen duration={duration} onOpenChange={onOpenChange}>
|
|
25
25
|
<NaturalToast.Body>
|
|
26
|
-
<NaturalToast.Title>
|
|
27
|
-
{icon
|
|
26
|
+
<NaturalToast.Title classNames='items-center'>
|
|
27
|
+
<Icon icon={icon} size={5} classNames='inline mr-1' />
|
|
28
28
|
<span>{title}</span>
|
|
29
29
|
</NaturalToast.Title>
|
|
30
30
|
{description && <NaturalToast.Description>{description}</NaturalToast.Description>}
|
|
@@ -61,25 +61,22 @@ export const LayoutSettings = ({ settings }: { settings: DeckSettingsProps }) =>
|
|
|
61
61
|
</Select.Portal>
|
|
62
62
|
</Select.Root>
|
|
63
63
|
</SettingsValue>
|
|
64
|
-
<SettingsValue label={t('settings show
|
|
65
|
-
<Input.Switch checked={settings.
|
|
64
|
+
<SettingsValue label={t('settings show hints label')}>
|
|
65
|
+
<Input.Switch checked={settings.showHints} onCheckedChange={(checked) => (settings.showHints = checked)} />
|
|
66
66
|
</SettingsValue>
|
|
67
67
|
{!isSocket && (
|
|
68
68
|
<SettingsValue label={t('settings native redirect label')}>
|
|
69
69
|
<Input.Switch
|
|
70
70
|
checked={settings.enableNativeRedirect}
|
|
71
|
-
onCheckedChange={(checked) => (settings.enableNativeRedirect =
|
|
71
|
+
onCheckedChange={(checked) => (settings.enableNativeRedirect = checked)}
|
|
72
72
|
/>
|
|
73
73
|
</SettingsValue>
|
|
74
74
|
)}
|
|
75
75
|
<SettingsValue label={t('settings custom slots')}>
|
|
76
|
-
<Input.Switch
|
|
77
|
-
checked={settings.customSlots}
|
|
78
|
-
onCheckedChange={(checked) => (settings.customSlots = !!checked)}
|
|
79
|
-
/>
|
|
76
|
+
<Input.Switch checked={settings.customSlots} onCheckedChange={(checked) => (settings.customSlots = checked)} />
|
|
80
77
|
</SettingsValue>
|
|
81
78
|
<SettingsValue label={t('settings flat deck')}>
|
|
82
|
-
<Input.Switch checked={settings.flatDeck} onCheckedChange={(checked) => (settings.flatDeck =
|
|
79
|
+
<Input.Switch checked={settings.flatDeck} onCheckedChange={(checked) => (settings.flatDeck = checked)} />
|
|
83
80
|
</SettingsValue>
|
|
84
81
|
</>
|
|
85
82
|
);
|
package/src/hooks/useNode.ts
CHANGED
|
@@ -16,9 +16,13 @@ import { type Graph, type Node } from '@dxos/plugin-graph';
|
|
|
16
16
|
*/
|
|
17
17
|
// TODO(wittjosiah): Factor out.
|
|
18
18
|
export const useNode = <T = any>(graph: Graph, id?: string, timeout?: number): Node<T> | undefined => {
|
|
19
|
-
const [nodeState, setNodeState] = useState<Node<T> | undefined>(id ? graph.findNode(id) : undefined);
|
|
19
|
+
const [nodeState, setNodeState] = useState<Node<T> | undefined>(id ? graph.findNode(id, false) : undefined);
|
|
20
20
|
|
|
21
21
|
useEffect(() => {
|
|
22
|
+
if (!id && nodeState) {
|
|
23
|
+
setNodeState(undefined);
|
|
24
|
+
}
|
|
25
|
+
|
|
22
26
|
if (nodeState?.id === id || !id) {
|
|
23
27
|
return;
|
|
24
28
|
}
|
package/src/layout.ts
CHANGED
package/src/meta.ts
CHANGED
package/src/translations.ts
CHANGED
|
@@ -10,13 +10,12 @@ export default [
|
|
|
10
10
|
'main header label': 'Main header',
|
|
11
11
|
'open navigation sidebar label': 'Open navigation sidebar.',
|
|
12
12
|
'open complementary sidebar label': 'Open complementary sidebar.',
|
|
13
|
-
'open settings label': 'Show settings',
|
|
14
13
|
'plugin error message': 'Content failed to render.',
|
|
15
14
|
'content fallback message': 'Unsupported',
|
|
16
15
|
'content fallback description':
|
|
17
16
|
'No plugin had a response for the address you navigated to. Double-check the URL, and ensure you’ve enabled a plugin that supports the object.',
|
|
18
17
|
'toggle fullscreen label': 'Toggle fullscreen',
|
|
19
|
-
'settings show
|
|
18
|
+
'settings show hints label': 'Show hints',
|
|
20
19
|
'settings native redirect label': 'Enable native url redirect (experimental)',
|
|
21
20
|
'settings custom slots': 'Theme option (experimental)',
|
|
22
21
|
'settings new plank position start label': 'Start',
|
|
@@ -27,7 +26,6 @@ export default [
|
|
|
27
26
|
'undo action label': 'Undo',
|
|
28
27
|
'undo action alt': 'Undo previous action',
|
|
29
28
|
'undo close label': 'Dismiss',
|
|
30
|
-
'open comments label': 'Open comments',
|
|
31
29
|
'error fallback message': 'Unable to open this item',
|
|
32
30
|
'plank heading fallback label': 'Untitled',
|
|
33
31
|
'actions menu label': 'Options',
|
|
@@ -41,6 +39,12 @@ export default [
|
|
|
41
39
|
'settings overscroll centering label': 'Centering',
|
|
42
40
|
'settings overscroll none label': 'None',
|
|
43
41
|
'settings flat deck': 'Flatten deck appearance',
|
|
42
|
+
|
|
43
|
+
// ComplementarySidebar
|
|
44
|
+
'open settings label': 'Show settings',
|
|
45
|
+
'open comments label': 'Show Comments',
|
|
46
|
+
'open automation label': 'Show Automation',
|
|
47
|
+
'open debug label': 'Show Debug',
|
|
44
48
|
},
|
|
45
49
|
},
|
|
46
50
|
},
|
package/src/types.ts
CHANGED
|
@@ -20,7 +20,7 @@ export const OverscrollOptions = ['none', 'centering'] as const;
|
|
|
20
20
|
export type Overscroll = (typeof OverscrollOptions)[number];
|
|
21
21
|
|
|
22
22
|
export type DeckSettingsProps = {
|
|
23
|
-
|
|
23
|
+
showHints: boolean;
|
|
24
24
|
customSlots: boolean;
|
|
25
25
|
flatDeck: boolean;
|
|
26
26
|
enableNativeRedirect: boolean;
|
package/src/util/overscroll.ts
CHANGED
|
@@ -56,9 +56,9 @@ export const calculateOverscroll = (
|
|
|
56
56
|
plankSizing: Record<string, number>,
|
|
57
57
|
sidebarOpen: boolean,
|
|
58
58
|
complementarySidebarOpen: boolean,
|
|
59
|
-
): Pick<CSSProperties, '
|
|
59
|
+
): Pick<CSSProperties, 'paddingInlineStart' | 'paddingInlineEnd'> | undefined => {
|
|
60
60
|
if (!planks?.length) {
|
|
61
|
-
return;
|
|
61
|
+
return { paddingInlineStart: 0, paddingInlineEnd: 0 };
|
|
62
62
|
}
|
|
63
63
|
|
|
64
64
|
// TODO(Zan): Move complementary sidebar size (360px), sidebar size (270px), plank resize handle size (20px) to CSS variables.
|
|
@@ -73,7 +73,7 @@ export const calculateOverscroll = (
|
|
|
73
73
|
const plankSize = getPlankSize(plank.id);
|
|
74
74
|
const overscrollPadding = `max(0px, calc(((100dvw - ${sidebarWidth} - ${complementarySidebarWidth} - (${plankSize} + 20px)) / 2)))`;
|
|
75
75
|
|
|
76
|
-
return {
|
|
76
|
+
return { paddingInlineStart: overscrollPadding, paddingInlineEnd: overscrollPadding };
|
|
77
77
|
} else {
|
|
78
78
|
// Center the plank on the screen.
|
|
79
79
|
const first = planks[0];
|
|
@@ -83,8 +83,8 @@ export const calculateOverscroll = (
|
|
|
83
83
|
const lastSize = getPlankSize(last.id);
|
|
84
84
|
|
|
85
85
|
return {
|
|
86
|
-
|
|
87
|
-
|
|
86
|
+
paddingInlineStart: `max(0px, calc(((100dvw - (${firstSize} + 20px)) / 2) - ${sidebarWidth}))`,
|
|
87
|
+
paddingInlineEnd: `max(0px, calc(((100dvw - (${lastSize} + 20px)) / 2) - ${complementarySidebarWidth}))`,
|
|
88
88
|
};
|
|
89
89
|
}
|
|
90
90
|
};
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 3,
|
|
3
|
-
"sources": ["../../../src/meta.ts"],
|
|
4
|
-
"sourcesContent": ["//\n// Copyright 2023 DXOS.org\n//\n\nexport const DECK_PLUGIN = 'dxos.org/plugin/deck';\n\nexport default {\n id: DECK_PLUGIN,\n name: 'Deck',\n};\n"],
|
|
5
|
-
"mappings": ";AAIO,IAAMA,cAAc;AAE3B,IAAA,eAAe;EACbC,IAAID;EACJE,MAAM;AACR;",
|
|
6
|
-
"names": ["DECK_PLUGIN", "id", "name"]
|
|
7
|
-
}
|