shelving 1.264.1 → 1.265.0
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/ui/README.md +2 -2
- package/ui/block/Blockquote.module.css +0 -3
- package/ui/block/Card.md +3 -7
- package/ui/block/Card.module.css +0 -3
- package/ui/block/Heading.md +0 -1
- package/ui/block/Heading.module.css +0 -3
- package/ui/block/Label.module.css +0 -3
- package/ui/block/List.md +1 -2
- package/ui/block/List.module.css +0 -3
- package/ui/block/Panel.md +1 -2
- package/ui/block/Panel.module.css +0 -3
- package/ui/block/Paragraph.md +1 -2
- package/ui/block/Paragraph.module.css +0 -3
- package/ui/block/Preformatted.module.css +0 -3
- package/ui/block/Subheading.md +0 -1
- package/ui/block/Subheading.module.css +0 -3
- package/ui/block/Title.md +0 -1
- package/ui/block/Title.module.css +0 -3
- package/ui/button/Button.md +1 -2
- package/ui/button/Button.module.css +0 -3
- package/ui/docs/DocumentationPage.test.tsx +2 -4
- package/ui/docs/DocumentationParams.js +1 -1
- package/ui/docs/DocumentationParams.tsx +1 -1
- package/ui/docs/DocumentationProperties.js +1 -1
- package/ui/docs/DocumentationProperties.tsx +1 -1
- package/ui/docs/DocumentationReferences.js +1 -1
- package/ui/docs/DocumentationReferences.tsx +1 -1
- package/ui/docs/DocumentationReturns.js +1 -1
- package/ui/docs/DocumentationReturns.tsx +1 -1
- package/ui/docs/DocumentationThrows.js +1 -1
- package/ui/docs/DocumentationThrows.tsx +1 -1
- package/ui/form/Field.test.tsx +1 -1
- package/ui/form/Progress.d.ts +9 -6
- package/ui/form/Progress.js +12 -6
- package/ui/form/Progress.md +62 -0
- package/ui/form/Progress.module.css +18 -21
- package/ui/form/Progress.tsx +19 -22
- package/ui/inline/Deleted.md +4 -5
- package/ui/inline/Deleted.module.css +1 -3
- package/ui/inline/Inserted.md +4 -5
- package/ui/inline/Inserted.module.css +1 -3
- package/ui/inline/Link.md +2 -3
- package/ui/inline/Link.module.css +1 -3
- package/ui/inline/Mark.js +1 -2
- package/ui/inline/Mark.md +5 -5
- package/ui/inline/Mark.module.css +2 -3
- package/ui/inline/Mark.tsx +0 -2
- package/ui/input/SchemaInput.test.tsx +3 -2
- package/ui/layout/CenteredLayout.d.ts +7 -8
- package/ui/layout/CenteredLayout.js +16 -4
- package/ui/layout/CenteredLayout.md +14 -6
- package/ui/layout/CenteredLayout.module.css +16 -22
- package/ui/layout/CenteredLayout.tsx +30 -13
- package/ui/layout/SidebarLayout.js +12 -5
- package/ui/layout/SidebarLayout.md +4 -4
- package/ui/layout/SidebarLayout.module.css +6 -9
- package/ui/layout/SidebarLayout.tsx +20 -17
- package/ui/misc/MetaContext.test.tsx +2 -3
- package/ui/misc/Tag.md +1 -2
- package/ui/misc/Tag.module.css +0 -3
- package/ui/notice/Message.module.css +0 -3
- package/ui/notice/Notice.md +1 -2
- package/ui/notice/Notice.module.css +0 -3
- package/ui/router/Router.test.tsx +1 -4
- package/ui/style/TINT_CLASS.md +1 -1
- package/ui/style/Width.d.ts +2 -1
- package/ui/style/Width.module.css +47 -17
- package/ui/style/Width.tsx +22 -1
- package/ui/style/getWidthClass.md +5 -9
- package/ui/table/Table.md +1 -1
- package/ui/tree/TreeButton.test.tsx +2 -5
- package/ui/tree/TreeContext.test.tsx +2 -3
- package/ui/tree/TreeMarkup.test.tsx +2 -5
- package/ui/tree/TreeRouter.test.tsx +2 -5
package/package.json
CHANGED
package/ui/README.md
CHANGED
|
@@ -27,7 +27,7 @@ The styling system lives in `style/` and has four moving parts: design tokens, t
|
|
|
27
27
|
|
|
28
28
|
**Styling props.** The cross-cutting visual options are props, each backed by a helper in `style/` that maps the prop to a class. Colour and status move the tint anchor — `getColorClass()` and `getStatusClass()`; font size, weight, family, and case, plus text alignment, tint, and wrapping, all come from `getTypographyClass()`, which extends `ColorVariants` and composes `getColorClass()` so every typographic element also accepts `color` (use `getColorClass()` directly only for colour-without-typography, e.g. `<Icon>`); spacing, block-padding, inline-padding ("indent"), and gap from `getSpaceClass()`, `getPaddingClass()`, `getIndentClass()`, and `getGapClass()`; width constraints from `getWidthClass()`; flex layout from `getFlexClass()`; and opt-in scrolling from `getScrollClass()`. Each helper's page lists its exact prop values and what they set. A component opts into the props it wants by extending the matching `*Props` interfaces and composing the `getXxxClass(props)` calls.
|
|
29
29
|
|
|
30
|
-
Each painting component also exposes
|
|
30
|
+
Each painting component also exposes per-property theme hooks (`--card-background`, `--card-radius`, …) for surgical overrides. To recolour a component or a whole region, apply `color=` / `status=` — on it or an ancestor scope — rather than a per-component tint hook; components no longer set the tint anchor themselves. Those hooks are documented in each component's own **Styling** section (see `<Card>` for the precedent).
|
|
31
31
|
|
|
32
32
|
## Theming
|
|
33
33
|
|
|
@@ -50,7 +50,7 @@ Each base token lives in a themed module that documents the variables it defines
|
|
|
50
50
|
- `space` · `width` — layout spacing and widths (`--space-*`, `--width-*`).
|
|
51
51
|
- `radius` · `stroke` · `shadow` · `duration` — surface tokens (`--radius-*`, `--stroke-*`, `--shadow-*`, `--duration-*`).
|
|
52
52
|
|
|
53
|
-
The **tint ladder** is the one exception that doesn't follow the override-a-variable pattern: its 21 steps are *recomputed* from a single anchor inside every tinted scope, so you move the anchor rather than overriding individual steps. See `TINT_CLASS` for the full theming guide, and each component's **Styling** section for its per-
|
|
53
|
+
The **tint ladder** is the one exception that doesn't follow the override-a-variable pattern: its 21 steps are *recomputed* from a single anchor inside every tinted scope, so you move the anchor (via `color=` / `status=`) rather than overriding individual steps. See `TINT_CLASS` for the full theming guide, and each component's **Styling** section for its per-property hooks.
|
|
54
54
|
|
|
55
55
|
## Finding your way around
|
|
56
56
|
|
|
@@ -7,9 +7,6 @@
|
|
|
7
7
|
@layer components {
|
|
8
8
|
.blockquote,
|
|
9
9
|
.prose blockquote {
|
|
10
|
-
/* Theme */
|
|
11
|
-
--tint-50: var(--blockquote-tint, inherit);
|
|
12
|
-
|
|
13
10
|
/* Box */
|
|
14
11
|
margin-block: var(--blockquote-space, var(--space-paragraph));
|
|
15
12
|
border-inline-start: var(--blockquote-border, var(--blockquote-stroke, var(--stroke-normal)) solid var(--tint-80));
|
package/ui/block/Card.md
CHANGED
|
@@ -45,11 +45,10 @@ import { Card, Subheading } from "shelving/ui";
|
|
|
45
45
|
|
|
46
46
|
## Styling
|
|
47
47
|
|
|
48
|
-
`Card` paints from the [tint ladder](/ui/TINT_CLASS); override these hooks at `:root` (or any ancestor scope) to retheme.
|
|
48
|
+
`Card` paints from the [tint ladder](/ui/TINT_CLASS); override these hooks at `:root` (or any ancestor scope) to retheme. Apply `color=` / `status=` (on the card or an ancestor scope) to recolour everything at once — surface, border, text, and hover shade re-derive together; reach for a per-property hook for a single surgical change.
|
|
49
49
|
|
|
50
50
|
| Variable | Styles | Default |
|
|
51
51
|
|---|---|---|
|
|
52
|
-
| `--card-tint` | Tint anchor for the card scope — recolours surface, border, text and hover together | `inherit` (flows from `color=` / `status=` / parent) |
|
|
53
52
|
| `--card-background` | Surface fill | `var(--tint-90)` |
|
|
54
53
|
| `--card-hover-background` | Surface fill when a navigable card is hovered | `var(--tint-95)` |
|
|
55
54
|
| `--card-color` | Text colour | `var(--tint-00)` |
|
|
@@ -71,9 +70,6 @@ import { Card, Subheading } from "shelving/ui";
|
|
|
71
70
|
--card-shadow: var(--shadow-small);
|
|
72
71
|
--card-radius: var(--radius-small);
|
|
73
72
|
}
|
|
74
|
-
|
|
75
|
-
/* Retint every card purple — surface, border, text and hover all follow. */
|
|
76
|
-
:root {
|
|
77
|
-
--card-tint: var(--color-purple);
|
|
78
|
-
}
|
|
79
73
|
```
|
|
74
|
+
|
|
75
|
+
To recolour cards, apply `color=` / `status=` to the card (or a tinted ancestor scope) — e.g. `<Card color="purple">` — rather than a per-component tint hook.
|
package/ui/block/Card.module.css
CHANGED
package/ui/block/Heading.md
CHANGED
|
@@ -44,7 +44,6 @@ import { Heading } from "shelving/ui";
|
|
|
44
44
|
|
|
45
45
|
| Variable | Styles | Default |
|
|
46
46
|
|---|---|---|
|
|
47
|
-
| `--heading-tint` | Tint anchor for the heading scope | `inherit` (flows from `color=` / parent) |
|
|
48
47
|
| `--heading-color` | Text colour | `inherit` |
|
|
49
48
|
| `--heading-space-before` | Top margin | `var(--space-section)` (2rem) |
|
|
50
49
|
| `--heading-space` | Bottom margin | `var(--space-paragraph)` (16px) |
|
package/ui/block/List.md
CHANGED
|
@@ -31,11 +31,10 @@ import { List } from "shelving/ui";
|
|
|
31
31
|
|
|
32
32
|
## Styling
|
|
33
33
|
|
|
34
|
-
`List` paints from the [tint ladder](/ui/TINT_CLASS) for its markers only;
|
|
34
|
+
`List` paints from the [tint ladder](/ui/TINT_CLASS) for its markers only; apply `color=` (on the list or an ancestor scope) to recolour the markers, or reach for a per-property hook for a single change.
|
|
35
35
|
|
|
36
36
|
| Variable | Styles | Default |
|
|
37
37
|
|---|---|---|
|
|
38
|
-
| `--list-tint` | Tint anchor for the list scope | `inherit` (flows from `color=` / parent) |
|
|
39
38
|
| `--list-space` | Outer block margin (top + bottom) | `var(--space-paragraph)` (16px) |
|
|
40
39
|
| `--list-gap` | Space between items | `var(--space-xsmall)` |
|
|
41
40
|
| `--list-indent` | Inline start padding (marker gutter) | `1.125em` unordered / `1.8em` ordered |
|
package/ui/block/List.module.css
CHANGED
package/ui/block/Panel.md
CHANGED
|
@@ -31,11 +31,10 @@ import { Panel, Block, Title, Paragraph } from "shelving/ui";
|
|
|
31
31
|
|
|
32
32
|
## Styling
|
|
33
33
|
|
|
34
|
-
`Panel` paints from the [tint ladder](/ui/TINT_CLASS);
|
|
34
|
+
`Panel` paints from the [tint ladder](/ui/TINT_CLASS); apply `color=` / `status=` (on the panel or an ancestor scope) to recolour the whole scope at once — surface, border, and text re-derive together — or reach for a per-property hook for a single change.
|
|
35
35
|
|
|
36
36
|
| Variable | Styles | Default |
|
|
37
37
|
|---|---|---|
|
|
38
|
-
| `--panel-tint` | Tint anchor for the panel scope — recolours surface, border, and text together | `inherit` (flows from `color=` / `status=` / parent) |
|
|
39
38
|
| `--panel-background` | Surface fill | `var(--tint-90)` |
|
|
40
39
|
| `--panel-color` | Text colour | `var(--tint-00)` |
|
|
41
40
|
| `--panel-border` | Top/bottom border shorthand | `var(--panel-stroke) solid var(--tint-80)` |
|
package/ui/block/Paragraph.md
CHANGED
|
@@ -31,11 +31,10 @@ import { Paragraph, Strong, Link } from "shelving/ui";
|
|
|
31
31
|
|
|
32
32
|
## Styling
|
|
33
33
|
|
|
34
|
-
`Paragraph` exposes a single hook for its own block margin
|
|
34
|
+
`Paragraph` exposes a single hook for its own block margin; it paints no colour of its own, so it inherits the surrounding text colour. Apply `color=` / `status=` to set the tint scope for any ladder-reading inline content inside it.
|
|
35
35
|
|
|
36
36
|
| Variable | Styles | Default |
|
|
37
37
|
|---|---|---|
|
|
38
|
-
| `--paragraph-tint` | Tint anchor for the paragraph scope | `inherit` (flows from `color=` / parent) |
|
|
39
38
|
| `--paragraph-space` | Outer block margin (top + bottom) | `var(--space-paragraph)` (16px) |
|
|
40
39
|
|
|
41
40
|
**Global tokens it reads:** `--space-paragraph`.
|
package/ui/block/Subheading.md
CHANGED
|
@@ -39,7 +39,6 @@ import { Title, Heading, Subheading } from "shelving/ui";
|
|
|
39
39
|
|
|
40
40
|
| Variable | Styles | Default |
|
|
41
41
|
|---|---|---|
|
|
42
|
-
| `--subheading-tint` | Tint anchor for the subheading scope | `inherit` (flows from `color=` / parent) |
|
|
43
42
|
| `--subheading-color` | Text colour | `inherit` |
|
|
44
43
|
| `--subheading-space-before` | Top margin | `var(--space-section)` (2rem) |
|
|
45
44
|
| `--subheading-space` | Bottom margin | `var(--space-paragraph)` (16px) |
|
|
@@ -6,9 +6,6 @@
|
|
|
6
6
|
@layer components {
|
|
7
7
|
.subheading,
|
|
8
8
|
.prose :is(h3, h4, h5, h6) {
|
|
9
|
-
/* Theme */
|
|
10
|
-
--tint-50: var(--subheading-tint, inherit);
|
|
11
|
-
|
|
12
9
|
/* Box */
|
|
13
10
|
margin-block-start: var(--subheading-space-before, var(--space-section));
|
|
14
11
|
margin-block-end: var(--subheading-space, var(--space-paragraph));
|
package/ui/block/Title.md
CHANGED
|
@@ -38,7 +38,6 @@ import { Panel, Block, Title } from "shelving/ui";
|
|
|
38
38
|
|
|
39
39
|
| Variable | Styles | Default |
|
|
40
40
|
|---|---|---|
|
|
41
|
-
| `--title-tint` | Tint anchor for the title scope | `inherit` (flows from `color=` / parent) |
|
|
42
41
|
| `--title-color` | Text colour | `inherit` |
|
|
43
42
|
| `--title-space-before` | Top margin | `var(--space-section)` (2rem) |
|
|
44
43
|
| `--title-space` | Bottom margin | `var(--space-paragraph)` (16px) |
|
package/ui/button/Button.md
CHANGED
|
@@ -46,11 +46,10 @@ import { getButtonClass } from "shelving/ui";
|
|
|
46
46
|
|
|
47
47
|
## Styling
|
|
48
48
|
|
|
49
|
-
`Button` paints from the [tint ladder](/ui/TINT_CLASS). Override these hooks at `:root` or any ancestor scope;
|
|
49
|
+
`Button` paints from the [tint ladder](/ui/TINT_CLASS). Override these hooks at `:root` or any ancestor scope; apply `color=` / `status=` (on the button or an ancestor scope) to recolour the whole button, or use a per-property hook for one change.
|
|
50
50
|
|
|
51
51
|
| Variable | Styles | Default |
|
|
52
52
|
|---|---|---|
|
|
53
|
-
| `--button-tint` | Tint anchor for the button scope | `inherit` (flows from `color=` / `status=` / parent) |
|
|
54
53
|
| `--button-background` | Surface fill | `var(--tint-90)` |
|
|
55
54
|
| `--button-hover-background` | Surface fill on hover / focus | `var(--tint-95)` |
|
|
56
55
|
| `--button-hover-border` | Border on hover / focus | `var(--button-stroke) solid var(--tint-90)` |
|
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
import { describe, expect, test } from "bun:test";
|
|
2
2
|
import type { ReactNode } from "react";
|
|
3
3
|
import { renderToStaticMarkup } from "react-dom/server";
|
|
4
|
-
import
|
|
5
|
-
import {
|
|
6
|
-
import { createMeta } from "../util/meta.js";
|
|
7
|
-
import { DocumentationPage } from "./DocumentationPage.js";
|
|
4
|
+
import { createMeta, DocumentationPage, MetaContext } from "shelving/ui";
|
|
5
|
+
import type { DocumentationElement } from "shelving/util/tree";
|
|
8
6
|
|
|
9
7
|
/** Make a minimal `tree-documentation` child element of a given kind. */
|
|
10
8
|
function doc(name: string, kind: string): DocumentationElement {
|
|
@@ -29,7 +29,7 @@ export function DocumentationParams({ params }) {
|
|
|
29
29
|
const map = useTreeMap();
|
|
30
30
|
if (!params?.length)
|
|
31
31
|
return null;
|
|
32
|
-
return (_jsx(Section, { children: _jsx(Scroll, { horizontal: true, children: _jsxs(Table, { children: [_jsx("thead", { children: _jsxs("tr", { children: [_jsx(Cell, { header: true, width: "fit", children: "Param" }), _jsx(Cell, { header: true, width: "fit", children: "Type" }), _jsx(Cell, { header: true, width: "
|
|
32
|
+
return (_jsx(Section, { children: _jsx(Scroll, { horizontal: true, children: _jsxs(Table, { children: [_jsx("thead", { children: _jsxs("tr", { children: [_jsx(Cell, { header: true, width: "fit", children: "Param" }), _jsx(Cell, { header: true, width: "fit", children: "Type" }), _jsx(Cell, { header: true, width: "16x", grow: true })] }) }), _jsx("tbody", { children: params.map(({ name, type = DEFAULT_TYPE, description, default: def, optional }) => {
|
|
33
33
|
const { members, optional: typeOptional } = splitType(type);
|
|
34
34
|
// An options-bag param whose (single, concrete) type resolves to a documented interface/object type is flattened into its individual fields as indented child rows.
|
|
35
35
|
const single = members.length === 1 ? members[0] : undefined;
|
|
@@ -24,7 +24,7 @@ export function DocumentationProperties({ properties }) {
|
|
|
24
24
|
const map = useTreeMap();
|
|
25
25
|
if (!properties?.length)
|
|
26
26
|
return null;
|
|
27
|
-
return (_jsx(Section, { children: _jsx(Scroll, { horizontal: true, children: _jsxs(Table, { children: [_jsx("thead", { children: _jsxs("tr", { children: [_jsx(Cell, { header: true, width: "fit", children: "Property" }), _jsx(Cell, { header: true, width: "fit", children: "Type" }), _jsx(Cell, { header: true, width: "
|
|
27
|
+
return (_jsx(Section, { children: _jsx(Scroll, { horizontal: true, children: _jsxs(Table, { children: [_jsx("thead", { children: _jsxs("tr", { children: [_jsx(Cell, { header: true, width: "fit", children: "Property" }), _jsx(Cell, { header: true, width: "fit", children: "Type" }), _jsx(Cell, { header: true, width: "16x", grow: true })] }) }), _jsx("tbody", { children: properties.map(({ name, type = DEFAULT_TYPE, description, default: def, optional, readonly }) => {
|
|
28
28
|
const { members, optional: typeOptional } = splitType(type);
|
|
29
29
|
return (_jsxs("tr", { children: [_jsx(Cell, { nowrap: true, children: _jsx(Code, { nowrap: true, children: `.${name}` }) }), _jsx("td", { children: _jsx(DocumentationType, { members: members }) }), _jsx("td", { children: _jsx(DocumentationDescription, { description: description || getTreeElement(map, members[0] ?? DEFAULT_TYPE)?.props.description, default: def, optional: !!optional || typeOptional, readonly: readonly }) })] }, `${name}-${type}`));
|
|
30
30
|
}) })] }) }) }));
|
|
@@ -19,5 +19,5 @@ export function DocumentationReferences({ types }) {
|
|
|
19
19
|
const map = useTreeMap();
|
|
20
20
|
if (!types?.length)
|
|
21
21
|
return null;
|
|
22
|
-
return (_jsx(Section, { children: _jsx(Scroll, { horizontal: true, children: _jsxs(Table, { children: [_jsx("thead", { children: _jsxs("tr", { children: [_jsx(Cell, { header: true, width: "fit", children: "Type" }), _jsx(Cell, { header: true, width: "
|
|
22
|
+
return (_jsx(Section, { children: _jsx(Scroll, { horizontal: true, children: _jsxs(Table, { children: [_jsx("thead", { children: _jsxs("tr", { children: [_jsx(Cell, { header: true, width: "fit", children: "Type" }), _jsx(Cell, { header: true, width: "16x", grow: true })] }) }), _jsx("tbody", { children: types.map(type => (_jsxs("tr", { children: [_jsx("td", { children: _jsx(TreeLink, { name: type }) }), _jsx("td", { children: _jsx(DocumentationDescription, { description: getTreeElement(map, type)?.props.description }) })] }, type))) })] }) }) }));
|
|
23
23
|
}
|
|
@@ -21,5 +21,5 @@ export function DocumentationReturns({ returns }) {
|
|
|
21
21
|
const map = useTreeMap();
|
|
22
22
|
if (!returns?.length)
|
|
23
23
|
return null;
|
|
24
|
-
return (_jsx(Section, { children: _jsx(Scroll, { horizontal: true, children: _jsxs(Table, { children: [_jsx("thead", { children: _jsxs("tr", { children: [_jsx(Cell, { header: true, width: "fit", children: "Return" }), _jsx(Cell, { header: true, width: "
|
|
24
|
+
return (_jsx(Section, { children: _jsx(Scroll, { horizontal: true, children: _jsxs(Table, { children: [_jsx("thead", { children: _jsxs("tr", { children: [_jsx(Cell, { header: true, width: "fit", children: "Return" }), _jsx(Cell, { header: true, width: "16x", grow: true })] }) }), _jsx("tbody", { children: returns.map(({ type = DEFAULT_TYPE, description }) => (_jsxs("tr", { children: [_jsx("td", { children: _jsx(TreeLink, { name: type, nowrap: true }) }), _jsx("td", { children: _jsx(DocumentationDescription, { description: description || getTreeElement(map, type)?.props.description }) })] }, `${type}-${description}`))) })] }) }) }));
|
|
25
25
|
}
|
|
@@ -21,5 +21,5 @@ export function DocumentationThrows({ throws }) {
|
|
|
21
21
|
const map = useTreeMap();
|
|
22
22
|
if (!throws?.length)
|
|
23
23
|
return null;
|
|
24
|
-
return (_jsx(Section, { children: _jsx(Scroll, { horizontal: true, children: _jsxs(Table, { children: [_jsx("thead", { children: _jsxs("tr", { children: [_jsx(Cell, { header: true, width: "fit", children: "Throws" }), _jsx(Cell, { header: true, width: "
|
|
24
|
+
return (_jsx(Section, { children: _jsx(Scroll, { horizontal: true, children: _jsxs(Table, { children: [_jsx("thead", { children: _jsxs("tr", { children: [_jsx(Cell, { header: true, width: "fit", children: "Throws" }), _jsx(Cell, { header: true, width: "16x", grow: true })] }) }), _jsx("tbody", { children: throws.map(({ type = DEFAULT_TYPE, description }) => (_jsxs("tr", { children: [_jsx("td", { children: _jsx(TreeLink, { name: type, nowrap: true }) }), _jsx("td", { children: _jsx(DocumentationDescription, { description: description || getTreeElement(map, type)?.props.description }) })] }, `${type}-${description}`))) })] }) }) }));
|
|
25
25
|
}
|
package/ui/form/Field.test.tsx
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { describe, expect, test } from "bun:test";
|
|
2
2
|
import { renderToStaticMarkup } from "react-dom/server";
|
|
3
|
-
import { Field } from "
|
|
3
|
+
import { Field } from "shelving/ui";
|
|
4
4
|
|
|
5
5
|
describe("Field", () => {
|
|
6
6
|
test("does not render a message container when there is no message", () => {
|
package/ui/form/Progress.d.ts
CHANGED
|
@@ -1,24 +1,27 @@
|
|
|
1
1
|
import type { ReactElement } from "react";
|
|
2
|
+
import { type ColorVariants } from "../style/Color.js";
|
|
3
|
+
import { type StatusVariants } from "../style/Status.js";
|
|
2
4
|
/**
|
|
3
5
|
* Props for `Progress`, a continuous horizontal progress bar.
|
|
4
6
|
*
|
|
5
7
|
* @see https://shelving.cc/ui/ProgressProps
|
|
6
8
|
*/
|
|
7
|
-
export interface ProgressProps {
|
|
9
|
+
export interface ProgressProps extends ColorVariants, StatusVariants {
|
|
8
10
|
value: number;
|
|
9
11
|
min?: number;
|
|
10
12
|
max?: number;
|
|
11
|
-
success?: boolean;
|
|
12
|
-
warning?: boolean;
|
|
13
|
-
danger?: boolean;
|
|
14
13
|
}
|
|
15
14
|
/**
|
|
16
15
|
* Show progress as a single continuous horizontal bar, filled to `value` within the `min`–`max` range (matches `getPercent()` and `formatPercent()`).
|
|
17
|
-
* -
|
|
16
|
+
* - Renders a native `<progress>` element, so `role="progressbar"` and the value/max semantics come from the browser.
|
|
17
|
+
* - `<progress>` has no `min` attribute (its implicit minimum is `0`), so the range is normalised to `value - min` / `max - min` before it's handed to the element.
|
|
18
|
+
* - The browser clamps `value` to the `0`–`max` range, so an out-of-range `value` shows an empty or full bar rather than overspilling.
|
|
19
|
+
* - Paints from the tint ladder, so `color=` / `status=` recolour the fill (and track) by moving the tint anchor.
|
|
18
20
|
*
|
|
19
21
|
* @returns A progress bar element.
|
|
20
22
|
* @kind component
|
|
21
23
|
* @example <Progress value={3} max={4} />
|
|
24
|
+
* @example <Progress value={90} status="success" />
|
|
22
25
|
* @see https://shelving.cc/ui/Progress
|
|
23
26
|
*/
|
|
24
|
-
export declare function Progress({ value, min, max,
|
|
27
|
+
export declare function Progress({ value, min, max, ...props }: ProgressProps): ReactElement;
|
package/ui/form/Progress.js
CHANGED
|
@@ -1,19 +1,25 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import { formatPercent } from "../../util/format.js";
|
|
3
|
-
import {
|
|
3
|
+
import { getColorClass } from "../style/Color.js";
|
|
4
|
+
import { getStatusClass } from "../style/Status.js";
|
|
4
5
|
import { getClass, getModuleClass } from "../util/css.js";
|
|
5
6
|
import styles from "./Progress.module.css";
|
|
6
7
|
/**
|
|
7
8
|
* Show progress as a single continuous horizontal bar, filled to `value` within the `min`–`max` range (matches `getPercent()` and `formatPercent()`).
|
|
8
|
-
* -
|
|
9
|
+
* - Renders a native `<progress>` element, so `role="progressbar"` and the value/max semantics come from the browser.
|
|
10
|
+
* - `<progress>` has no `min` attribute (its implicit minimum is `0`), so the range is normalised to `value - min` / `max - min` before it's handed to the element.
|
|
11
|
+
* - The browser clamps `value` to the `0`–`max` range, so an out-of-range `value` shows an empty or full bar rather than overspilling.
|
|
12
|
+
* - Paints from the tint ladder, so `color=` / `status=` recolour the fill (and track) by moving the tint anchor.
|
|
9
13
|
*
|
|
10
14
|
* @returns A progress bar element.
|
|
11
15
|
* @kind component
|
|
12
16
|
* @example <Progress value={3} max={4} />
|
|
17
|
+
* @example <Progress value={90} status="success" />
|
|
13
18
|
* @see https://shelving.cc/ui/Progress
|
|
14
19
|
*/
|
|
15
|
-
export function Progress({ value, min = 0, max = 100,
|
|
16
|
-
|
|
17
|
-
const
|
|
18
|
-
return (_jsx("
|
|
20
|
+
export function Progress({ value, min = 0, max = 100, ...props }) {
|
|
21
|
+
// `<progress>` has no `min`, so shift the range to a `0`-based one. A non-positive span would make `<progress>` indeterminate, so fall back to an empty determinate bar.
|
|
22
|
+
const span = max - min;
|
|
23
|
+
return (_jsx("progress", { className: getClass(getModuleClass(styles, "progress"), //
|
|
24
|
+
getColorClass(props), getStatusClass(props)), value: span > 0 ? value - min : 0, max: span > 0 ? span : 1, "aria-valuetext": formatPercent(value - min, max - min) }));
|
|
19
25
|
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# Progress
|
|
2
|
+
|
|
3
|
+
A continuous horizontal progress bar for the completion of a task. Rendered as a native `<progress>` element, so the `progressbar` role and value/max semantics come from the browser rather than hand-written ARIA.
|
|
4
|
+
|
|
5
|
+
**Things to know:**
|
|
6
|
+
|
|
7
|
+
- Use `Progress` for **task completion** — a value heading toward "done" (an upload, a multi-step form, a load). For a static reading within a range that can move up and down (disk usage, a score), a gauge is the right model, not a progress bar.
|
|
8
|
+
- `value` is filled within the `min`–`max` range (defaults `0`–`100`), matching `getPercent()` and `formatPercent()`. `<progress>` has no `min` attribute, so the range is normalised to `value - min` / `max - min` before it reaches the element.
|
|
9
|
+
- The browser clamps `value` to the `0`–`max` range, so an out-of-range `value` renders an empty or full bar rather than overspilling. A non-positive range (`min === max`) falls back to an empty bar rather than the indeterminate state.
|
|
10
|
+
- `aria-valuetext` carries the formatted percentage, so assistive tech announces e.g. "75%".
|
|
11
|
+
- Paints from the [tint ladder](/ui/TINT_CLASS): `color=` and `status=` move the tint anchor for the bar, so the fill (and track) re-derive together — `status="success"` gives a green bar, `color="purple"` a purple one. Without either, the bar takes the ambient tint (`--tint-50`, gray by default).
|
|
12
|
+
|
|
13
|
+
## Usage
|
|
14
|
+
|
|
15
|
+
### Basic
|
|
16
|
+
|
|
17
|
+
```tsx
|
|
18
|
+
import { Progress } from "shelving/ui";
|
|
19
|
+
|
|
20
|
+
// 75% full.
|
|
21
|
+
<Progress value={3} max={4} />
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
### Custom range
|
|
25
|
+
|
|
26
|
+
```tsx
|
|
27
|
+
import { Progress } from "shelving/ui";
|
|
28
|
+
|
|
29
|
+
// 50% full — value 15 within the 10–20 range.
|
|
30
|
+
<Progress value={15} min={10} max={20} />
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
### Status and colour
|
|
34
|
+
|
|
35
|
+
```tsx
|
|
36
|
+
import { Progress } from "shelving/ui";
|
|
37
|
+
|
|
38
|
+
<Progress value={90} status="success" />
|
|
39
|
+
<Progress value={20} status="danger" />
|
|
40
|
+
<Progress value={60} color="purple" />
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## Styling
|
|
44
|
+
|
|
45
|
+
`Progress` paints a track (the element / `::-webkit-progress-bar`) and a fill (`::-webkit-progress-value` / `::-moz-progress-bar`) from the [tint ladder](/ui/TINT_CLASS). Apply `color=` / `status=` (on the bar or a tinted ancestor scope) to recolour the fill and track together — reach for a per-property hook only for a single surgical change. Override these hooks at `:root` (or any ancestor scope) to retheme.
|
|
46
|
+
|
|
47
|
+
| Variable | Styles | Default |
|
|
48
|
+
|---|---|---|
|
|
49
|
+
| `--progress-height` | Bar thickness | `0.375rem` (6px) |
|
|
50
|
+
| `--progress-radius` | Corner radius | `999px` (pill) |
|
|
51
|
+
| `--progress-background` | Track fill | `var(--tint-90)` |
|
|
52
|
+
| `--progress-color` | Fill colour | `var(--tint-50)` |
|
|
53
|
+
|
|
54
|
+
**Global tokens it reads** — move these to retheme broadly rather than overriding the hooks directly: the tint-ladder steps `--tint-50` (fill) and `--tint-90` (track), plus `--duration-fast` (the fill's grow transition). To recolour a bar, prefer `color=` / `status=` (or a tinted ancestor scope) over the per-component hooks.
|
|
55
|
+
|
|
56
|
+
```css
|
|
57
|
+
/* Theme: a chunkier, square-cornered bar. */
|
|
58
|
+
:root {
|
|
59
|
+
--progress-height: 0.75rem;
|
|
60
|
+
--progress-radius: var(--radius-small);
|
|
61
|
+
}
|
|
62
|
+
```
|
|
@@ -4,35 +4,32 @@
|
|
|
4
4
|
@import "../style/Tint.module.css";
|
|
5
5
|
|
|
6
6
|
@layer components {
|
|
7
|
-
/* Continuous progress bar — a single track that fills horizontally. */
|
|
8
|
-
.
|
|
9
|
-
|
|
7
|
+
/* Continuous progress bar — a native `<progress>` styled as a single track that fills horizontally. */
|
|
8
|
+
.progress {
|
|
9
|
+
/* Strip the native rendering so the track/fill can be painted from tokens. */
|
|
10
|
+
appearance: none;
|
|
11
|
+
display: block;
|
|
10
12
|
margin: 0;
|
|
13
|
+
border: none;
|
|
11
14
|
inline-size: 100%;
|
|
12
|
-
|
|
15
|
+
block-size: var(--progress-height, 0.375rem);
|
|
13
16
|
border-radius: var(--progress-radius, 999px);
|
|
14
|
-
background: var(--progress-background, var(--tint-90));
|
|
15
17
|
overflow: hidden;
|
|
18
|
+
/* Track colour — Firefox uses the element itself as the track; WebKit/Blink repaints it on the pseudo-element below. */
|
|
19
|
+
background: var(--progress-background, var(--tint-90));
|
|
16
20
|
}
|
|
17
21
|
|
|
18
|
-
.
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
/* Width is set inline; clamp overspill to the track here. */
|
|
22
|
-
min-width: 0px;
|
|
23
|
-
max-width: 100%;
|
|
24
|
-
background: var(--progress-color, var(--color-blue));
|
|
25
|
-
transition: all var(--duration-fast);
|
|
22
|
+
/* Track (WebKit/Blink) — Firefox has no track pseudo-element. */
|
|
23
|
+
.progress::-webkit-progress-bar {
|
|
24
|
+
background: var(--progress-background, var(--tint-90));
|
|
26
25
|
}
|
|
27
26
|
|
|
28
|
-
/*
|
|
29
|
-
.
|
|
30
|
-
background: var(--progress-
|
|
31
|
-
|
|
32
|
-
.warning .fill {
|
|
33
|
-
background: var(--progress-warning, var(--color-orange));
|
|
27
|
+
/* Fill — WebKit/Blink and Firefox expose the fill under different pseudo-elements, so both repeat the same paint. Painted from the tint ladder, so `color=` / `status=` recolour it. */
|
|
28
|
+
.progress::-webkit-progress-value {
|
|
29
|
+
background: var(--progress-color, var(--tint-50));
|
|
30
|
+
transition: inline-size var(--duration-fast);
|
|
34
31
|
}
|
|
35
|
-
.
|
|
36
|
-
background: var(--progress-
|
|
32
|
+
.progress::-moz-progress-bar {
|
|
33
|
+
background: var(--progress-color, var(--tint-50));
|
|
37
34
|
}
|
|
38
35
|
}
|