shelving 1.268.0 → 1.268.2
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/package.json +1 -1
- package/react/createAPIContext.d.ts +4 -1
- package/react/createAPIContext.js +7 -4
- package/react/createDBContext.d.ts +4 -1
- package/react/createDBContext.js +9 -9
- package/ui/docs/DocumentationCard.js +2 -1
- package/ui/docs/DocumentationCard.tsx +4 -1
- package/ui/form/Progress.css +23 -0
- package/ui/form/Progress.d.ts +1 -0
- package/ui/form/Progress.js +1 -0
- package/ui/form/Progress.module.css +1 -10
- package/ui/form/Progress.tsx +1 -0
- package/ui/layout/SidebarLayout.js +4 -9
- package/ui/layout/SidebarLayout.module.css +51 -16
- package/ui/layout/SidebarLayout.tsx +30 -32
- package/ui/transition/CollapseTransition.d.ts +0 -1
- package/ui/transition/CollapseTransition.js +4 -2
- package/ui/transition/CollapseTransition.tsx +5 -2
- package/ui/transition/Transition.d.ts +16 -5
- package/ui/transition/Transition.js +9 -9
- package/ui/transition/Transition.tsx +32 -18
- /package/ui/transition/{CollapseTransition.css → CollapseTransition.module.css} +0 -0
- /package/ui/transition/{Transition.css → Transition.module.css} +0 -0
package/package.json
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { type ReactElement } from "react";
|
|
2
2
|
import type { Endpoint } from "../api/endpoint/Endpoint.js";
|
|
3
|
+
import { APICache } from "../api/index.js";
|
|
3
4
|
import type { APIProvider } from "../api/provider/APIProvider.js";
|
|
4
5
|
import type { EndpointStore } from "../api/store/EndpointStore.js";
|
|
5
6
|
import type { ChildProps } from "../ui/index.js";
|
|
@@ -13,8 +14,10 @@ export interface APIContext<P, R> {
|
|
|
13
14
|
/** React hook to return an `EndpointStore` for the specified endpoint/payload in the current `APIProvider` context. */
|
|
14
15
|
useAPI<PP extends P, RR extends R>(this: void, endpoint: Endpoint<PP, RR>, payload: PP): EndpointStore<PP, RR>;
|
|
15
16
|
useAPI<PP extends P, RR extends R>(this: void, endpoint: Nullish<Endpoint<PP, RR>>, payload: PP): EndpointStore<PP, RR> | undefined;
|
|
16
|
-
/**
|
|
17
|
+
/** Create a new `APICache` and provide it as context to child nodes, to allow them to use `useAPI()` */
|
|
17
18
|
readonly APIContext: (props: ChildProps) => ReactElement;
|
|
19
|
+
/** Return the underlying `APICache` for `<APIContext>` */
|
|
20
|
+
requireAPICache(): APICache<P, R>;
|
|
18
21
|
}
|
|
19
22
|
/**
|
|
20
23
|
* Create an API context.
|
|
@@ -15,15 +15,18 @@ import { useStore } from "./useStore.js";
|
|
|
15
15
|
*/
|
|
16
16
|
export function createAPIContext(provider) {
|
|
17
17
|
const CacheContext = createContext(undefined);
|
|
18
|
-
function
|
|
18
|
+
function requireAPICache(caller = requireAPICache) {
|
|
19
19
|
const cache = use(CacheContext);
|
|
20
20
|
if (!cache)
|
|
21
|
-
throw new RequiredError(`
|
|
22
|
-
return
|
|
21
|
+
throw new RequiredError(`Must be used inside <APIContext>`, { caller });
|
|
22
|
+
return cache;
|
|
23
|
+
}
|
|
24
|
+
function useAPI(endpoint, payload) {
|
|
25
|
+
return useStore(endpoint ? requireAPICache(useAPI).get(endpoint).get(payload) : undefined);
|
|
23
26
|
}
|
|
24
27
|
function APIContext({ children }) {
|
|
25
28
|
const cache = useInstance(APICache, provider);
|
|
26
29
|
return _jsx(CacheContext, { value: cache, children: children });
|
|
27
30
|
}
|
|
28
|
-
return { useAPI, APIContext };
|
|
31
|
+
return { requireAPICache, useAPI, APIContext };
|
|
29
32
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { type ReactElement, type ReactNode } from "react";
|
|
2
|
+
import { DBCache } from "../db/cache/DBCache.js";
|
|
2
3
|
import type { Collection } from "../db/collection/Collection.js";
|
|
3
4
|
import type { DBProvider } from "../db/provider/DBProvider.js";
|
|
4
5
|
import type { ItemStore } from "../db/store/ItemStore.js";
|
|
@@ -23,10 +24,12 @@ export interface DBContext<I extends Identifier, T extends Data> {
|
|
|
23
24
|
query: Nullish<Query<Item<II, TT>>>): QueryStore<II, TT> | undefined;
|
|
24
25
|
useQuery<II extends I, TT extends T>(collection: Collection<string, II, TT>, //
|
|
25
26
|
query: Query<Item<II, TT>>): QueryStore<II, TT>;
|
|
26
|
-
/**
|
|
27
|
+
/** Create a new `DBCache` and provide it as context to child nodes, to allow them to use `useItem()` and `useQuery()` */
|
|
27
28
|
readonly DBContext: ({ children }: {
|
|
28
29
|
children: ReactNode;
|
|
29
30
|
}) => ReactElement;
|
|
31
|
+
/** Return the underlying `DBCache` for `<DBContext>` */
|
|
32
|
+
requireDBCache(): DBCache<I, T>;
|
|
30
33
|
}
|
|
31
34
|
/**
|
|
32
35
|
* Create a data context
|
package/react/createDBContext.js
CHANGED
|
@@ -15,23 +15,23 @@ import { useStore } from "./useStore.js";
|
|
|
15
15
|
*/
|
|
16
16
|
export function createDBContext(provider) {
|
|
17
17
|
const CacheContext = createContext(undefined);
|
|
18
|
-
function
|
|
19
|
-
id) {
|
|
18
|
+
function requireDBCache(caller = requireDBCache) {
|
|
20
19
|
const cache = use(CacheContext);
|
|
21
20
|
if (!cache)
|
|
22
|
-
throw new RequiredError(
|
|
23
|
-
return
|
|
21
|
+
throw new RequiredError(`Must be used inside <DBContext>`, { caller });
|
|
22
|
+
return cache;
|
|
23
|
+
}
|
|
24
|
+
function useItem(collection, //
|
|
25
|
+
id) {
|
|
26
|
+
return useStore(collection && id ? requireDBCache(useItem).getItem(collection, id) : undefined);
|
|
24
27
|
}
|
|
25
28
|
function useQuery(collection, //
|
|
26
29
|
query) {
|
|
27
|
-
|
|
28
|
-
if (!cache)
|
|
29
|
-
throw new RequiredError("useQuery() can only be used inside <DBContext>", { caller: useQuery });
|
|
30
|
-
return useStore(collection && query ? cache.getQuery(collection, query) : undefined);
|
|
30
|
+
return useStore(collection && query ? requireDBCache(useQuery).getQuery(collection, query) : undefined);
|
|
31
31
|
}
|
|
32
32
|
function DBContext({ children }) {
|
|
33
33
|
const cache = useInstance(DBCache, provider);
|
|
34
34
|
return _jsx(CacheContext, { value: cache, children: children });
|
|
35
35
|
}
|
|
36
|
-
return { useItem, useQuery, DBContext };
|
|
36
|
+
return { requireDBCache, useItem, useQuery, DBContext };
|
|
37
37
|
}
|
|
@@ -3,6 +3,7 @@ import { Card } from "../block/Card.js";
|
|
|
3
3
|
import { Paragraph } from "../block/Paragraph.js";
|
|
4
4
|
import { Row } from "../block/Row.js";
|
|
5
5
|
import { Subheading } from "../block/Subheading.js";
|
|
6
|
+
import { CollapseTransition } from "../transition/CollapseTransition.js";
|
|
6
7
|
import { DocumentationButtons } from "./DocumentationButtons.js";
|
|
7
8
|
import { DocumentationKind, getDocumentationKindColor } from "./DocumentationKind.js";
|
|
8
9
|
import { DocumentationSignatures } from "./DocumentationSignatures.js";
|
|
@@ -19,5 +20,5 @@ export function DocumentationCard({ path, title, name, kind, description, signat
|
|
|
19
20
|
class: _memberOf, ...props }) {
|
|
20
21
|
// `path` is the symbol's own canonical URL, stamped by `flattenTree()` — link straight to it.
|
|
21
22
|
const color = kind ? getDocumentationKindColor(kind) : undefined;
|
|
22
|
-
return (_jsxs(Card, { href: path, color: color, children: [_jsx(Subheading, { space: "none", children: _jsxs(Row, { left: true, wrap: true, gap: "xsmall", children: [title ?? name, kind && _jsx(DocumentationKind, { kind: kind })] }) }), _jsx(DocumentationButtons, { ...props, space: "none" }), description && _jsx(Paragraph, { children: description }), _jsx(DocumentationSignatures, { signatures: signatures })] }));
|
|
23
|
+
return (_jsxs(Card, { href: path, color: color, children: [_jsx(Subheading, { space: "none", children: _jsxs(Row, { left: true, wrap: true, gap: "xsmall", children: [title ?? name, kind && _jsx(DocumentationKind, { kind: kind })] }) }), _jsx(CollapseTransition, { children: _jsx(DocumentationButtons, { ...props, space: "none" }) }), description && _jsx(Paragraph, { children: description }), _jsx(DocumentationSignatures, { signatures: signatures })] }));
|
|
23
24
|
}
|
|
@@ -4,6 +4,7 @@ import { Card } from "../block/Card.js";
|
|
|
4
4
|
import { Paragraph } from "../block/Paragraph.js";
|
|
5
5
|
import { Row } from "../block/Row.js";
|
|
6
6
|
import { Subheading } from "../block/Subheading.js";
|
|
7
|
+
import { CollapseTransition } from "../transition/CollapseTransition.js";
|
|
7
8
|
import { DocumentationButtons } from "./DocumentationButtons.js";
|
|
8
9
|
import { DocumentationKind, getDocumentationKindColor } from "./DocumentationKind.js";
|
|
9
10
|
import { DocumentationSignatures } from "./DocumentationSignatures.js";
|
|
@@ -37,7 +38,9 @@ export function DocumentationCard({
|
|
|
37
38
|
{kind && <DocumentationKind kind={kind} />}
|
|
38
39
|
</Row>
|
|
39
40
|
</Subheading>
|
|
40
|
-
<
|
|
41
|
+
<CollapseTransition>
|
|
42
|
+
<DocumentationButtons {...props} space="none" />
|
|
43
|
+
</CollapseTransition>
|
|
41
44
|
{description && <Paragraph>{description}</Paragraph>}
|
|
42
45
|
<DocumentationSignatures signatures={signatures} />
|
|
43
46
|
</Card>
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Keyframes for the `Progress` indeterminate animation.
|
|
3
|
+
*
|
|
4
|
+
* This lives in a plain (non-module) CSS file, not `Progress.module.css`, because Bun's CSS-modules
|
|
5
|
+
* transform renames the `@keyframes` identifier (appends the module hash) but does *not* rewrite the
|
|
6
|
+
* matching `animation` reference, so a keyframes block defined in the module file animates against a
|
|
7
|
+
* name that no longer exists. Keeping the declaration global here leaves the name stable, and the
|
|
8
|
+
* un-rewritten `animation: progress-flow` reference in `Progress.module.css` resolves to it. This
|
|
9
|
+
* mirrors how the `transition` components keep their keyframes in sibling `.css` files.
|
|
10
|
+
*
|
|
11
|
+
* Tracked in dhoulb/shelving#259 (revert once Bun ships the fix for oven-sh/bun#18921).
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
/* Conveyor the block fully off the left edge, across, and fully off the right — then it loops back round unseen. `-70%`/`170%` fully clear a `40%`-wide block, and `linear` timing keeps a constant speed with no ease-out stall at the ends. */
|
|
15
|
+
@keyframes progress-flow {
|
|
16
|
+
from {
|
|
17
|
+
background-position: -70% 0;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
to {
|
|
21
|
+
background-position: 170% 0;
|
|
22
|
+
}
|
|
23
|
+
}
|
package/ui/form/Progress.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ import type { ReactElement } from "react";
|
|
|
2
2
|
import { type Nullish } from "../../util/null.js";
|
|
3
3
|
import { type ColorVariants } from "../style/Color.js";
|
|
4
4
|
import { type StatusVariants } from "../style/Status.js";
|
|
5
|
+
import "./Progress.css";
|
|
5
6
|
/**
|
|
6
7
|
* Props for `Progress`, a continuous horizontal progress bar.
|
|
7
8
|
*
|
package/ui/form/Progress.js
CHANGED
|
@@ -4,6 +4,7 @@ import { notNullish } from "../../util/null.js";
|
|
|
4
4
|
import { getColorClass } from "../style/Color.js";
|
|
5
5
|
import { getStatusClass } from "../style/Status.js";
|
|
6
6
|
import { getClass, getModuleClass } from "../util/css.js";
|
|
7
|
+
import "./Progress.css";
|
|
7
8
|
import styles from "./Progress.module.css";
|
|
8
9
|
/**
|
|
9
10
|
* Show progress as a single continuous horizontal bar, filled to `value` within the `min`–`max` range (matches `getPercent()` and `formatPercent()`).
|
|
@@ -73,13 +73,4 @@
|
|
|
73
73
|
}
|
|
74
74
|
}
|
|
75
75
|
|
|
76
|
-
/*
|
|
77
|
-
@keyframes progress-flow {
|
|
78
|
-
from {
|
|
79
|
-
background-position: -70% 0;
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
to {
|
|
83
|
-
background-position: 170% 0;
|
|
84
|
-
}
|
|
85
|
-
}
|
|
76
|
+
/* The `progress-flow` keyframes referenced above live in the sibling non-module `Progress.css` — see the note there for why they can't be defined in this module file. */
|
package/ui/form/Progress.tsx
CHANGED
|
@@ -4,6 +4,7 @@ import { type Nullish, notNullish } from "../../util/null.js";
|
|
|
4
4
|
import { type ColorVariants, getColorClass } from "../style/Color.js";
|
|
5
5
|
import { getStatusClass, type StatusVariants } from "../style/Status.js";
|
|
6
6
|
import { getClass, getModuleClass } from "../util/css.js";
|
|
7
|
+
import "./Progress.css";
|
|
7
8
|
import styles from "./Progress.module.css";
|
|
8
9
|
|
|
9
10
|
/**
|
|
@@ -12,6 +12,7 @@ const LAYOUT_TOGGLE_CLASS = getModuleClass(LAYOUT_CSS, "toggle");
|
|
|
12
12
|
const LAYOUT_OVERLAY_CLASS = getModuleClass(LAYOUT_CSS, "overlay");
|
|
13
13
|
const LAYOUT_MAIN_CLASS = getModuleClass(LAYOUT_CSS, "main");
|
|
14
14
|
const LAYOUT_OPEN_CLASS = getModuleClass(LAYOUT_CSS, "open");
|
|
15
|
+
const LAYOUT_RIGHT_CLASS = getModuleClass(LAYOUT_CSS, "right");
|
|
15
16
|
/**
|
|
16
17
|
* Layout with a fixed-width side column (typically navigation) next to a scrollable main content column.
|
|
17
18
|
* - The sidebar is rendered as `<nav>` — it almost always contains the page's primary navigation.
|
|
@@ -31,13 +32,7 @@ export function SidebarLayout({ sidebar, children, right = false }) {
|
|
|
31
32
|
if (path)
|
|
32
33
|
setOpen(false);
|
|
33
34
|
}, [path]);
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
// — keeping the scroll position of this `.content` container (and all page state) intact across
|
|
38
|
-
// back/forward navigation. The sidebar and drawer state stay outside the cache, so they are neither
|
|
39
|
-
// duplicated nor remounted as the URL changes.
|
|
40
|
-
const contentEl = (_jsx(RouteCache, { children: _jsxs("div", { className: LAYOUT_CONTENT_CLASS, children: [_jsx("div", { className: LAYOUT_TOGGLE_CLASS, children: _jsx(Button, { title: open ? "Close menu" : "Show menu", onClick: () => setOpen(o => !o), children: open ? _jsx(XMarkIcon, {}) : _jsx(Bars3Icon, {}) }) }), children] }) }, "content"));
|
|
41
|
-
const overlayEl = open && (_jsx("button", { type: "button", className: LAYOUT_OVERLAY_CLASS, "aria-label": "Close menu", onClick: () => setOpen(false) }, "overlay"));
|
|
42
|
-
return _jsx("main", { className: LAYOUT_MAIN_CLASS, children: right ? [contentEl, sidebarEl, overlayEl] : [sidebarEl, contentEl, overlayEl] });
|
|
35
|
+
// DOM order is always sidebar → content → overlay; the `right` variant flips the visual column order in CSS, so the markup never reshuffles.
|
|
36
|
+
return (_jsxs("main", { className: getClass(LAYOUT_MAIN_CLASS, right && LAYOUT_RIGHT_CLASS), children: [_jsx("nav", { className: getClass(LAYOUT_SIDEBAR_CLASS, //
|
|
37
|
+
open && LAYOUT_OPEN_CLASS), children: sidebar }), _jsx(RouteCache, { children: _jsxs("div", { className: LAYOUT_CONTENT_CLASS, children: [_jsx("div", { className: LAYOUT_TOGGLE_CLASS, children: _jsx(Button, { title: open ? "Close menu" : "Show menu", onClick: () => setOpen(o => !o), children: open ? _jsx(XMarkIcon, {}) : _jsx(Bars3Icon, {}) }) }), children] }) }), _jsx("button", { type: "button", className: getClass(LAYOUT_OVERLAY_CLASS, open && LAYOUT_OPEN_CLASS), "aria-label": "Close menu", onClick: () => setOpen(false) })] }));
|
|
43
38
|
}
|
|
@@ -14,8 +14,11 @@
|
|
|
14
14
|
* document so `.main` is the only scroll container.
|
|
15
15
|
*
|
|
16
16
|
* On narrow viewports the sidebar becomes an off-canvas drawer that slides off the left edge of the
|
|
17
|
-
* screen; the `.toggle` button opens/closes it and the
|
|
18
|
-
* drawer (both are hidden entirely on wide viewports).
|
|
17
|
+
* screen (the right edge for the `.right` variant); the `.toggle` button opens/closes it and the
|
|
18
|
+
* `.overlay` dims the page behind the open drawer (both are hidden entirely on wide viewports).
|
|
19
|
+
*
|
|
20
|
+
* The `.right` variant keeps the DOM order static (sidebar → content) and mirrors the visual order in
|
|
21
|
+
* CSS by swapping the grid columns, so the markup never reshuffles.
|
|
19
22
|
*/
|
|
20
23
|
|
|
21
24
|
@layer components {
|
|
@@ -103,6 +106,25 @@
|
|
|
103
106
|
background: var(--sidebar-layout-background, var(--tint-100));
|
|
104
107
|
}
|
|
105
108
|
|
|
109
|
+
/*
|
|
110
|
+
* Right variant — mirror the layout so the sidebar sits on the right, without touching DOM order.
|
|
111
|
+
* The grid columns are swapped, the sidebar and content are placed into them explicitly, and the
|
|
112
|
+
* divider border moves to the sidebar's inner (left) edge.
|
|
113
|
+
*/
|
|
114
|
+
.main.right {
|
|
115
|
+
grid-template-columns: 1fr var(--sidebar-layout-width, 17.5rem);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
.main.right .sidebar {
|
|
119
|
+
grid-column: 2;
|
|
120
|
+
border-right: none;
|
|
121
|
+
border-left: var(--sidebar-layout-border, var(--stroke-normal) solid var(--tint-80));
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
.main.right .content {
|
|
125
|
+
grid-column: 1;
|
|
126
|
+
}
|
|
127
|
+
|
|
106
128
|
/* Wrapper for the menu toggle button — hidden on wide viewports, shown on narrow ones (see media query). */
|
|
107
129
|
.toggle {
|
|
108
130
|
display: none;
|
|
@@ -115,19 +137,10 @@
|
|
|
115
137
|
display: none;
|
|
116
138
|
}
|
|
117
139
|
|
|
118
|
-
|
|
119
|
-
from {
|
|
120
|
-
opacity: 0;
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
to {
|
|
124
|
-
opacity: 1;
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
/* On narrow viewports the sidebar becomes an off-canvas drawer that slides in from the left. */
|
|
140
|
+
/* On narrow viewports the sidebar becomes an off-canvas drawer that slides in from the left (right for the `right` variant). */
|
|
129
141
|
@media (width <= 48rem) {
|
|
130
|
-
.main
|
|
142
|
+
.main,
|
|
143
|
+
.main.right {
|
|
131
144
|
grid-template-columns: 1fr;
|
|
132
145
|
}
|
|
133
146
|
|
|
@@ -150,6 +163,17 @@
|
|
|
150
163
|
box-shadow: 0 0 1.5rem #0006;
|
|
151
164
|
}
|
|
152
165
|
|
|
166
|
+
/* Right variant: dock the drawer to the right edge and slide it in from the right instead. */
|
|
167
|
+
.main.right .sidebar {
|
|
168
|
+
left: auto;
|
|
169
|
+
right: 0;
|
|
170
|
+
transform: translateX(100%);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
.main.right .sidebar.open {
|
|
174
|
+
transform: translateX(0);
|
|
175
|
+
}
|
|
176
|
+
|
|
153
177
|
/* Pin the toggle button to the top-right so it stays reachable while the content column scrolls. */
|
|
154
178
|
.toggle {
|
|
155
179
|
display: block;
|
|
@@ -159,7 +183,7 @@
|
|
|
159
183
|
z-index: 110;
|
|
160
184
|
}
|
|
161
185
|
|
|
162
|
-
/* Dim the page behind the open drawer; clicking the overlay closes it. */
|
|
186
|
+
/* Dim the page behind the open drawer; clicking the overlay closes it. Always mounted, so it fades both ways via the `open` class. */
|
|
163
187
|
.overlay {
|
|
164
188
|
/* Box */
|
|
165
189
|
display: block;
|
|
@@ -173,7 +197,18 @@
|
|
|
173
197
|
/* Style */
|
|
174
198
|
background: var(--shadow-color);
|
|
175
199
|
cursor: pointer;
|
|
176
|
-
|
|
200
|
+
|
|
201
|
+
/* Hidden and non-interactive until open. Transition `visibility` alongside `opacity` so the overlay stays in the a11y tree (and clickable) only while shown, then flips hidden at the end of the fade-out. */
|
|
202
|
+
opacity: 0;
|
|
203
|
+
visibility: hidden;
|
|
204
|
+
transition:
|
|
205
|
+
opacity var(--duration-normal) ease-in-out,
|
|
206
|
+
visibility var(--duration-normal);
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
.overlay.open {
|
|
210
|
+
opacity: 1;
|
|
211
|
+
visibility: visible;
|
|
177
212
|
}
|
|
178
213
|
}
|
|
179
214
|
}
|
|
@@ -13,6 +13,7 @@ const LAYOUT_TOGGLE_CLASS = getModuleClass(LAYOUT_CSS, "toggle");
|
|
|
13
13
|
const LAYOUT_OVERLAY_CLASS = getModuleClass(LAYOUT_CSS, "overlay");
|
|
14
14
|
const LAYOUT_MAIN_CLASS = getModuleClass(LAYOUT_CSS, "main");
|
|
15
15
|
const LAYOUT_OPEN_CLASS = getModuleClass(LAYOUT_CSS, "open");
|
|
16
|
+
const LAYOUT_RIGHT_CLASS = getModuleClass(LAYOUT_CSS, "right");
|
|
16
17
|
|
|
17
18
|
/**
|
|
18
19
|
* Props for `<SidebarLayout>` — the `sidebar` column content, main `children`, and a `right` placement flag.
|
|
@@ -46,38 +47,35 @@ export function SidebarLayout({ sidebar, children, right = false }: SidebarLayou
|
|
|
46
47
|
if (path) setOpen(false);
|
|
47
48
|
}, [path]);
|
|
48
49
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
<Button title={open ? "Close menu" : "Show menu"} onClick={() => setOpen(o => !o)}>
|
|
70
|
-
{open ? <XMarkIcon /> : <Bars3Icon />}
|
|
71
|
-
</Button>
|
|
50
|
+
// DOM order is always sidebar → content → overlay; the `right` variant flips the visual column order in CSS, so the markup never reshuffles.
|
|
51
|
+
return (
|
|
52
|
+
<main className={getClass(LAYOUT_MAIN_CLASS, right && LAYOUT_RIGHT_CLASS)}>
|
|
53
|
+
<nav
|
|
54
|
+
className={getClass(
|
|
55
|
+
LAYOUT_SIDEBAR_CLASS, //
|
|
56
|
+
open && LAYOUT_OPEN_CLASS,
|
|
57
|
+
)}
|
|
58
|
+
>
|
|
59
|
+
{sidebar}
|
|
60
|
+
</nav>
|
|
61
|
+
{/* Wrap the scrolling content column in `<RouteCache>` so recently-visited pages stay mounted but hidden — keeping the scroll position of this `.content` container (and all page state) intact across back/forward navigation. The sidebar and drawer state stay outside the cache, so they are neither duplicated nor remounted as the URL changes. */}
|
|
62
|
+
<RouteCache>
|
|
63
|
+
<div className={LAYOUT_CONTENT_CLASS}>
|
|
64
|
+
<div className={LAYOUT_TOGGLE_CLASS}>
|
|
65
|
+
<Button title={open ? "Close menu" : "Show menu"} onClick={() => setOpen(o => !o)}>
|
|
66
|
+
{open ? <XMarkIcon /> : <Bars3Icon />}
|
|
67
|
+
</Button>
|
|
68
|
+
</div>
|
|
69
|
+
{children}
|
|
72
70
|
</div>
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
71
|
+
</RouteCache>
|
|
72
|
+
{/* Overlay is always mounted (on narrow viewports) so it can fade both in and out via the `open` class — a conditionally-mounted element can't animate on the way out. */}
|
|
73
|
+
<button
|
|
74
|
+
type="button"
|
|
75
|
+
className={getClass(LAYOUT_OVERLAY_CLASS, open && LAYOUT_OPEN_CLASS)}
|
|
76
|
+
aria-label="Close menu"
|
|
77
|
+
onClick={() => setOpen(false)}
|
|
78
|
+
/>
|
|
79
|
+
</main>
|
|
76
80
|
);
|
|
77
|
-
|
|
78
|
-
const overlayEl = open && (
|
|
79
|
-
<button key="overlay" type="button" className={LAYOUT_OVERLAY_CLASS} aria-label="Close menu" onClick={() => setOpen(false)} />
|
|
80
|
-
);
|
|
81
|
-
|
|
82
|
-
return <main className={LAYOUT_MAIN_CLASS}>{right ? [contentEl, sidebarEl, overlayEl] : [sidebarEl, contentEl, overlayEl]}</main>;
|
|
83
81
|
}
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
import "
|
|
2
|
+
import { getModuleClass } from "../util/css.js";
|
|
3
|
+
import COLLAPSE_CSS from "./CollapseTransition.module.css";
|
|
3
4
|
import { Transition } from "./Transition.js";
|
|
5
|
+
const COLLAPSE_CLASS = getModuleClass(COLLAPSE_CSS, "collapse");
|
|
4
6
|
/**
|
|
5
7
|
* Transition that collapses its children in and out by animating their size.
|
|
6
8
|
*
|
|
@@ -8,5 +10,5 @@ import { Transition } from "./Transition.js";
|
|
|
8
10
|
* @see https://shelving.cc/ui/CollapseTransition
|
|
9
11
|
*/
|
|
10
12
|
export function CollapseTransition(props) {
|
|
11
|
-
return _jsx(Transition, { default:
|
|
13
|
+
return _jsx(Transition, { default: COLLAPSE_CLASS, ...props });
|
|
12
14
|
}
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import type { ReactElement } from "react";
|
|
2
|
-
import "
|
|
2
|
+
import { getModuleClass } from "../util/css.js";
|
|
3
|
+
import COLLAPSE_CSS from "./CollapseTransition.module.css";
|
|
3
4
|
import { Transition, type TransitionProps } from "./Transition.js";
|
|
4
5
|
|
|
6
|
+
const COLLAPSE_CLASS = getModuleClass(COLLAPSE_CSS, "collapse");
|
|
7
|
+
|
|
5
8
|
/**
|
|
6
9
|
* Props for the `CollapseTransition` component — the shared transition variant props.
|
|
7
10
|
*
|
|
@@ -16,5 +19,5 @@ export interface CollapseTransitionProps extends TransitionProps {}
|
|
|
16
19
|
* @see https://shelving.cc/ui/CollapseTransition
|
|
17
20
|
*/
|
|
18
21
|
export function CollapseTransition(props: CollapseTransitionProps): ReactElement {
|
|
19
|
-
return <Transition default=
|
|
22
|
+
return <Transition default={COLLAPSE_CLASS} {...props} />;
|
|
20
23
|
}
|
|
@@ -1,16 +1,27 @@
|
|
|
1
1
|
import { type ReactElement } from "react";
|
|
2
2
|
import type { ChildProps } from "../util/props.js";
|
|
3
|
-
import type { TransitionClasses } from "./util.js";
|
|
4
|
-
import "./Transition.css";
|
|
5
3
|
/**
|
|
6
4
|
* Variant props shared by every transition component.
|
|
7
5
|
*
|
|
8
|
-
* @see https://shelving.cc/ui/
|
|
6
|
+
* @see https://shelving.cc/ui/TransitionVariants
|
|
9
7
|
*/
|
|
10
|
-
export interface
|
|
8
|
+
export interface TransitionVariants {
|
|
11
9
|
/** Render this transition above other transitions (z-index: 100 on the group). */
|
|
12
10
|
overlay?: boolean | undefined;
|
|
13
11
|
}
|
|
12
|
+
/**
|
|
13
|
+
* React props for `<Transition>` component.
|
|
14
|
+
*
|
|
15
|
+
* @see https://shelving.cc/ui/TransitionProps
|
|
16
|
+
*/
|
|
17
|
+
export interface TransitionProps extends ChildProps, TransitionVariants {
|
|
18
|
+
/** The default CSS class. */
|
|
19
|
+
default?: string | undefined;
|
|
20
|
+
/** The CSS class to apply when the transition is moving forward. */
|
|
21
|
+
forward?: string | undefined;
|
|
22
|
+
/** The CSS class to apply when the transition is moving backward. */
|
|
23
|
+
back?: string | undefined;
|
|
24
|
+
}
|
|
14
25
|
/**
|
|
15
26
|
* Wrap children in a React View Transition, applying the configured transition classes.
|
|
16
27
|
*
|
|
@@ -22,4 +33,4 @@ export interface TransitionProps extends ChildProps {
|
|
|
22
33
|
* @returns A `<ViewTransition>` element wrapping the children.
|
|
23
34
|
* @see https://shelving.cc/ui/Transition
|
|
24
35
|
*/
|
|
25
|
-
export declare function Transition({ children, default: d, forward, back,
|
|
36
|
+
export declare function Transition({ children, default: d, forward, back, overlay }: TransitionProps): ReactElement;
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
/// <reference types="react/canary" />
|
|
3
3
|
import { ViewTransition } from "react";
|
|
4
|
-
import { getClass } from "../util/css.js";
|
|
5
|
-
import "./Transition.css";
|
|
4
|
+
import { getClass, getModuleClass } from "../util/css.js";
|
|
5
|
+
import TRANSITION_CSS from "./Transition.module.css";
|
|
6
|
+
const TRANSITION_OVERLAY_CLASS = getModuleClass(TRANSITION_CSS, "overlay");
|
|
6
7
|
/**
|
|
7
8
|
* Wrap children in a React View Transition, applying the configured transition classes.
|
|
8
9
|
*
|
|
@@ -14,11 +15,10 @@ import "./Transition.css";
|
|
|
14
15
|
* @returns A `<ViewTransition>` element wrapping the children.
|
|
15
16
|
* @see https://shelving.cc/ui/Transition
|
|
16
17
|
*/
|
|
17
|
-
export function Transition({ children, default: d, forward = d, back = d,
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
return _jsx(ViewTransition, { default: classes, children: children });
|
|
18
|
+
export function Transition({ children, default: d, forward = d, back = d, overlay = false }) {
|
|
19
|
+
return (_jsx(ViewTransition, { default: {
|
|
20
|
+
default: getClass(d, overlay && TRANSITION_OVERLAY_CLASS),
|
|
21
|
+
forward: getClass(forward, overlay && TRANSITION_OVERLAY_CLASS),
|
|
22
|
+
back: getClass(back, overlay && TRANSITION_OVERLAY_CLASS),
|
|
23
|
+
}, children: children }));
|
|
24
24
|
}
|
|
@@ -1,20 +1,35 @@
|
|
|
1
1
|
/// <reference types="react/canary" />
|
|
2
2
|
import { type ReactElement, ViewTransition } from "react";
|
|
3
|
-
import { getClass } from "../util/css.js";
|
|
3
|
+
import { getClass, getModuleClass } from "../util/css.js";
|
|
4
4
|
import type { ChildProps } from "../util/props.js";
|
|
5
|
-
import
|
|
6
|
-
|
|
5
|
+
import TRANSITION_CSS from "./Transition.module.css";
|
|
6
|
+
|
|
7
|
+
const TRANSITION_OVERLAY_CLASS = getModuleClass(TRANSITION_CSS, "overlay");
|
|
7
8
|
|
|
8
9
|
/**
|
|
9
10
|
* Variant props shared by every transition component.
|
|
10
11
|
*
|
|
11
|
-
* @see https://shelving.cc/ui/
|
|
12
|
+
* @see https://shelving.cc/ui/TransitionVariants
|
|
12
13
|
*/
|
|
13
|
-
export interface
|
|
14
|
+
export interface TransitionVariants {
|
|
14
15
|
/** Render this transition above other transitions (z-index: 100 on the group). */
|
|
15
16
|
overlay?: boolean | undefined;
|
|
16
17
|
}
|
|
17
18
|
|
|
19
|
+
/**
|
|
20
|
+
* React props for `<Transition>` component.
|
|
21
|
+
*
|
|
22
|
+
* @see https://shelving.cc/ui/TransitionProps
|
|
23
|
+
*/
|
|
24
|
+
export interface TransitionProps extends ChildProps, TransitionVariants {
|
|
25
|
+
/** The default CSS class. */
|
|
26
|
+
default?: string | undefined;
|
|
27
|
+
/** The CSS class to apply when the transition is moving forward. */
|
|
28
|
+
forward?: string | undefined;
|
|
29
|
+
/** The CSS class to apply when the transition is moving backward. */
|
|
30
|
+
back?: string | undefined;
|
|
31
|
+
}
|
|
32
|
+
|
|
18
33
|
/**
|
|
19
34
|
* Wrap children in a React View Transition, applying the configured transition classes.
|
|
20
35
|
*
|
|
@@ -26,17 +41,16 @@ export interface TransitionProps extends ChildProps {
|
|
|
26
41
|
* @returns A `<ViewTransition>` element wrapping the children.
|
|
27
42
|
* @see https://shelving.cc/ui/Transition
|
|
28
43
|
*/
|
|
29
|
-
export function Transition({
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
return <ViewTransition default={classes}>{children}</ViewTransition>;
|
|
44
|
+
export function Transition({ children, default: d, forward = d, back = d, overlay = false }: TransitionProps): ReactElement {
|
|
45
|
+
return (
|
|
46
|
+
<ViewTransition
|
|
47
|
+
default={{
|
|
48
|
+
default: getClass(d, overlay && TRANSITION_OVERLAY_CLASS),
|
|
49
|
+
forward: getClass(forward, overlay && TRANSITION_OVERLAY_CLASS),
|
|
50
|
+
back: getClass(back, overlay && TRANSITION_OVERLAY_CLASS),
|
|
51
|
+
}}
|
|
52
|
+
>
|
|
53
|
+
{children}
|
|
54
|
+
</ViewTransition>
|
|
55
|
+
);
|
|
42
56
|
}
|
|
File without changes
|
|
File without changes
|