shelving 1.285.2 → 1.285.4

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.285.2",
3
+ "version": "1.285.4",
4
4
  "author": "Dave Houlbrooke <dave@shax.com>",
5
5
  "repository": {
6
6
  "type": "git",
@@ -42,6 +42,7 @@
42
42
  "description": "Toolkit for using data in JavaScript.",
43
43
  "engineStrict": true,
44
44
  "engines": {
45
+ "bun": ">=1.4.0",
45
46
  "node": ">=16.0.0"
46
47
  },
47
48
  "keywords": [
@@ -3,7 +3,6 @@ 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
5
  import type { ClassProps } from "../util/props.js";
6
- import "./Progress.css";
7
6
  /**
8
7
  * Props for `Progress`, a continuous horizontal progress bar.
9
8
  *
@@ -4,7 +4,6 @@ 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";
8
7
  import styles from "./Progress.module.css";
9
8
  /**
10
9
  * Show progress as a single continuous horizontal bar, filled to `value` within the `min`–`max` range (matches `getPercent()` and `formatPercent()`).
@@ -39,6 +39,17 @@
39
39
  background: var(--progress-color, var(--tint-50));
40
40
  }
41
41
 
42
+ /* 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. */
43
+ @keyframes progress-flow {
44
+ from {
45
+ background-position: -70% 0;
46
+ }
47
+
48
+ to {
49
+ background-position: 170% 0;
50
+ }
51
+ }
52
+
42
53
  /* Indeterminate — a native `<progress>` with no `value`, so a solid block of fill colour slides across the track on a loop for a task of unknown duration. */
43
54
  .progress:indeterminate,
44
55
  .prose progress:indeterminate {
@@ -47,7 +58,12 @@
47
58
  background-image: linear-gradient(90deg, var(--progress-color, var(--tint-50)), var(--progress-color, var(--tint-50)));
48
59
  background-repeat: no-repeat;
49
60
  background-size: 40% 100%;
50
- animation: progress-flow var(--progress-duration, 1.5s) linear infinite;
61
+
62
+ /* Longhands, not the `animation` shorthand: Bun rewrites `animation-name` to the hashed keyframes name but leaves a shorthand that holds a `var()` untouched. See dhoulb/shelving#259. */
63
+ animation-name: progress-flow;
64
+ animation-duration: var(--progress-duration, 1.5s);
65
+ animation-timing-function: linear;
66
+ animation-iteration-count: infinite;
51
67
  }
52
68
 
53
69
  /* The native track/fill pseudo-elements would paint over the sliding block, so make them transparent and let the element's own background show through. */
@@ -65,5 +81,3 @@
65
81
 
66
82
  /* No `prefers-reduced-motion` override — movement is the indeterminate bar's only signal, so freezing it reads as a stalled determinate bar rather than "working". The animation is a small element-sized loop, not a large vestibular-trigger transition. */
67
83
  }
68
-
69
- /* 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. */
@@ -5,7 +5,6 @@ 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
7
  import type { ClassProps } from "../util/props.js";
8
- import "./Progress.css";
9
8
  import styles from "./Progress.module.css";
10
9
 
11
10
  /**
@@ -1,3 +1,4 @@
1
+ /* Global CSS, not a CSS module. Bun hashes a class inside `::view-transition-*()` from the file basename, but hashes the exported names from the file path, so a module-scoped view-transition class can never match the name that JS passes to `view-transition-class`. Keep these names global until Bun fixes this — see dhoulb/shelving#325. */
1
2
  ::view-transition-image-pair(.collapse) {
2
3
  overflow: hidden;
3
4
  }
@@ -1,4 +1,5 @@
1
1
  import type { ReactElement } from "react";
2
+ import "./CollapseTransition.css";
2
3
  import { type TransitionProps } from "./Transition.js";
3
4
  /**
4
5
  * Props for the `CollapseTransition` component — the shared transition variant props.
@@ -1,8 +1,6 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
- import { getModuleClass } from "../util/css.js";
3
- import COLLAPSE_CSS from "./CollapseTransition.module.css";
2
+ import "./CollapseTransition.css";
4
3
  import { Transition } from "./Transition.js";
5
- const COLLAPSE_CLASS = getModuleClass(COLLAPSE_CSS, "collapse");
6
4
  /**
7
5
  * Transition that collapses its children in and out by animating their size.
8
6
  *
@@ -10,5 +8,5 @@ const COLLAPSE_CLASS = getModuleClass(COLLAPSE_CSS, "collapse");
10
8
  * @see https://shelving.cc/ui/CollapseTransition
11
9
  */
12
10
  export function CollapseTransition(props) {
13
- return _jsx(Transition, { default: COLLAPSE_CLASS, ...props });
11
+ return _jsx(Transition, { default: "collapse", ...props });
14
12
  }
@@ -1,10 +1,7 @@
1
1
  import type { ReactElement } from "react";
2
- import { getModuleClass } from "../util/css.js";
3
- import COLLAPSE_CSS from "./CollapseTransition.module.css";
2
+ import "./CollapseTransition.css";
4
3
  import { Transition, type TransitionProps } from "./Transition.js";
5
4
 
6
- const COLLAPSE_CLASS = getModuleClass(COLLAPSE_CSS, "collapse");
7
-
8
5
  /**
9
6
  * Props for the `CollapseTransition` component — the shared transition variant props.
10
7
  *
@@ -19,5 +16,5 @@ export interface CollapseTransitionProps extends TransitionProps {}
19
16
  * @see https://shelving.cc/ui/CollapseTransition
20
17
  */
21
18
  export function CollapseTransition(props: CollapseTransitionProps): ReactElement {
22
- return <Transition default={COLLAPSE_CLASS} {...props} />;
19
+ return <Transition default="collapse" {...props} />;
23
20
  }
@@ -1,4 +1,5 @@
1
1
  import type { ReactElement } from "react";
2
+ import "./FadeTransition.css";
2
3
  import { type TransitionProps } from "./Transition.js";
3
4
  /**
4
5
  * Props for the `FadeTransition` component — the shared transition variant props.
@@ -1,4 +1,5 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
+ import "./FadeTransition.css";
2
3
  import { Transition } from "./Transition.js";
3
4
  /**
4
5
  * Transition that fades its children in and out by animating their opacity.
@@ -1,4 +1,5 @@
1
1
  import type { ReactElement } from "react";
2
+ import "./FadeTransition.css";
2
3
  import { Transition, type TransitionProps } from "./Transition.js";
3
4
 
4
5
  /**
@@ -4,7 +4,7 @@ A direction-aware `<Transition>` preset that slides its children horizontally
4
4
 
5
5
  **Things to know:**
6
6
 
7
- - Defaults to `slideRight`; with the type set to `"forward"` it slides right (`slideRight`), and to `"back"` it slides left (`slideLeft`).
7
+ - Slides right by default and when the type is `"forward"`; slides left when the type is `"back"`.
8
8
  - Set the direction with `setTransitionType("forward" | "back")` inside a `startTransition()` callback before navigating — see `<Transition>`.
9
9
  - Pass `overlay` to raise the transition group above surrounding content during the animation (`z-index: 100`).
10
10
  - Under `prefers-reduced-motion: reduce` the slide distance is forced to `0`, so the transition degrades to an opacity-only crossfade with no positional movement (large viewport-level slides are exactly what the preference exists to suppress).
@@ -0,0 +1,4 @@
1
+ /* Global CSS, not a CSS module. Bun hashes a class inside `::view-transition-*()` from the file basename, but hashes the exported names from the file path, so a module-scoped view-transition class can never match the name that JS passes to `view-transition-class`. Keep these names global until Bun fixes this — see dhoulb/shelving#325. */
2
+ ::view-transition-group(.overlay) {
3
+ z-index: 100;
4
+ }
@@ -1,5 +1,6 @@
1
1
  import { type ReactElement } from "react";
2
2
  import type { ChildProps } from "../util/props.js";
3
+ import "./Transition.css";
3
4
  /**
4
5
  * Variant props shared by every transition component.
5
6
  *
@@ -27,7 +28,7 @@ export interface TransitionProps extends ChildProps, TransitionVariants {
27
28
  *
28
29
  * - Allows known view transition types in `TransitionClasses` (`default`/`forward`/`back`) to be overridden.
29
30
  * - These must correspond to a `::view-transition(.className)` that is set in CSS.
30
- * - Supports variant classes, e.g. `<Transition overlay>` applies `::view-transition(.overlay)` from `Transition.css`.
31
+ * - Supports variant classes, e.g. `<Transition overlay>` applies `::view-transition-group(.overlay)` from `Transition.css`.
31
32
  *
32
33
  * @kind component
33
34
  * @returns A `<ViewTransition>` element wrapping the children.
@@ -1,15 +1,16 @@
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, getModuleClass } from "../util/css.js";
5
- import TRANSITION_CSS from "./Transition.module.css";
6
- const TRANSITION_OVERLAY_CLASS = getModuleClass(TRANSITION_CSS, "overlay");
4
+ import { getClass } from "../util/css.js";
5
+ import "./Transition.css";
6
+ // A global class name, not a CSS-module export — see the note in `Transition.css`.
7
+ const TRANSITION_OVERLAY_CLASS = "overlay";
7
8
  /**
8
9
  * Wrap children in a React View Transition, applying the configured transition classes.
9
10
  *
10
11
  * - Allows known view transition types in `TransitionClasses` (`default`/`forward`/`back`) to be overridden.
11
12
  * - These must correspond to a `::view-transition(.className)` that is set in CSS.
12
- * - Supports variant classes, e.g. `<Transition overlay>` applies `::view-transition(.overlay)` from `Transition.css`.
13
+ * - Supports variant classes, e.g. `<Transition overlay>` applies `::view-transition-group(.overlay)` from `Transition.css`.
13
14
  *
14
15
  * @kind component
15
16
  * @returns A `<ViewTransition>` element wrapping the children.
@@ -24,11 +24,11 @@ import { Transition } from "shelving/ui";
24
24
  ### Overlay
25
25
 
26
26
  ```tsx
27
- import { Transition } from "shelving/ui";
27
+ import { FadeTransition } from "shelving/ui";
28
28
 
29
- <Transition default="fade" overlay>
29
+ <FadeTransition overlay>
30
30
  <Notification/>
31
- </Transition>
31
+ </FadeTransition>
32
32
  ```
33
33
 
34
34
  ### Setting the direction with `setTransitionType()`
@@ -1,10 +1,11 @@
1
1
  /// <reference types="react/canary" />
2
2
  import { type ReactElement, ViewTransition } from "react";
3
- import { getClass, getModuleClass } from "../util/css.js";
3
+ import { getClass } from "../util/css.js";
4
4
  import type { ChildProps } from "../util/props.js";
5
- import TRANSITION_CSS from "./Transition.module.css";
5
+ import "./Transition.css";
6
6
 
7
- const TRANSITION_OVERLAY_CLASS = getModuleClass(TRANSITION_CSS, "overlay");
7
+ // A global class name, not a CSS-module export — see the note in `Transition.css`.
8
+ const TRANSITION_OVERLAY_CLASS = "overlay";
8
9
 
9
10
  /**
10
11
  * Variant props shared by every transition component.
@@ -35,7 +36,7 @@ export interface TransitionProps extends ChildProps, TransitionVariants {
35
36
  *
36
37
  * - Allows known view transition types in `TransitionClasses` (`default`/`forward`/`back`) to be overridden.
37
38
  * - These must correspond to a `::view-transition(.className)` that is set in CSS.
38
- * - Supports variant classes, e.g. `<Transition overlay>` applies `::view-transition(.overlay)` from `Transition.css`.
39
+ * - Supports variant classes, e.g. `<Transition overlay>` applies `::view-transition-group(.overlay)` from `Transition.css`.
39
40
  *
40
41
  * @kind component
41
42
  * @returns A `<ViewTransition>` element wrapping the children.
@@ -4,7 +4,7 @@ A direction-aware `<Transition>` preset that slides its children vertically —
4
4
 
5
5
  **Things to know:**
6
6
 
7
- - Defaults to `slideDown`; with the type set to `"forward"` it slides down (`slideDown`), and to `"back"` it slides up (`slideUp`).
7
+ - Slides down by default and when the type is `"forward"`; slides up when the type is `"back"`.
8
8
  - Set the direction with `setTransitionType("forward" | "back")` inside a `startTransition()` callback before navigating — see `<Transition>`.
9
9
  - Pass `overlay` to raise the transition group above surrounding content during the animation (`z-index: 100`).
10
10
  - Under `prefers-reduced-motion: reduce` the slide distance is forced to `0`, so the transition degrades to an opacity-only crossfade with no positional movement (large viewport-level slides are exactly what the preference exists to suppress).
@@ -1,4 +1,7 @@
1
1
  import { describe, expect, test } from "bun:test";
2
+ import { mkdtemp, rm } from "node:fs/promises";
3
+ import { tmpdir } from "node:os";
4
+ import { join } from "node:path";
2
5
  import { getClass, getModuleClass } from "shelving/ui";
3
6
 
4
7
  describe("getClass", () => {
@@ -27,3 +30,47 @@ describe("getModuleClass", () => {
27
30
  expect(getModuleClass({ track: "abc123" }, "missing")).toBeUndefined();
28
31
  });
29
32
  });
33
+
34
+ /** Bundle a `.module.css` file with `Bun.build()` and return its CSS-modules exports and the emitted CSS. */
35
+ async function _bundle(path: string): Promise<{ readonly exports: Record<string, string | undefined>; readonly css: string }> {
36
+ const dir = await mkdtemp(join(tmpdir(), "shelving-css-"));
37
+ try {
38
+ const entry = join(dir, "entry.ts");
39
+ await Bun.write(entry, `export { default as styles } from ${JSON.stringify(path)};\n`);
40
+ const { success, logs, outputs } = await Bun.build({ entrypoints: [entry], outdir: join(dir, "out"), target: "bun" });
41
+ if (!success) throw new Error(logs.map(l => l.message).join("\n"));
42
+ const js = outputs.find(o => o.kind === "entry-point");
43
+ const css = outputs.find(o => o.path.endsWith(".css"));
44
+ if (!js || !css) throw new Error("Bundle produced no JS entry or CSS asset");
45
+ const { styles } = (await import(js.path)) as { styles: Record<string, string | undefined> };
46
+ return { exports: styles, css: await css.text() };
47
+ } finally {
48
+ await rm(dir, { recursive: true, force: true });
49
+ }
50
+ }
51
+
52
+ // Bun bundler behaviour the `ui` module depends on, so a Bun change shows up here rather than silently in the browser.
53
+ describe("Bun CSS modules", () => {
54
+ test("hashes `animation-name` to match its `@keyframes` declaration", async () => {
55
+ const { css } = await _bundle(join(import.meta.dir, "../form/Progress.module.css"));
56
+ const name = css.match(/@keyframes (progress-flow_[\w-]+)/)?.[1];
57
+ expect(name).toBeString();
58
+ expect(css).toContain(`animation-name: ${name};`);
59
+ });
60
+
61
+ // Canary: Bun hashes a class inside `::view-transition-*()` from the file basename but hashes exports from the path, so the two never match in a real build. When this test fails, Bun has fixed it — move the view-transition classes into their `.module.css` files (see dhoulb/shelving#325).
62
+ test("does not yet hash a view-transition class to match its export", async () => {
63
+ const dir = await mkdtemp(join(tmpdir(), "shelving-css-"));
64
+ try {
65
+ const path = join(dir, "sub", "canary.module.css");
66
+ await Bun.write(path, ".plain { color: red; }\n.vt {}\n::view-transition-group(.vt) { z-index: 1; }\n");
67
+ const { exports, css } = await _bundle(path);
68
+ expect(exports.plain).toBeString();
69
+ expect(exports.vt).toBeString();
70
+ expect(css).toContain(`.${exports.plain}`);
71
+ expect(css).not.toContain(`(.${exports.vt})`);
72
+ } finally {
73
+ await rm(dir, { recursive: true, force: true });
74
+ }
75
+ });
76
+ });
@@ -1,23 +0,0 @@
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
- }
@@ -1,3 +0,0 @@
1
- ::view-transition-group(.overlay) {
2
- z-index: 100;
3
- }