shelving 1.243.0 → 1.244.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.
Files changed (51) hide show
  1. package/package.json +1 -1
  2. package/ui/block/Card.js +1 -1
  3. package/ui/block/Card.tsx +3 -1
  4. package/ui/block/Prose.js +2 -2
  5. package/ui/block/Prose.tsx +25 -25
  6. package/ui/block/Table.d.ts +4 -3
  7. package/ui/block/Table.js +3 -2
  8. package/ui/block/Table.md +1 -0
  9. package/ui/block/Table.module.css +1 -0
  10. package/ui/block/Table.tsx +5 -3
  11. package/ui/dialog/Dialog.js +1 -1
  12. package/ui/dialog/Dialog.tsx +2 -2
  13. package/ui/dialog/Modal.js +2 -1
  14. package/ui/dialog/Modal.tsx +2 -1
  15. package/ui/docs/DocumentationPage.js +1 -1
  16. package/ui/docs/DocumentationPage.tsx +5 -5
  17. package/ui/form/ButtonInput.js +2 -2
  18. package/ui/form/ButtonInput.tsx +7 -2
  19. package/ui/form/CheckboxInput.js +2 -2
  20. package/ui/form/CheckboxInput.tsx +8 -3
  21. package/ui/form/DateInput.js +2 -2
  22. package/ui/form/DateInput.tsx +2 -2
  23. package/ui/form/Field.js +2 -2
  24. package/ui/form/Field.tsx +4 -4
  25. package/ui/form/Form.js +2 -1
  26. package/ui/form/Form.tsx +3 -2
  27. package/ui/form/Input.d.ts +1 -1
  28. package/ui/form/Input.js +4 -4
  29. package/ui/form/Input.tsx +4 -4
  30. package/ui/form/NumberInput.js +2 -2
  31. package/ui/form/NumberInput.tsx +2 -2
  32. package/ui/form/OutputInput.js +2 -2
  33. package/ui/form/OutputInput.tsx +5 -2
  34. package/ui/form/Popover.js +3 -2
  35. package/ui/form/Popover.tsx +3 -2
  36. package/ui/form/Progress.js +3 -3
  37. package/ui/form/Progress.tsx +15 -5
  38. package/ui/form/RadioInput.js +2 -2
  39. package/ui/form/RadioInput.tsx +10 -3
  40. package/ui/form/SelectInput.js +2 -2
  41. package/ui/form/SelectInput.tsx +4 -4
  42. package/ui/form/TextInput.js +3 -3
  43. package/ui/form/TextInput.tsx +3 -3
  44. package/ui/layout/CenteredLayout.js +2 -2
  45. package/ui/layout/CenteredLayout.tsx +3 -3
  46. package/ui/layout/SidebarLayout.js +5 -5
  47. package/ui/layout/SidebarLayout.tsx +16 -7
  48. package/ui/misc/Loading.js +2 -1
  49. package/ui/misc/Loading.tsx +10 -3
  50. package/ui/notice/Message.js +2 -2
  51. package/ui/notice/Message.tsx +2 -2
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "shelving",
3
- "version": "1.243.0",
3
+ "version": "1.244.0",
4
4
  "author": "Dave Houlbrooke <dave@shax.com>",
5
5
  "repository": {
6
6
  "type": "git",
package/ui/block/Card.js CHANGED
@@ -21,6 +21,6 @@ import CARD_CSS from "./Card.module.css";
21
21
  * @see https://dhoulb.github.io/shelving/ui/block/Card/Card
22
22
  */
23
23
  export function Card({ children, href, onClick, title = "Open", ...props }) {
24
- const overlay = (href || onClick) && _jsx(Clickable, { title: title, href: href, onClick: onClick, ...props, className: CARD_CSS.overlay });
24
+ const overlay = (href || onClick) && (_jsx(Clickable, { title: title, href: href, onClick: onClick, ...props, className: getModuleClass(CARD_CSS, "overlay") }));
25
25
  return (_jsxs("article", { className: getClass(getModuleClass(CARD_CSS, "card"), getStatusClass(props), getColorClass(props), getPaddingClass(props), getSpaceClass(props), getTypographyClass(props), getWidthClass(props)), children: [overlay, overlay ? _jsx("div", { children: children }) : children] }));
26
26
  }
package/ui/block/Card.tsx CHANGED
@@ -36,7 +36,9 @@ export interface CardProps
36
36
  * @see https://dhoulb.github.io/shelving/ui/block/Card/Card
37
37
  */
38
38
  export function Card({ children, href, onClick, title = "Open", ...props }: CardProps): ReactElement {
39
- const overlay = (href || onClick) && <Clickable title={title} href={href} onClick={onClick} {...props} className={CARD_CSS.overlay} />;
39
+ const overlay = (href || onClick) && (
40
+ <Clickable title={title} href={href} onClick={onClick} {...props} className={getModuleClass(CARD_CSS, "overlay")} />
41
+ );
40
42
  return (
41
43
  <article
42
44
  className={getClass(
package/ui/block/Prose.js CHANGED
@@ -9,7 +9,7 @@ import SMALL_CSS from "../inline/Small.module.css";
9
9
  import STRONG_CSS from "../inline/Strong.module.css";
10
10
  import SUBSCRIPT_CSS from "../inline/Subscript.module.css";
11
11
  import SUPERSCRIPT_CSS from "../inline/Superscript.module.css";
12
- import { getClass } from "../util/css.js";
12
+ import { getClass, getModuleClass } from "../util/css.js";
13
13
  import ADDRESS_CSS from "./Address.module.css";
14
14
  import BLOCKQUOTE_CSS from "./Blockquote.module.css";
15
15
  import CAPTION_CSS from "./Caption.module.css";
@@ -25,7 +25,7 @@ import SUBHEADING_CSS from "./Subheading.module.css";
25
25
  import TABLE_CSS from "./Table.module.css";
26
26
  import TITLE_CSS from "./Title.module.css";
27
27
  // Combine the `.prose` class from every block and inline component's CSS module into a single string.
28
- const PROSE_STYLES = getClass(PARAGRAPH_CSS.prose, HEADING_CSS.prose, SUBHEADING_CSS.prose, ADDRESS_CSS.prose, BLOCKQUOTE_CSS.prose, SECTION_CSS.prose, CODE_CSS.prose, DEFINITIONS_CSS.prose, DELETED_CSS.prose, EMPHASIS_CSS.prose, IMAGE_CSS.prose, INSERTED_CSS.prose, CAPTION_CSS.prose, LIST_CSS.prose, TITLE_CSS.prose, LINK_CSS.prose, MARK_CSS.prose, PREFORMATTED_CSS.prose, SMALL_CSS.prose, STRONG_CSS.prose, SUBSCRIPT_CSS.prose, SUPERSCRIPT_CSS.prose, TABLE_CSS.prose, DIVIDER_CSS.prose);
28
+ const PROSE_STYLES = getClass(getModuleClass(PARAGRAPH_CSS, "prose"), getModuleClass(HEADING_CSS, "prose"), getModuleClass(SUBHEADING_CSS, "prose"), getModuleClass(ADDRESS_CSS, "prose"), getModuleClass(BLOCKQUOTE_CSS, "prose"), getModuleClass(SECTION_CSS, "prose"), getModuleClass(CODE_CSS, "prose"), getModuleClass(DEFINITIONS_CSS, "prose"), getModuleClass(DELETED_CSS, "prose"), getModuleClass(EMPHASIS_CSS, "prose"), getModuleClass(IMAGE_CSS, "prose"), getModuleClass(INSERTED_CSS, "prose"), getModuleClass(CAPTION_CSS, "prose"), getModuleClass(LIST_CSS, "prose"), getModuleClass(TITLE_CSS, "prose"), getModuleClass(LINK_CSS, "prose"), getModuleClass(MARK_CSS, "prose"), getModuleClass(PREFORMATTED_CSS, "prose"), getModuleClass(SMALL_CSS, "prose"), getModuleClass(STRONG_CSS, "prose"), getModuleClass(SUBSCRIPT_CSS, "prose"), getModuleClass(SUPERSCRIPT_CSS, "prose"), getModuleClass(TABLE_CSS, "prose"), getModuleClass(DIVIDER_CSS, "prose"));
29
29
  /**
30
30
  * A section of longform text containing lots of `<p>` or `<ul>` style elements.
31
31
  * - Applies the prose variant of every block and inline component so nested content picks up the right longform spacing and typography.
@@ -9,7 +9,7 @@ import SMALL_CSS from "../inline/Small.module.css";
9
9
  import STRONG_CSS from "../inline/Strong.module.css";
10
10
  import SUBSCRIPT_CSS from "../inline/Subscript.module.css";
11
11
  import SUPERSCRIPT_CSS from "../inline/Superscript.module.css";
12
- import { getClass } from "../util/css.js";
12
+ import { getClass, getModuleClass } from "../util/css.js";
13
13
  import type { OptionalChildProps } from "../util/props.js";
14
14
  import ADDRESS_CSS from "./Address.module.css";
15
15
  import BLOCKQUOTE_CSS from "./Blockquote.module.css";
@@ -28,30 +28,30 @@ import TITLE_CSS from "./Title.module.css";
28
28
 
29
29
  // Combine the `.prose` class from every block and inline component's CSS module into a single string.
30
30
  const PROSE_STYLES = getClass(
31
- PARAGRAPH_CSS.prose,
32
- HEADING_CSS.prose,
33
- SUBHEADING_CSS.prose,
34
- ADDRESS_CSS.prose,
35
- BLOCKQUOTE_CSS.prose,
36
- SECTION_CSS.prose,
37
- CODE_CSS.prose,
38
- DEFINITIONS_CSS.prose,
39
- DELETED_CSS.prose,
40
- EMPHASIS_CSS.prose,
41
- IMAGE_CSS.prose,
42
- INSERTED_CSS.prose,
43
- CAPTION_CSS.prose,
44
- LIST_CSS.prose,
45
- TITLE_CSS.prose,
46
- LINK_CSS.prose,
47
- MARK_CSS.prose,
48
- PREFORMATTED_CSS.prose,
49
- SMALL_CSS.prose,
50
- STRONG_CSS.prose,
51
- SUBSCRIPT_CSS.prose,
52
- SUPERSCRIPT_CSS.prose,
53
- TABLE_CSS.prose,
54
- DIVIDER_CSS.prose,
31
+ getModuleClass(PARAGRAPH_CSS, "prose"),
32
+ getModuleClass(HEADING_CSS, "prose"),
33
+ getModuleClass(SUBHEADING_CSS, "prose"),
34
+ getModuleClass(ADDRESS_CSS, "prose"),
35
+ getModuleClass(BLOCKQUOTE_CSS, "prose"),
36
+ getModuleClass(SECTION_CSS, "prose"),
37
+ getModuleClass(CODE_CSS, "prose"),
38
+ getModuleClass(DEFINITIONS_CSS, "prose"),
39
+ getModuleClass(DELETED_CSS, "prose"),
40
+ getModuleClass(EMPHASIS_CSS, "prose"),
41
+ getModuleClass(IMAGE_CSS, "prose"),
42
+ getModuleClass(INSERTED_CSS, "prose"),
43
+ getModuleClass(CAPTION_CSS, "prose"),
44
+ getModuleClass(LIST_CSS, "prose"),
45
+ getModuleClass(TITLE_CSS, "prose"),
46
+ getModuleClass(LINK_CSS, "prose"),
47
+ getModuleClass(MARK_CSS, "prose"),
48
+ getModuleClass(PREFORMATTED_CSS, "prose"),
49
+ getModuleClass(SMALL_CSS, "prose"),
50
+ getModuleClass(STRONG_CSS, "prose"),
51
+ getModuleClass(SUBSCRIPT_CSS, "prose"),
52
+ getModuleClass(SUPERSCRIPT_CSS, "prose"),
53
+ getModuleClass(TABLE_CSS, "prose"),
54
+ getModuleClass(DIVIDER_CSS, "prose"),
55
55
  );
56
56
 
57
57
  /**
@@ -2,13 +2,14 @@ import type { ReactElement } from "react";
2
2
  import { type ColorVariants } from "../style/Color.js";
3
3
  import { type SpaceVariants } from "../style/Space.js";
4
4
  import { type TypographyVariants } from "../style/Typography.js";
5
+ import { type WidthVariants } from "../style/Width.js";
5
6
  import type { ChildProps } from "../util/props.js";
6
7
  /**
7
- * Props for `Table` — colour, space, and typography variants plus `children`.
8
+ * Props for `Table` — colour, space, typography, and width variants plus `children`.
8
9
  *
9
10
  * @see https://dhoulb.github.io/shelving/ui/block/Table/TableProps
10
11
  */
11
- export interface TableProps extends ColorVariants, SpaceVariants, TypographyVariants, ChildProps {
12
+ export interface TableProps extends ColorVariants, SpaceVariants, TypographyVariants, WidthVariants, ChildProps {
12
13
  }
13
14
  /**
14
15
  * Table block — rendered as `<table>`.
@@ -16,7 +17,7 @@ export interface TableProps extends ColorVariants, SpaceVariants, TypographyVari
16
17
  * - `<th>` / `<td>` cells draw the borders (the `<table>` element itself has none); override their weight via the `--table-border` / `--table-stroke` hooks.
17
18
  *
18
19
  * @kind component
19
- * @param props Colour, space, and typography variants plus `children`.
20
+ * @param props Colour, space, typography, and width variants plus `children`.
20
21
  * @returns Rendered `<table>` element.
21
22
  * @example <Table><tbody><tr><td>Cell</td></tr></tbody></Table>
22
23
  * @see https://dhoulb.github.io/shelving/ui/block/Table/Table
package/ui/block/Table.js CHANGED
@@ -2,6 +2,7 @@ import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { getColorClass } from "../style/Color.js";
3
3
  import { getSpaceClass } from "../style/Space.js";
4
4
  import { getTypographyClass } from "../style/Typography.js";
5
+ import { getWidthClass } from "../style/Width.js";
5
6
  import { getClass, getModuleClass } from "../util/css.js";
6
7
  import TABLE_CSS from "./Table.module.css";
7
8
  const TABLE_CLASS = getModuleClass(TABLE_CSS, "table");
@@ -11,12 +12,12 @@ const TABLE_CLASS = getModuleClass(TABLE_CSS, "table");
11
12
  * - `<th>` / `<td>` cells draw the borders (the `<table>` element itself has none); override their weight via the `--table-border` / `--table-stroke` hooks.
12
13
  *
13
14
  * @kind component
14
- * @param props Colour, space, and typography variants plus `children`.
15
+ * @param props Colour, space, typography, and width variants plus `children`.
15
16
  * @returns Rendered `<table>` element.
16
17
  * @example <Table><tbody><tr><td>Cell</td></tr></tbody></Table>
17
18
  * @see https://dhoulb.github.io/shelving/ui/block/Table/Table
18
19
  */
19
20
  export function Table({ children, ...props }) {
20
21
  return (_jsx("table", { className: getClass(TABLE_CLASS, //
21
- getColorClass(props), getSpaceClass(props), getTypographyClass(props)), children: children }));
22
+ getColorClass(props), getSpaceClass(props), getTypographyClass(props), getWidthClass(props)), children: children }));
22
23
  }
package/ui/block/Table.md CHANGED
@@ -8,6 +8,7 @@ A data table — renders a `<table>`. Compose the usual `<thead>` / `<tbody>` /
8
8
  - First and last cells in a row drop their outer inline padding so the table aligns flush with the surrounding text column.
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
+ - Spans the full width of its container by default; set the `width` variant (`narrow` / `normal` / `wide` / `full` / `fit`) to constrain it.
11
12
  - Inside [`Prose`](/ui/Prose) a raw `<table>` picks up the same styling, so Markdown-rendered tables match component ones.
12
13
 
13
14
  ## Usage
@@ -11,6 +11,7 @@
11
11
  .prose table {
12
12
  /* Box */
13
13
  display: table;
14
+ inline-size: 100%;
14
15
  margin-inline: 0;
15
16
  margin-block: var(--table-space, var(--space-paragraph));
16
17
  text-indent: 0;
@@ -2,6 +2,7 @@ import type { ReactElement } from "react";
2
2
  import { type ColorVariants, getColorClass } from "../style/Color.js";
3
3
  import { getSpaceClass, type SpaceVariants } from "../style/Space.js";
4
4
  import { getTypographyClass, type TypographyVariants } from "../style/Typography.js";
5
+ import { getWidthClass, type WidthVariants } from "../style/Width.js";
5
6
  import { getClass, getModuleClass } from "../util/css.js";
6
7
  import type { ChildProps } from "../util/props.js";
7
8
  import TABLE_CSS from "./Table.module.css";
@@ -9,11 +10,11 @@ import TABLE_CSS from "./Table.module.css";
9
10
  const TABLE_CLASS = getModuleClass(TABLE_CSS, "table");
10
11
 
11
12
  /**
12
- * Props for `Table` — colour, space, and typography variants plus `children`.
13
+ * Props for `Table` — colour, space, typography, and width variants plus `children`.
13
14
  *
14
15
  * @see https://dhoulb.github.io/shelving/ui/block/Table/TableProps
15
16
  */
16
- export interface TableProps extends ColorVariants, SpaceVariants, TypographyVariants, ChildProps {}
17
+ export interface TableProps extends ColorVariants, SpaceVariants, TypographyVariants, WidthVariants, ChildProps {}
17
18
 
18
19
  /**
19
20
  * Table block — rendered as `<table>`.
@@ -21,7 +22,7 @@ export interface TableProps extends ColorVariants, SpaceVariants, TypographyVari
21
22
  * - `<th>` / `<td>` cells draw the borders (the `<table>` element itself has none); override their weight via the `--table-border` / `--table-stroke` hooks.
22
23
  *
23
24
  * @kind component
24
- * @param props Colour, space, and typography variants plus `children`.
25
+ * @param props Colour, space, typography, and width variants plus `children`.
25
26
  * @returns Rendered `<table>` element.
26
27
  * @example <Table><tbody><tr><td>Cell</td></tr></tbody></Table>
27
28
  * @see https://dhoulb.github.io/shelving/ui/block/Table/Table
@@ -34,6 +35,7 @@ export function Table({ children, ...props }: TableProps): ReactElement {
34
35
  getColorClass(props),
35
36
  getSpaceClass(props),
36
37
  getTypographyClass(props),
38
+ getWidthClass(props),
37
39
  )}
38
40
  >
39
41
  {children}
@@ -18,7 +18,7 @@ export const Dialog = memo(({ children, onClose, ...props }) => {
18
18
  useEffect(() => {
19
19
  ref.current?.showModal();
20
20
  }, []);
21
- return (_jsx(Suspense, { fallback: null, children: _jsxs("dialog", { ref: ref, className: styles.dialog, onClick: _closeOnBackdropClick, onClose: onClose, ...props, children: [children, _jsx("div", { className: styles.close, children: _jsx(DialogCloseButton, { plain: true }) })] }) }));
21
+ return (_jsx(Suspense, { fallback: null, children: _jsxs("dialog", { ref: ref, className: getModuleClass(styles, "dialog"), onClick: _closeOnBackdropClick, onClose: onClose, ...props, children: [children, _jsx("div", { className: getModuleClass(styles, "close"), children: _jsx(DialogCloseButton, { plain: true }) })] }) }));
22
22
  });
23
23
  /** When the user clicks anywhere on a `<dialog>` element (and the click isn't on a link etc), then close the dialog. */
24
24
  function _closeOnBackdropClick({ currentTarget, target }) {
@@ -35,9 +35,9 @@ export const Dialog = memo(({ children, onClose, ...props }: DialogProps) => {
35
35
  return (
36
36
  <Suspense fallback={null}>
37
37
  {/** biome-ignore lint/a11y/useKeyWithClickEvents: Dialogs also show a close button. */}
38
- <dialog ref={ref} className={styles.dialog} onClick={_closeOnBackdropClick} onClose={onClose} {...props}>
38
+ <dialog ref={ref} className={getModuleClass(styles, "dialog")} onClick={_closeOnBackdropClick} onClose={onClose} {...props}>
39
39
  {children}
40
- <div className={styles.close}>
40
+ <div className={getModuleClass(styles, "close")}>
41
41
  <DialogCloseButton plain />
42
42
  </div>
43
43
  </dialog>
@@ -1,4 +1,5 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { getModuleClass } from "../util/css.js";
2
3
  import styles from "./Modal.module.css";
3
4
  /**
4
5
  * Styled `<aside>` overlay container for modal content.
@@ -10,5 +11,5 @@ import styles from "./Modal.module.css";
10
11
  * @see https://dhoulb.github.io/shelving/ui/dialog/Modal/Modal
11
12
  */
12
13
  export function Modal({ children }) {
13
- return _jsx("aside", { className: styles.modal, children: children });
14
+ return _jsx("aside", { className: getModuleClass(styles, "modal"), children: children });
14
15
  }
@@ -1,4 +1,5 @@
1
1
  import type { ReactElement } from "react";
2
+ import { getModuleClass } from "../util/css.js";
2
3
  import type { OptionalChildProps } from "../util/props.js";
3
4
  import styles from "./Modal.module.css";
4
5
 
@@ -19,5 +20,5 @@ export interface ModalProps extends OptionalChildProps {}
19
20
  * @see https://dhoulb.github.io/shelving/ui/dialog/Modal/Modal
20
21
  */
21
22
  export function Modal({ children }: ModalProps): ReactElement {
22
- return <aside className={styles.modal}>{children}</aside>;
23
+ return <aside className={getModuleClass(styles, "modal")}>{children}</aside>;
23
24
  }
@@ -68,5 +68,5 @@ function DocumentationChildren({ elements }) {
68
68
  * @see https://dhoulb.github.io/shelving/ui/docs/DocumentationPage/DocumentationPage
69
69
  */
70
70
  export function DocumentationPage({ title, name, kind = "unknown", description, content, signatures, params, returns, throws, examples, children, ...props }) {
71
- 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 ? (_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("th", { children: "Description" })] }) }), _jsx("tbody", { children: params.map(({ name, type = DEFAULT_TYPE, description = "", default: def }) => (_jsxs("tr", { children: [_jsx("td", { children: _jsx(Code, { size: "normal", children: name }) }), _jsx("td", { children: _jsx(Code, { size: "normal", children: type }) }), _jsx("td", { children: def ? _jsx(Code, { size: "normal", children: def }) : "-" }), _jsx("td", { children: description })] }, `${name}-${type}-${description}`))) })] }) }) })), returns?.length && (_jsx(Section, { children: _jsx(Scroll, { horizontal: true, children: _jsxs(Table, { children: [_jsx("thead", { children: _jsxs("tr", { children: [_jsx("th", { children: "Return" }), _jsx("th", { children: "Description" })] }) }), _jsx("tbody", { children: returns.map(({ type = DEFAULT_TYPE, description = "" }) => (_jsxs("tr", { children: [_jsx("td", { children: _jsx(Code, { size: "normal", children: type }) }), _jsx("td", { children: description })] }, `${type}-${description}`))) })] }) }) })), throws?.length && (_jsx(Section, { children: _jsx(Scroll, { horizontal: true, children: _jsxs(Table, { children: [_jsx("thead", { children: _jsxs("tr", { children: [_jsx("th", { children: "Throws" }), _jsx("th", { children: "Description" })] }) }), _jsx("tbody", { children: throws.map(({ type = DEFAULT_TYPE, description = "" }) => (_jsxs("tr", { children: [_jsx("td", { children: _jsx(Code, { size: "normal", children: type }) }), _jsx("td", { children: description })] }, `${type}-${description}`))) })] }) }) }))] })) : 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 })] }) }));
71
+ 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 ? (_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("th", { children: "Description" })] }) }), _jsx("tbody", { children: params.map(({ name, type = DEFAULT_TYPE, description = "", default: def }) => (_jsxs("tr", { children: [_jsx("td", { children: _jsx(Code, { children: name }) }), _jsx("td", { children: _jsx(Code, { children: type }) }), _jsx("td", { children: def ? _jsx(Code, { children: def }) : "-" }), _jsx("td", { children: description })] }, `${name}-${type}-${description}`))) })] }) }) })), returns?.length && (_jsx(Section, { children: _jsx(Scroll, { horizontal: true, children: _jsxs(Table, { children: [_jsx("thead", { children: _jsxs("tr", { children: [_jsx("th", { children: "Return" }), _jsx("th", { children: "Description" })] }) }), _jsx("tbody", { children: returns.map(({ type = DEFAULT_TYPE, description = "" }) => (_jsxs("tr", { children: [_jsx("td", { children: _jsx(Code, { children: type }) }), _jsx("td", { children: description })] }, `${type}-${description}`))) })] }) }) })), throws?.length && (_jsx(Section, { children: _jsx(Scroll, { horizontal: true, children: _jsxs(Table, { children: [_jsx("thead", { children: _jsxs("tr", { children: [_jsx("th", { children: "Throws" }), _jsx("th", { children: "Description" })] }) }), _jsx("tbody", { children: throws.map(({ type = DEFAULT_TYPE, description = "" }) => (_jsxs("tr", { children: [_jsx("td", { children: _jsx(Code, { children: type }) }), _jsx("td", { children: description })] }, `${type}-${description}`))) })] }) }) }))] })) : 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 })] }) }));
72
72
  }
@@ -127,12 +127,12 @@ export function DocumentationPage({
127
127
  {params.map(({ name, type = DEFAULT_TYPE, description = "", default: def }) => (
128
128
  <tr key={`${name}-${type}-${description}`}>
129
129
  <td>
130
- <Code size="normal">{name}</Code>
130
+ <Code>{name}</Code>
131
131
  </td>
132
132
  <td>
133
- <Code size="normal">{type}</Code>
133
+ <Code>{type}</Code>
134
134
  </td>
135
- <td>{def ? <Code size="normal">{def}</Code> : "-"}</td>
135
+ <td>{def ? <Code>{def}</Code> : "-"}</td>
136
136
  <td>{description}</td>
137
137
  </tr>
138
138
  ))}
@@ -155,7 +155,7 @@ export function DocumentationPage({
155
155
  {returns.map(({ type = DEFAULT_TYPE, description = "" }) => (
156
156
  <tr key={`${type}-${description}`}>
157
157
  <td>
158
- <Code size="normal">{type}</Code>
158
+ <Code>{type}</Code>
159
159
  </td>
160
160
  <td>{description}</td>
161
161
  </tr>
@@ -179,7 +179,7 @@ export function DocumentationPage({
179
179
  {throws.map(({ type = DEFAULT_TYPE, description = "" }) => (
180
180
  <tr key={`${type}-${description}`}>
181
181
  <td>
182
- <Code size="normal">{type}</Code>
182
+ <Code>{type}</Code>
183
183
  </td>
184
184
  <td>{description}</td>
185
185
  </tr>
@@ -1,7 +1,7 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { notNullish } from "../../util/null.js";
3
3
  import { getFlexClass } from "../style/Flex.js";
4
- import { getClass } from "../util/css.js";
4
+ import { getClass, getModuleClass } from "../util/css.js";
5
5
  import { Clickable } from "./Clickable.js";
6
6
  import { getInputClass } from "./Input.js";
7
7
  import INPUT_CSS from "./Input.module.css";
@@ -16,5 +16,5 @@ import INPUT_CSS from "./Input.module.css";
16
16
  */
17
17
  export function ButtonInput({ title, placeholder, children = title, ...props }) {
18
18
  const hasChildren = notNullish(children);
19
- return (_jsx(Clickable, { ...props, className: getClass(getInputClass(props), INPUT_CSS.button, getFlexClass(props), hasChildren && INPUT_CSS.placeholder), children: hasChildren ? children : placeholder }));
19
+ return (_jsx(Clickable, { ...props, className: getClass(getInputClass(props), getModuleClass(INPUT_CSS, "button"), getFlexClass(props), hasChildren && getModuleClass(INPUT_CSS, "placeholder")), children: hasChildren ? children : placeholder }));
20
20
  }
@@ -1,7 +1,7 @@
1
1
  import type { ReactElement } from "react";
2
2
  import { notNullish } from "../../util/null.js";
3
3
  import { type FlexVariants, getFlexClass } from "../style/Flex.js";
4
- import { getClass } from "../util/css.js";
4
+ import { getClass, getModuleClass } from "../util/css.js";
5
5
  import { Clickable, type ClickableProps } from "./Clickable.js";
6
6
  import { getInputClass, type InputProps, type InputVariants } from "./Input.js";
7
7
  import INPUT_CSS from "./Input.module.css";
@@ -27,7 +27,12 @@ export function ButtonInput({ title, placeholder, children = title, ...props }:
27
27
  return (
28
28
  <Clickable
29
29
  {...props}
30
- className={getClass(getInputClass(props), INPUT_CSS.button, getFlexClass(props), hasChildren && INPUT_CSS.placeholder)}
30
+ className={getClass(
31
+ getInputClass(props),
32
+ getModuleClass(INPUT_CSS, "button"),
33
+ getFlexClass(props),
34
+ hasChildren && getModuleClass(INPUT_CSS, "placeholder"),
35
+ )}
31
36
  >
32
37
  {hasChildren ? children : placeholder}
33
38
  </Clickable>
@@ -1,7 +1,7 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { notNullish } from "../../util/null.js";
3
3
  import { getFlexClass } from "../style/Flex.js";
4
- import { getClass } from "../util/css.js";
4
+ import { getClass, getModuleClass } from "../util/css.js";
5
5
  import { getInputClass } from "./Input.js";
6
6
  import INPUT_CSS from "./Input.module.css";
7
7
  /**
@@ -15,5 +15,5 @@ import INPUT_CSS from "./Input.module.css";
15
15
  */
16
16
  export function CheckboxInput({ name, title, placeholder = title || "Yes", required = false, disabled = false, message = "", value = false, onValue, children, ...variants }) {
17
17
  const hasChildren = notNullish(children);
18
- return (_jsxs("label", { className: getClass(getInputClass(variants), INPUT_CSS.label, getFlexClass(variants), hasChildren && INPUT_CSS.placeholder), "aria-invalid": !!message, children: [_jsx("input", { name: name, type: "checkbox", defaultChecked: !!value, onChange: e => onValue?.(!!e.currentTarget.checked), required: required, disabled: disabled, title: message, className: INPUT_CSS.radio }), _jsx("span", { children: hasChildren ? children : placeholder })] }));
18
+ return (_jsxs("label", { className: getClass(getInputClass(variants), getModuleClass(INPUT_CSS, "label"), getFlexClass(variants), hasChildren && getModuleClass(INPUT_CSS, "placeholder")), "aria-invalid": !!message, children: [_jsx("input", { name: name, type: "checkbox", defaultChecked: !!value, onChange: e => onValue?.(!!e.currentTarget.checked), required: required, disabled: disabled, title: message, className: getModuleClass(INPUT_CSS, "radio") }), _jsx("span", { children: hasChildren ? children : placeholder })] }));
19
19
  }
@@ -1,7 +1,7 @@
1
1
  import type { ReactElement } from "react";
2
2
  import { notNullish } from "../../util/null.js";
3
3
  import { type FlexVariants, getFlexClass } from "../style/Flex.js";
4
- import { getClass } from "../util/css.js";
4
+ import { getClass, getModuleClass } from "../util/css.js";
5
5
  import type { OptionalChildProps } from "../util/props.js";
6
6
  import { getInputClass, type InputVariants, type ValueInputProps } from "./Input.js";
7
7
  import INPUT_CSS from "./Input.module.css";
@@ -37,7 +37,12 @@ export function CheckboxInput({
37
37
  const hasChildren = notNullish(children);
38
38
  return (
39
39
  <label
40
- className={getClass(getInputClass(variants), INPUT_CSS.label, getFlexClass(variants), hasChildren && INPUT_CSS.placeholder)}
40
+ className={getClass(
41
+ getInputClass(variants),
42
+ getModuleClass(INPUT_CSS, "label"),
43
+ getFlexClass(variants),
44
+ hasChildren && getModuleClass(INPUT_CSS, "placeholder"),
45
+ )}
41
46
  aria-invalid={!!message}
42
47
  >
43
48
  <input
@@ -48,7 +53,7 @@ export function CheckboxInput({
48
53
  required={required}
49
54
  disabled={disabled}
50
55
  title={message}
51
- className={INPUT_CSS.radio}
56
+ className={getModuleClass(INPUT_CSS, "radio")}
52
57
  />
53
58
  <span>{hasChildren ? children : placeholder}</span>
54
59
  </label>
@@ -1,6 +1,6 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { getDateString, getDateTimeString, getTimeString } from "../../util/date.js";
3
- import { getClass } from "../util/css.js";
3
+ import { getClass, getModuleClass } from "../util/css.js";
4
4
  import { getInputClass } from "./Input.js";
5
5
  import INPUT_CSS from "./Input.module.css";
6
6
  /** Convert a `PossibleDate` to a string for a specific date `<input type="etc">` type. */
@@ -24,5 +24,5 @@ required = false, disabled = false, message = "", value, onValue, min, max, inpu
24
24
  onValue?.(e.currentTarget.value ?? undefined);
25
25
  };
26
26
  const dateToString = _DATE_TO_STRING[input];
27
- return (_jsx("input", { name: name, type: input, min: dateToString(min), max: dateToString(max), disabled: disabled, required: required, defaultValue: dateToString(value), placeholder: placeholder || " ", className: getClass(getInputClass(variants), INPUT_CSS.text), onChange: onChange, onInput: onChange, title: message, "aria-invalid": !!message, step: step }));
27
+ return (_jsx("input", { name: name, type: input, min: dateToString(min), max: dateToString(max), disabled: disabled, required: required, defaultValue: dateToString(value), placeholder: placeholder || " ", className: getClass(getInputClass(variants), getModuleClass(INPUT_CSS, "text")), onChange: onChange, onInput: onChange, title: message, "aria-invalid": !!message, step: step }));
28
28
  }
@@ -1,7 +1,7 @@
1
1
  import type { ReactElement, SyntheticEvent } from "react";
2
2
  import type { DateInputType } from "../../schema/DateSchema.js";
3
3
  import { getDateString, getDateTimeString, getTimeString, type PossibleDate } from "../../util/date.js";
4
- import { getClass } from "../util/css.js";
4
+ import { getClass, getModuleClass } from "../util/css.js";
5
5
  import { getInputClass, type InputVariants, type ValueInputProps } from "./Input.js";
6
6
  import INPUT_CSS from "./Input.module.css";
7
7
 
@@ -64,7 +64,7 @@ export function DateInput({
64
64
  required={required}
65
65
  defaultValue={dateToString(value)}
66
66
  placeholder={placeholder || " "}
67
- className={getClass(getInputClass(variants), INPUT_CSS.text)}
67
+ className={getClass(getInputClass(variants), getModuleClass(INPUT_CSS, "text"))}
68
68
  onChange={onChange}
69
69
  onInput={onChange}
70
70
  title={message}
package/ui/form/Field.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { Message } from "../notice/Message.js";
3
- import { getClass } from "../util/css.js";
3
+ import { getClass, getModuleClass } from "../util/css.js";
4
4
  import styles from "./Field.module.css";
5
5
  /**
6
6
  * A `<Field>` wraps around a form control/input, to shows a small `<label>` above it.
@@ -9,5 +9,5 @@ import styles from "./Field.module.css";
9
9
  * @see https://dhoulb.github.io/shelving/ui/form/Field/Field
10
10
  */
11
11
  export function Field({ title, description, message, half, children }) {
12
- return (_jsxs("label", { className: getClass(styles.field, half && styles.half), children: [(title || description) && (_jsxs("div", { children: [title ? _jsx("div", { className: styles.title, children: title }) : null, description && _jsx("div", { className: styles.description, children: description })] })), children, message && (_jsx(Message, { status: "error", right: true, children: message }))] }));
12
+ return (_jsxs("label", { className: getClass(getModuleClass(styles, "field"), half && getModuleClass(styles, "half")), children: [(title || description) && (_jsxs("div", { children: [title ? _jsx("div", { className: getModuleClass(styles, "title"), children: title }) : null, description && _jsx("div", { className: getModuleClass(styles, "description"), children: description })] })), children, message && (_jsx(Message, { status: "error", right: true, children: message }))] }));
13
13
  }
package/ui/form/Field.tsx CHANGED
@@ -1,6 +1,6 @@
1
1
  import type { ReactElement, ReactNode } from "react";
2
2
  import { Message } from "../notice/Message.js";
3
- import { getClass } from "../util/css.js";
3
+ import { getClass, getModuleClass } from "../util/css.js";
4
4
  import type { ChildProps } from "../util/props.js";
5
5
  import styles from "./Field.module.css";
6
6
 
@@ -27,11 +27,11 @@ export interface FieldProps extends ChildProps {
27
27
  export function Field({ title, description, message, half, children }: FieldProps): ReactElement {
28
28
  return (
29
29
  // biome-ignore lint/a11y/noLabelWithoutControl: Generally `children` will contain a field.
30
- <label className={getClass(styles.field, half && styles.half)}>
30
+ <label className={getClass(getModuleClass(styles, "field"), half && getModuleClass(styles, "half"))}>
31
31
  {(title || description) && (
32
32
  <div>
33
- {title ? <div className={styles.title}>{title}</div> : null}
34
- {description && <div className={styles.description}>{description}</div>}
33
+ {title ? <div className={getModuleClass(styles, "title")}>{title}</div> : null}
34
+ {description && <div className={getModuleClass(styles, "description")}>{description}</div>}
35
35
  </div>
36
36
  )}
37
37
  {children}
package/ui/form/Form.js CHANGED
@@ -2,6 +2,7 @@ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-run
2
2
  import { useInstance } from "../../react/useInstance.js";
3
3
  import { useStore } from "../../react/useStore.js";
4
4
  import { isAsync } from "../../util/async.js";
5
+ import { getModuleClass } from "../util/css.js";
5
6
  import { notifySuccess, notifyThrown } from "../util/notice.js";
6
7
  import styles from "./Form.module.css";
7
8
  import { FormContext } from "./FormContext.js";
@@ -23,7 +24,7 @@ export function Form({ schema, data: initialData, onSubmit, submit, messages, ch
23
24
  // Close the parent dialog on successful submit.
24
25
  if (result)
25
26
  dialog?.close();
26
- }, className: styles.form, noValidate: true, children: _jsx("fieldset", { className: styles.fieldset, disabled: busy, children: _jsx(FormContext, { value: store, children: children }) }) }, store.key));
27
+ }, className: getModuleClass(styles, "form"), noValidate: true, children: _jsx("fieldset", { className: getModuleClass(styles, "fieldset"), disabled: busy, children: _jsx(FormContext, { value: store, children: children }) }) }, store.key));
27
28
  }
28
29
  /**
29
30
  * Run a form submit callback and publish success/error notices based on its return or thrown value.
package/ui/form/Form.tsx CHANGED
@@ -6,6 +6,7 @@ import { isAsync } from "../../util/async.js";
6
6
  import type { Data, PartialData } from "../../util/data.js";
7
7
  import type { ImmutableDictionary } from "../../util/dictionary.js";
8
8
  import type { Arguments } from "../../util/function.js";
9
+ import { getModuleClass } from "../util/css.js";
9
10
  import { type NoticeCallback, notifySuccess, notifyThrown } from "../util/notice.js";
10
11
  import type { OptionalChildProps } from "../util/props.js";
11
12
  import styles from "./Form.module.css";
@@ -91,10 +92,10 @@ export function Form({
91
92
  // Close the parent dialog on successful submit.
92
93
  if (result) dialog?.close();
93
94
  }}
94
- className={styles.form}
95
+ className={getModuleClass(styles, "form")}
95
96
  noValidate={true}
96
97
  >
97
- <fieldset className={styles.fieldset} disabled={busy}>
98
+ <fieldset className={getModuleClass(styles, "fieldset")} disabled={busy}>
98
99
  <FormContext value={store}>{children}</FormContext>
99
100
  </fieldset>
100
101
  </form>
@@ -15,7 +15,7 @@ export interface InputVariants extends WidthVariants {
15
15
  *
16
16
  * @param props The input's styling variants (width, …).
17
17
  * @returns The merged base input `className` string.
18
- * @example getClass(getInputClass(props), INPUT_CSS.text) // a text input that also honours width variants
18
+ * @example getClass(getInputClass(props), getModuleClass(INPUT_CSS, "text")) // a text input that also honours width variants
19
19
  * @see https://dhoulb.github.io/shelving/ui/form/Input/getInputClass
20
20
  */
21
21
  export declare function getInputClass(props: InputVariants): string;
package/ui/form/Input.js CHANGED
@@ -2,18 +2,18 @@ import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { LOADING } from "../misc/Loading.js";
3
3
  import { getFlexClass } from "../style/Flex.js";
4
4
  import { getWidthClass } from "../style/Width.js";
5
- import { getClass } from "../util/css.js";
5
+ import { getClass, getModuleClass } from "../util/css.js";
6
6
  import INPUT_CSS from "./Input.module.css";
7
7
  /**
8
8
  * Build the shared base `className` for a form input from its styling variants — the base input class plus any `InputVariants`.
9
9
  *
10
10
  * @param props The input's styling variants (width, …).
11
11
  * @returns The merged base input `className` string.
12
- * @example getClass(getInputClass(props), INPUT_CSS.text) // a text input that also honours width variants
12
+ * @example getClass(getInputClass(props), getModuleClass(INPUT_CSS, "text")) // a text input that also honours width variants
13
13
  * @see https://dhoulb.github.io/shelving/ui/form/Input/getInputClass
14
14
  */
15
15
  export function getInputClass(props) {
16
- return getClass(INPUT_CSS.input, getWidthClass(props));
16
+ return getClass(getModuleClass(INPUT_CSS, "input"), getWidthClass(props));
17
17
  }
18
18
  /** Input that is loading. */
19
19
  export const LOADING_INPUT = _jsx("div", { className: getClass(getInputClass({}), getFlexClass({})), children: LOADING });
@@ -22,5 +22,5 @@ export const LOADING_INPUT = _jsx("div", { className: getClass(getInputClass({})
22
22
  * - This is so you can put an icon before or after an input.
23
23
  */
24
24
  export function InputWrapper({ children }) {
25
- return _jsx("div", { className: getClass(getInputClass({}), INPUT_CSS.wrapper), children: children });
25
+ return _jsx("div", { className: getClass(getInputClass({}), getModuleClass(INPUT_CSS, "wrapper")), children: children });
26
26
  }
package/ui/form/Input.tsx CHANGED
@@ -2,7 +2,7 @@ import type { ReactElement } from "react";
2
2
  import { LOADING } from "../misc/Loading.js";
3
3
  import { getFlexClass } from "../style/Flex.js";
4
4
  import { getWidthClass, type WidthVariants } from "../style/Width.js";
5
- import { getClass } from "../util/css.js";
5
+ import { getClass, getModuleClass } from "../util/css.js";
6
6
  import type { ChildProps } from "../util/props.js";
7
7
  import INPUT_CSS from "./Input.module.css";
8
8
 
@@ -20,11 +20,11 @@ export interface InputVariants extends WidthVariants {}
20
20
  *
21
21
  * @param props The input's styling variants (width, …).
22
22
  * @returns The merged base input `className` string.
23
- * @example getClass(getInputClass(props), INPUT_CSS.text) // a text input that also honours width variants
23
+ * @example getClass(getInputClass(props), getModuleClass(INPUT_CSS, "text")) // a text input that also honours width variants
24
24
  * @see https://dhoulb.github.io/shelving/ui/form/Input/getInputClass
25
25
  */
26
26
  export function getInputClass(props: InputVariants): string {
27
- return getClass(INPUT_CSS.input, getWidthClass(props));
27
+ return getClass(getModuleClass(INPUT_CSS, "input"), getWidthClass(props));
28
28
  }
29
29
 
30
30
  /**
@@ -63,5 +63,5 @@ export const LOADING_INPUT = <div className={getClass(getInputClass({}), getFlex
63
63
  * - This is so you can put an icon before or after an input.
64
64
  */
65
65
  export function InputWrapper({ children }: ChildProps): ReactElement {
66
- return <div className={getClass(getInputClass({}), INPUT_CSS.wrapper)}>{children}</div>;
66
+ return <div className={getClass(getInputClass({}), getModuleClass(INPUT_CSS, "wrapper"))}>{children}</div>;
67
67
  }
@@ -1,7 +1,7 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { formatNumber } from "../../util/format.js";
3
3
  import { getNumber } from "../../util/number.js";
4
- import { getClass } from "../util/css.js";
4
+ import { getClass, getModuleClass } from "../util/css.js";
5
5
  import { getInputClass } from "./Input.js";
6
6
  import INPUT_CSS from "./Input.module.css";
7
7
  /**
@@ -25,5 +25,5 @@ formatter = formatNumber, ...variants }) {
25
25
  const num = getNumber(currentTarget.value);
26
26
  currentTarget.value = num !== undefined ? formatter(num) : "";
27
27
  };
28
- return (_jsx("input", { name: name, type: "text", inputMode: "decimal", defaultValue: value !== undefined ? formatter(value) : "", required: required, disabled: disabled, placeholder: placeholder || " ", className: getClass(getInputClass(variants), INPUT_CSS.text), onChange: onChange, onInput: onChange, onBlur: onBlur, title: message, "aria-invalid": !!message }));
28
+ return (_jsx("input", { name: name, type: "text", inputMode: "decimal", defaultValue: value !== undefined ? formatter(value) : "", required: required, disabled: disabled, placeholder: placeholder || " ", className: getClass(getInputClass(variants), getModuleClass(INPUT_CSS, "text")), onChange: onChange, onInput: onChange, onBlur: onBlur, title: message, "aria-invalid": !!message }));
29
29
  }
@@ -1,7 +1,7 @@
1
1
  import type { ReactElement, SyntheticEvent } from "react";
2
2
  import { formatNumber } from "../../util/format.js";
3
3
  import { getNumber } from "../../util/number.js";
4
- import { getClass } from "../util/css.js";
4
+ import { getClass, getModuleClass } from "../util/css.js";
5
5
  import { getInputClass, type InputVariants, type ValueInputProps } from "./Input.js";
6
6
  import INPUT_CSS from "./Input.module.css";
7
7
 
@@ -60,7 +60,7 @@ export function NumberInput({
60
60
  required={required}
61
61
  disabled={disabled}
62
62
  placeholder={placeholder || " "}
63
- className={getClass(getInputClass(variants), INPUT_CSS.text)}
63
+ className={getClass(getInputClass(variants), getModuleClass(INPUT_CSS, "text"))}
64
64
  onChange={onChange}
65
65
  onInput={onChange}
66
66
  onBlur={onBlur}
@@ -1,7 +1,7 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { notNullish } from "../../util/index.js";
3
3
  import { getFlexClass } from "../style/Flex.js";
4
- import { getClass } from "../util/css.js";
4
+ import { getClass, getModuleClass } from "../util/css.js";
5
5
  import { getInputClass } from "./Input.js";
6
6
  import INPUT_CSS from "./Input.module.css";
7
7
  /**
@@ -14,5 +14,5 @@ import INPUT_CSS from "./Input.module.css";
14
14
  */
15
15
  export function OutputInput({ title, placeholder, children = title, ...props }) {
16
16
  const hasChildren = notNullish(children);
17
- return (_jsx("output", { ...props, className: getClass(getInputClass(props), getFlexClass(props), hasChildren && INPUT_CSS.placeholder), children: hasChildren ? children : placeholder }));
17
+ return (_jsx("output", { ...props, className: getClass(getInputClass(props), getFlexClass(props), hasChildren && getModuleClass(INPUT_CSS, "placeholder")), children: hasChildren ? children : placeholder }));
18
18
  }
@@ -1,7 +1,7 @@
1
1
  import type { ReactElement } from "react";
2
2
  import { notNullish } from "../../util/index.js";
3
3
  import { type FlexVariants, getFlexClass } from "../style/Flex.js";
4
- import { getClass } from "../util/css.js";
4
+ import { getClass, getModuleClass } from "../util/css.js";
5
5
  import type { OptionalChildProps } from "../util/index.js";
6
6
  import { getInputClass, type InputProps, type InputVariants } from "./Input.js";
7
7
  import INPUT_CSS from "./Input.module.css";
@@ -24,7 +24,10 @@ export interface OutputInputProps extends InputProps, OptionalChildProps, FlexVa
24
24
  export function OutputInput({ title, placeholder, children = title, ...props }: OutputInputProps): ReactElement {
25
25
  const hasChildren = notNullish(children);
26
26
  return (
27
- <output {...props} className={getClass(getInputClass(props), getFlexClass(props), hasChildren && INPUT_CSS.placeholder)}>
27
+ <output
28
+ {...props}
29
+ className={getClass(getInputClass(props), getFlexClass(props), hasChildren && getModuleClass(INPUT_CSS, "placeholder"))}
30
+ >
28
31
  {hasChildren ? children : placeholder}
29
32
  </output>
30
33
  );
@@ -1,5 +1,6 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { isTruthy } from "../../util/boolean.js";
3
+ import { getModuleClass } from "../util/css.js";
3
4
  import { eventPreventDefault } from "../util/event.js";
4
5
  import { eventLoopFocus } from "../util/focus.js";
5
6
  import styles from "./Popover.module.css";
@@ -17,10 +18,10 @@ import styles from "./Popover.module.css";
17
18
  */
18
19
  export function Popover({ children: [trigger, ...popover], //
19
20
  onClose, open = popover.flat().some(isTruthy), }) {
20
- return (_jsxs("div", { className: styles.wrap, onBlur: e => {
21
+ return (_jsxs("div", { className: getModuleClass(styles, "wrap"), onBlur: e => {
21
22
  if (e.relatedTarget)
22
23
  eventLoopFocus(e);
23
24
  else
24
25
  onClose?.();
25
- }, onKeyDown: e => e.key === "Escape" && onClose?.(), children: [trigger, open ? (_jsx("section", { className: styles.panel, onMouseDown: eventPreventDefault, children: popover })) : null] }));
26
+ }, onKeyDown: e => e.key === "Escape" && onClose?.(), children: [trigger, open ? (_jsx("section", { className: getModuleClass(styles, "panel"), onMouseDown: eventPreventDefault, children: popover })) : null] }));
26
27
  }
@@ -5,6 +5,7 @@ import type { ReactElement, ReactNode } from "react";
5
5
 
6
6
  import { isTruthy } from "../../util/boolean.js";
7
7
  import type { Callback } from "../../util/function.js";
8
+ import { getModuleClass } from "../util/css.js";
8
9
  import { eventPreventDefault } from "../util/event.js";
9
10
  import { eventLoopFocus } from "../util/focus.js";
10
11
  import styles from "./Popover.module.css";
@@ -59,7 +60,7 @@ export function Popover({
59
60
  }: PopoverProps): ReactElement {
60
61
  return (
61
62
  <div
62
- className={styles.wrap}
63
+ className={getModuleClass(styles, "wrap")}
63
64
  onBlur={e => {
64
65
  if (e.relatedTarget) eventLoopFocus(e);
65
66
  else onClose?.();
@@ -68,7 +69,7 @@ export function Popover({
68
69
  >
69
70
  {trigger}
70
71
  {open ? (
71
- <section className={styles.panel} onMouseDown={eventPreventDefault}>
72
+ <section className={getModuleClass(styles, "panel")} onMouseDown={eventPreventDefault}>
72
73
  {popover}
73
74
  </section>
74
75
  ) : null}
@@ -1,5 +1,5 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
- import { getClass } from "../util/css.js";
2
+ import { getClass, getModuleClass } from "../util/css.js";
3
3
  import styles from "./Progress.module.css";
4
4
  /**
5
5
  * Show progress as a single continuous horizontal bar, filled to `value` clamped between `0` and `1`.
@@ -12,7 +12,7 @@ import styles from "./Progress.module.css";
12
12
  export function Progress({ value, success, warning, danger }) {
13
13
  const clamped = Number.isFinite(value) ? Math.min(1, Math.max(0, value)) : 0;
14
14
  const progressStyle = { ["--progress-value"]: `${clamped * 100}%` };
15
- return (_jsx("div", { className: getClass(styles.track, success && styles.success, warning && styles.warning, danger && styles.danger), style: progressStyle, children: _jsx("span", { className: styles.fill }) }));
15
+ return (_jsx("div", { className: getClass(getModuleClass(styles, "track"), success && getModuleClass(styles, "success"), warning && getModuleClass(styles, "warning"), danger && getModuleClass(styles, "danger")), style: progressStyle, children: _jsx("span", { className: getModuleClass(styles, "fill") }) }));
16
16
  }
17
17
  /**
18
18
  * Show step progress as a horizontal bar of `total` segments, of which `current + 1` are filled.
@@ -26,5 +26,5 @@ export function SegmentedProgress({ total, current, success, warning, danger })
26
26
  if (total <= 0)
27
27
  return null;
28
28
  const progressStyle = { ["--progress-steps"]: total };
29
- return (_jsx("div", { className: getClass(styles.segmented, success && styles.success, warning && styles.warning, danger && styles.danger), style: progressStyle, children: Array.from({ length: total }, (_, i) => (_jsx("span", { className: getClass(styles.item, i <= current && styles.active) }, i.toString()))) }));
29
+ return (_jsx("div", { className: getClass(getModuleClass(styles, "segmented"), success && getModuleClass(styles, "success"), warning && getModuleClass(styles, "warning"), danger && getModuleClass(styles, "danger")), style: progressStyle, children: Array.from({ length: total }, (_, i) => (_jsx("span", { className: getClass(getModuleClass(styles, "item"), i <= current && getModuleClass(styles, "active")) }, i.toString()))) }));
30
30
  }
@@ -1,5 +1,5 @@
1
1
  import type { CSSProperties, ReactElement } from "react";
2
- import { getClass } from "../util/css.js";
2
+ import { getClass, getModuleClass } from "../util/css.js";
3
3
  import styles from "./Progress.module.css";
4
4
 
5
5
  /**
@@ -28,10 +28,15 @@ export function Progress({ value, success, warning, danger }: ProgressProps): Re
28
28
 
29
29
  return (
30
30
  <div
31
- className={getClass(styles.track, success && styles.success, warning && styles.warning, danger && styles.danger)}
31
+ className={getClass(
32
+ getModuleClass(styles, "track"),
33
+ success && getModuleClass(styles, "success"),
34
+ warning && getModuleClass(styles, "warning"),
35
+ danger && getModuleClass(styles, "danger"),
36
+ )}
32
37
  style={progressStyle}
33
38
  >
34
- <span className={styles.fill} />
39
+ <span className={getModuleClass(styles, "fill")} />
35
40
  </div>
36
41
  );
37
42
  }
@@ -63,11 +68,16 @@ export function SegmentedProgress({ total, current, success, warning, danger }:
63
68
 
64
69
  return (
65
70
  <div
66
- className={getClass(styles.segmented, success && styles.success, warning && styles.warning, danger && styles.danger)}
71
+ className={getClass(
72
+ getModuleClass(styles, "segmented"),
73
+ success && getModuleClass(styles, "success"),
74
+ warning && getModuleClass(styles, "warning"),
75
+ danger && getModuleClass(styles, "danger"),
76
+ )}
67
77
  style={progressStyle}
68
78
  >
69
79
  {Array.from({ length: total }, (_, i) => (
70
- <span key={i.toString()} className={getClass(styles.item, i <= current && styles.active)} />
80
+ <span key={i.toString()} className={getClass(getModuleClass(styles, "item"), i <= current && getModuleClass(styles, "active"))} />
71
81
  ))}
72
82
  </div>
73
83
  );
@@ -1,7 +1,7 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { notNullish } from "../../util/null.js";
3
3
  import { getFlexClass } from "../style/Flex.js";
4
- import { getClass } from "../util/css.js";
4
+ import { getClass, getModuleClass } from "../util/css.js";
5
5
  import { getInputClass } from "./Input.js";
6
6
  import INPUT_CSS from "./Input.module.css";
7
7
  /**
@@ -15,5 +15,5 @@ import INPUT_CSS from "./Input.module.css";
15
15
  */
16
16
  export function RadioInput({ name, title, placeholder = title || "Choose", required = false, disabled = false, message = "", value = false, onValue, children, ...props }) {
17
17
  const hasChildren = notNullish(children);
18
- return (_jsxs("label", { className: getClass(getInputClass(props), INPUT_CSS.label, getFlexClass(props), hasChildren && INPUT_CSS.placeholder), children: [_jsx("input", { className: INPUT_CSS.radio, type: "radio", name: name, defaultChecked: value, onChange: () => onValue(true), disabled: disabled, required: required, "aria-invalid": !!message }), hasChildren ? children : placeholder] }));
18
+ return (_jsxs("label", { className: getClass(getInputClass(props), getModuleClass(INPUT_CSS, "label"), getFlexClass(props), hasChildren && getModuleClass(INPUT_CSS, "placeholder")), children: [_jsx("input", { className: getModuleClass(INPUT_CSS, "radio"), type: "radio", name: name, defaultChecked: value, onChange: () => onValue(true), disabled: disabled, required: required, "aria-invalid": !!message }), hasChildren ? children : placeholder] }));
19
19
  }
@@ -1,7 +1,7 @@
1
1
  import type { ReactElement } from "react";
2
2
  import { notNullish } from "../../util/null.js";
3
3
  import { type FlexVariants, getFlexClass } from "../style/Flex.js";
4
- import { getClass } from "../util/css.js";
4
+ import { getClass, getModuleClass } from "../util/css.js";
5
5
  import type { OptionalChildProps } from "../util/props.js";
6
6
  import { getInputClass, type InputVariants, type ValueInputProps } from "./Input.js";
7
7
  import INPUT_CSS from "./Input.module.css";
@@ -36,9 +36,16 @@ export function RadioInput({
36
36
  }: RadioInputProps): ReactElement {
37
37
  const hasChildren = notNullish(children);
38
38
  return (
39
- <label className={getClass(getInputClass(props), INPUT_CSS.label, getFlexClass(props), hasChildren && INPUT_CSS.placeholder)}>
39
+ <label
40
+ className={getClass(
41
+ getInputClass(props),
42
+ getModuleClass(INPUT_CSS, "label"),
43
+ getFlexClass(props),
44
+ hasChildren && getModuleClass(INPUT_CSS, "placeholder"),
45
+ )}
46
+ >
40
47
  <input
41
- className={INPUT_CSS.radio}
48
+ className={getModuleClass(INPUT_CSS, "radio")}
42
49
  type="radio"
43
50
  name={name}
44
51
  defaultChecked={value}
@@ -1,8 +1,8 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { getProps } from "../../util/object.js";
3
- import { getClass } from "../util/css.js";
3
+ import { getClass, getModuleClass } from "../util/css.js";
4
4
  import { getInputClass } from "./Input.js";
5
5
  import INPUT_CSS from "./Input.module.css";
6
6
  export function SelectInput({ name, placeholder = "Choose...", required = false, disabled = false, message = "", value = "", onValue, options, ...variants }) {
7
- return (_jsxs("select", { name: name, defaultValue: value, onChange: e => onValue(e.currentTarget.value || undefined), className: getClass(getInputClass(variants), INPUT_CSS.select), title: message, "aria-invalid": !!message, disabled: disabled, required: required, children: [!required || !value ? (_jsx("option", { value: "", className: INPUT_CSS.empty, children: placeholder })) : null, getProps(options).map(([k, t]) => (_jsx("option", { value: k, className: INPUT_CSS.value, children: t }, k)))] }));
7
+ return (_jsxs("select", { name: name, defaultValue: value, onChange: e => onValue(e.currentTarget.value || undefined), className: getClass(getInputClass(variants), getModuleClass(INPUT_CSS, "select")), title: message, "aria-invalid": !!message, disabled: disabled, required: required, children: [!required || !value ? (_jsx("option", { value: "", className: getModuleClass(INPUT_CSS, "empty"), children: placeholder })) : null, getProps(options).map(([k, t]) => (_jsx("option", { value: k, className: getModuleClass(INPUT_CSS, "value"), children: t }, k)))] }));
8
8
  }
@@ -1,7 +1,7 @@
1
1
  import type { ReactElement } from "react";
2
2
  import type { ChoiceOptions } from "../../schema/ChoiceSchema.js";
3
3
  import { getProps } from "../../util/object.js";
4
- import { getClass } from "../util/css.js";
4
+ import { getClass, getModuleClass } from "../util/css.js";
5
5
  import { getInputClass, type InputVariants, type ValueInputProps } from "./Input.js";
6
6
  import INPUT_CSS from "./Input.module.css";
7
7
 
@@ -41,19 +41,19 @@ export function SelectInput({
41
41
  name={name}
42
42
  defaultValue={value}
43
43
  onChange={e => onValue(e.currentTarget.value || undefined)}
44
- className={getClass(getInputClass(variants), INPUT_CSS.select)}
44
+ className={getClass(getInputClass(variants), getModuleClass(INPUT_CSS, "select"))}
45
45
  title={message}
46
46
  aria-invalid={!!message}
47
47
  disabled={disabled}
48
48
  required={required}
49
49
  >
50
50
  {!required || !value ? (
51
- <option value="" className={INPUT_CSS.empty}>
51
+ <option value="" className={getModuleClass(INPUT_CSS, "empty")}>
52
52
  {placeholder}
53
53
  </option>
54
54
  ) : null}
55
55
  {getProps(options).map(([k, t]) => (
56
- <option key={k} value={k} className={INPUT_CSS.value}>
56
+ <option key={k} value={k} className={getModuleClass(INPUT_CSS, "value")}>
57
57
  {t}
58
58
  </option>
59
59
  ))}
@@ -1,6 +1,6 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { PASSTHROUGH } from "../../util/function.js";
3
- import { getClass } from "../util/css.js";
3
+ import { getClass, getModuleClass } from "../util/css.js";
4
4
  import { getInputClass } from "./Input.js";
5
5
  import INPUT_CSS from "./Input.module.css";
6
6
  /**
@@ -23,12 +23,12 @@ required = false, disabled = false, message = "", value, onValue, input = "text"
23
23
  onValue?.(formatter(e.currentTarget.value));
24
24
  _resize(e.currentTarget, rows);
25
25
  };
26
- return (_jsx("textarea", { ref: el => void (el && _resize(el, rows)), name: name, defaultValue: value !== undefined ? formatter(value) : "", minLength: Number.isFinite(min) ? min : 0, maxLength: Number.isFinite(max) ? max : undefined, rows: rows, required: required && min > 0, disabled: disabled, placeholder: placeholder || " ", className: getClass(getInputClass(variants), INPUT_CSS.text, INPUT_CSS.multiline), onInput: onChange, onChange: onChange, onBlur: onBlur, title: message, "aria-invalid": !!message }));
26
+ return (_jsx("textarea", { ref: el => void (el && _resize(el, rows)), name: name, defaultValue: value !== undefined ? formatter(value) : "", minLength: Number.isFinite(min) ? min : 0, maxLength: Number.isFinite(max) ? max : undefined, rows: rows, required: required && min > 0, disabled: disabled, placeholder: placeholder || " ", className: getClass(getInputClass(variants), getModuleClass(INPUT_CSS, "text"), getModuleClass(INPUT_CSS, "multiline")), onInput: onChange, onChange: onChange, onBlur: onBlur, title: message, "aria-invalid": !!message }));
27
27
  }
28
28
  const onChange = (e) => {
29
29
  onValue?.(formatter(e.currentTarget.value));
30
30
  };
31
- return (_jsx("input", { name: name, type: input, defaultValue: value !== undefined ? formatter(value) : "", minLength: Number.isFinite(min) ? min : 0, maxLength: Number.isFinite(max) ? max : undefined, required: required && min > 0, disabled: disabled, placeholder: placeholder || " ", className: getClass(getInputClass(variants), INPUT_CSS.text), onInput: onChange, onChange: onChange, onBlur: onBlur, title: message, "aria-invalid": !!message }));
31
+ return (_jsx("input", { name: name, type: input, defaultValue: value !== undefined ? formatter(value) : "", minLength: Number.isFinite(min) ? min : 0, maxLength: Number.isFinite(max) ? max : undefined, required: required && min > 0, disabled: disabled, placeholder: placeholder || " ", className: getClass(getInputClass(variants), getModuleClass(INPUT_CSS, "text")), onInput: onChange, onChange: onChange, onBlur: onBlur, title: message, "aria-invalid": !!message }));
32
32
  }
33
33
  /**
34
34
  * @todo This can be removed when Firefox supports `field-sizing:` in CSS.
@@ -1,7 +1,7 @@
1
1
  import type { ReactElement, SyntheticEvent } from "react";
2
2
  import type { StringInputType } from "../../schema/StringSchema.js";
3
3
  import { PASSTHROUGH } from "../../util/function.js";
4
- import { getClass } from "../util/css.js";
4
+ import { getClass, getModuleClass } from "../util/css.js";
5
5
  import { getInputClass, type InputVariants, type ValueInputProps } from "./Input.js";
6
6
  import INPUT_CSS from "./Input.module.css";
7
7
 
@@ -69,7 +69,7 @@ export function TextInput({
69
69
  required={required && min > 0}
70
70
  disabled={disabled}
71
71
  placeholder={placeholder || " "}
72
- className={getClass(getInputClass(variants), INPUT_CSS.text, INPUT_CSS.multiline)}
72
+ className={getClass(getInputClass(variants), getModuleClass(INPUT_CSS, "text"), getModuleClass(INPUT_CSS, "multiline"))}
73
73
  onInput={onChange}
74
74
  onChange={onChange}
75
75
  onBlur={onBlur}
@@ -93,7 +93,7 @@ export function TextInput({
93
93
  required={required && min > 0}
94
94
  disabled={disabled}
95
95
  placeholder={placeholder || " "}
96
- className={getClass(getInputClass(variants), INPUT_CSS.text)}
96
+ className={getClass(getInputClass(variants), getModuleClass(INPUT_CSS, "text"))}
97
97
  onInput={onChange}
98
98
  onChange={onChange}
99
99
  onBlur={onBlur}
@@ -1,6 +1,6 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { requireMetaURL } from "../misc/MetaContext.js";
3
- import { getClass } from "../util/css.js";
3
+ import { getClass, getModuleClass } from "../util/css.js";
4
4
  import CENTERED_LAYOUT_CSS from "./CenteredLayout.module.css";
5
5
  import { LAYOUT_CLASS } from "./Layout.js";
6
6
  /**
@@ -16,5 +16,5 @@ import { LAYOUT_CLASS } from "./Layout.js";
16
16
  */
17
17
  export function CenteredLayout({ children, fullWidth = false }) {
18
18
  const { path } = requireMetaURL();
19
- return (_jsx("main", { className: getClass(CENTERED_LAYOUT_CSS.main, LAYOUT_CLASS), children: _jsx("div", { className: CENTERED_LAYOUT_CSS.mainInner, style: fullWidth ? { maxWidth: "none" } : undefined, children: children }) }, path));
19
+ return (_jsx("main", { className: getClass(getModuleClass(CENTERED_LAYOUT_CSS, "main"), LAYOUT_CLASS), children: _jsx("div", { className: getModuleClass(CENTERED_LAYOUT_CSS, "mainInner"), style: fullWidth ? { maxWidth: "none" } : undefined, children: children }) }, path));
20
20
  }
@@ -1,6 +1,6 @@
1
1
  import type { ReactElement } from "react";
2
2
  import { requireMetaURL } from "../misc/MetaContext.js";
3
- import { getClass } from "../util/css.js";
3
+ import { getClass, getModuleClass } from "../util/css.js";
4
4
  import type { OptionalChildProps } from "../util/props.js";
5
5
  import CENTERED_LAYOUT_CSS from "./CenteredLayout.module.css";
6
6
  import { LAYOUT_CLASS } from "./Layout.js";
@@ -28,8 +28,8 @@ export interface CenteredLayoutProps extends OptionalChildProps {
28
28
  export function CenteredLayout({ children, fullWidth = false }: CenteredLayoutProps): ReactElement {
29
29
  const { path } = requireMetaURL();
30
30
  return (
31
- <main key={path} className={getClass(CENTERED_LAYOUT_CSS.main, LAYOUT_CLASS)}>
32
- <div className={CENTERED_LAYOUT_CSS.mainInner} style={fullWidth ? { maxWidth: "none" } : undefined}>
31
+ <main key={path} className={getClass(getModuleClass(CENTERED_LAYOUT_CSS, "main"), LAYOUT_CLASS)}>
32
+ <div className={getModuleClass(CENTERED_LAYOUT_CSS, "mainInner")} style={fullWidth ? { maxWidth: "none" } : undefined}>
33
33
  {children}
34
34
  </div>
35
35
  </main>
@@ -3,7 +3,7 @@ import { Bars3Icon, XMarkIcon } from "@heroicons/react/24/solid";
3
3
  import { useEffect, useState } from "react";
4
4
  import { Button } from "../form/Button.js";
5
5
  import { requireMetaURL } from "../misc/MetaContext.js";
6
- import { getClass } from "../util/css.js";
6
+ import { getClass, getModuleClass } from "../util/css.js";
7
7
  import { LAYOUT_CLASS } from "./Layout.js";
8
8
  import SIDEBAR_LAYOUT_CSS from "./SidebarLayout.module.css";
9
9
  /**
@@ -30,8 +30,8 @@ export function SidebarLayout({ sidebar, children, right = false }) {
30
30
  if (path)
31
31
  setOpen(false);
32
32
  }, [path]);
33
- const sidebarEl = (_jsx("nav", { className: getClass(SIDEBAR_LAYOUT_CSS.sidebar, open && SIDEBAR_LAYOUT_CSS.open), children: sidebar }, "sidebar"));
34
- const contentEl = (_jsxs("div", { className: getClass(LAYOUT_CLASS, SIDEBAR_LAYOUT_CSS.content), children: [_jsx("div", { className: SIDEBAR_LAYOUT_CSS.toggle, children: _jsx(Button, { title: open ? "Close menu" : "Show menu", onClick: () => setOpen(o => !o), children: open ? _jsx(XMarkIcon, {}) : _jsx(Bars3Icon, {}) }) }), _jsx("div", { className: SIDEBAR_LAYOUT_CSS.contentInner, children: children })] }, path));
35
- const overlayEl = open && (_jsx("button", { type: "button", className: SIDEBAR_LAYOUT_CSS.overlay, "aria-label": "Close menu", onClick: () => setOpen(false) }, "overlay"));
36
- return (_jsx("main", { className: getClass(SIDEBAR_LAYOUT_CSS.main, LAYOUT_CLASS), children: right ? [contentEl, sidebarEl, overlayEl] : [sidebarEl, contentEl, overlayEl] }));
33
+ const sidebarEl = (_jsx("nav", { className: getClass(getModuleClass(SIDEBAR_LAYOUT_CSS, "sidebar"), open && getModuleClass(SIDEBAR_LAYOUT_CSS, "open")), children: sidebar }, "sidebar"));
34
+ const contentEl = (_jsxs("div", { className: getClass(LAYOUT_CLASS, getModuleClass(SIDEBAR_LAYOUT_CSS, "content")), children: [_jsx("div", { className: getModuleClass(SIDEBAR_LAYOUT_CSS, "toggle"), children: _jsx(Button, { title: open ? "Close menu" : "Show menu", onClick: () => setOpen(o => !o), children: open ? _jsx(XMarkIcon, {}) : _jsx(Bars3Icon, {}) }) }), _jsx("div", { className: getModuleClass(SIDEBAR_LAYOUT_CSS, "contentInner"), children: children })] }, path));
35
+ const overlayEl = open && (_jsx("button", { type: "button", className: getModuleClass(SIDEBAR_LAYOUT_CSS, "overlay"), "aria-label": "Close menu", onClick: () => setOpen(false) }, "overlay"));
36
+ return (_jsx("main", { className: getClass(getModuleClass(SIDEBAR_LAYOUT_CSS, "main"), LAYOUT_CLASS), children: right ? [contentEl, sidebarEl, overlayEl] : [sidebarEl, contentEl, overlayEl] }));
37
37
  }
@@ -2,7 +2,7 @@ import { Bars3Icon, XMarkIcon } from "@heroicons/react/24/solid";
2
2
  import { type ReactElement, type ReactNode, useEffect, useState } from "react";
3
3
  import { Button } from "../form/Button.js";
4
4
  import { requireMetaURL } from "../misc/MetaContext.js";
5
- import { getClass } from "../util/css.js";
5
+ import { getClass, getModuleClass } from "../util/css.js";
6
6
  import type { OptionalChildProps } from "../util/props.js";
7
7
  import { LAYOUT_CLASS } from "./Layout.js";
8
8
  import SIDEBAR_LAYOUT_CSS from "./SidebarLayout.module.css";
@@ -45,25 +45,34 @@ export function SidebarLayout({ sidebar, children, right = false }: SidebarLayou
45
45
  }, [path]);
46
46
 
47
47
  const sidebarEl = (
48
- <nav key="sidebar" className={getClass(SIDEBAR_LAYOUT_CSS.sidebar, open && SIDEBAR_LAYOUT_CSS.open)}>
48
+ <nav
49
+ key="sidebar"
50
+ className={getClass(getModuleClass(SIDEBAR_LAYOUT_CSS, "sidebar"), open && getModuleClass(SIDEBAR_LAYOUT_CSS, "open"))}
51
+ >
49
52
  {sidebar}
50
53
  </nav>
51
54
  );
52
55
  const contentEl = (
53
- <div key={path} className={getClass(LAYOUT_CLASS, SIDEBAR_LAYOUT_CSS.content)}>
54
- <div className={SIDEBAR_LAYOUT_CSS.toggle}>
56
+ <div key={path} className={getClass(LAYOUT_CLASS, getModuleClass(SIDEBAR_LAYOUT_CSS, "content"))}>
57
+ <div className={getModuleClass(SIDEBAR_LAYOUT_CSS, "toggle")}>
55
58
  <Button title={open ? "Close menu" : "Show menu"} onClick={() => setOpen(o => !o)}>
56
59
  {open ? <XMarkIcon /> : <Bars3Icon />}
57
60
  </Button>
58
61
  </div>
59
- <div className={SIDEBAR_LAYOUT_CSS.contentInner}>{children}</div>
62
+ <div className={getModuleClass(SIDEBAR_LAYOUT_CSS, "contentInner")}>{children}</div>
60
63
  </div>
61
64
  );
62
65
  const overlayEl = open && (
63
- <button key="overlay" type="button" className={SIDEBAR_LAYOUT_CSS.overlay} aria-label="Close menu" onClick={() => setOpen(false)} />
66
+ <button
67
+ key="overlay"
68
+ type="button"
69
+ className={getModuleClass(SIDEBAR_LAYOUT_CSS, "overlay")}
70
+ aria-label="Close menu"
71
+ onClick={() => setOpen(false)}
72
+ />
64
73
  );
65
74
  return (
66
- <main className={getClass(SIDEBAR_LAYOUT_CSS.main, LAYOUT_CLASS)}>
75
+ <main className={getClass(getModuleClass(SIDEBAR_LAYOUT_CSS, "main"), LAYOUT_CLASS)}>
67
76
  {right ? [contentEl, sidebarEl, overlayEl] : [sidebarEl, contentEl, overlayEl]}
68
77
  </main>
69
78
  );
@@ -1,4 +1,5 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { getModuleClass } from "../util/css.js";
2
3
  import styles from "./Loading.module.css";
3
4
  /**
4
5
  * Animated spinner SVG used as a loading indicator.
@@ -11,7 +12,7 @@ import styles from "./Loading.module.css";
11
12
  * @see https://dhoulb.github.io/shelving/ui/misc/Loading/Loading
12
13
  */
13
14
  export function Loading() {
14
- return (_jsxs("svg", { "aria-hidden": "true", viewBox: "0 0 24 24", xmlns: "http://www.w3.org/2000/svg", className: styles.spinner, "data-slot": "icon", children: [_jsx("title", { children: "Loading..." }), _jsx("circle", { className: styles.track, cx: "12", cy: "12", r: "9", pathLength: "100" }), _jsxs("g", { children: [_jsx("animateTransform", { attributeName: "transform", attributeType: "xml", type: "rotate", from: "0 12 12", to: "360 12 12", dur: "0.5s", repeatCount: "indefinite" }), _jsx("circle", { className: styles.indicator, cx: "12", cy: "12", r: "9", pathLength: "100" })] })] }));
15
+ return (_jsxs("svg", { "aria-hidden": "true", viewBox: "0 0 24 24", xmlns: "http://www.w3.org/2000/svg", className: getModuleClass(styles, "spinner"), "data-slot": "icon", children: [_jsx("title", { children: "Loading..." }), _jsx("circle", { className: getModuleClass(styles, "track"), cx: "12", cy: "12", r: "9", pathLength: "100" }), _jsxs("g", { children: [_jsx("animateTransform", { attributeName: "transform", attributeType: "xml", type: "rotate", from: "0 12 12", to: "360 12 12", dur: "0.5s", repeatCount: "indefinite" }), _jsx("circle", { className: getModuleClass(styles, "indicator"), cx: "12", cy: "12", r: "9", pathLength: "100" })] })] }));
15
16
  }
16
17
  /**
17
18
  * Shared `<Loading>` element with a stable `key`, ready to drop into `Suspense` fallbacks and lists.
@@ -1,4 +1,5 @@
1
1
  import type { ReactElement } from "react";
2
+ import { getModuleClass } from "../util/css.js";
2
3
  import styles from "./Loading.module.css";
3
4
 
4
5
  declare const _componentProps: unique symbol;
@@ -24,9 +25,15 @@ export interface LoadingProps {
24
25
  */
25
26
  export function Loading(): ReactElement {
26
27
  return (
27
- <svg aria-hidden="true" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" className={styles.spinner} data-slot="icon">
28
+ <svg
29
+ aria-hidden="true"
30
+ viewBox="0 0 24 24"
31
+ xmlns="http://www.w3.org/2000/svg"
32
+ className={getModuleClass(styles, "spinner")}
33
+ data-slot="icon"
34
+ >
28
35
  <title>Loading...</title>
29
- <circle className={styles.track} cx="12" cy="12" r="9" pathLength="100" />
36
+ <circle className={getModuleClass(styles, "track")} cx="12" cy="12" r="9" pathLength="100" />
30
37
  <g>
31
38
  <animateTransform
32
39
  attributeName="transform"
@@ -37,7 +44,7 @@ export function Loading(): ReactElement {
37
44
  dur="0.5s"
38
45
  repeatCount="indefinite"
39
46
  />
40
- <circle className={styles.indicator} cx="12" cy="12" r="9" pathLength="100" />
47
+ <circle className={getModuleClass(styles, "indicator")} cx="12" cy="12" r="9" pathLength="100" />
41
48
  </g>
42
49
  </svg>
43
50
  );
@@ -2,9 +2,9 @@ import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { getParagraphClass } from "../block/Paragraph.js";
3
3
  import { LOADING } from "../misc/Loading.js";
4
4
  import { getStatusClass } from "../style/Status.js";
5
- import { getClass } from "../util/css.js";
5
+ import { getClass, getModuleClass } from "../util/css.js";
6
6
  import MESSAGE_CSS from "./Message.module.css";
7
- const MESSAGE_CLASS = getClass(MESSAGE_CSS.message);
7
+ const MESSAGE_CLASS = getModuleClass(MESSAGE_CSS, "message");
8
8
  /**
9
9
  * Status-coloured paragraph used for inline feedback messages.
10
10
  *
@@ -2,10 +2,10 @@ import { getParagraphClass, type ParagraphProps } from "../block/Paragraph.js";
2
2
  import { LOADING } from "../misc/Loading.js";
3
3
  import type { ColorVariants } from "../style/Color.js";
4
4
  import { getStatusClass, type StatusVariants } from "../style/Status.js";
5
- import { getClass } from "../util/css.js";
5
+ import { getClass, getModuleClass } from "../util/css.js";
6
6
  import MESSAGE_CSS from "./Message.module.css";
7
7
 
8
- const MESSAGE_CLASS = getClass(MESSAGE_CSS.message);
8
+ const MESSAGE_CLASS = getModuleClass(MESSAGE_CSS, "message");
9
9
 
10
10
  /**
11
11
  * Props for `<Message>` — paragraph props plus colour and status styling variants.