@dxos/plugin-deck 0.6.8-staging.77f93a3 → 0.6.8-staging.dec6b33
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 +85 -67
- 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/DeckLayout.d.ts +6 -6
- package/dist/types/src/components/DeckLayout/DeckLayout.d.ts.map +1 -1
- package/dist/types/src/components/DeckLayout/Plank.d.ts.map +1 -1
- package/dist/types/src/layout.d.ts +1 -1
- package/dist/types/src/layout.d.ts.map +1 -1
- package/dist/types/src/util/overscroll.d.ts +47 -6
- package/dist/types/src/util/overscroll.d.ts.map +1 -1
- package/package.json +27 -26
- package/src/DeckPlugin.tsx +5 -3
- package/src/components/DeckLayout/DeckLayout.tsx +81 -75
- package/src/components/DeckLayout/Plank.tsx +19 -6
- package/src/layout.ts +4 -2
- package/src/util/overscroll.ts +60 -67
|
@@ -3,11 +3,12 @@
|
|
|
3
3
|
//
|
|
4
4
|
|
|
5
5
|
import { Sidebar as MenuIcon } from '@phosphor-icons/react';
|
|
6
|
-
import React, { useMemo } from 'react';
|
|
6
|
+
import React, { useEffect, useMemo, useRef } from 'react';
|
|
7
7
|
|
|
8
8
|
import {
|
|
9
9
|
SLUG_PATH_SEPARATOR,
|
|
10
10
|
type Attention,
|
|
11
|
+
type LayoutEntry,
|
|
11
12
|
type LayoutParts,
|
|
12
13
|
Surface,
|
|
13
14
|
type Toast as ToastSchema,
|
|
@@ -32,28 +33,27 @@ import { useDeckContext } from '../DeckContext';
|
|
|
32
33
|
import { useLayout } from '../LayoutContext';
|
|
33
34
|
|
|
34
35
|
export type DeckLayoutProps = {
|
|
35
|
-
showHintsFooter: boolean;
|
|
36
|
-
overscroll: Overscroll;
|
|
37
|
-
flatDeck?: boolean;
|
|
38
|
-
toasts: ToastSchema[];
|
|
39
|
-
onDismissToast: (id: string) => void;
|
|
40
|
-
// TODO(burdon): Rename planks or just items?
|
|
41
36
|
layoutParts: LayoutParts;
|
|
42
37
|
attention: Attention;
|
|
38
|
+
toasts: ToastSchema[];
|
|
39
|
+
flatDeck?: boolean;
|
|
40
|
+
overscroll: Overscroll;
|
|
41
|
+
showHintsFooter: boolean;
|
|
43
42
|
slots?: {
|
|
44
43
|
wallpaper?: { classNames?: string };
|
|
45
44
|
};
|
|
45
|
+
onDismissToast: (id: string) => void;
|
|
46
46
|
};
|
|
47
47
|
|
|
48
48
|
export const DeckLayout = ({
|
|
49
|
-
|
|
49
|
+
layoutParts,
|
|
50
|
+
attention,
|
|
50
51
|
toasts,
|
|
51
|
-
onDismissToast,
|
|
52
52
|
flatDeck,
|
|
53
|
-
attention,
|
|
54
|
-
layoutParts,
|
|
55
|
-
slots,
|
|
56
53
|
overscroll,
|
|
54
|
+
showHintsFooter,
|
|
55
|
+
slots,
|
|
56
|
+
onDismissToast,
|
|
57
57
|
}: DeckLayoutProps) => {
|
|
58
58
|
const context = useLayout();
|
|
59
59
|
const {
|
|
@@ -69,9 +69,7 @@ export const DeckLayout = ({
|
|
|
69
69
|
} = context;
|
|
70
70
|
const { t } = useTranslation(DECK_PLUGIN);
|
|
71
71
|
const { plankSizing } = useDeckContext();
|
|
72
|
-
|
|
73
|
-
const searchEnabled = !!usePlugin('dxos.org/plugin/search');
|
|
74
|
-
|
|
72
|
+
const searchPlugin = usePlugin('dxos.org/plugin/search');
|
|
75
73
|
const fullScreenSlug = useMemo(() => firstIdInPart(layoutParts, 'fullScreen'), [layoutParts]);
|
|
76
74
|
|
|
77
75
|
const complementarySlug = useMemo(() => {
|
|
@@ -83,19 +81,37 @@ export const DeckLayout = ({
|
|
|
83
81
|
|
|
84
82
|
const activeId = useMemo(() => Array.from(attention.attended ?? [])[0], [attention.attended]);
|
|
85
83
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
84
|
+
const deckRef = useRef<HTMLDivElement | null>(null);
|
|
85
|
+
useEffect(() => {
|
|
86
|
+
// TODO(burdon): Can we prevent the need to re-scroll since the planks are preserved?
|
|
87
|
+
// E.g., hide the deck and just move the solo article?
|
|
88
|
+
if (layoutMode === 'deck' && activeId) {
|
|
89
|
+
// setTimeout(() => {
|
|
90
|
+
// const el = deckRef.current?.querySelector(`article[data-attendable-id="${activeId}"]`);
|
|
91
|
+
// el?.scrollIntoView({ behavior: 'smooth', inline: 'center' });
|
|
92
|
+
// }, 0);
|
|
93
|
+
}
|
|
94
|
+
}, [layoutMode, activeId]);
|
|
95
|
+
|
|
96
|
+
// TODO(burdon): Needs cleaning up.
|
|
97
|
+
const parts: LayoutEntry[] = useMemo(() => {
|
|
98
|
+
const parts = [...(layoutParts.main ?? [])];
|
|
99
|
+
for (const part of layoutParts.solo ?? []) {
|
|
100
|
+
if (!parts.find((entry) => entry.id === part.id)) {
|
|
101
|
+
parts.push(part);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
return parts;
|
|
105
|
+
}, [layoutParts.main, layoutParts.solo]);
|
|
106
|
+
|
|
107
|
+
const showPlank = (part: LayoutEntry) => {
|
|
108
|
+
return layoutMode === 'deck' || layoutParts.solo?.find((entry) => entry.id === part.id);
|
|
109
|
+
};
|
|
95
110
|
|
|
96
|
-
const
|
|
97
|
-
|
|
98
|
-
|
|
111
|
+
const padding =
|
|
112
|
+
layoutMode === 'deck' && overscroll === 'centering'
|
|
113
|
+
? calculateOverscroll(layoutParts.main, plankSizing, sidebarOpen, complementarySidebarOpen)
|
|
114
|
+
: {};
|
|
99
115
|
|
|
100
116
|
if (layoutMode === 'fullscreen') {
|
|
101
117
|
return <Fullscreen id={fullScreenSlug} />;
|
|
@@ -114,7 +130,7 @@ export const DeckLayout = ({
|
|
|
114
130
|
}
|
|
115
131
|
}}
|
|
116
132
|
>
|
|
117
|
-
{/* TODO(burdon):
|
|
133
|
+
{/* TODO(burdon): Factor out hook to set document title. */}
|
|
118
134
|
<ActiveNode id={activeId} />
|
|
119
135
|
|
|
120
136
|
<Main.Root
|
|
@@ -149,73 +165,63 @@ export const DeckLayout = ({
|
|
|
149
165
|
<Surface role='notch-end' />
|
|
150
166
|
</Main.Notch>
|
|
151
167
|
|
|
152
|
-
{/*
|
|
168
|
+
{/* Left sidebar. */}
|
|
153
169
|
<Sidebar attention={attention} layoutParts={layoutParts} />
|
|
154
170
|
|
|
171
|
+
{/* Right sidebar. */}
|
|
155
172
|
<ComplementarySidebar id={complementarySlug} layoutParts={layoutParts} flatDeck={flatDeck} />
|
|
156
173
|
|
|
157
174
|
{/* Dialog overlay to dismiss dialogs. */}
|
|
158
175
|
<Main.Overlay />
|
|
159
176
|
|
|
160
177
|
{/* No content. */}
|
|
161
|
-
{
|
|
162
|
-
<Main.Content>
|
|
178
|
+
{parts.length === 0 && (
|
|
179
|
+
<Main.Content handlesFocus>
|
|
163
180
|
<ContentEmpty />
|
|
164
181
|
</Main.Content>
|
|
165
182
|
)}
|
|
166
183
|
|
|
167
184
|
{/* Solo/deck mode. */}
|
|
168
|
-
{
|
|
169
|
-
<Main.Content bounce classNames=
|
|
170
|
-
<
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
layoutMode === '
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
key={layoutEntry.id}
|
|
199
|
-
entry={layoutEntry}
|
|
200
|
-
layoutParts={layoutParts}
|
|
201
|
-
part='main'
|
|
202
|
-
resizeable
|
|
203
|
-
flatDeck={flatDeck}
|
|
204
|
-
searchEnabled={searchEnabled}
|
|
205
|
-
/>
|
|
206
|
-
);
|
|
207
|
-
})}
|
|
208
|
-
</Deck.Root>
|
|
185
|
+
{parts.length !== 0 && (
|
|
186
|
+
<Main.Content bounce classNames='grid block-end-[--statusbar-size]' handlesFocus>
|
|
187
|
+
<div role='none' className={layoutMode === 'solo' ? 'contents' : 'relative'}>
|
|
188
|
+
<Deck.Root
|
|
189
|
+
ref={deckRef}
|
|
190
|
+
solo={layoutMode === 'solo'}
|
|
191
|
+
style={padding}
|
|
192
|
+
classNames={[
|
|
193
|
+
!flatDeck && 'surface-deck',
|
|
194
|
+
layoutMode === 'deck' && [
|
|
195
|
+
'absolute inset-0',
|
|
196
|
+
'transition-[padding] duration-200 ease-in-out',
|
|
197
|
+
slots?.wallpaper?.classNames,
|
|
198
|
+
],
|
|
199
|
+
]}
|
|
200
|
+
>
|
|
201
|
+
{parts.map((layoutEntry) => (
|
|
202
|
+
<Plank
|
|
203
|
+
key={layoutEntry.id}
|
|
204
|
+
entry={layoutEntry}
|
|
205
|
+
layoutParts={layoutParts}
|
|
206
|
+
part={layoutMode === 'solo' && layoutEntry.id === activeId ? 'solo' : 'main'}
|
|
207
|
+
flatDeck={flatDeck}
|
|
208
|
+
searchEnabled={!!searchPlugin}
|
|
209
|
+
resizeable={layoutMode === 'deck'}
|
|
210
|
+
classNames={showPlank(layoutEntry) ? '' : 'hidden'}
|
|
211
|
+
/>
|
|
212
|
+
))}
|
|
213
|
+
</Deck.Root>
|
|
214
|
+
</div>
|
|
209
215
|
</Main.Content>
|
|
210
216
|
)}
|
|
211
217
|
|
|
212
|
-
{/*
|
|
213
|
-
<Main.Content role='none' classNames=
|
|
218
|
+
{/* TODO(burdon): Why Main.Content? */}
|
|
219
|
+
<Main.Content role='none' classNames='fixed inset-inline-0 block-end-0 z-[2]'>
|
|
214
220
|
<Surface role='status-bar' limit={1} />
|
|
215
221
|
</Main.Content>
|
|
216
222
|
|
|
217
223
|
{/* Help hints. */}
|
|
218
|
-
{/* TODO(burdon):
|
|
224
|
+
{/* TODO(burdon): Need to make room for this in status bar. */}
|
|
219
225
|
{showHintsFooter && (
|
|
220
226
|
<div className='fixed bottom-0 left-0 right-0 h-[32px] z-[1] flex justify-center'>
|
|
221
227
|
<Surface role='hints' limit={1} />
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
//
|
|
4
4
|
|
|
5
5
|
import { Plus } from '@phosphor-icons/react';
|
|
6
|
-
import React, { useCallback, useLayoutEffect, useRef } from 'react';
|
|
6
|
+
import React, { type KeyboardEvent, useCallback, useLayoutEffect, useRef } from 'react';
|
|
7
7
|
|
|
8
8
|
import {
|
|
9
9
|
LayoutAction,
|
|
@@ -48,8 +48,10 @@ export const Plank = ({ entry, layoutParts, part, resizeable, flatDeck, searchEn
|
|
|
48
48
|
const { plankSizing } = useDeckContext();
|
|
49
49
|
const { graph } = useGraph();
|
|
50
50
|
const node = useNode(graph, entry.id);
|
|
51
|
+
const rootElement = useRef<HTMLDivElement | null>(null);
|
|
51
52
|
|
|
52
53
|
const attendableAttrs = createAttendableAttributes(entry.id);
|
|
54
|
+
const coordinate: LayoutCoordinate = { part, entryId: entry.id };
|
|
53
55
|
|
|
54
56
|
const size = plankSizing?.[entry.id] as number | undefined;
|
|
55
57
|
const setSize = useCallback(
|
|
@@ -59,18 +61,29 @@ export const Plank = ({ entry, layoutParts, part, resizeable, flatDeck, searchEn
|
|
|
59
61
|
[dispatch, entry.id],
|
|
60
62
|
);
|
|
61
63
|
|
|
62
|
-
|
|
64
|
+
// TODO(thure): Tabster’s focus group should handle moving focus to Main, but something is blocking it.
|
|
65
|
+
const handleKeyDown = useCallback((event: KeyboardEvent) => {
|
|
66
|
+
if (event.target === event.currentTarget && event.key === 'Escape') {
|
|
67
|
+
rootElement.current?.closest('main')?.focus();
|
|
68
|
+
}
|
|
69
|
+
}, []);
|
|
63
70
|
|
|
64
|
-
const ref = useRef<HTMLDivElement | null>(null);
|
|
65
71
|
useLayoutEffect(() => {
|
|
66
72
|
if (scrollIntoView === entry.id) {
|
|
67
|
-
|
|
73
|
+
rootElement.current?.scrollIntoView({ behavior: 'smooth', inline: 'center' });
|
|
68
74
|
}
|
|
69
75
|
}, [scrollIntoView]);
|
|
70
76
|
|
|
71
77
|
return (
|
|
72
|
-
<NaturalPlank.Root
|
|
73
|
-
|
|
78
|
+
<NaturalPlank.Root
|
|
79
|
+
size={size}
|
|
80
|
+
setSize={setSize}
|
|
81
|
+
classNames={classNames}
|
|
82
|
+
{...attendableAttrs}
|
|
83
|
+
onKeyDown={handleKeyDown}
|
|
84
|
+
ref={rootElement}
|
|
85
|
+
>
|
|
86
|
+
<NaturalPlank.Content classNames={[!flatDeck && 'surface-base']}>
|
|
74
87
|
{node ? (
|
|
75
88
|
<>
|
|
76
89
|
<NodePlankHeading
|
package/src/layout.ts
CHANGED
|
@@ -7,12 +7,12 @@ import {
|
|
|
7
7
|
type LayoutAdjustment,
|
|
8
8
|
type LayoutCoordinate,
|
|
9
9
|
type LayoutEntry,
|
|
10
|
+
type LayoutPart,
|
|
10
11
|
type LayoutParts,
|
|
11
12
|
SLUG_ENTRY_SEPARATOR,
|
|
12
13
|
SLUG_KEY_VALUE_SEPARATOR,
|
|
13
14
|
SLUG_LIST_SEPARATOR,
|
|
14
15
|
SLUG_PATH_SEPARATOR,
|
|
15
|
-
type LayoutPart,
|
|
16
16
|
} from '@dxos/app-framework';
|
|
17
17
|
|
|
18
18
|
import { type NewPlankPositioning } from './types';
|
|
@@ -161,7 +161,9 @@ export const mergeLayoutParts = (...layoutParts: LayoutParts[]): LayoutParts =>
|
|
|
161
161
|
};
|
|
162
162
|
|
|
163
163
|
//
|
|
164
|
-
//
|
|
164
|
+
// URI Projection
|
|
165
|
+
//
|
|
166
|
+
|
|
165
167
|
const parseLayoutEntry = (itemString: string): LayoutEntry => {
|
|
166
168
|
// Layout entries are in the form of 'id~path' or just 'id'
|
|
167
169
|
const [id, path] = itemString.split(SLUG_PATH_SEPARATOR);
|
package/src/util/overscroll.ts
CHANGED
|
@@ -2,96 +2,89 @@
|
|
|
2
2
|
// Copyright 2024 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import
|
|
6
|
-
import { PLANK_DEFAULTS } from '@dxos/react-ui-deck';
|
|
5
|
+
import type { CSSProperties } from 'react';
|
|
7
6
|
|
|
8
|
-
import { type
|
|
7
|
+
import { type LayoutEntry } from '@dxos/app-framework';
|
|
8
|
+
import { PLANK_DEFAULTS } from '@dxos/react-ui-deck';
|
|
9
9
|
|
|
10
|
+
/**
|
|
11
|
+
* ┌────────────────────────────────────────────────────────────────────────────────────────────────────┐
|
|
12
|
+
* | Overscroll Padding Calculation for Centering Planks on Screen. │
|
|
13
|
+
* ├────────────────────────────────────────────────────────────────────────────────────────────────────┤
|
|
14
|
+
* │ NOTE(Zan): I found the way you calculate the overscroll padding to center a plank on the screen │
|
|
15
|
+
* │ at the edges of the scroll context a bit confusing, so I've diagrammed it here. │
|
|
16
|
+
* │ │
|
|
17
|
+
* │ Multiple Planks: │
|
|
18
|
+
* │ ─────────────── │
|
|
19
|
+
* | Use the following overscroll padding calculation centering the boundary planks on the SCREEN. │
|
|
20
|
+
* │ │
|
|
21
|
+
* │ Left Padding: Right Padding: │
|
|
22
|
+
* │ ┌───┬────┬──────────────────┬──────┐ ┌──────┬──────────────────┬────┬───┐ │
|
|
23
|
+
* │ │ │████│ Ideal │ │ │ │ Ideal │████│ │ │
|
|
24
|
+
* │ │ S │█PL█│ first │ │ │ │ last │█PR█│ C │ │
|
|
25
|
+
* │ │ │████│ plank │ │ │ │ plank │████│ │ │
|
|
26
|
+
* │ └───┴────┴──────────────────┴──────┘ └──────┴──────────────────┴────┴───┘ │
|
|
27
|
+
* │ <--------- screen width -----------> <---------- screen width ----------> │
|
|
28
|
+
* │ │
|
|
29
|
+
* │ PL = ((screen width - Plank Width) / 2) - S │
|
|
30
|
+
* │ PR = ((screen width - Plank Width) / 2) - C │
|
|
31
|
+
* │ │
|
|
32
|
+
* │ S = Sidebar width C = Complementary sidebar width │
|
|
33
|
+
* │ PL = Padding Left PR = Padding Right │
|
|
34
|
+
* │ │
|
|
35
|
+
* │ Single Plank: │
|
|
36
|
+
* │ ───────────── │
|
|
37
|
+
* │ For a single plank we use the following overscroll padding calculation to center the plank in │
|
|
38
|
+
* │ the content area: │
|
|
39
|
+
* │ │
|
|
40
|
+
* │ ┌───┬───────────────────────┬───┬───────────────────────┬───┐ │
|
|
41
|
+
* │ │ │███████████████████████│ │███████████████████████│ │ │
|
|
42
|
+
* │ │ S │█████ Left Padding ████│ P │████ Right Padding ████│ C │ │
|
|
43
|
+
* │ │ │███████████████████████│ │███████████████████████│ │ │
|
|
44
|
+
* │ └───┴───────────────────────┴───┴───────────────────────┴───┘ │
|
|
45
|
+
* │ <------------------------ screen width ---------------------> │
|
|
46
|
+
* │ │
|
|
47
|
+
* │ Left/Right Padding Width = (screen width - S - P - C) / 2 │
|
|
48
|
+
* │ │
|
|
49
|
+
* │ S = Sidebar width (may be 0) │
|
|
50
|
+
* │ P = Plank width (centered) │
|
|
51
|
+
* │ C = Complementary sidebar width (may be 0) │
|
|
52
|
+
* └────────────────────────────────────────────────────────────────────────────────────────────────────┘
|
|
53
|
+
*/
|
|
10
54
|
export const calculateOverscroll = (
|
|
11
|
-
|
|
12
|
-
layoutParts: LayoutParts,
|
|
55
|
+
planks: LayoutEntry[] | undefined,
|
|
13
56
|
plankSizing: Record<string, number>,
|
|
14
57
|
sidebarOpen: boolean,
|
|
15
58
|
complementarySidebarOpen: boolean,
|
|
16
|
-
|
|
17
|
-
)
|
|
18
|
-
if (!(layoutMode === 'deck' && overscroll === 'centering')) {
|
|
19
|
-
return;
|
|
20
|
-
}
|
|
21
|
-
if (!layoutParts.main || layoutParts.main.length === 0) {
|
|
59
|
+
): Pick<CSSProperties, 'paddingLeft' | 'paddingRight'> | undefined => {
|
|
60
|
+
if (!planks?.length) {
|
|
22
61
|
return;
|
|
23
62
|
}
|
|
24
63
|
|
|
25
|
-
/**
|
|
26
|
-
* ┌────────────────────────────────────────────────────────────────────────────────────────────────────┐
|
|
27
|
-
* | Overscroll Padding Calculation for Centering Planks on Screen. │
|
|
28
|
-
* ├────────────────────────────────────────────────────────────────────────────────────────────────────┤
|
|
29
|
-
* │ NOTE(Zan): I found the way you calculate the overscroll padding to center a plank on the screen │
|
|
30
|
-
* │ at the edges of the scroll context a bit confusing, so I've diagrammed it here. │
|
|
31
|
-
* │ │
|
|
32
|
-
* │ Multiple Planks: │
|
|
33
|
-
* │ ─────────────── │
|
|
34
|
-
* | Use the following overscroll padding calculation centering the boundary planks on the SCREEN. │
|
|
35
|
-
* │ │
|
|
36
|
-
* │ Left Padding: Right Padding: │
|
|
37
|
-
* │ ┌───┬────┬──────────────────┬──────┐ ┌──────┬──────────────────┬────┬───┐ │
|
|
38
|
-
* │ │ │████│ Ideal │ │ │ │ Ideal │████│ │ │
|
|
39
|
-
* │ │ S │█PL█│ first │ │ │ │ last │█PR█│ C │ │
|
|
40
|
-
* │ │ │████│ plank │ │ │ │ plank │████│ │ │
|
|
41
|
-
* │ └───┴────┴──────────────────┴──────┘ └──────┴──────────────────┴────┴───┘ │
|
|
42
|
-
* │ <--------- screen width -----------> <---------- screen width ----------> │
|
|
43
|
-
* │ │
|
|
44
|
-
* │ PL = ((screen width - Plank Width) / 2) - S │
|
|
45
|
-
* │ PR = ((screen width - Plank Width) / 2) - C │
|
|
46
|
-
* │ │
|
|
47
|
-
* │ S = Sidebar width C = Complementary sidebar width │
|
|
48
|
-
* │ PL = Padding Left PR = Padding Right │
|
|
49
|
-
* │ │
|
|
50
|
-
* │ Single Plank: │
|
|
51
|
-
* │ ───────────── │
|
|
52
|
-
* │ For a single plank we use the following overscroll padding calculation to center the plank in │
|
|
53
|
-
* │ the content area: │
|
|
54
|
-
* │ │
|
|
55
|
-
* │ ┌───┬───────────────────────┬───┬───────────────────────┬───┐ │
|
|
56
|
-
* │ │ │███████████████████████│ │███████████████████████│ │ │
|
|
57
|
-
* │ │ S │█████ Left Padding ████│ P │████ Right Padding ████│ C │ │
|
|
58
|
-
* │ │ │███████████████████████│ │███████████████████████│ │ │
|
|
59
|
-
* │ └───┴───────────────────────┴───┴───────────────────────┴───┘ │
|
|
60
|
-
* │ <------------------------ screen width ---------------------> │
|
|
61
|
-
* │ │
|
|
62
|
-
* │ Left/Right Padding Width = (screen width - S - P - C) / 2 │
|
|
63
|
-
* │ │
|
|
64
|
-
* │ S = Sidebar width (may be 0) │
|
|
65
|
-
* │ P = Plank width (centered) │
|
|
66
|
-
* │ C = Complementary sidebar width (may be 0) │
|
|
67
|
-
* └────────────────────────────────────────────────────────────────────────────────────────────────────┘
|
|
68
|
-
*/
|
|
69
|
-
|
|
70
64
|
// TODO(Zan): Move complementary sidebar size (360px), sidebar size (270px), plank resize handle size (20px) to CSS variables.
|
|
71
65
|
const sidebarWidth = sidebarOpen ? '270px' : '0px';
|
|
72
66
|
const complementarySidebarWidth = complementarySidebarOpen ? '360px' : '0px';
|
|
73
67
|
|
|
74
68
|
const getPlankSize = (id: string) => (plankSizing[id] ?? PLANK_DEFAULTS.size).toFixed(2) + 'rem';
|
|
75
69
|
|
|
76
|
-
if (
|
|
70
|
+
if (planks.length === 1) {
|
|
77
71
|
// Center the plank in the content area.
|
|
78
|
-
|
|
79
|
-
const plank = layoutParts.main[0];
|
|
72
|
+
const plank = planks[0];
|
|
80
73
|
const plankSize = getPlankSize(plank.id);
|
|
81
74
|
const overscrollPadding = `max(0px, calc(((100dvw - ${sidebarWidth} - ${complementarySidebarWidth} - (${plankSize} + 20px)) / 2)))`;
|
|
82
75
|
|
|
83
76
|
return { paddingLeft: overscrollPadding, paddingRight: overscrollPadding };
|
|
84
77
|
} else {
|
|
85
78
|
// Center the plank on the screen.
|
|
79
|
+
const first = planks[0];
|
|
80
|
+
const firstSize = getPlankSize(first.id);
|
|
86
81
|
|
|
87
|
-
const
|
|
88
|
-
const
|
|
89
|
-
const paddingLeft = `max(0px, calc(((100dvw - (${firstPlankInlineSize} + 20px)) / 2) - ${sidebarWidth}))`;
|
|
90
|
-
|
|
91
|
-
const lastPlank = layoutParts.main[layoutParts.main.length - 1];
|
|
92
|
-
const lastPlankInlineSize = getPlankSize(lastPlank.id);
|
|
93
|
-
const paddingRight = `max(0px, calc(((100dvw - (${lastPlankInlineSize} + 20px)) / 2) - ${complementarySidebarWidth}))`;
|
|
82
|
+
const last = planks[planks.length - 1];
|
|
83
|
+
const lastSize = getPlankSize(last.id);
|
|
94
84
|
|
|
95
|
-
return {
|
|
85
|
+
return {
|
|
86
|
+
paddingLeft: `max(0px, calc(((100dvw - (${firstSize} + 20px)) / 2) - ${sidebarWidth}))`,
|
|
87
|
+
paddingRight: `max(0px, calc(((100dvw - (${lastSize} + 20px)) / 2) - ${complementarySidebarWidth}))`,
|
|
88
|
+
};
|
|
96
89
|
}
|
|
97
90
|
};
|