shelving 1.218.1 → 1.219.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/ui/block/Card.d.ts +8 -2
- package/ui/block/Card.js +9 -3
- package/ui/block/Card.tsx +17 -4
- package/ui/block/Flex.module.css +2 -1
- package/ui/block/README.md +2 -0
- package/ui/misc/Catcher.d.ts +1 -1
- package/ui/misc/Catcher.js +7 -2
- package/ui/misc/Catcher.tsx +14 -2
- package/util/equal.js +1 -1
- package/util/number.js +1 -1
package/package.json
CHANGED
package/ui/block/Card.d.ts
CHANGED
|
@@ -1,18 +1,24 @@
|
|
|
1
1
|
import type { ReactElement, ReactNode } from "react";
|
|
2
2
|
import { type ClickableProps } from "../form/Clickable.js";
|
|
3
|
-
|
|
3
|
+
import { type ColorVariants } from "../misc/Color.js";
|
|
4
|
+
import { type Status } from "../misc/Status.js";
|
|
5
|
+
export interface CardProps extends ClickableProps, ColorVariants {
|
|
4
6
|
children?: ReactNode;
|
|
5
7
|
/** Constrain the card to narrow width (defaults to full-width). */
|
|
6
8
|
narrow?: boolean | undefined;
|
|
7
9
|
/** Constrain the card to wide width (defaults to full-width). */
|
|
8
10
|
wide?: boolean | undefined;
|
|
11
|
+
/** Status colour for the card (e.g. `"error"`, `"success"`). */
|
|
12
|
+
status?: Status | undefined;
|
|
9
13
|
}
|
|
10
14
|
/**
|
|
11
15
|
* Cards are boxed areas for content to sit within — rendered as `<article>` since each card represents a self-contained piece of content.
|
|
12
16
|
* - When `href` or `onClick` is set the card becomes navigable: a stretched overlay `<a>` / `<button>` covers the entire card while the children render normally inside.
|
|
13
17
|
* - Real interactive elements inside the card (e.g. inline `<a>` links) stay clickable thanks to `position: relative; z-index: 2` rules in the stylesheet.
|
|
18
|
+
* - Accepts a `status` colour and raw `ColorVariants` — the card styles the box; lay out its contents however the use case needs.
|
|
14
19
|
*
|
|
15
20
|
* @example <Card><Subheading>Static</Subheading></Card>
|
|
16
21
|
* @example <Card href="/foo" title="Open foo"><Subheading>Clickable</Subheading></Card>
|
|
22
|
+
* @example <Card status="error"><StatusIcon status="error" xxlarge /><Subheading>Not found</Subheading></Card>
|
|
17
23
|
*/
|
|
18
|
-
export declare function Card({ children, href, onClick, title, ...props }: CardProps): ReactElement;
|
|
24
|
+
export declare function Card({ children, href, onClick, title, status, ...props }: CardProps): ReactElement;
|
package/ui/block/Card.js
CHANGED
|
@@ -1,16 +1,22 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { Clickable } from "../form/Clickable.js";
|
|
3
|
-
import {
|
|
3
|
+
import { getColorClass } from "../misc/Color.js";
|
|
4
|
+
import { getStatusClass } from "../misc/Status.js";
|
|
5
|
+
import { getClass, getModuleClass } from "../util/css.js";
|
|
4
6
|
import CARD_CSS from "./Card.module.css";
|
|
5
7
|
/**
|
|
6
8
|
* Cards are boxed areas for content to sit within — rendered as `<article>` since each card represents a self-contained piece of content.
|
|
7
9
|
* - When `href` or `onClick` is set the card becomes navigable: a stretched overlay `<a>` / `<button>` covers the entire card while the children render normally inside.
|
|
8
10
|
* - Real interactive elements inside the card (e.g. inline `<a>` links) stay clickable thanks to `position: relative; z-index: 2` rules in the stylesheet.
|
|
11
|
+
* - Accepts a `status` colour and raw `ColorVariants` — the card styles the box; lay out its contents however the use case needs.
|
|
9
12
|
*
|
|
10
13
|
* @example <Card><Subheading>Static</Subheading></Card>
|
|
11
14
|
* @example <Card href="/foo" title="Open foo"><Subheading>Clickable</Subheading></Card>
|
|
15
|
+
* @example <Card status="error"><StatusIcon status="error" xxlarge /><Subheading>Not found</Subheading></Card>
|
|
12
16
|
*/
|
|
13
|
-
export function Card({ children, href, onClick, title = "Open", ...props }) {
|
|
17
|
+
export function Card({ children, href, onClick, title = "Open", status, ...props }) {
|
|
14
18
|
const overlay = (href || onClick) && _jsx(Clickable, { title: title, href: href, onClick: onClick, ...props, className: CARD_CSS.overlay });
|
|
15
|
-
return (_jsxs("article", { className: getModuleClass(CARD_CSS, "card", props),
|
|
19
|
+
return (_jsxs("article", { className: getClass(getModuleClass(CARD_CSS, "card", props), //
|
|
20
|
+
status && getStatusClass(status), // Cards can have status colours.
|
|
21
|
+
getColorClass(props)), children: [overlay, overlay ? _jsx("div", { children: children }) : children] }));
|
|
16
22
|
}
|
package/ui/block/Card.tsx
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import type { ReactElement, ReactNode } from "react";
|
|
2
2
|
import { Clickable, type ClickableProps } from "../form/Clickable.js";
|
|
3
|
-
import {
|
|
3
|
+
import { type ColorVariants, getColorClass } from "../misc/Color.js";
|
|
4
|
+
import { getStatusClass, type Status } from "../misc/Status.js";
|
|
5
|
+
import { getClass, getModuleClass } from "../util/css.js";
|
|
4
6
|
import CARD_CSS from "./Card.module.css";
|
|
5
7
|
|
|
6
|
-
export interface CardProps extends ClickableProps {
|
|
8
|
+
export interface CardProps extends ClickableProps, ColorVariants {
|
|
7
9
|
children?: ReactNode;
|
|
8
10
|
|
|
9
11
|
/** Constrain the card to narrow width (defaults to full-width). */
|
|
@@ -11,20 +13,31 @@ export interface CardProps extends ClickableProps {
|
|
|
11
13
|
|
|
12
14
|
/** Constrain the card to wide width (defaults to full-width). */
|
|
13
15
|
wide?: boolean | undefined;
|
|
16
|
+
|
|
17
|
+
/** Status colour for the card (e.g. `"error"`, `"success"`). */
|
|
18
|
+
status?: Status | undefined;
|
|
14
19
|
}
|
|
15
20
|
|
|
16
21
|
/**
|
|
17
22
|
* Cards are boxed areas for content to sit within — rendered as `<article>` since each card represents a self-contained piece of content.
|
|
18
23
|
* - When `href` or `onClick` is set the card becomes navigable: a stretched overlay `<a>` / `<button>` covers the entire card while the children render normally inside.
|
|
19
24
|
* - Real interactive elements inside the card (e.g. inline `<a>` links) stay clickable thanks to `position: relative; z-index: 2` rules in the stylesheet.
|
|
25
|
+
* - Accepts a `status` colour and raw `ColorVariants` — the card styles the box; lay out its contents however the use case needs.
|
|
20
26
|
*
|
|
21
27
|
* @example <Card><Subheading>Static</Subheading></Card>
|
|
22
28
|
* @example <Card href="/foo" title="Open foo"><Subheading>Clickable</Subheading></Card>
|
|
29
|
+
* @example <Card status="error"><StatusIcon status="error" xxlarge /><Subheading>Not found</Subheading></Card>
|
|
23
30
|
*/
|
|
24
|
-
export function Card({ children, href, onClick, title = "Open", ...props }: CardProps): ReactElement {
|
|
31
|
+
export function Card({ children, href, onClick, title = "Open", status, ...props }: CardProps): ReactElement {
|
|
25
32
|
const overlay = (href || onClick) && <Clickable title={title} href={href} onClick={onClick} {...props} className={CARD_CSS.overlay} />;
|
|
26
33
|
return (
|
|
27
|
-
<article
|
|
34
|
+
<article
|
|
35
|
+
className={getClass(
|
|
36
|
+
getModuleClass(CARD_CSS, "card", props), //
|
|
37
|
+
status && getStatusClass(status), // Cards can have status colours.
|
|
38
|
+
getColorClass(props), // Cards can also have raw colour overrides.
|
|
39
|
+
)}
|
|
40
|
+
>
|
|
28
41
|
{overlay}
|
|
29
42
|
{overlay ? <div>{children}</div> : children}
|
|
30
43
|
</article>
|
package/ui/block/Flex.module.css
CHANGED
|
@@ -62,7 +62,8 @@
|
|
|
62
62
|
&& > * {
|
|
63
63
|
margin: 0; /* No margins on children (high specificity). */
|
|
64
64
|
}
|
|
65
|
-
|
|
65
|
+
/* Low-precedence default — `:where()` keeps specificity at zero so an icon's own size variant (e.g. `<StatusIcon large>`) can override it. */
|
|
66
|
+
:where(& > [data-slot="icon"]) {
|
|
66
67
|
inline-size: var(--elements-size-icon, var(--size-icon));
|
|
67
68
|
block-size: var(--elements-size-icon, var(--size-icon));
|
|
68
69
|
flex: none;
|
package/ui/block/README.md
CHANGED
|
@@ -41,6 +41,8 @@ import { Card, Paragraph, Subheading } from "shelving/ui";
|
|
|
41
41
|
|
|
42
42
|
`href` turns the card into a navigable overlay. Real interactive elements inside (like inline `<Link>` components) stay clickable via `position: relative; z-index: 2` rules in the stylesheet.
|
|
43
43
|
|
|
44
|
+
`<Card>` also accepts a `status` colour and raw `ColorVariants` — e.g. `<Card status="error">` for a prominent error panel. The card styles the box; compose its contents however the use case needs.
|
|
45
|
+
|
|
44
46
|
### Structured page section
|
|
45
47
|
|
|
46
48
|
```tsx
|
package/ui/misc/Catcher.d.ts
CHANGED
|
@@ -40,6 +40,6 @@ export interface ErrorNoticeProps extends ErrorComponentProps {
|
|
|
40
40
|
export declare function ErrorNotice({ reason }: ErrorNoticeProps): ReactElement;
|
|
41
41
|
export interface ErrorPageProps extends ErrorComponentProps {
|
|
42
42
|
}
|
|
43
|
-
/** Output a `<Page>` with
|
|
43
|
+
/** Output a `<Page>` with an error `<Card>` for an unknown error reason. */
|
|
44
44
|
export declare function ErrorPage({ reason }: ErrorPageProps): ReactElement;
|
|
45
45
|
export {};
|
package/ui/misc/Catcher.js
CHANGED
|
@@ -2,10 +2,14 @@ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-run
|
|
|
2
2
|
import { ArrowPathIcon } from "@heroicons/react/24/solid";
|
|
3
3
|
import { Component, createContext, use } from "react";
|
|
4
4
|
import { getMessage } from "../../util/error.js";
|
|
5
|
+
import { Card } from "../block/Card.js";
|
|
6
|
+
import { Flex } from "../block/Flex.js";
|
|
7
|
+
import { Subheading } from "../block/Subheading.js";
|
|
5
8
|
import { Button } from "../form/Button.js";
|
|
6
9
|
import { CenteredLayout } from "../layout/CenteredLayout.js";
|
|
7
10
|
import { Notice } from "../notice/Notice.js";
|
|
8
11
|
import { Page } from "../page/Page.js";
|
|
12
|
+
import { StatusIcon } from "./StatusIcon.js";
|
|
9
13
|
const RetryContext = createContext(undefined);
|
|
10
14
|
RetryContext.displayName = "RetryContext";
|
|
11
15
|
const RETRY_CHILDREN = (_jsxs(_Fragment, { children: [_jsx(ArrowPathIcon, {}), "Retry"] }));
|
|
@@ -51,7 +55,8 @@ export function ErrorNotice({ reason }) {
|
|
|
51
55
|
const message = getMessage(reason) ?? "Unknown error";
|
|
52
56
|
return (_jsxs(Notice, { status: "error", children: [_jsx("p", { children: message }), _jsx(RetryButton, { small: true, fit: true })] }));
|
|
53
57
|
}
|
|
54
|
-
/** Output a `<Page>` with
|
|
58
|
+
/** Output a `<Page>` with an error `<Card>` for an unknown error reason. */
|
|
55
59
|
export function ErrorPage({ reason }) {
|
|
56
|
-
|
|
60
|
+
const message = getMessage(reason) ?? "Unknown error";
|
|
61
|
+
return (_jsx(Page, { title: "Error", children: _jsx(CenteredLayout, { children: _jsx(Card, { status: "error", children: _jsxs(Subheading, { children: [_jsxs(Flex, { left: true, children: [_jsx(StatusIcon, { status: "error", xlarge: true }), " ", message] }), _jsx(RetryButton, {})] }) }) }) }));
|
|
57
62
|
}
|
package/ui/misc/Catcher.tsx
CHANGED
|
@@ -2,10 +2,14 @@ import { ArrowPathIcon } from "@heroicons/react/24/solid";
|
|
|
2
2
|
import { Component, createContext, type ReactElement, type ReactNode, use } from "react";
|
|
3
3
|
import { getMessage } from "../../util/error.js";
|
|
4
4
|
import type { Callback } from "../../util/function.js";
|
|
5
|
+
import { Card } from "../block/Card.js";
|
|
6
|
+
import { Flex } from "../block/Flex.js";
|
|
7
|
+
import { Subheading } from "../block/Subheading.js";
|
|
5
8
|
import { Button, type ButtonVariants } from "../form/Button.js";
|
|
6
9
|
import { CenteredLayout } from "../layout/CenteredLayout.js";
|
|
7
10
|
import { Notice } from "../notice/Notice.js";
|
|
8
11
|
import { Page } from "../page/Page.js";
|
|
12
|
+
import { StatusIcon } from "./StatusIcon.js";
|
|
9
13
|
|
|
10
14
|
const RetryContext = createContext<Callback | undefined>(undefined);
|
|
11
15
|
RetryContext.displayName = "RetryContext";
|
|
@@ -102,12 +106,20 @@ export function ErrorNotice({ reason }: ErrorNoticeProps): ReactElement {
|
|
|
102
106
|
|
|
103
107
|
export interface ErrorPageProps extends ErrorComponentProps {}
|
|
104
108
|
|
|
105
|
-
/** Output a `<Page>` with
|
|
109
|
+
/** Output a `<Page>` with an error `<Card>` for an unknown error reason. */
|
|
106
110
|
export function ErrorPage({ reason }: ErrorPageProps): ReactElement {
|
|
111
|
+
const message = getMessage(reason) ?? "Unknown error";
|
|
107
112
|
return (
|
|
108
113
|
<Page title="Error">
|
|
109
114
|
<CenteredLayout>
|
|
110
|
-
<
|
|
115
|
+
<Card status="error">
|
|
116
|
+
<Subheading>
|
|
117
|
+
<Flex left>
|
|
118
|
+
<StatusIcon status="error" xlarge /> {message}
|
|
119
|
+
</Flex>
|
|
120
|
+
<RetryButton />
|
|
121
|
+
</Subheading>
|
|
122
|
+
</Card>
|
|
111
123
|
</CenteredLayout>
|
|
112
124
|
</Page>
|
|
113
125
|
);
|
package/util/equal.js
CHANGED
|
@@ -121,7 +121,7 @@ export function isArrayWith(left, right) {
|
|
|
121
121
|
}
|
|
122
122
|
/** Is unknown value `left` not an array or does not include `right`? */
|
|
123
123
|
export function notArrayWith(left, right) {
|
|
124
|
-
return !
|
|
124
|
+
return !isArrayWith(left, right);
|
|
125
125
|
}
|
|
126
126
|
/**
|
|
127
127
|
* Are two objects equal based on their own props?
|
package/util/number.js
CHANGED
|
@@ -26,7 +26,7 @@ export function getNumber(value) {
|
|
|
26
26
|
if (typeof value === "string")
|
|
27
27
|
return getNumber(Number.parseFloat(value.replace(NOT_NUMERIC_REGEXP, "")));
|
|
28
28
|
if (value instanceof Date)
|
|
29
|
-
getNumber(value.getTime());
|
|
29
|
+
return getNumber(value.getTime());
|
|
30
30
|
}
|
|
31
31
|
const NOT_NUMERIC_REGEXP = /[^0-9-.]/g;
|
|
32
32
|
/**
|