@dxos/plugin-deck 0.6.8-main.046e6cf → 0.6.8-staging.63bcb81
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 +120 -93
- 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 -12
- 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 +24 -24
- package/src/components/DeckLayout/DeckLayout.tsx +80 -79
- package/src/components/DeckLayout/Plank.tsx +26 -12
- package/src/layout.ts +4 -2
- package/src/util/overscroll.ts +61 -68
|
@@ -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,
|
|
@@ -16,7 +17,7 @@ import {
|
|
|
16
17
|
} from '@dxos/app-framework';
|
|
17
18
|
import { Button, Dialog, Main, Popover, useTranslation } from '@dxos/react-ui';
|
|
18
19
|
import { Deck } from '@dxos/react-ui-deck';
|
|
19
|
-
import { getSize
|
|
20
|
+
import { getSize } from '@dxos/react-ui-theme';
|
|
20
21
|
|
|
21
22
|
import { ActiveNode } from './ActiveNode';
|
|
22
23
|
import { ComplementarySidebar } from './ComplementarySidebar';
|
|
@@ -32,30 +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
36
|
layoutParts: LayoutParts;
|
|
41
37
|
attention: Attention;
|
|
42
|
-
|
|
38
|
+
toasts: ToastSchema[];
|
|
39
|
+
flatDeck?: boolean;
|
|
40
|
+
overscroll: Overscroll;
|
|
41
|
+
showHintsFooter: boolean;
|
|
43
42
|
slots?: {
|
|
44
43
|
wallpaper?: { classNames?: string };
|
|
45
|
-
deck?: { classNames?: string };
|
|
46
|
-
plank?: { classNames?: string };
|
|
47
44
|
};
|
|
45
|
+
onDismissToast: (id: string) => void;
|
|
48
46
|
};
|
|
49
47
|
|
|
50
48
|
export const DeckLayout = ({
|
|
51
|
-
|
|
49
|
+
layoutParts,
|
|
50
|
+
attention,
|
|
52
51
|
toasts,
|
|
53
|
-
onDismissToast,
|
|
54
52
|
flatDeck,
|
|
55
|
-
attention,
|
|
56
|
-
layoutParts,
|
|
57
|
-
slots,
|
|
58
53
|
overscroll,
|
|
54
|
+
showHintsFooter,
|
|
55
|
+
slots,
|
|
56
|
+
onDismissToast,
|
|
59
57
|
}: DeckLayoutProps) => {
|
|
60
58
|
const context = useLayout();
|
|
61
59
|
const {
|
|
@@ -71,7 +69,7 @@ export const DeckLayout = ({
|
|
|
71
69
|
} = context;
|
|
72
70
|
const { t } = useTranslation(DECK_PLUGIN);
|
|
73
71
|
const { plankSizing } = useDeckContext();
|
|
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(() => {
|
|
@@ -81,17 +79,39 @@ export const DeckLayout = ({
|
|
|
81
79
|
}
|
|
82
80
|
}, [layoutParts]);
|
|
83
81
|
|
|
84
|
-
const searchEnabled = !!usePlugin('dxos.org/plugin/search');
|
|
85
82
|
const activeId = useMemo(() => Array.from(attention.attended ?? [])[0], [attention.attended]);
|
|
86
83
|
|
|
87
|
-
const
|
|
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
|
+
};
|
|
110
|
+
|
|
111
|
+
const padding =
|
|
112
|
+
layoutMode === 'deck' && overscroll === 'centering'
|
|
113
|
+
? calculateOverscroll(layoutParts.main, plankSizing, sidebarOpen, complementarySidebarOpen)
|
|
114
|
+
: {};
|
|
95
115
|
|
|
96
116
|
if (layoutMode === 'fullscreen') {
|
|
97
117
|
return <Fullscreen id={fullScreenSlug} />;
|
|
@@ -110,6 +130,7 @@ export const DeckLayout = ({
|
|
|
110
130
|
}
|
|
111
131
|
}}
|
|
112
132
|
>
|
|
133
|
+
{/* TODO(burdon): Factor out hook to set document title. */}
|
|
113
134
|
<ActiveNode id={activeId} />
|
|
114
135
|
|
|
115
136
|
<Main.Root
|
|
@@ -144,83 +165,63 @@ export const DeckLayout = ({
|
|
|
144
165
|
<Surface role='notch-end' />
|
|
145
166
|
</Main.Notch>
|
|
146
167
|
|
|
147
|
-
{/*
|
|
168
|
+
{/* Left sidebar. */}
|
|
148
169
|
<Sidebar attention={attention} layoutParts={layoutParts} />
|
|
149
170
|
|
|
171
|
+
{/* Right sidebar. */}
|
|
150
172
|
<ComplementarySidebar id={complementarySlug} layoutParts={layoutParts} flatDeck={flatDeck} />
|
|
151
173
|
|
|
152
174
|
{/* Dialog overlay to dismiss dialogs. */}
|
|
153
175
|
<Main.Overlay />
|
|
154
176
|
|
|
155
|
-
{/*
|
|
156
|
-
{
|
|
157
|
-
<Main.Content
|
|
158
|
-
<
|
|
159
|
-
<Deck.Root
|
|
160
|
-
classNames={mx(
|
|
161
|
-
'absolute inset-0',
|
|
162
|
-
!flatDeck && 'surface-deck',
|
|
163
|
-
slots?.wallpaper?.classNames,
|
|
164
|
-
slots?.deck?.classNames,
|
|
165
|
-
'transition-[padding] duration-200 ease-in-out',
|
|
166
|
-
)}
|
|
167
|
-
style={{ ...overscrollAmount }}
|
|
168
|
-
>
|
|
169
|
-
{layoutParts.main.map((layoutEntry) => {
|
|
170
|
-
return (
|
|
171
|
-
<Plank
|
|
172
|
-
key={layoutEntry.id}
|
|
173
|
-
entry={layoutEntry}
|
|
174
|
-
layoutParts={layoutParts}
|
|
175
|
-
part='main'
|
|
176
|
-
resizeable
|
|
177
|
-
flatDeck={flatDeck}
|
|
178
|
-
searchEnabled={searchEnabled}
|
|
179
|
-
/>
|
|
180
|
-
);
|
|
181
|
-
})}
|
|
182
|
-
</Deck.Root>
|
|
183
|
-
</div>
|
|
177
|
+
{/* No content. */}
|
|
178
|
+
{parts.length === 0 && (
|
|
179
|
+
<Main.Content handlesFocus>
|
|
180
|
+
<ContentEmpty />
|
|
184
181
|
</Main.Content>
|
|
185
182
|
)}
|
|
186
183
|
|
|
187
|
-
{/* Solo
|
|
188
|
-
{
|
|
189
|
-
<Main.Content bounce classNames=
|
|
190
|
-
<
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
184
|
+
{/* Solo/deck mode. */}
|
|
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) => (
|
|
196
202
|
<Plank
|
|
197
203
|
key={layoutEntry.id}
|
|
198
204
|
entry={layoutEntry}
|
|
199
205
|
layoutParts={layoutParts}
|
|
200
|
-
part='solo'
|
|
206
|
+
part={layoutMode === 'solo' && layoutEntry.id === activeId ? 'solo' : 'main'}
|
|
201
207
|
flatDeck={flatDeck}
|
|
202
|
-
|
|
208
|
+
searchEnabled={!!searchPlugin}
|
|
209
|
+
resizeable={layoutMode === 'deck'}
|
|
210
|
+
classNames={showPlank(layoutEntry) ? '' : 'hidden'}
|
|
203
211
|
/>
|
|
204
|
-
)
|
|
205
|
-
|
|
206
|
-
</
|
|
207
|
-
</Main.Content>
|
|
208
|
-
)}
|
|
209
|
-
|
|
210
|
-
{((layoutMode === 'solo' && (!layoutParts.solo || layoutParts.solo.length === 0)) ||
|
|
211
|
-
(layoutMode === 'deck' && (!layoutParts.main || layoutParts.main.length === 0))) && (
|
|
212
|
-
<Main.Content>
|
|
213
|
-
<ContentEmpty />
|
|
212
|
+
))}
|
|
213
|
+
</Deck.Root>
|
|
214
|
+
</div>
|
|
214
215
|
</Main.Content>
|
|
215
216
|
)}
|
|
216
217
|
|
|
217
|
-
{/*
|
|
218
|
-
<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]'>
|
|
219
220
|
<Surface role='status-bar' limit={1} />
|
|
220
221
|
</Main.Content>
|
|
221
222
|
|
|
222
223
|
{/* Help hints. */}
|
|
223
|
-
{/* TODO(burdon):
|
|
224
|
+
{/* TODO(burdon): Need to make room for this in status bar. */}
|
|
224
225
|
{showHintsFooter && (
|
|
225
226
|
<div className='fixed bottom-0 left-0 right-0 h-[32px] z-[1] flex justify-center'>
|
|
226
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 } from 'react';
|
|
6
|
+
import React, { type KeyboardEvent, useCallback, useLayoutEffect, useRef } from 'react';
|
|
7
7
|
|
|
8
8
|
import {
|
|
9
9
|
LayoutAction,
|
|
@@ -24,7 +24,6 @@ import { Plank as NaturalPlank } from '@dxos/react-ui-deck';
|
|
|
24
24
|
import { NodePlankHeading } from './NodePlankHeading';
|
|
25
25
|
import { PlankContentError, PlankError } from './PlankError';
|
|
26
26
|
import { PlankLoading } from './PlankLoading';
|
|
27
|
-
import { NAV_ID } from './constants';
|
|
28
27
|
import { DeckAction } from '../../DeckPlugin';
|
|
29
28
|
import { useNode } from '../../hooks';
|
|
30
29
|
import { DECK_PLUGIN } from '../../meta';
|
|
@@ -49,8 +48,10 @@ export const Plank = ({ entry, layoutParts, part, resizeable, flatDeck, searchEn
|
|
|
49
48
|
const { plankSizing } = useDeckContext();
|
|
50
49
|
const { graph } = useGraph();
|
|
51
50
|
const node = useNode(graph, entry.id);
|
|
51
|
+
const rootElement = useRef<HTMLDivElement | null>(null);
|
|
52
52
|
|
|
53
53
|
const attendableAttrs = createAttendableAttributes(entry.id);
|
|
54
|
+
const coordinate: LayoutCoordinate = { part, entryId: entry.id };
|
|
54
55
|
|
|
55
56
|
const size = plankSizing?.[entry.id] as number | undefined;
|
|
56
57
|
const setSize = useCallback(
|
|
@@ -60,23 +61,36 @@ export const Plank = ({ entry, layoutParts, part, resizeable, flatDeck, searchEn
|
|
|
60
61
|
[dispatch, entry.id],
|
|
61
62
|
);
|
|
62
63
|
|
|
63
|
-
|
|
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
|
+
}, []);
|
|
70
|
+
|
|
71
|
+
useLayoutEffect(() => {
|
|
72
|
+
if (scrollIntoView === entry.id) {
|
|
73
|
+
rootElement.current?.scrollIntoView({ behavior: 'smooth', inline: 'center' });
|
|
74
|
+
}
|
|
75
|
+
}, [scrollIntoView]);
|
|
64
76
|
|
|
65
77
|
return (
|
|
66
|
-
<NaturalPlank.Root
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
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']}>
|
|
73
87
|
{node ? (
|
|
74
88
|
<>
|
|
75
89
|
<NodePlankHeading
|
|
90
|
+
id={entry.id}
|
|
91
|
+
node={node}
|
|
76
92
|
layoutPart={coordinate.part}
|
|
77
93
|
layoutParts={layoutParts}
|
|
78
|
-
node={node}
|
|
79
|
-
id={entry.id}
|
|
80
94
|
popoverAnchorId={popoverAnchorId}
|
|
81
95
|
flatDeck={flatDeck}
|
|
82
96
|
/>
|
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,70 +2,64 @@
|
|
|
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
|
-
|
|
55
|
+
planks: LayoutEntry[] | undefined,
|
|
56
|
+
plankSizing: Record<string, number>,
|
|
12
57
|
sidebarOpen: boolean,
|
|
13
58
|
complementarySidebarOpen: boolean,
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
overscroll: Overscroll,
|
|
17
|
-
) => {
|
|
18
|
-
if (!(layoutMode === 'deck' && overscroll === 'centering')) {
|
|
59
|
+
): Pick<CSSProperties, 'paddingLeft' | 'paddingRight'> | undefined => {
|
|
60
|
+
if (!planks?.length) {
|
|
19
61
|
return;
|
|
20
62
|
}
|
|
21
|
-
if (!layoutParts.main || layoutParts.main.length === 0) {
|
|
22
|
-
return;
|
|
23
|
-
}
|
|
24
|
-
|
|
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
63
|
|
|
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';
|
|
@@ -73,25 +67,24 @@ export const calculateOverscroll = (
|
|
|
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
|
};
|