@dxos/plugin-deck 0.6.12-main.5cc132e → 0.6.12-main.78ddbdf
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/index.mjs +87 -76
- package/dist/lib/browser/index.mjs.map +3 -3
- package/dist/lib/browser/meta.json +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.map +1 -1
- package/dist/types/src/components/DeckLayout/NodePlankHeading.d.ts.map +1 -1
- package/dist/types/src/hooks/useNode.d.ts.map +1 -1
- package/package.json +28 -28
- package/src/DeckPlugin.tsx +80 -51
- package/src/components/DeckLayout/ComplementarySidebar.tsx +18 -16
- package/src/components/DeckLayout/DeckLayout.tsx +34 -43
- package/src/components/DeckLayout/NodePlankHeading.tsx +6 -9
- package/src/hooks/useNode.ts +5 -1
|
@@ -7,7 +7,7 @@ import React from 'react';
|
|
|
7
7
|
import { type LayoutParts, SLUG_PATH_SEPARATOR, Surface } from '@dxos/app-framework';
|
|
8
8
|
import { useGraph } from '@dxos/plugin-graph';
|
|
9
9
|
import { Main } from '@dxos/react-ui';
|
|
10
|
-
import { createAttendableAttributes } from '@dxos/react-ui-attention';
|
|
10
|
+
import { createAttendableAttributes, useAttendedIds } from '@dxos/react-ui-attention';
|
|
11
11
|
import { deckGrid } from '@dxos/react-ui-deck';
|
|
12
12
|
import { mx } from '@dxos/react-ui-theme';
|
|
13
13
|
|
|
@@ -18,32 +18,34 @@ import { useNode, useNodeActionExpander } from '../../hooks';
|
|
|
18
18
|
import { useLayout } from '../LayoutContext';
|
|
19
19
|
|
|
20
20
|
export type ComplementarySidebarProps = {
|
|
21
|
-
|
|
21
|
+
context?: string;
|
|
22
22
|
layoutParts: LayoutParts;
|
|
23
23
|
flatDeck?: boolean;
|
|
24
24
|
};
|
|
25
25
|
|
|
26
|
-
export const ComplementarySidebar = ({
|
|
26
|
+
export const ComplementarySidebar = ({ context, layoutParts, flatDeck }: ComplementarySidebarProps) => {
|
|
27
27
|
const { popoverAnchorId } = useLayout();
|
|
28
|
+
const attended = useAttendedIds();
|
|
29
|
+
const id = attended[0] ? `${attended[0]}${SLUG_PATH_SEPARATOR}${context}` : undefined;
|
|
28
30
|
const { graph } = useGraph();
|
|
29
31
|
const node = useNode(graph, id);
|
|
30
|
-
// const complementaryAvailable = useMemo(() => id === NAV_ID || !!node, [id, node]);
|
|
31
32
|
const complementaryAttrs = createAttendableAttributes(id?.split(SLUG_PATH_SEPARATOR)[0] ?? 'never');
|
|
32
33
|
|
|
33
34
|
useNodeActionExpander(node);
|
|
34
35
|
|
|
35
36
|
return (
|
|
36
37
|
<Main.ComplementarySidebar {...complementaryAttrs}>
|
|
37
|
-
{
|
|
38
|
-
<
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
38
|
+
<div role='none' className={mx(deckGrid, 'grid-cols-1 bs-full')}>
|
|
39
|
+
<NodePlankHeading
|
|
40
|
+
node={node}
|
|
41
|
+
id={id}
|
|
42
|
+
layoutParts={layoutParts}
|
|
43
|
+
layoutPart='complementary'
|
|
44
|
+
popoverAnchorId={popoverAnchorId}
|
|
45
|
+
flatDeck={flatDeck}
|
|
46
|
+
/>
|
|
47
|
+
{/* TODO(wittjosiah): Render some placeholder when node is undefined. */}
|
|
48
|
+
{node && (
|
|
47
49
|
<Surface
|
|
48
50
|
role='article'
|
|
49
51
|
data={{ subject: node.data, part: 'complementary', popoverAnchorId }}
|
|
@@ -51,8 +53,8 @@ export const ComplementarySidebar = ({ id, layoutParts, flatDeck }: Complementar
|
|
|
51
53
|
fallback={PlankContentError}
|
|
52
54
|
placeholder={<PlankLoading />}
|
|
53
55
|
/>
|
|
54
|
-
|
|
55
|
-
|
|
56
|
+
)}
|
|
57
|
+
</div>
|
|
56
58
|
</Main.ComplementarySidebar>
|
|
57
59
|
);
|
|
58
60
|
};
|
|
@@ -3,10 +3,9 @@
|
|
|
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
8
|
import {
|
|
9
|
-
SLUG_PATH_SEPARATOR,
|
|
10
9
|
type Attention,
|
|
11
10
|
type LayoutEntry,
|
|
12
11
|
type LayoutParts,
|
|
@@ -15,7 +14,7 @@ import {
|
|
|
15
14
|
firstIdInPart,
|
|
16
15
|
usePlugin,
|
|
17
16
|
} from '@dxos/app-framework';
|
|
18
|
-
import { Button, Dialog, Main, Popover, useTranslation } from '@dxos/react-ui';
|
|
17
|
+
import { Button, Dialog, Main, Popover, useOnTransition, useTranslation } from '@dxos/react-ui';
|
|
19
18
|
import { Deck } from '@dxos/react-ui-deck';
|
|
20
19
|
import { getSize } from '@dxos/react-ui-theme';
|
|
21
20
|
|
|
@@ -73,33 +72,38 @@ export const DeckLayout = ({
|
|
|
73
72
|
const searchPlugin = usePlugin('dxos.org/plugin/search');
|
|
74
73
|
const fullScreenSlug = useMemo(() => firstIdInPart(layoutParts, 'fullScreen'), [layoutParts]);
|
|
75
74
|
|
|
76
|
-
const
|
|
77
|
-
const deckRef = useRef<HTMLDivElement
|
|
78
|
-
|
|
75
|
+
const scrollLeftRef = useRef<number | null>();
|
|
76
|
+
const deckRef = useRef<HTMLDivElement>(null);
|
|
77
|
+
|
|
78
|
+
// Ensure the first plank is attended when the deck is first rendered.
|
|
79
|
+
useEffect(() => {
|
|
80
|
+
const firstId = layoutMode === 'solo' ? firstIdInPart(layoutParts, 'solo') : firstIdInPart(layoutParts, 'main');
|
|
81
|
+
if (attention.attended.size === 0 && firstId) {
|
|
82
|
+
// TODO(wittjosiah): Focusing the type button is a workaround.
|
|
83
|
+
// If the plank is directly focused on first load the focus ring appears.
|
|
84
|
+
document.querySelector<HTMLElement>(`article[data-attendable-id="${firstId}"] button`)?.focus();
|
|
85
|
+
}
|
|
86
|
+
}, []);
|
|
79
87
|
|
|
80
88
|
/**
|
|
81
89
|
* Clear scroll restoration state if the window is resized
|
|
82
90
|
*/
|
|
83
91
|
const handleResize = useCallback(() => {
|
|
84
|
-
|
|
92
|
+
scrollLeftRef.current = null;
|
|
85
93
|
}, []);
|
|
94
|
+
|
|
86
95
|
useEffect(() => {
|
|
87
96
|
window.addEventListener('resize', handleResize);
|
|
88
97
|
return () => window.removeEventListener('resize', handleResize);
|
|
89
98
|
}, [handleResize]);
|
|
90
99
|
|
|
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;
|
|
100
|
+
const restoreScroll = useCallback(() => {
|
|
101
|
+
if (deckRef.current && scrollLeftRef.current != null) {
|
|
102
|
+
deckRef.current.scrollLeft = scrollLeftRef.current;
|
|
101
103
|
}
|
|
102
|
-
}, [
|
|
104
|
+
}, []);
|
|
105
|
+
|
|
106
|
+
useOnTransition(layoutMode, (mode) => mode !== 'deck', 'deck', restoreScroll);
|
|
103
107
|
|
|
104
108
|
/**
|
|
105
109
|
* Save scroll position as the user scrolls
|
|
@@ -107,20 +111,12 @@ export const DeckLayout = ({
|
|
|
107
111
|
const handleScroll = useCallback(
|
|
108
112
|
(event: UIEvent) => {
|
|
109
113
|
if (layoutMode === 'deck' && event.currentTarget === event.target) {
|
|
110
|
-
|
|
111
|
-
setScrollLeft((event.target as HTMLDivElement).scrollLeft);
|
|
114
|
+
scrollLeftRef.current = (event.target as HTMLDivElement).scrollLeft;
|
|
112
115
|
}
|
|
113
116
|
},
|
|
114
117
|
[layoutMode],
|
|
115
118
|
);
|
|
116
119
|
|
|
117
|
-
const complementarySlug = useMemo(() => {
|
|
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
120
|
const firstAttendedId = useMemo(() => Array.from(attention.attended ?? [])[0], [attention.attended]);
|
|
125
121
|
|
|
126
122
|
useEffect(() => {
|
|
@@ -145,10 +141,12 @@ export const DeckLayout = ({
|
|
|
145
141
|
return parts;
|
|
146
142
|
}, [layoutParts.main, layoutParts.solo]);
|
|
147
143
|
|
|
148
|
-
const padding =
|
|
149
|
-
layoutMode === 'deck' && overscroll === 'centering'
|
|
150
|
-
|
|
151
|
-
|
|
144
|
+
const padding = useMemo(() => {
|
|
145
|
+
if (layoutMode === 'deck' && overscroll === 'centering') {
|
|
146
|
+
return calculateOverscroll(layoutParts.main, plankSizing, sidebarOpen, complementarySidebarOpen);
|
|
147
|
+
}
|
|
148
|
+
return {};
|
|
149
|
+
}, [layoutMode, overscroll, layoutParts.main, plankSizing, sidebarOpen, complementarySidebarOpen]);
|
|
152
150
|
|
|
153
151
|
if (layoutMode === 'fullscreen') {
|
|
154
152
|
return <Fullscreen id={fullScreenSlug} />;
|
|
@@ -173,25 +171,17 @@ export const DeckLayout = ({
|
|
|
173
171
|
<Main.Root
|
|
174
172
|
navigationSidebarOpen={context.sidebarOpen}
|
|
175
173
|
onNavigationSidebarOpenChange={(next) => (context.sidebarOpen = next)}
|
|
176
|
-
{
|
|
177
|
-
|
|
178
|
-
onComplementarySidebarOpenChange: (next) => (context.complementarySidebarOpen = next),
|
|
179
|
-
})}
|
|
174
|
+
complementarySidebarOpen={context.complementarySidebarOpen}
|
|
175
|
+
onComplementarySidebarOpenChange={(next) => (context.complementarySidebarOpen = next)}
|
|
180
176
|
>
|
|
181
177
|
{/* Notch */}
|
|
182
178
|
<Main.Notch classNames='z-[21]'>
|
|
183
179
|
<Surface role='notch-start' />
|
|
184
|
-
<Button
|
|
185
|
-
// disabled={!sidebarAvailable}
|
|
186
|
-
onClick={() => (context.sidebarOpen = !context.sidebarOpen)}
|
|
187
|
-
variant='ghost'
|
|
188
|
-
classNames='p-1'
|
|
189
|
-
>
|
|
180
|
+
<Button onClick={() => (context.sidebarOpen = !context.sidebarOpen)} variant='ghost' classNames='p-1'>
|
|
190
181
|
<span className='sr-only'>{t('open navigation sidebar label')}</span>
|
|
191
182
|
<MenuIcon weight='light' className={getSize(5)} />
|
|
192
183
|
</Button>
|
|
193
184
|
<Button
|
|
194
|
-
// disabled={!complementaryAvailable}
|
|
195
185
|
onClick={() => (context.complementarySidebarOpen = !context.complementarySidebarOpen)}
|
|
196
186
|
variant='ghost'
|
|
197
187
|
classNames='p-1'
|
|
@@ -206,7 +196,8 @@ export const DeckLayout = ({
|
|
|
206
196
|
<Sidebar attention={attention} layoutParts={layoutParts} />
|
|
207
197
|
|
|
208
198
|
{/* Right sidebar. */}
|
|
209
|
-
|
|
199
|
+
{/* TODO(wittjosiah): Get context from layout parts. */}
|
|
200
|
+
<ComplementarySidebar context='comments' layoutParts={layoutParts} flatDeck={flatDeck} />
|
|
210
201
|
|
|
211
202
|
{/* Dialog overlay to dismiss dialogs. */}
|
|
212
203
|
<Main.Overlay />
|
|
@@ -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,
|
|
@@ -19,8 +17,8 @@ import {
|
|
|
19
17
|
type LayoutEntry,
|
|
20
18
|
} from '@dxos/app-framework';
|
|
21
19
|
import { type Node, useGraph } from '@dxos/plugin-graph';
|
|
22
|
-
import { Popover, toLocalizedString, useMediaQuery, useTranslation } from '@dxos/react-ui';
|
|
23
|
-
import { PlankHeading
|
|
20
|
+
import { Icon, Popover, toLocalizedString, useMediaQuery, useTranslation } from '@dxos/react-ui';
|
|
21
|
+
import { PlankHeading } from '@dxos/react-ui-deck';
|
|
24
22
|
import { TextTooltip } from '@dxos/react-ui-text-tooltip';
|
|
25
23
|
|
|
26
24
|
import { DECK_PLUGIN } from '../../meta';
|
|
@@ -47,7 +45,7 @@ export const NodePlankHeading = ({
|
|
|
47
45
|
}) => {
|
|
48
46
|
const { t } = useTranslation(DECK_PLUGIN);
|
|
49
47
|
const { graph } = useGraph();
|
|
50
|
-
const
|
|
48
|
+
const icon = node?.properties?.icon ?? 'ph--placeholder--regular';
|
|
51
49
|
const label = pending
|
|
52
50
|
? t('pending heading')
|
|
53
51
|
: toLocalizedString(node?.properties?.label ?? ['plank heading fallback label', { ns: DECK_PLUGIN }], t);
|
|
@@ -80,7 +78,7 @@ export const NodePlankHeading = ({
|
|
|
80
78
|
<ActionRoot>
|
|
81
79
|
{node ? (
|
|
82
80
|
<PlankHeading.ActionsMenu
|
|
83
|
-
|
|
81
|
+
icon={icon}
|
|
84
82
|
attendableId={attendableId}
|
|
85
83
|
triggerLabel={t('actions menu label')}
|
|
86
84
|
actions={graph.actions(node)}
|
|
@@ -93,7 +91,7 @@ export const NodePlankHeading = ({
|
|
|
93
91
|
) : (
|
|
94
92
|
<PlankHeading.Button>
|
|
95
93
|
<span className='sr-only'>{label}</span>
|
|
96
|
-
<Icon {
|
|
94
|
+
<Icon icon={icon} size={5} />
|
|
97
95
|
</PlankHeading.Button>
|
|
98
96
|
)}
|
|
99
97
|
</ActionRoot>
|
|
@@ -143,7 +141,6 @@ export const NodePlankHeading = ({
|
|
|
143
141
|
action: NavigationAction.CLOSE,
|
|
144
142
|
data: {
|
|
145
143
|
activeParts: {
|
|
146
|
-
complementary: [`${id}${SLUG_PATH_SEPARATOR}comments${SLUG_COLLECTION_INDICATOR}`],
|
|
147
144
|
[layoutPart]: [id],
|
|
148
145
|
},
|
|
149
146
|
},
|
|
@@ -151,7 +148,7 @@ export const NodePlankHeading = ({
|
|
|
151
148
|
: { action: NavigationAction.ADJUST, data: { type: eventType, layoutCoordinate } },
|
|
152
149
|
);
|
|
153
150
|
}}
|
|
154
|
-
close={
|
|
151
|
+
close={layoutPart === 'complementary' ? 'minify-end' : true}
|
|
155
152
|
/>
|
|
156
153
|
</PlankHeading.Root>
|
|
157
154
|
);
|
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
|
}
|