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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "shelving",
3
- "version": "1.268.0",
3
+ "version": "1.268.2",
4
4
  "author": "Dave Houlbrooke <dave@shax.com>",
5
5
  "repository": {
6
6
  "type": "git",
@@ -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
- /** The `<APIContext>` wrapper to give your React components access to this API provider. */
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 useAPI(endpoint, payload) {
18
+ function requireAPICache(caller = requireAPICache) {
19
19
  const cache = use(CacheContext);
20
20
  if (!cache)
21
- throw new RequiredError(`useAPI() can only be used inside <APIContext>`, { caller: useAPI });
22
- return useStore(endpoint ? cache.get(endpoint).get(payload) : undefined);
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
- /** The `<DataContext>` wrapper to give your React components access to this data. */
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
@@ -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 useItem(collection, //
19
- id) {
18
+ function requireDBCache(caller = requireDBCache) {
20
19
  const cache = use(CacheContext);
21
20
  if (!cache)
22
- throw new RequiredError("useItem() can only be used inside <DBContext>", { caller: useItem });
23
- return useStore(collection && id ? cache.getItem(collection, id) : undefined);
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
- const cache = use(CacheContext);
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
- <DocumentationButtons {...props} space="none" />
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
+ }
@@ -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
  *
@@ -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
- /* 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. */
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. */
@@ -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
- const sidebarEl = (_jsx("nav", { className: getClass(LAYOUT_SIDEBAR_CLASS, //
35
- open && LAYOUT_OPEN_CLASS), children: sidebar }, "sidebar"));
36
- // Wrap the scrolling content column in `<RouteCache>` so recently-visited pages stay mounted but hidden
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 `.overlay` dims the page behind the open
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
- @keyframes overlay-fade-in {
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
- animation: overlay-fade-in var(--duration-normal) ease-in-out;
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
- const sidebarEl = (
50
- <nav
51
- key="sidebar"
52
- className={getClass(
53
- LAYOUT_SIDEBAR_CLASS, //
54
- open && LAYOUT_OPEN_CLASS,
55
- )}
56
- >
57
- {sidebar}
58
- </nav>
59
- );
60
-
61
- // Wrap the scrolling content column in `<RouteCache>` so recently-visited pages stay mounted but hidden
62
- // — keeping the scroll position of this `.content` container (and all page state) intact across
63
- // back/forward navigation. The sidebar and drawer state stay outside the cache, so they are neither
64
- // duplicated nor remounted as the URL changes.
65
- const contentEl = (
66
- <RouteCache key="content">
67
- <div className={LAYOUT_CONTENT_CLASS}>
68
- <div className={LAYOUT_TOGGLE_CLASS}>
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
- {children}
74
- </div>
75
- </RouteCache>
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,5 +1,4 @@
1
1
  import type { ReactElement } from "react";
2
- import "./CollapseTransition.css";
3
2
  import { type TransitionProps } from "./Transition.js";
4
3
  /**
5
4
  * Props for the `CollapseTransition` component — the shared transition variant props.
@@ -1,6 +1,8 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
- import "./CollapseTransition.css";
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: "collapse", ...props });
13
+ return _jsx(Transition, { default: COLLAPSE_CLASS, ...props });
12
14
  }
@@ -1,7 +1,10 @@
1
1
  import type { ReactElement } from "react";
2
- import "./CollapseTransition.css";
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="collapse" {...props} />;
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/TransitionProps
6
+ * @see https://shelving.cc/ui/TransitionVariants
9
7
  */
10
- export interface TransitionProps extends ChildProps {
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, ...variants }: Partial<TransitionClasses> & TransitionProps): ReactElement;
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, ...variants }) {
18
- const classes = {
19
- default: getClass(d, variants),
20
- forward: getClass(forward, variants),
21
- back: getClass(back, variants),
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 type { TransitionClasses } from "./util.js";
6
- import "./Transition.css";
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/TransitionProps
12
+ * @see https://shelving.cc/ui/TransitionVariants
12
13
  */
13
- export interface TransitionProps extends ChildProps {
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
- children,
31
- default: d,
32
- forward = d,
33
- back = d,
34
- ...variants
35
- }: Partial<TransitionClasses> & TransitionProps): ReactElement {
36
- const classes: TransitionClasses = {
37
- default: getClass(d, variants),
38
- forward: getClass(forward, variants),
39
- back: getClass(back, variants),
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
  }