shelving 1.247.0 → 1.248.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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "shelving",
3
- "version": "1.247.0",
3
+ "version": "1.248.0",
4
4
  "author": "Dave Houlbrooke <dave@shax.com>",
5
5
  "repository": {
6
6
  "type": "git",
package/ui/block/Table.md CHANGED
@@ -9,6 +9,7 @@ A data table — renders a `<table>`. Compose the usual `<thead>` / `<tbody>` /
9
9
  - Wrap a wide table in a horizontally scrollable container if it may exceed the content width on small screens.
10
10
  - Like the other block components it collapses its outer block margin when it is the first or last child.
11
11
  - Spans the full width of its container by default; set the `width` variant (`narrow` / `normal` / `wide` / `full` / `fit`) to constrain it.
12
+ - Size columns with [`<TableHeader>`](/ui/TableHeader) / [`<TableCell>`](/ui/TableCell) and the `width` variant — `width="fit"` hugs the content, and `width="12x" grow` gives a column a hard minimum it can grow past (cells honour `min-width`, so the table scrolls rather than collapsing the column on a narrow screen). A column's width is the widest of its cells.
12
13
  - Inside [`Prose`](/ui/Prose) a raw `<table>` picks up the same styling, so Markdown-rendered tables match component ones.
13
14
 
14
15
  ## Usage
@@ -50,6 +51,7 @@ import { Table } from "shelving/ui";
50
51
 
51
52
  ## See also
52
53
 
54
+ - [`TableHeader`](/ui/TableHeader) / [`TableCell`](/ui/TableCell) — `<th>` / `<td>` cells that set column widths and typography via variants.
53
55
  - [`Definitions`](/ui/Definitions) — term/value pairs when a two-column key/value layout fits better than a grid.
54
56
  - [`Prose`](/ui/Prose) — styles raw `<table>` inside longform content.
55
57
  - [`ui`](/ui) — the styling system: tint ladder, label tokens, and theming.
@@ -0,0 +1,23 @@
1
+ import type { ReactElement } from "react";
2
+ import { type TypographyVariants } from "../style/Typography.js";
3
+ import { type WidthVariants } from "../style/Width.js";
4
+ import type { ChildProps } from "../util/props.js";
5
+ /**
6
+ * Props for `TableCell` — width and typography variants plus `children`.
7
+ *
8
+ * @see https://dhoulb.github.io/shelving/ui/block/TableCell/TableCellProps
9
+ */
10
+ export interface TableCellProps extends WidthVariants, TypographyVariants, ChildProps {
11
+ }
12
+ /**
13
+ * Table body cell — rendered as `<td>`.
14
+ * - Sets its column's width via the `width` variant. Unlike a `<col>`, a cell honours `min-width`, so `width="12x" grow` gives the column a hard minimum that fills the remaining space and keeps the table from collapsing the column on a narrow viewport.
15
+ * - A column's width is the widest of its cells, so use this to size a column that has no header to set the width on.
16
+ *
17
+ * @kind component
18
+ * @param props Width and typography variant props plus `children`.
19
+ * @returns Rendered `<td>` element.
20
+ * @example <TableCell width="12x" grow>A longer description that wants a sensible minimum width.</TableCell>
21
+ * @see https://dhoulb.github.io/shelving/ui/block/TableCell/TableCell
22
+ */
23
+ export declare function TableCell({ children, ...props }: TableCellProps): ReactElement;
@@ -0,0 +1,18 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { getTypographyClass } from "../style/Typography.js";
3
+ import { getWidthClass } from "../style/Width.js";
4
+ import { getClass } from "../util/css.js";
5
+ /**
6
+ * Table body cell — rendered as `<td>`.
7
+ * - Sets its column's width via the `width` variant. Unlike a `<col>`, a cell honours `min-width`, so `width="12x" grow` gives the column a hard minimum that fills the remaining space and keeps the table from collapsing the column on a narrow viewport.
8
+ * - A column's width is the widest of its cells, so use this to size a column that has no header to set the width on.
9
+ *
10
+ * @kind component
11
+ * @param props Width and typography variant props plus `children`.
12
+ * @returns Rendered `<td>` element.
13
+ * @example <TableCell width="12x" grow>A longer description that wants a sensible minimum width.</TableCell>
14
+ * @see https://dhoulb.github.io/shelving/ui/block/TableCell/TableCell
15
+ */
16
+ export function TableCell({ children, ...props }) {
17
+ return _jsx("td", { className: getClass(getWidthClass(props), getTypographyClass(props)) || undefined, children: children });
18
+ }
@@ -0,0 +1,35 @@
1
+ # TableCell
2
+
3
+ A table body cell — renders a `<td>`. Use it for the body rows of a [`Table`](/ui/Table); it sets the column's width and cell typography.
4
+
5
+ **Things to know:**
6
+
7
+ - Set the column's width with the `width` variant. Because a cell (unlike a `<col>`) honours `min-width`, `width="12x" grow` gives the column a *hard* minimum that fills the remaining space and keeps the table from collapsing the column on a narrow screen — the table scrolls instead. See [`getWidthClass`](/ui/getWidthClass).
8
+ - A column's width is the widest of its cells, so this is how you size a column that has no header to set the width on (e.g. a trailing description column).
9
+ - Carries the same [typography variants](/ui/getTypographyClass) as the prose components (`size`, `weight`, `font`, `tint`, alignment).
10
+ - For a column that does have a header, prefer setting the width once on its [`TableHeader`](/ui/TableHeader).
11
+
12
+ ## Usage
13
+
14
+ ```tsx
15
+ import { Table, TableCell } from "shelving/ui";
16
+
17
+ <Table>
18
+ <tbody>
19
+ <tr>
20
+ <TableCell>name</TableCell>
21
+ <TableCell width="12x" grow>A longer description that should keep a sensible minimum width.</TableCell>
22
+ </tr>
23
+ </tbody>
24
+ </Table>
25
+ ```
26
+
27
+ ## Styling
28
+
29
+ `TableCell` paints nothing of its own — it composes the [`width`](/ui/getWidthClass) and [`typography`](/ui/getTypographyClass) variants onto a `<td>`, and inherits the cell styling (borders, padding) from the surrounding [`Table`](/ui/Table). It exposes no hooks of its own.
30
+
31
+ ## See also
32
+
33
+ - [`TableHeader`](/ui/TableHeader) — the `<th>` equivalent for header cells.
34
+ - [`Table`](/ui/Table) — the table the cells belong to.
35
+ - [`getWidthClass`](/ui/getWidthClass) — the full `width` / `grow` variant reference.
@@ -0,0 +1,27 @@
1
+ import type { ReactElement } from "react";
2
+ import { getTypographyClass, type TypographyVariants } from "../style/Typography.js";
3
+ import { getWidthClass, type WidthVariants } from "../style/Width.js";
4
+ import { getClass } from "../util/css.js";
5
+ import type { ChildProps } from "../util/props.js";
6
+
7
+ /**
8
+ * Props for `TableCell` — width and typography variants plus `children`.
9
+ *
10
+ * @see https://dhoulb.github.io/shelving/ui/block/TableCell/TableCellProps
11
+ */
12
+ export interface TableCellProps extends WidthVariants, TypographyVariants, ChildProps {}
13
+
14
+ /**
15
+ * Table body cell — rendered as `<td>`.
16
+ * - Sets its column's width via the `width` variant. Unlike a `<col>`, a cell honours `min-width`, so `width="12x" grow` gives the column a hard minimum that fills the remaining space and keeps the table from collapsing the column on a narrow viewport.
17
+ * - A column's width is the widest of its cells, so use this to size a column that has no header to set the width on.
18
+ *
19
+ * @kind component
20
+ * @param props Width and typography variant props plus `children`.
21
+ * @returns Rendered `<td>` element.
22
+ * @example <TableCell width="12x" grow>A longer description that wants a sensible minimum width.</TableCell>
23
+ * @see https://dhoulb.github.io/shelving/ui/block/TableCell/TableCell
24
+ */
25
+ export function TableCell({ children, ...props }: TableCellProps): ReactElement {
26
+ return <td className={getClass(getWidthClass(props), getTypographyClass(props)) || undefined}>{children}</td>;
27
+ }
@@ -0,0 +1,23 @@
1
+ import type { ReactElement } from "react";
2
+ import { type TypographyVariants } from "../style/Typography.js";
3
+ import { type WidthVariants } from "../style/Width.js";
4
+ import type { ChildProps } from "../util/props.js";
5
+ /**
6
+ * Props for `TableHeader` — width and typography variants plus `children`.
7
+ *
8
+ * @see https://dhoulb.github.io/shelving/ui/block/TableHeader/TableHeaderProps
9
+ */
10
+ export interface TableHeaderProps extends WidthVariants, TypographyVariants, ChildProps {
11
+ }
12
+ /**
13
+ * Table header cell — rendered as `<th>`.
14
+ * - Sets its column's width via the `width` variant (e.g. `width="fit"` to hug the content). Unlike a `<col>`, a cell honours `min-width`, so `width="12x" grow` gives the column a hard minimum it can grow past.
15
+ * - A column's width is the widest of its cells, so sizing the `<th>` sizes the whole column.
16
+ *
17
+ * @kind component
18
+ * @param props Width and typography variant props plus `children`.
19
+ * @returns Rendered `<th>` element.
20
+ * @example <TableHeader width="fit">Parameter</TableHeader>
21
+ * @see https://dhoulb.github.io/shelving/ui/block/TableHeader/TableHeader
22
+ */
23
+ export declare function TableHeader({ children, ...props }: TableHeaderProps): ReactElement;
@@ -0,0 +1,18 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { getTypographyClass } from "../style/Typography.js";
3
+ import { getWidthClass } from "../style/Width.js";
4
+ import { getClass } from "../util/css.js";
5
+ /**
6
+ * Table header cell — rendered as `<th>`.
7
+ * - Sets its column's width via the `width` variant (e.g. `width="fit"` to hug the content). Unlike a `<col>`, a cell honours `min-width`, so `width="12x" grow` gives the column a hard minimum it can grow past.
8
+ * - A column's width is the widest of its cells, so sizing the `<th>` sizes the whole column.
9
+ *
10
+ * @kind component
11
+ * @param props Width and typography variant props plus `children`.
12
+ * @returns Rendered `<th>` element.
13
+ * @example <TableHeader width="fit">Parameter</TableHeader>
14
+ * @see https://dhoulb.github.io/shelving/ui/block/TableHeader/TableHeader
15
+ */
16
+ export function TableHeader({ children, ...props }) {
17
+ return _jsx("th", { className: getClass(getWidthClass(props), getTypographyClass(props)) || undefined, children: children });
18
+ }
@@ -0,0 +1,43 @@
1
+ # TableHeader
2
+
3
+ A table header cell — renders a `<th>`. Use it for the header row of a [`Table`](/ui/Table); it sets the column's width and label typography.
4
+
5
+ **Things to know:**
6
+
7
+ - Set the column's width with the `width` variant — `width="fit"` hugs the content. Because a cell (unlike a `<col>`) honours `min-width`, `width="12x" grow` gives the column a *hard* minimum it can grow past, so the table scrolls rather than collapsing the column on a narrow screen. See [`getWidthClass`](/ui/getWidthClass).
8
+ - A column's width is the widest of its cells, so sizing the `<th>` sizes the whole column — set the width once on the header instead of on every body cell.
9
+ - Carries the same [typography variants](/ui/getTypographyClass) as the prose components (`size`, `weight`, `font`, `tint`, alignment), though the `Table` already styles header cells in the label style by default.
10
+ - For a column that has no header, set the width on its [`TableCell`](/ui/TableCell)s instead.
11
+
12
+ ## Usage
13
+
14
+ ```tsx
15
+ import { Table, TableCell, TableHeader } from "shelving/ui";
16
+
17
+ <Table>
18
+ <thead>
19
+ <tr>
20
+ <TableHeader width="fit">Parameter</TableHeader>
21
+ <TableHeader width="fit">Type</TableHeader>
22
+ <TableHeader width="12x" grow>Description</TableHeader>
23
+ </tr>
24
+ </thead>
25
+ <tbody>
26
+ <tr>
27
+ <TableCell>name</TableCell>
28
+ <TableCell>string</TableCell>
29
+ <TableCell>The name of the thing.</TableCell>
30
+ </tr>
31
+ </tbody>
32
+ </Table>
33
+ ```
34
+
35
+ ## Styling
36
+
37
+ `TableHeader` paints nothing of its own — it composes the [`width`](/ui/getWidthClass) and [`typography`](/ui/getTypographyClass) variants onto a `<th>`, and inherits the header styling (label font, borders, padding) from the surrounding [`Table`](/ui/Table). It exposes no hooks of its own.
38
+
39
+ ## See also
40
+
41
+ - [`TableCell`](/ui/TableCell) — the `<td>` equivalent for body cells.
42
+ - [`Table`](/ui/Table) — the table the cells belong to.
43
+ - [`getWidthClass`](/ui/getWidthClass) — the full `width` / `grow` variant reference.
@@ -0,0 +1,27 @@
1
+ import type { ReactElement } from "react";
2
+ import { getTypographyClass, type TypographyVariants } from "../style/Typography.js";
3
+ import { getWidthClass, type WidthVariants } from "../style/Width.js";
4
+ import { getClass } from "../util/css.js";
5
+ import type { ChildProps } from "../util/props.js";
6
+
7
+ /**
8
+ * Props for `TableHeader` — width and typography variants plus `children`.
9
+ *
10
+ * @see https://dhoulb.github.io/shelving/ui/block/TableHeader/TableHeaderProps
11
+ */
12
+ export interface TableHeaderProps extends WidthVariants, TypographyVariants, ChildProps {}
13
+
14
+ /**
15
+ * Table header cell — rendered as `<th>`.
16
+ * - Sets its column's width via the `width` variant (e.g. `width="fit"` to hug the content). Unlike a `<col>`, a cell honours `min-width`, so `width="12x" grow` gives the column a hard minimum it can grow past.
17
+ * - A column's width is the widest of its cells, so sizing the `<th>` sizes the whole column.
18
+ *
19
+ * @kind component
20
+ * @param props Width and typography variant props plus `children`.
21
+ * @returns Rendered `<th>` element.
22
+ * @example <TableHeader width="fit">Parameter</TableHeader>
23
+ * @see https://dhoulb.github.io/shelving/ui/block/TableHeader/TableHeader
24
+ */
25
+ export function TableHeader({ children, ...props }: TableHeaderProps): ReactElement {
26
+ return <th className={getClass(getWidthClass(props), getTypographyClass(props)) || undefined}>{children}</th>;
27
+ }
@@ -14,5 +14,7 @@ export * from "./Preformatted.js";
14
14
  export * from "./Prose.js";
15
15
  export * from "./Subheading.js";
16
16
  export * from "./Table.js";
17
+ export * from "./TableCell.js";
18
+ export * from "./TableHeader.js";
17
19
  export * from "./Title.js";
18
20
  export * from "./Video.js";
package/ui/block/index.js CHANGED
@@ -14,5 +14,7 @@ export * from "./Preformatted.js";
14
14
  export * from "./Prose.js";
15
15
  export * from "./Subheading.js";
16
16
  export * from "./Table.js";
17
+ export * from "./TableCell.js";
18
+ export * from "./TableHeader.js";
17
19
  export * from "./Title.js";
18
20
  export * from "./Video.js";
package/ui/block/index.ts CHANGED
@@ -14,5 +14,7 @@ export * from "./Preformatted.js";
14
14
  export * from "./Prose.js";
15
15
  export * from "./Subheading.js";
16
16
  export * from "./Table.js";
17
+ export * from "./TableCell.js";
18
+ export * from "./TableHeader.js";
17
19
  export * from "./Title.js";
18
20
  export * from "./Video.js";
@@ -8,6 +8,8 @@ import { Preformatted } from "../block/Preformatted.js";
8
8
  import { Prose } from "../block/Prose.js";
9
9
  import { Header, Section } from "../block/Section.js";
10
10
  import { Table } from "../block/Table.js";
11
+ import { TableCell } from "../block/TableCell.js";
12
+ import { TableHeader } from "../block/TableHeader.js";
11
13
  import { Title } from "../block/Title.js";
12
14
  import { Code } from "../inline/Code.js";
13
15
  import { Markup } from "../misc/Markup.js";
@@ -79,9 +81,9 @@ function DocumentationChildren({ elements }) {
79
81
  */
80
82
  export function DocumentationPage({ title, name, kind = "unknown", description, content, signatures, params, returns, throws, types, examples, children, ...props }) {
81
83
  const map = useTreeMap();
82
- return (_jsx(Page, { title: title ?? name, description: description, children: _jsxs(Block, { color: getDocumentationKindColor(kind), children: [_jsx(Panel, { children: _jsxs(Header, { children: [_jsx(TreeBreadcrumbs, {}), _jsx(Title, { children: _jsxs(Row, { left: true, wrap: true, children: [title ?? name, kind && _jsx(DocumentationKind, { kind: kind, size: "normal" })] }) }), _jsx(DocumentationButtons, { ...props })] }) }), signatures?.length || params?.length || returns?.length || throws?.length || types?.length ? (_jsxs(Section, { children: [_jsx(DocumentationSignatures, { signatures: signatures }), params?.length && (_jsx(Section, { children: _jsx(Scroll, { horizontal: true, children: _jsxs(Table, { children: [_jsx("thead", { children: _jsxs("tr", { children: [_jsx("th", { children: "Parameter" }), _jsx("th", { children: "Type" }), _jsx("th", { children: "Default" })] }) }), _jsx("tbody", { children: params.map(({ name, type = DEFAULT_TYPE, description, default: def }) => {
84
+ return (_jsx(Page, { title: title ?? name, description: description, children: _jsxs(Block, { color: getDocumentationKindColor(kind), children: [_jsx(Panel, { children: _jsxs(Header, { children: [_jsx(TreeBreadcrumbs, {}), _jsx(Title, { children: _jsxs(Row, { left: true, wrap: true, children: [title ?? name, kind && _jsx(DocumentationKind, { kind: kind, size: "normal" })] }) }), _jsx(DocumentationButtons, { ...props })] }) }), signatures?.length || params?.length || returns?.length || throws?.length || types?.length ? (_jsxs(Section, { children: [_jsx(DocumentationSignatures, { signatures: signatures }), params?.length && (_jsx(Section, { children: _jsx(Scroll, { horizontal: true, children: _jsxs(Table, { children: [_jsx("thead", { children: _jsxs("tr", { children: [_jsx(TableHeader, { width: "fit", children: "Param" }), _jsx(TableHeader, { width: "fit", children: "Type" }), _jsx(TableHeader, { width: "fit", children: "Default" })] }) }), _jsx("tbody", { children: params.map(({ name, type = DEFAULT_TYPE, description, default: def }) => {
83
85
  // An options-bag param whose type resolves to a documented interface/object type is flattened into its individual fields as indented child rows.
84
86
  const resolved = getTreeElement(map, type)?.props;
85
- return (_jsxs(Fragment, { children: [_jsxs("tr", { children: [_jsx("td", { children: _jsx(Code, { children: name }) }), _jsx("td", { children: _jsx(TreeLink, { name: type }) }), _jsx("td", { children: def ? _jsx(Code, { children: def }) : "-" }), _jsx("td", { children: description || resolved?.description || "" })] }), resolved?.properties?.map(prop => (_jsxs("tr", { children: [_jsx("td", { children: _jsx(Code, { children: `.${prop.name}` }) }), _jsx("td", { children: _jsx(TreeLink, { name: prop.type ?? DEFAULT_TYPE }) }), _jsx("td", { children: prop.default ? _jsx(Code, { children: prop.default }) : "-" }), _jsx("td", { children: _getRowDescription(map, prop.type ?? DEFAULT_TYPE, prop.description) })] }, `${name}.${prop.name}`)))] }, `${name}-${type}`));
86
- }) })] }) }) })), returns?.length && (_jsx(Section, { children: _jsx(Scroll, { horizontal: true, children: _jsxs(Table, { children: [_jsx("thead", { children: _jsx("tr", { children: _jsx("th", { children: "Return" }) }) }), _jsx("tbody", { children: returns.map(({ type = DEFAULT_TYPE, description }) => (_jsxs("tr", { children: [_jsx("td", { children: _jsx(TreeLink, { name: type }) }), _jsx("td", { children: _getRowDescription(map, type, description) })] }, `${type}-${description}`))) })] }) }) })), throws?.length && (_jsx(Section, { children: _jsx(Scroll, { horizontal: true, children: _jsxs(Table, { children: [_jsx("thead", { children: _jsx("tr", { children: _jsx("th", { children: "Throws" }) }) }), _jsx("tbody", { children: throws.map(({ type = DEFAULT_TYPE, description }) => (_jsxs("tr", { children: [_jsx("td", { children: _jsx(TreeLink, { name: type }) }), _jsx("td", { children: _getRowDescription(map, type, description) })] }, `${type}-${description}`))) })] }) }) })), types?.length && (_jsx(Section, { children: _jsx(Scroll, { horizontal: true, children: _jsxs(Table, { children: [_jsx("thead", { children: _jsx("tr", { children: _jsx("th", { children: "Type" }) }) }), _jsx("tbody", { children: types.map(type => (_jsxs("tr", { children: [_jsx("td", { children: _jsx(TreeLink, { name: type }) }), _jsx("td", { children: _getRowDescription(map, type) })] }, type))) })] }) }) }))] })) : null, content && (_jsx(Section, { children: _jsx(Prose, { children: _jsx(Markup, { children: content }) }) })), examples?.length && (_jsxs(Section, { children: [_jsx(Heading, { children: "Examples" }), examples.map(({ description }) => (_jsx(Preformatted, { children: description }, description)))] })), _jsx(DocumentationChildren, { elements: children })] }) }));
87
+ return (_jsxs(Fragment, { children: [_jsxs("tr", { children: [_jsx(TableCell, { children: _jsx(Code, { children: name }) }), _jsx(TableCell, { children: _jsx(TreeLink, { name: type }) }), _jsx(TableCell, { children: def ? _jsx(Code, { children: def }) : "-" }), _jsx(TableCell, { width: "20x", grow: true, children: description || resolved?.description || "" })] }), resolved?.properties?.map(prop => (_jsxs("tr", { children: [_jsx(TableCell, { children: _jsx(Code, { children: `.${prop.name}` }) }), _jsx(TableCell, { children: _jsx(TreeLink, { name: prop.type ?? DEFAULT_TYPE }) }), _jsx(TableCell, { children: prop.default ? _jsx(Code, { children: prop.default }) : "-" }), _jsx(TableCell, { width: "20x", grow: true, children: _getRowDescription(map, prop.type ?? DEFAULT_TYPE, prop.description) })] }, `${name}.${prop.name}`)))] }, `${name}-${type}`));
88
+ }) })] }) }) })), returns?.length && (_jsx(Section, { children: _jsx(Scroll, { horizontal: true, children: _jsxs(Table, { children: [_jsx("thead", { children: _jsx("tr", { children: _jsx(TableHeader, { width: "fit", children: "Return" }) }) }), _jsx("tbody", { children: returns.map(({ type = DEFAULT_TYPE, description }) => (_jsxs("tr", { children: [_jsx(TableCell, { children: _jsx(TreeLink, { name: type }) }), _jsx(TableCell, { width: "20x", grow: true, children: _getRowDescription(map, type, description) })] }, `${type}-${description}`))) })] }) }) })), throws?.length && (_jsx(Section, { children: _jsx(Scroll, { horizontal: true, children: _jsxs(Table, { children: [_jsx("thead", { children: _jsx("tr", { children: _jsx(TableHeader, { width: "fit", children: "Throws" }) }) }), _jsx("tbody", { children: throws.map(({ type = DEFAULT_TYPE, description }) => (_jsxs("tr", { children: [_jsx(TableCell, { children: _jsx(TreeLink, { name: type }) }), _jsx(TableCell, { width: "20x", grow: true, children: _getRowDescription(map, type, description) })] }, `${type}-${description}`))) })] }) }) })), types?.length && (_jsx(Section, { children: _jsx(Scroll, { horizontal: true, children: _jsxs(Table, { children: [_jsx("thead", { children: _jsx("tr", { children: _jsx(TableHeader, { width: "fit", children: "Type" }) }) }), _jsx("tbody", { children: types.map(type => (_jsxs("tr", { children: [_jsx(TableCell, { children: _jsx(TreeLink, { name: type }) }), _jsx(TableCell, { width: "20x", grow: true, children: _getRowDescription(map, type) })] }, type))) })] }) }) }))] })) : null, content && (_jsx(Section, { children: _jsx(Prose, { children: _jsx(Markup, { children: content }) }) })), examples?.length && (_jsxs(Section, { children: [_jsx(Heading, { children: "Examples" }), examples.map(({ description }) => (_jsx(Preformatted, { children: description }, description)))] })), _jsx(DocumentationChildren, { elements: children })] }) }));
87
89
  }
@@ -6,7 +6,7 @@ The full detail page for a documented symbol — the default renderer for `tree-
6
6
 
7
7
  - Above the title it renders an ancestor trail via [`TreeBreadcrumbs`](/ui/TreeBreadcrumbs), so it needs a [`TreeProvider`](/ui/TreeProvider) above it to resolve the ancestors.
8
8
  - The title carries a [`DocumentationKind`](/ui/DocumentationKind) tag, and below it [`DocumentationButtons`](/ui/DocumentationButtons) lists the symbol's relations (`member of`, `extends`, `implements`) as links.
9
- - It renders the signature(s) via [`DocumentationSignatures`](/ui/DocumentationSignatures) (one block per overload, each carrying the symbol's name), then prose content, then conditional `Parameters` / `Returns` / `Throws` / `Examples` sections — each only appears when it has entries. Parameters render as a [`Table`](/ui/Table) (`Parameter` / `Type` / `Default` / `Description` columns, a `-` standing in where a parameter has no default); Returns and Throws render as two-column tables (`Return` / `Throws` plus `Description`), with the type in the first column.
9
+ - It renders the signature(s) via [`DocumentationSignatures`](/ui/DocumentationSignatures) (one block per overload, each carrying the symbol's name), then prose content, then conditional `Parameters` / `Returns` / `Throws` / `Examples` sections — each only appears when it has entries. Parameters render as a [`Table`](/ui/Table) (`Param` / `Type` / `Default` / `Description` columns, a `-` standing in where a parameter has no default); Returns and Throws render as two-column tables (`Return` / `Throws` plus `Description`), with the type in the first column.
10
10
  - Child symbols follow, grouped by `kind` into card sections (`Components`, `Functions`, `Classes`, `Interfaces`, `Types`, `Constants`, `Methods`, `Properties`) rendered as [`DocumentationCard`](/ui/DocumentationCard)s inside a [`TreeCards`](/ui/TreeCards) listing. A new documented kind needs an entry in `KIND_SECTIONS` here (and a colour in [`DocumentationKind`](/ui/DocumentationKind)).
11
11
 
12
12
  ## Usage
@@ -57,7 +57,7 @@ describe("DocumentationPage", () => {
57
57
  "./makeThing",
58
58
  );
59
59
  // Parameters table headers — name, type, default, description in separate columns.
60
- expect(html).toContain("<th>Parameter</th>");
60
+ expect(html).toContain("<th>Param</th>");
61
61
  expect(html).toContain("<th>Type</th>");
62
62
  expect(html).toContain("<th>Default</th>");
63
63
  // A param with a default renders it; one without gets a hyphen.
@@ -8,6 +8,8 @@ import { Preformatted } from "../block/Preformatted.js";
8
8
  import { Prose } from "../block/Prose.js";
9
9
  import { Header, Section } from "../block/Section.js";
10
10
  import { Table } from "../block/Table.js";
11
+ import { TableCell } from "../block/TableCell.js";
12
+ import { TableHeader } from "../block/TableHeader.js";
11
13
  import { Title } from "../block/Title.js";
12
14
  import { Code } from "../inline/Code.js";
13
15
  import { Markup } from "../misc/Markup.js";
@@ -129,9 +131,9 @@ export function DocumentationPage({
129
131
  <Table>
130
132
  <thead>
131
133
  <tr>
132
- <th>Parameter</th>
133
- <th>Type</th>
134
- <th>Default</th>
134
+ <TableHeader width="fit">Param</TableHeader>
135
+ <TableHeader width="fit">Type</TableHeader>
136
+ <TableHeader width="fit">Default</TableHeader>
135
137
  </tr>
136
138
  </thead>
137
139
  <tbody>
@@ -141,25 +143,29 @@ export function DocumentationPage({
141
143
  return (
142
144
  <Fragment key={`${name}-${type}`}>
143
145
  <tr>
144
- <td>
146
+ <TableCell>
145
147
  <Code>{name}</Code>
146
- </td>
147
- <td>
148
+ </TableCell>
149
+ <TableCell>
148
150
  <TreeLink name={type} />
149
- </td>
150
- <td>{def ? <Code>{def}</Code> : "-"}</td>
151
- <td>{description || resolved?.description || ""}</td>
151
+ </TableCell>
152
+ <TableCell>{def ? <Code>{def}</Code> : "-"}</TableCell>
153
+ <TableCell width="20x" grow>
154
+ {description || resolved?.description || ""}
155
+ </TableCell>
152
156
  </tr>
153
157
  {resolved?.properties?.map(prop => (
154
158
  <tr key={`${name}.${prop.name}`}>
155
- <td>
159
+ <TableCell>
156
160
  <Code>{`.${prop.name}`}</Code>
157
- </td>
158
- <td>
161
+ </TableCell>
162
+ <TableCell>
159
163
  <TreeLink name={prop.type ?? DEFAULT_TYPE} />
160
- </td>
161
- <td>{prop.default ? <Code>{prop.default}</Code> : "-"}</td>
162
- <td>{_getRowDescription(map, prop.type ?? DEFAULT_TYPE, prop.description)}</td>
164
+ </TableCell>
165
+ <TableCell>{prop.default ? <Code>{prop.default}</Code> : "-"}</TableCell>
166
+ <TableCell width="20x" grow>
167
+ {_getRowDescription(map, prop.type ?? DEFAULT_TYPE, prop.description)}
168
+ </TableCell>
163
169
  </tr>
164
170
  ))}
165
171
  </Fragment>
@@ -176,16 +182,18 @@ export function DocumentationPage({
176
182
  <Table>
177
183
  <thead>
178
184
  <tr>
179
- <th>Return</th>
185
+ <TableHeader width="fit">Return</TableHeader>
180
186
  </tr>
181
187
  </thead>
182
188
  <tbody>
183
189
  {returns.map(({ type = DEFAULT_TYPE, description }) => (
184
190
  <tr key={`${type}-${description}`}>
185
- <td>
191
+ <TableCell>
186
192
  <TreeLink name={type} />
187
- </td>
188
- <td>{_getRowDescription(map, type, description)}</td>
193
+ </TableCell>
194
+ <TableCell width="20x" grow>
195
+ {_getRowDescription(map, type, description)}
196
+ </TableCell>
189
197
  </tr>
190
198
  ))}
191
199
  </tbody>
@@ -199,16 +207,18 @@ export function DocumentationPage({
199
207
  <Table>
200
208
  <thead>
201
209
  <tr>
202
- <th>Throws</th>
210
+ <TableHeader width="fit">Throws</TableHeader>
203
211
  </tr>
204
212
  </thead>
205
213
  <tbody>
206
214
  {throws.map(({ type = DEFAULT_TYPE, description }) => (
207
215
  <tr key={`${type}-${description}`}>
208
- <td>
216
+ <TableCell>
209
217
  <TreeLink name={type} />
210
- </td>
211
- <td>{_getRowDescription(map, type, description)}</td>
218
+ </TableCell>
219
+ <TableCell width="20x" grow>
220
+ {_getRowDescription(map, type, description)}
221
+ </TableCell>
212
222
  </tr>
213
223
  ))}
214
224
  </tbody>
@@ -222,16 +232,18 @@ export function DocumentationPage({
222
232
  <Table>
223
233
  <thead>
224
234
  <tr>
225
- <th>Type</th>
235
+ <TableHeader width="fit">Type</TableHeader>
226
236
  </tr>
227
237
  </thead>
228
238
  <tbody>
229
239
  {types.map(type => (
230
240
  <tr key={type}>
231
- <td>
241
+ <TableCell>
232
242
  <TreeLink name={type} />
233
- </td>
234
- <td>{_getRowDescription(map, type)}</td>
243
+ </TableCell>
244
+ <TableCell width="20x" grow>
245
+ {_getRowDescription(map, type)}
246
+ </TableCell>
235
247
  </tr>
236
248
  ))}
237
249
  </tbody>
@@ -1,27 +1,34 @@
1
1
  /**
2
- * Enumerated max-inline-size constraints selectable via the `width` variant prop.
3
- * - `narrow` / `normal` / `wide` — fixed max-widths from the `--width-*` tokens.
4
- * - `full` — take the full available width, removing any default max-width constraint.
2
+ * Enumerated inline-size selectable via the `width` variant prop.
3
+ * - `narrow` / `normal` / `wide` — fixed widths from the `--width-*` tokens (capped at 100%).
4
+ * - `full` — take the full available width.
5
5
  * - `fit` — shrink to fit the content's intrinsic width (`fit-content`).
6
+ * - `1x` … `128x` — exact widths that are multiples of `--space-normal` (16px), following the spacing scale (`1x`–`8x` in single steps, then `10x`/`12x`/`14x`/`16x`, `20x`/`24x`/`28x`/`32x`, then `40x` … `128x` in eights).
6
7
  *
7
8
  * @see https://dhoulb.github.io/shelving/ui/style/Width/UIWidth
8
9
  */
9
- export type UIWidth = "narrow" | "normal" | "wide" | "full" | "fit";
10
+ export type UIWidth = "narrow" | "normal" | "wide" | "full" | "fit" | "1x" | "2x" | "3x" | "4x" | "5x" | "6x" | "7x" | "8x" | "10x" | "12x" | "14x" | "16x" | "20x" | "24x" | "28x" | "32x" | "40x" | "48x" | "56x" | "64x" | "72x" | "80x" | "88x" | "96x" | "104x" | "112x" | "120x" | "128x";
10
11
  /**
11
- * Variant props that constrain (or unconstrain) a block-level component's max-width, e.g. `width="narrow"`.
12
+ * Variant props that set (or unconstrain) a component's inline-size, e.g. `width="narrow"` or `width="12x"`.
12
13
  *
13
14
  * @see https://dhoulb.github.io/shelving/ui/style/Width/WidthVariants
14
15
  */
15
16
  export interface WidthVariants {
16
- /** Max-inline-size constraint of the element. */
17
+ /** Inline-size of the element. */
17
18
  width?: UIWidth | undefined;
19
+ /**
20
+ * Let the element grow past its `width` instead of staying exact.
21
+ * - Turns the chosen `width` into a floor (`min-inline-size`) and adds `flex-grow: 1` so the element expands to fill the available space when it's a flex item or table column.
22
+ */
23
+ grow?: boolean | undefined;
18
24
  }
19
25
  /**
20
- * Get the max-width class for a component from its `width` variant prop.
26
+ * Get the inline-size class for a component from its `width` / `grow` variant props.
21
27
  *
22
- * @param variants Variant props containing the optional `width` constraint.
23
- * @returns The width class string, or `undefined` when no `width` is set.
24
- * @example getWidthClass({ width: "narrow" }) // "narrow"
28
+ * @param variants Variant props containing the optional `width` value and `grow` flag.
29
+ * @returns The width class string, or `undefined` when neither `width` nor `grow` is set.
30
+ * @example getWidthClass({ width: "narrow" }) // "width narrow"
31
+ * @example getWidthClass({ width: "12x", grow: true }) // "width 12x grow"
25
32
  * @see https://dhoulb.github.io/shelving/ui/style/Width/getWidthClass
26
33
  */
27
- export declare function getWidthClass({ width }: WidthVariants): string | undefined;
34
+ export declare function getWidthClass({ width, grow }: WidthVariants): string | undefined;
package/ui/style/Width.js CHANGED
@@ -1,13 +1,16 @@
1
1
  import { getModuleClass } from "../util/css.js";
2
2
  import WIDTH_CSS from "./Width.module.css";
3
3
  /**
4
- * Get the max-width class for a component from its `width` variant prop.
4
+ * Get the inline-size class for a component from its `width` / `grow` variant props.
5
5
  *
6
- * @param variants Variant props containing the optional `width` constraint.
7
- * @returns The width class string, or `undefined` when no `width` is set.
8
- * @example getWidthClass({ width: "narrow" }) // "narrow"
6
+ * @param variants Variant props containing the optional `width` value and `grow` flag.
7
+ * @returns The width class string, or `undefined` when neither `width` nor `grow` is set.
8
+ * @example getWidthClass({ width: "narrow" }) // "width narrow"
9
+ * @example getWidthClass({ width: "12x", grow: true }) // "width 12x grow"
9
10
  * @see https://dhoulb.github.io/shelving/ui/style/Width/getWidthClass
10
11
  */
11
- export function getWidthClass({ width }) {
12
- return width && getModuleClass(WIDTH_CSS, width);
12
+ export function getWidthClass({ width, grow }) {
13
+ if (!width && !grow)
14
+ return undefined;
15
+ return getModuleClass(WIDTH_CSS, "width", width, { grow });
13
16
  }
@@ -1,4 +1,5 @@
1
1
  @import "layers.css";
2
+ @import "Space.module.css";
2
3
 
3
4
  @layer defaults {
4
5
  :root {
@@ -8,11 +9,8 @@
8
9
  --width-wide: 75rem;
9
10
  }
10
11
 
11
- .narrow,
12
- .normal,
13
- .wide,
14
- .full,
15
- .fit {
12
+ /* Frame shared by every width value (weak default — a component's own margin can still beat the centering). */
13
+ .width {
16
14
  box-sizing: border-box;
17
15
  max-inline-size: 100%;
18
16
  margin-inline: auto;
@@ -20,21 +18,119 @@
20
18
  }
21
19
 
22
20
  @layer variants {
23
- /* Width variants constrain a block-level component's max-inline-size. */
21
+ /* Apply the chosen value (set as `--width` by the value classes below). */
22
+ .width {
23
+ inline-size: var(--width, auto);
24
+ }
25
+
26
+ /* Semantic widths. */
24
27
  .narrow {
25
- inline-size: var(--width-narrow);
28
+ --width: var(--width-narrow);
26
29
  }
27
30
  .normal {
28
- inline-size: var(--width-normal);
31
+ --width: var(--width-normal);
29
32
  }
30
33
  .wide {
31
- inline-size: var(--width-wide);
34
+ --width: var(--width-wide);
32
35
  }
33
36
  .full {
37
+ --width: 100%;
34
38
  margin-inline: 0;
35
- inline-size: 100%;
36
39
  }
37
40
  .fit {
38
- inline-size: fit-content;
41
+ --width: fit-content;
42
+ }
43
+
44
+ /* Numeric exact widths — multiples of `--space-normal` (16px). */
45
+ .\31 x {
46
+ --width: calc(var(--space-normal) * 1);
47
+ }
48
+ .\32 x {
49
+ --width: calc(var(--space-normal) * 2);
50
+ }
51
+ .\33 x {
52
+ --width: calc(var(--space-normal) * 3);
53
+ }
54
+ .\34 x {
55
+ --width: calc(var(--space-normal) * 4);
56
+ }
57
+ .\35 x {
58
+ --width: calc(var(--space-normal) * 5);
59
+ }
60
+ .\36 x {
61
+ --width: calc(var(--space-normal) * 6);
62
+ }
63
+ .\37 x {
64
+ --width: calc(var(--space-normal) * 7);
65
+ }
66
+ .\38 x {
67
+ --width: calc(var(--space-normal) * 8);
68
+ }
69
+ .\31 0x {
70
+ --width: calc(var(--space-normal) * 10);
71
+ }
72
+ .\31 2x {
73
+ --width: calc(var(--space-normal) * 12);
74
+ }
75
+ .\31 4x {
76
+ --width: calc(var(--space-normal) * 14);
77
+ }
78
+ .\31 6x {
79
+ --width: calc(var(--space-normal) * 16);
80
+ }
81
+ .\32 0x {
82
+ --width: calc(var(--space-normal) * 20);
83
+ }
84
+ .\32 4x {
85
+ --width: calc(var(--space-normal) * 24);
86
+ }
87
+ .\32 8x {
88
+ --width: calc(var(--space-normal) * 28);
89
+ }
90
+ .\33 2x {
91
+ --width: calc(var(--space-normal) * 32);
92
+ }
93
+ .\34 0x {
94
+ --width: calc(var(--space-normal) * 40);
95
+ }
96
+ .\34 8x {
97
+ --width: calc(var(--space-normal) * 48);
98
+ }
99
+ .\35 6x {
100
+ --width: calc(var(--space-normal) * 56);
101
+ }
102
+ .\36 4x {
103
+ --width: calc(var(--space-normal) * 64);
104
+ }
105
+ .\37 2x {
106
+ --width: calc(var(--space-normal) * 72);
107
+ }
108
+ .\38 0x {
109
+ --width: calc(var(--space-normal) * 80);
110
+ }
111
+ .\38 8x {
112
+ --width: calc(var(--space-normal) * 88);
113
+ }
114
+ .\39 6x {
115
+ --width: calc(var(--space-normal) * 96);
116
+ }
117
+ .\31 04x {
118
+ --width: calc(var(--space-normal) * 104);
119
+ }
120
+ .\31 12x {
121
+ --width: calc(var(--space-normal) * 112);
122
+ }
123
+ .\31 20x {
124
+ --width: calc(var(--space-normal) * 120);
125
+ }
126
+ .\31 28x {
127
+ --width: calc(var(--space-normal) * 128);
128
+ }
129
+
130
+ /* Grow modifier — turn the chosen width into a floor and expand to fill (flex items + table columns). */
131
+ .grow {
132
+ margin-inline: 0;
133
+ min-inline-size: var(--width);
134
+ flex-grow: 1;
39
135
  }
40
136
  }
@@ -2,33 +2,74 @@ import { getModuleClass } from "../util/css.js";
2
2
  import WIDTH_CSS from "./Width.module.css";
3
3
 
4
4
  /**
5
- * Enumerated max-inline-size constraints selectable via the `width` variant prop.
6
- * - `narrow` / `normal` / `wide` — fixed max-widths from the `--width-*` tokens.
7
- * - `full` — take the full available width, removing any default max-width constraint.
5
+ * Enumerated inline-size selectable via the `width` variant prop.
6
+ * - `narrow` / `normal` / `wide` — fixed widths from the `--width-*` tokens (capped at 100%).
7
+ * - `full` — take the full available width.
8
8
  * - `fit` — shrink to fit the content's intrinsic width (`fit-content`).
9
+ * - `1x` … `128x` — exact widths that are multiples of `--space-normal` (16px), following the spacing scale (`1x`–`8x` in single steps, then `10x`/`12x`/`14x`/`16x`, `20x`/`24x`/`28x`/`32x`, then `40x` … `128x` in eights).
9
10
  *
10
11
  * @see https://dhoulb.github.io/shelving/ui/style/Width/UIWidth
11
12
  */
12
- export type UIWidth = "narrow" | "normal" | "wide" | "full" | "fit";
13
+ export type UIWidth =
14
+ | "narrow"
15
+ | "normal"
16
+ | "wide"
17
+ | "full"
18
+ | "fit"
19
+ | "1x"
20
+ | "2x"
21
+ | "3x"
22
+ | "4x"
23
+ | "5x"
24
+ | "6x"
25
+ | "7x"
26
+ | "8x"
27
+ | "10x"
28
+ | "12x"
29
+ | "14x"
30
+ | "16x"
31
+ | "20x"
32
+ | "24x"
33
+ | "28x"
34
+ | "32x"
35
+ | "40x"
36
+ | "48x"
37
+ | "56x"
38
+ | "64x"
39
+ | "72x"
40
+ | "80x"
41
+ | "88x"
42
+ | "96x"
43
+ | "104x"
44
+ | "112x"
45
+ | "120x"
46
+ | "128x";
13
47
 
14
48
  /**
15
- * Variant props that constrain (or unconstrain) a block-level component's max-width, e.g. `width="narrow"`.
49
+ * Variant props that set (or unconstrain) a component's inline-size, e.g. `width="narrow"` or `width="12x"`.
16
50
  *
17
51
  * @see https://dhoulb.github.io/shelving/ui/style/Width/WidthVariants
18
52
  */
19
53
  export interface WidthVariants {
20
- /** Max-inline-size constraint of the element. */
54
+ /** Inline-size of the element. */
21
55
  width?: UIWidth | undefined;
56
+ /**
57
+ * Let the element grow past its `width` instead of staying exact.
58
+ * - Turns the chosen `width` into a floor (`min-inline-size`) and adds `flex-grow: 1` so the element expands to fill the available space when it's a flex item or table column.
59
+ */
60
+ grow?: boolean | undefined;
22
61
  }
23
62
 
24
63
  /**
25
- * Get the max-width class for a component from its `width` variant prop.
64
+ * Get the inline-size class for a component from its `width` / `grow` variant props.
26
65
  *
27
- * @param variants Variant props containing the optional `width` constraint.
28
- * @returns The width class string, or `undefined` when no `width` is set.
29
- * @example getWidthClass({ width: "narrow" }) // "narrow"
66
+ * @param variants Variant props containing the optional `width` value and `grow` flag.
67
+ * @returns The width class string, or `undefined` when neither `width` nor `grow` is set.
68
+ * @example getWidthClass({ width: "narrow" }) // "width narrow"
69
+ * @example getWidthClass({ width: "12x", grow: true }) // "width 12x grow"
30
70
  * @see https://dhoulb.github.io/shelving/ui/style/Width/getWidthClass
31
71
  */
32
- export function getWidthClass({ width }: WidthVariants): string | undefined {
33
- return width && getModuleClass(WIDTH_CSS, width);
72
+ export function getWidthClass({ width, grow }: WidthVariants): string | undefined {
73
+ if (!width && !grow) return undefined;
74
+ return getModuleClass(WIDTH_CSS, "width", width, { grow });
34
75
  }
@@ -1,8 +1,17 @@
1
1
  # getWidthClass
2
2
 
3
- The `width` variant prop (`"narrow"`, `"normal"`, `"wide"`, `"full"`, `"fit"`) constrains a block-level component's max-inline-size — `<Card width="narrow">`, `<Block width="wide">`. It's an **override** for one-off layout; for an app-wide change, retune the width variables below in a theme file. `Section` already defaults to the `--width-normal` constraint, so most pages never set `width` at all.
3
+ The `width` variant prop sets a component's inline-size — `<Card width="narrow">`, `<Block width="wide">`, `<TableCell width="12x">`. It's an **override** for one-off layout; for an app-wide change, retune the width variables below in a theme file. `Section` already defaults to the `--width-normal` width, so most pages never set `width` at all.
4
4
 
5
- `getWidthClass({ width })` maps the prop to a width class. The `narrow`, `normal`, and `wide` constraints read the variables below; `full` takes the full available width and `fit` shrinks to the content.
5
+ `getWidthClass({ width, grow })` maps the props to a width class. Every value is capped at 100% of the container.
6
+
7
+ **Values:**
8
+
9
+ - `narrow` / `normal` / `wide` — fixed widths from the variables below.
10
+ - `full` — the full available width.
11
+ - `fit` — shrink to the content's intrinsic width (`fit-content`).
12
+ - `1x` … `128x` — exact widths that are multiples of [`--space-normal`](/ui/getSpaceClass) (16px), following the spacing scale: `1x`–`8x` in single steps, then `10x`/`12x`/`14x`/`16x`, `20x`/`24x`/`28x`/`32x`, then `40x` … `128x` in eights. So `12x` is 192px.
13
+
14
+ **The `grow` flag** turns the chosen `width` into a floor rather than an exact size: it applies the value as `min-inline-size` and adds `flex-grow: 1`, so the element expands to fill the available space when it's a flex item. Set it on a [`TableHeader`](/ui/TableHeader) or [`TableCell`](/ui/TableCell) (`width="12x" grow`) to give that column a 192px minimum that absorbs the remaining width and keeps the table from collapsing the column on a narrow viewport — cells honour `min-width`, so the table scrolls instead.
6
15
 
7
16
  ## Theme variables
8
17