strata-react 0.0.0-stage → 0.1.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/LICENSE +21 -0
- package/README.md +108 -2
- package/dist/components/app-shell.d.ts +29 -0
- package/dist/components/async-button.d.ts +17 -0
- package/dist/components/button.d.ts +16 -0
- package/dist/components/data-table.d.ts +38 -0
- package/dist/components/data-view.d.ts +30 -0
- package/dist/components/done.d.ts +13 -0
- package/dist/components/field.d.ts +29 -0
- package/dist/components/line-chart.d.ts +35 -0
- package/dist/components/metric.d.ts +15 -0
- package/dist/components/page-header.d.ts +13 -0
- package/dist/components/status.d.ts +8 -0
- package/dist/components/tabs.d.ts +15 -0
- package/dist/components/undo.d.ts +18 -0
- package/dist/components/virtual-list.d.ts +11 -0
- package/dist/index.d.ts +18 -0
- package/dist/index.js +1085 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/format.d.ts +28 -0
- package/dist/lib/icon.d.ts +6 -0
- package/dist/lib/utils.d.ts +3 -0
- package/dist/query/index.d.ts +24 -0
- package/dist/query-BXH-Gfj7.js +69 -0
- package/dist/query-BXH-Gfj7.js.map +1 -0
- package/dist/strata.css +181 -0
- package/dist/testing/index.d.ts +64 -0
- package/dist/testing.js +32 -0
- package/dist/testing.js.map +1 -0
- package/package.json +124 -4
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Jonathan Burnhams
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,109 @@
|
|
|
1
|
-
#
|
|
1
|
+
# strata-react
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
An opinionated React + Tailwind v4 framework for building product frontends: components,
|
|
4
|
+
UX patterns and styles, with the testing and review tooling that keeps them honest. Built
|
|
5
|
+
from the research in [jburnhams/ux-research](https://github.com/jburnhams/ux-research)
|
|
6
|
+
(`ui-library/`), which explains every decision.
|
|
7
|
+
|
|
8
|
+
It is opinionated about **structure, behaviour, states, accessibility and the stack**, and
|
|
9
|
+
deliberately silent about **the look**: there is no default theme. Each product generates its
|
|
10
|
+
own palette from its subject, and Tailwind's stock colours, sizes, radii and shadows are
|
|
11
|
+
removed so they cannot be typed by accident.
|
|
12
|
+
|
|
13
|
+
Rules for working in this repo, for people and coding agents: [`CLAUDE.md`](./CLAUDE.md).
|
|
14
|
+
|
|
15
|
+
## The stack
|
|
16
|
+
|
|
17
|
+
Node 26 · React 19 · TypeScript · Vite 8 · Tailwind v4 · class-variance-authority · lucide-react ·
|
|
18
|
+
TanStack Query, Table and Virtual · React Router · Vitest + Testing Library + MSW ·
|
|
19
|
+
Storybook 10 · Playwright Chromium + pixelmatch visual regression · GitHub Actions.
|
|
20
|
+
|
|
21
|
+
## Install
|
|
22
|
+
|
|
23
|
+
Not on npm yet. Install from GitHub (the `prepare` script builds it):
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
npm install github:jburnhams/strata-react react react-dom react-router @tanstack/react-query tailwindcss @tailwindcss/vite
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Set up an app
|
|
30
|
+
|
|
31
|
+
```css
|
|
32
|
+
/* src/app.css */
|
|
33
|
+
@import "tailwindcss";
|
|
34
|
+
@import "strata-react/strata.css"; /* token contract, namespace reset, type steps, base layer */
|
|
35
|
+
@import "./theme.generated.css"; /* your palette: ux-research ux-design/skill/scripts/palette.mjs */
|
|
36
|
+
@import "./theme.css"; /* your status roles, faces and subject colours, with reasons */
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
`theme.css` supplies what Strata names but does not choose: `--success`, `--success-subtle`,
|
|
40
|
+
`--warning…`, `--danger…`, `--info…` (pale tint + dark ink of one hue, ink at least 6:1),
|
|
41
|
+
`--font-sans` / `--font-display` in an `@theme` block, and optionally `--title-weight`,
|
|
42
|
+
`--title-stretch`, `--title-tracking`. See `.storybook/theme.css` for a complete example (the
|
|
43
|
+
kiln-room fixture).
|
|
44
|
+
|
|
45
|
+
```tsx
|
|
46
|
+
import { createBrowserRouter, RouterProvider } from "react-router";
|
|
47
|
+
import { StrataQueryProvider } from "strata-react";
|
|
48
|
+
import "./app.css";
|
|
49
|
+
|
|
50
|
+
const router = createBrowserRouter([{ path: "/", element: <KilnRoom /> }]);
|
|
51
|
+
|
|
52
|
+
createRoot(document.getElementById("root")!).render(
|
|
53
|
+
<StrataQueryProvider>
|
|
54
|
+
<RouterProvider router={router} />
|
|
55
|
+
</StrataQueryProvider>,
|
|
56
|
+
);
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
```tsx
|
|
60
|
+
// One question: "Is my work out of the kiln yet?"
|
|
61
|
+
function KilnRoom() {
|
|
62
|
+
const load = useLoad({ queryKey: ["bookings"], queryFn: () => fetchJson<Booking[]>("/api/bookings") });
|
|
63
|
+
return (
|
|
64
|
+
<AppShell product="Back Lane Clay" nav={nav}>
|
|
65
|
+
<PageHeader title="Kiln room" finding={{ text: "Kiln 2 is safe to open at about 14:50." }} />
|
|
66
|
+
<DataView
|
|
67
|
+
state={load}
|
|
68
|
+
isEmpty={(rows) => rows.length === 0}
|
|
69
|
+
skeleton={<TableSkeleton />}
|
|
70
|
+
empty={<EmptyState title="All three kilns are cold" />}
|
|
71
|
+
>
|
|
72
|
+
{(rows) => <DataTable caption="Work in the kilns" columns={columns} rows={rows} rowKey={(b) => b.id} sortParam="sort" />}
|
|
73
|
+
</DataView>
|
|
74
|
+
</AppShell>
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## Components
|
|
80
|
+
|
|
81
|
+
| Group | Exports | Notes |
|
|
82
|
+
|---|---|---|
|
|
83
|
+
| Actions | `Button`, `IconButton`, `AsyncButton`, `useAsyncAction` | Default is `outline`; one filled button per view; `IconButton` requires `label`; `AsyncButton` keeps one width through idle, pending, done and error |
|
|
84
|
+
| Form | `Field`, `Input`, `Textarea`, `NativeSelect`, `ChoiceGroup` | `Field` wires label, hint, error, `aria-describedby`, `aria-invalid` |
|
|
85
|
+
| Status and figures | `Status`, `Metric`, `MetricStrip` | A status is always a word; figures have no icons or sparklines |
|
|
86
|
+
| Data | `DataView`, `Load<T>`, `EmptyState`, `Skeleton`, `DataTable`, `TableSkeleton`, `VirtualList`, `LineChart` | `DataView` requires skeleton and empty; tables and lists virtualise from 100 rows; charts label values directly |
|
|
87
|
+
| Data loading | `StrataQueryProvider`, `createQueryClient`, `useLoad`, `toLoad`, `fetchJson`, `HttpError` | TanStack Query in, `Load<T>` out |
|
|
88
|
+
| Page and navigation | `AppShell`, `SideNav`, `Breadcrumb`, `Tabs`, `PageHeader` | React Router; tab and sort state in the URL |
|
|
89
|
+
| Feedback | `UndoBar`, `useUndoable`, `Done` | Undo before confirm; `Done` is the one celebration, and it must say what happens next |
|
|
90
|
+
| Helpers | `cn`, `format`, `createFormat` | `cn` knows `type-*`; all numbers and dates through `Intl` |
|
|
91
|
+
| Testing | `strata-react/testing`: `renderWithStrata`, `createTestQueryClient` | Router + query client around Testing Library's `render` |
|
|
92
|
+
|
|
93
|
+
Specs for the components not built yet (dialogs, sheets, menus, toasts, command menu, prose)
|
|
94
|
+
are in ux-research `ui-library/components.md`.
|
|
95
|
+
|
|
96
|
+
## Develop
|
|
97
|
+
|
|
98
|
+
```bash
|
|
99
|
+
npm install
|
|
100
|
+
npm run storybook # http://localhost:6006
|
|
101
|
+
npm test # typecheck + unit tests
|
|
102
|
+
npm run test:integration # build, then check the package and its CSS as an app sees them
|
|
103
|
+
npm run test:visual # screenshot every story (light, dark) and compare with baselines
|
|
104
|
+
npm run test:visual:update # accept the current screenshots as baselines
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
## Licence
|
|
108
|
+
|
|
109
|
+
MIT
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { IconComponent } from '../lib/icon';
|
|
2
|
+
import * as React from "react";
|
|
3
|
+
export type NavItem = {
|
|
4
|
+
to: string;
|
|
5
|
+
label: string;
|
|
6
|
+
icon?: IconComponent;
|
|
7
|
+
/** Match only this exact path, for an index route such as "/". */
|
|
8
|
+
end?: boolean;
|
|
9
|
+
};
|
|
10
|
+
export declare function AppShell({ product, nav, navLabel, topBar, children, className, }: {
|
|
11
|
+
/** The product's name or mark, linking home. */
|
|
12
|
+
product: React.ReactNode;
|
|
13
|
+
nav: NavItem[];
|
|
14
|
+
navLabel?: string;
|
|
15
|
+
topBar?: React.ReactNode;
|
|
16
|
+
children: React.ReactNode;
|
|
17
|
+
className?: string;
|
|
18
|
+
}): React.JSX.Element;
|
|
19
|
+
export declare function SideNav({ items, label, className }: {
|
|
20
|
+
items: NavItem[];
|
|
21
|
+
label: string;
|
|
22
|
+
className?: string;
|
|
23
|
+
}): React.JSX.Element;
|
|
24
|
+
export declare function Breadcrumb({ items }: {
|
|
25
|
+
items: {
|
|
26
|
+
label: string;
|
|
27
|
+
to?: string;
|
|
28
|
+
}[];
|
|
29
|
+
}): React.JSX.Element;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { ButtonProps } from './button';
|
|
2
|
+
import * as React from "react";
|
|
3
|
+
export type Phase = "idle" | "pending" | "done" | "error";
|
|
4
|
+
export type AsyncLabels = {
|
|
5
|
+
idle: string;
|
|
6
|
+
/** Ends in an ellipsis: "Saving…", "Booking…". */
|
|
7
|
+
pending: string;
|
|
8
|
+
/** The same verb, finished: "Saved", "Booked". */
|
|
9
|
+
done: string;
|
|
10
|
+
error?: string;
|
|
11
|
+
};
|
|
12
|
+
export declare function AsyncButton({ phase, labels, disabled, ...props }: Omit<ButtonProps, "children"> & {
|
|
13
|
+
phase: Phase;
|
|
14
|
+
labels: AsyncLabels;
|
|
15
|
+
}): React.JSX.Element;
|
|
16
|
+
/** Runs an async action and reports its phase. `done` returns to `idle` after `settleMs`. */
|
|
17
|
+
export declare function useAsyncAction<A extends unknown[]>(action: (...args: A) => Promise<void>, settleMs?: number): readonly [Phase, (...args: A) => Promise<void>];
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { VariantProps } from 'class-variance-authority';
|
|
2
|
+
import { IconComponent } from '../lib/icon';
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
export declare const buttonVariants: (props?: ({
|
|
5
|
+
variant?: "link" | "default" | "outline" | "secondary" | "ghost" | "destructive" | null | undefined;
|
|
6
|
+
size?: "default" | "sm" | "icon" | null | undefined;
|
|
7
|
+
} & import('class-variance-authority/types').ClassProp) | undefined) => string;
|
|
8
|
+
export type ButtonProps = React.ComponentProps<"button"> & VariantProps<typeof buttonVariants> & {
|
|
9
|
+
asChild?: boolean;
|
|
10
|
+
};
|
|
11
|
+
export declare function Button({ className, variant, size, asChild, type, ...props }: ButtonProps): React.JSX.Element;
|
|
12
|
+
export type IconButtonProps = Omit<ButtonProps, "size" | "children" | "aria-label"> & {
|
|
13
|
+
icon: IconComponent;
|
|
14
|
+
label: string;
|
|
15
|
+
};
|
|
16
|
+
export declare function IconButton({ icon: Icon, label, variant, ...props }: IconButtonProps): React.JSX.Element;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
export type Column<T> = {
|
|
3
|
+
key: string;
|
|
4
|
+
header: string;
|
|
5
|
+
cell: (row: T) => React.ReactNode;
|
|
6
|
+
/** Right-aligns and sets tabular figures. Use for every quantity. */
|
|
7
|
+
numeric?: boolean;
|
|
8
|
+
sort?: (a: T, b: T) => number;
|
|
9
|
+
/** Cells that may hold long text get truncated rather than stretching the table. */
|
|
10
|
+
truncate?: boolean;
|
|
11
|
+
className?: string;
|
|
12
|
+
};
|
|
13
|
+
export type Sort = {
|
|
14
|
+
key: string;
|
|
15
|
+
dir: "ascending" | "descending";
|
|
16
|
+
};
|
|
17
|
+
export type DataTableProps<T> = {
|
|
18
|
+
/** Names the table for screen readers and labels the scroll region. */
|
|
19
|
+
caption: string;
|
|
20
|
+
columns: Column<T>[];
|
|
21
|
+
rows: T[];
|
|
22
|
+
rowKey: (row: T) => string;
|
|
23
|
+
initialSort?: Sort;
|
|
24
|
+
/** Keep the sort in this URL search parameter. Needs a React Router context. */
|
|
25
|
+
sortParam?: string;
|
|
26
|
+
/** Row count from which only the visible rows render. Default 100. */
|
|
27
|
+
virtualizeFrom?: number;
|
|
28
|
+
/** Height of the scroll region once virtualised, in px. Default 480. */
|
|
29
|
+
maxHeight?: number;
|
|
30
|
+
className?: string;
|
|
31
|
+
};
|
|
32
|
+
export declare function parseSort(value: string | null): Sort | undefined;
|
|
33
|
+
export declare function serialiseSort(sort: Sort): string;
|
|
34
|
+
export declare function DataTable<T>(props: DataTableProps<T>): React.JSX.Element;
|
|
35
|
+
export declare function TableSkeleton({ rows, columns }: {
|
|
36
|
+
rows?: number;
|
|
37
|
+
columns?: number;
|
|
38
|
+
}): React.JSX.Element;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
export type Load<T> = {
|
|
3
|
+
status: "loading";
|
|
4
|
+
} | {
|
|
5
|
+
status: "error";
|
|
6
|
+
message: string;
|
|
7
|
+
retry: () => void;
|
|
8
|
+
} | {
|
|
9
|
+
status: "ready";
|
|
10
|
+
data: T;
|
|
11
|
+
};
|
|
12
|
+
export declare function DataView<T>({ state, isEmpty, skeleton, empty, children, className, }: {
|
|
13
|
+
state: Load<T>;
|
|
14
|
+
/** When the loaded data counts as empty. For arrays, `(rows) => rows.length === 0`. */
|
|
15
|
+
isEmpty: (data: T) => boolean;
|
|
16
|
+
/** Mirrors the final layout, so nothing jumps when data arrives. Not a centred spinner. */
|
|
17
|
+
skeleton: React.ReactNode;
|
|
18
|
+
/** What goes here and how to add it. An <EmptyState>, not an illustration. */
|
|
19
|
+
empty: React.ReactNode;
|
|
20
|
+
children: (data: T) => React.ReactNode;
|
|
21
|
+
className?: string;
|
|
22
|
+
}): React.JSX.Element;
|
|
23
|
+
export declare function EmptyState({ title, children, action }: {
|
|
24
|
+
title: string;
|
|
25
|
+
children?: React.ReactNode;
|
|
26
|
+
action?: React.ReactNode;
|
|
27
|
+
}): React.JSX.Element;
|
|
28
|
+
export declare function Skeleton({ className }: {
|
|
29
|
+
className?: string;
|
|
30
|
+
}): React.JSX.Element;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
export declare function Done({ title, next, figure, actions, focusOnMount, className, }: {
|
|
3
|
+
/** What happened, past tense, with its object. */
|
|
4
|
+
title: string;
|
|
5
|
+
/** What the person does now, or what happens next and when. */
|
|
6
|
+
next: React.ReactNode;
|
|
7
|
+
/** A drawing of the result made from the person's data in the subject's own terms. */
|
|
8
|
+
figure?: React.ReactNode;
|
|
9
|
+
actions?: React.ReactNode;
|
|
10
|
+
/** Off only where the result is not the page's subject (the state-matrix page). */
|
|
11
|
+
focusOnMount?: boolean;
|
|
12
|
+
className?: string;
|
|
13
|
+
}): React.JSX.Element;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
export type FieldProps = {
|
|
3
|
+
label: string;
|
|
4
|
+
/** The control's id, when the page needs a known one (an anchor, a test). Generated otherwise. */
|
|
5
|
+
id?: string;
|
|
6
|
+
/** What the person needs to know before typing, such as limits or units. */
|
|
7
|
+
hint?: React.ReactNode;
|
|
8
|
+
/** What went wrong and how to fix it, in one sentence. Shown under the control, never in a toast. */
|
|
9
|
+
error?: string;
|
|
10
|
+
children: React.ReactNode;
|
|
11
|
+
className?: string;
|
|
12
|
+
};
|
|
13
|
+
export declare function Field({ label, id: idProp, hint, error, children, className }: FieldProps): React.JSX.Element;
|
|
14
|
+
export declare function Input({ className, id, ...props }: React.ComponentProps<"input">): React.JSX.Element;
|
|
15
|
+
export declare function Textarea({ className, id, ...props }: React.ComponentProps<"textarea">): React.JSX.Element;
|
|
16
|
+
export declare function NativeSelect({ className, id, ...props }: React.ComponentProps<"select">): React.JSX.Element;
|
|
17
|
+
export type Choice = {
|
|
18
|
+
value: string;
|
|
19
|
+
label: string;
|
|
20
|
+
detail?: string;
|
|
21
|
+
};
|
|
22
|
+
export declare function ChoiceGroup({ legend, name, choices, value, onChange, error, }: {
|
|
23
|
+
legend: string;
|
|
24
|
+
name: string;
|
|
25
|
+
choices: Choice[];
|
|
26
|
+
value: string;
|
|
27
|
+
onChange: (value: string) => void;
|
|
28
|
+
error?: string;
|
|
29
|
+
}): React.JSX.Element;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
export type Point = {
|
|
3
|
+
x: number;
|
|
4
|
+
y: number;
|
|
5
|
+
};
|
|
6
|
+
export type LineChartProps = {
|
|
7
|
+
/** What the chart shows, as a sentence. Used as the accessible name and the table caption. */
|
|
8
|
+
label: string;
|
|
9
|
+
series: Point[];
|
|
10
|
+
seriesLabel: string;
|
|
11
|
+
planned?: Point[];
|
|
12
|
+
plannedLabel?: string;
|
|
13
|
+
band?: {
|
|
14
|
+
below: number;
|
|
15
|
+
label: string;
|
|
16
|
+
};
|
|
17
|
+
formatX: (x: number) => string;
|
|
18
|
+
formatY: (y: number) => string;
|
|
19
|
+
xTicks: number[];
|
|
20
|
+
/** CSS colour for the measured series. Defaults to the first chart token. */
|
|
21
|
+
colour?: string;
|
|
22
|
+
/** Colour the measured series by value: each stop applies from `at` upwards, blending to the
|
|
23
|
+
* next. Only where the colour means something in the subject; record the reason in DESIGN.md. */
|
|
24
|
+
colourScale?: {
|
|
25
|
+
at: number;
|
|
26
|
+
colour: string;
|
|
27
|
+
}[];
|
|
28
|
+
/** Says what the colours mean, shown beside the legend swatch when `colourScale` is set. */
|
|
29
|
+
colourScaleLabel?: string;
|
|
30
|
+
height?: number;
|
|
31
|
+
className?: string;
|
|
32
|
+
};
|
|
33
|
+
/** Four gridline steps of a size people write on a scale (1, 2, 2.5, 4 or 5 × 10ⁿ), ending above the max. */
|
|
34
|
+
export declare function niceMax(max: number): number;
|
|
35
|
+
export declare function LineChart({ label, series, seriesLabel, planned, plannedLabel, band, formatX, formatY, xTicks, colour, colourScale, colourScaleLabel, height, className, }: LineChartProps): React.JSX.Element;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
export declare function MetricStrip({ label, children, className }: {
|
|
3
|
+
label: string;
|
|
4
|
+
children: React.ReactNode;
|
|
5
|
+
className?: string;
|
|
6
|
+
}): React.JSX.Element;
|
|
7
|
+
export type MetricProps = {
|
|
8
|
+
label: string;
|
|
9
|
+
/** Already formatted. Ink colour, 28px, tabular figures: never the brand colour, never 48px. */
|
|
10
|
+
value: string;
|
|
11
|
+
unit?: string;
|
|
12
|
+
/** One sentence: the change in words with its window, or what the figure means right now. */
|
|
13
|
+
note?: React.ReactNode;
|
|
14
|
+
};
|
|
15
|
+
export declare function Metric({ label, value, unit, note }: MetricProps): React.JSX.Element;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
export declare function PageHeader({ title, scope, actions, finding, as: Heading, }: {
|
|
3
|
+
title: string;
|
|
4
|
+
/** The title's element: h1 on a screen, h2 where the header is shown inside another page. */
|
|
5
|
+
as?: "h1" | "h2";
|
|
6
|
+
/** What the screen covers: a date, a range, a filter. */
|
|
7
|
+
scope?: React.ReactNode;
|
|
8
|
+
actions?: React.ReactNode;
|
|
9
|
+
finding?: {
|
|
10
|
+
text: React.ReactNode;
|
|
11
|
+
action?: React.ReactNode;
|
|
12
|
+
};
|
|
13
|
+
}): React.JSX.Element;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { IconComponent } from '../lib/icon';
|
|
2
|
+
export type Tone = "neutral" | "info" | "success" | "warning" | "danger";
|
|
3
|
+
export declare function Status({ tone, icon: Icon, children, className, }: {
|
|
4
|
+
tone: Tone;
|
|
5
|
+
icon?: IconComponent;
|
|
6
|
+
children: string;
|
|
7
|
+
className?: string;
|
|
8
|
+
}): import('react').JSX.Element;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
export type TabItem = {
|
|
3
|
+
value: string;
|
|
4
|
+
label: string;
|
|
5
|
+
};
|
|
6
|
+
export declare function Tabs({ param, items, label, children, className, }: {
|
|
7
|
+
/** The search parameter that holds the selected tab. */
|
|
8
|
+
param: string;
|
|
9
|
+
items: TabItem[];
|
|
10
|
+
/** Names the tab list for screen readers. */
|
|
11
|
+
label: string;
|
|
12
|
+
/** Renders the selected panel. */
|
|
13
|
+
children: (value: string) => React.ReactNode;
|
|
14
|
+
className?: string;
|
|
15
|
+
}): React.JSX.Element;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
type Pending = {
|
|
3
|
+
id: string;
|
|
4
|
+
message: string;
|
|
5
|
+
commit: () => void;
|
|
6
|
+
};
|
|
7
|
+
export declare function useUndoable(windowMs?: number): {
|
|
8
|
+
pending: Pending | null;
|
|
9
|
+
schedule: (next: Pending) => void;
|
|
10
|
+
undo: () => void;
|
|
11
|
+
};
|
|
12
|
+
export declare function UndoBar({ pending, onUndo, inline, }: {
|
|
13
|
+
pending: Pending | null;
|
|
14
|
+
onUndo: () => void;
|
|
15
|
+
/** Render in the flow instead of fixed to the viewport; for the state-matrix page. */
|
|
16
|
+
inline?: boolean;
|
|
17
|
+
}): React.JSX.Element;
|
|
18
|
+
export {};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
export declare function VirtualList<T>({ label, items, itemKey, children, estimateSize, height, virtualizeFrom, className, }: {
|
|
3
|
+
label: string;
|
|
4
|
+
items: T[];
|
|
5
|
+
itemKey: (item: T) => string;
|
|
6
|
+
children: (item: T, index: number) => React.ReactNode;
|
|
7
|
+
estimateSize?: number;
|
|
8
|
+
height?: number;
|
|
9
|
+
virtualizeFrom?: number;
|
|
10
|
+
className?: string;
|
|
11
|
+
}): React.JSX.Element;
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export { cn, typeSteps } from './lib/utils';
|
|
2
|
+
export { createFormat, format, NBSP, type Format } from './lib/format';
|
|
3
|
+
export type { IconComponent } from './lib/icon';
|
|
4
|
+
export { Button, IconButton, buttonVariants, type ButtonProps, type IconButtonProps } from './components/button';
|
|
5
|
+
export { AsyncButton, useAsyncAction, type Phase, type AsyncLabels } from './components/async-button';
|
|
6
|
+
export { Field, Input, Textarea, NativeSelect, ChoiceGroup, type FieldProps, type Choice } from './components/field';
|
|
7
|
+
export { Status, type Tone } from './components/status';
|
|
8
|
+
export { Metric, MetricStrip, type MetricProps } from './components/metric';
|
|
9
|
+
export { DataView, EmptyState, Skeleton, type Load } from './components/data-view';
|
|
10
|
+
export { DataTable, TableSkeleton, parseSort, serialiseSort, type Column, type Sort, type DataTableProps } from './components/data-table';
|
|
11
|
+
export { VirtualList } from './components/virtual-list';
|
|
12
|
+
export { LineChart, type LineChartProps, type Point } from './components/line-chart';
|
|
13
|
+
export { PageHeader } from './components/page-header';
|
|
14
|
+
export { UndoBar, useUndoable } from './components/undo';
|
|
15
|
+
export { Done } from './components/done';
|
|
16
|
+
export { AppShell, SideNav, Breadcrumb, type NavItem } from './components/app-shell';
|
|
17
|
+
export { Tabs, type TabItem } from './components/tabs';
|
|
18
|
+
export { StrataQueryProvider, createQueryClient, toLoad, useLoad, fetchJson, HttpError, describeHttpError, type DescribeError, } from './query';
|