panelui-web 0.3.0 → 0.5.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.
@@ -0,0 +1,188 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ /**
4
+ * Menu — a list of actions that opens from a button.
5
+ *
6
+ * ```tsx
7
+ * <Menu>
8
+ * <Menu.Trigger render={<Button variant="outline" />}>Options</Menu.Trigger>
9
+ * <Menu.Content>
10
+ * <Menu.Item onClick={rename}>
11
+ * <PencilIcon /> Rename <Menu.Shortcut>⌘R</Menu.Shortcut>
12
+ * </Menu.Item>
13
+ * <Menu.Sub>
14
+ * <Menu.SubTrigger><ShareIcon /> Share</Menu.SubTrigger>
15
+ * <Menu.SubContent>
16
+ * <Menu.Item>Copy link</Menu.Item>
17
+ * </Menu.SubContent>
18
+ * </Menu.Sub>
19
+ * <Menu.Separator />
20
+ * <Menu.CheckboxItem checked={pinned} onCheckedChange={setPinned}>Pinned</Menu.CheckboxItem>
21
+ * <Menu.Item variant="destructive">Delete</Menu.Item>
22
+ * </Menu.Content>
23
+ * </Menu>
24
+ * ```
25
+ *
26
+ * ## One row shape
27
+ *
28
+ * Every row is the same: an icon, the label, and whatever belongs at the end
29
+ * of it, a shortcut, a badge, a check or the chevron of a submenu. Icons are
30
+ * written as the row's first child and sized by the row, so rows with and
31
+ * without them still line up when every row in a group has one.
32
+ *
33
+ * ## Submenus
34
+ *
35
+ * A submenu opens to the side when its row is hovered or pressed, or with
36
+ * the right arrow key, and closes with the left one. The pointer can cross
37
+ * diagonally to it without closing it on the way.
38
+ *
39
+ * ## Items that stay open
40
+ *
41
+ * Pressing an item closes the menu. A check item or a radio item keeps it
42
+ * open, so several can be switched in a row; pass `closeOnClick` to change
43
+ * either.
44
+ */
45
+ import { Menu as BaseMenu } from '@base-ui/react/menu';
46
+ import { tv } from 'tailwind-variants';
47
+ const menuVariants = tv({
48
+ slots: {
49
+ positioner: 'z-50 outline-none',
50
+ popup: [
51
+ 'min-w-48 origin-(--transform-origin) rounded-xl border bg-popover p-1 text-popover-foreground shadow-lg outline-none',
52
+ 'max-h-(--available-height) overflow-y-auto [scrollbar-color:var(--border)_transparent] [scrollbar-width:thin]',
53
+ 'transition-[opacity,scale] duration-150 ease-out',
54
+ 'data-starting-style:scale-95 data-starting-style:opacity-0 data-ending-style:scale-95 data-ending-style:opacity-0',
55
+ 'motion-reduce:transition-none',
56
+ ],
57
+ item: [
58
+ // The label never wraps; a description beside it is cut short instead.
59
+ 'flex min-h-8 cursor-default items-center gap-2.5 whitespace-nowrap rounded-lg px-2 py-1.5 text-sm outline-none select-none',
60
+ 'data-highlighted:bg-accent data-highlighted:text-accent-foreground',
61
+ 'data-disabled:pointer-events-none data-disabled:opacity-50',
62
+ // A submenu's row stays lit while its submenu is open, so it is clear what it belongs to.
63
+ 'data-popup-open:bg-accent',
64
+ '[&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*=size-])]:size-4',
65
+ '[&>svg:first-child]:text-muted-foreground data-highlighted:[&>svg:first-child]:text-current',
66
+ ],
67
+ end: 'ms-auto flex items-center ps-3',
68
+ check: 'ms-auto flex size-4 items-center justify-center ps-0 text-foreground',
69
+ description: 'min-w-0 truncate text-muted-foreground',
70
+ shortcut: 'ms-auto ps-3 text-xs tracking-wide text-muted-foreground',
71
+ badge: 'ms-auto inline-flex h-5 items-center gap-1 rounded-md bg-muted px-1.5 text-xs font-medium tabular-nums text-muted-foreground [&_svg:not([class*=size-])]:size-3',
72
+ chevron: 'ms-auto text-muted-foreground',
73
+ group: 'flex flex-col',
74
+ groupLabel: 'px-2 pb-1 pt-2 text-xs font-medium text-muted-foreground',
75
+ separator: 'mx-2 my-1 h-px bg-border',
76
+ },
77
+ variants: {
78
+ variant: {
79
+ default: {},
80
+ destructive: {
81
+ item: 'text-destructive data-highlighted:bg-destructive/10 data-highlighted:text-destructive [&>svg:first-child]:text-destructive',
82
+ },
83
+ },
84
+ },
85
+ });
86
+ /** Holds whether the menu is open. Takes `open`/`onOpenChange`, `defaultOpen` and `modal`. */
87
+ function MenuRoot(props) {
88
+ return _jsx(BaseMenu.Root, { ...props });
89
+ }
90
+ MenuRoot.displayName = 'Menu';
91
+ /** The button that opens the menu. `render` draws it as another element, such as a `Button`. */
92
+ function MenuTrigger(props) {
93
+ return _jsx(BaseMenu.Trigger, { "data-slot": "menu-trigger", ...props });
94
+ }
95
+ MenuTrigger.displayName = 'Menu.Trigger';
96
+ /** The list, opened in a layer over the page. It moves to the other side when there is no room. */
97
+ function MenuContent({ side = 'bottom', align = 'start', sideOffset = 6, alignOffset, container, anchor, className, ...props }) {
98
+ const slots = menuVariants();
99
+ return (_jsx(BaseMenu.Portal, { container: container, children: _jsx(BaseMenu.Positioner, { side: side, align: align, sideOffset: sideOffset, alignOffset: alignOffset, anchor: anchor, className: slots.positioner(), children: _jsx(BaseMenu.Popup, { "data-slot": "menu-content", className: slots.popup({ className }), ...props }) }) }));
100
+ }
101
+ MenuContent.displayName = 'Menu.Content';
102
+ /** One action. Put an icon first, then the label, then a `Menu.Shortcut` or `Menu.Badge` if it has one. */
103
+ function MenuItem({ variant, className, ...props }) {
104
+ return _jsx(BaseMenu.Item, { "data-slot": "menu-item", className: menuVariants({ variant }).item({ className }), ...props });
105
+ }
106
+ MenuItem.displayName = 'Menu.Item';
107
+ const Check = () => (_jsx("svg", { "aria-hidden": "true", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.25", strokeLinecap: "round", strokeLinejoin: "round", children: _jsx("path", { d: "M20 6 9 17l-5-5" }) }));
108
+ /** An item that is switched on and off, with a check at its end while on. The menu stays open. */
109
+ function MenuCheckboxItem({ className, children, ...props }) {
110
+ const slots = menuVariants();
111
+ return (_jsxs(BaseMenu.CheckboxItem, { "data-slot": "menu-checkbox-item", className: slots.item({ className }), ...props, children: [children, _jsx("span", { className: slots.check(), children: _jsx(BaseMenu.CheckboxItemIndicator, { className: "flex", children: _jsx(Check, {}) }) })] }));
112
+ }
113
+ MenuCheckboxItem.displayName = 'Menu.CheckboxItem';
114
+ /** Radio items of which one is chosen. Takes `value` and `onValueChange`. */
115
+ function MenuRadioGroup({ className, ...props }) {
116
+ return _jsx(BaseMenu.RadioGroup, { "data-slot": "menu-radio-group", className: menuVariants().group({ className }), ...props });
117
+ }
118
+ MenuRadioGroup.displayName = 'Menu.RadioGroup';
119
+ /** One choice in a radio group, with a check at its end while chosen. */
120
+ function MenuRadioItem({ className, children, ...props }) {
121
+ const slots = menuVariants();
122
+ return (_jsxs(BaseMenu.RadioItem, { "data-slot": "menu-radio-item", className: slots.item({ className }), ...props, children: [children, _jsx("span", { className: slots.check(), children: _jsx(BaseMenu.RadioItemIndicator, { className: "flex", children: _jsx(Check, {}) }) })] }));
123
+ }
124
+ MenuRadioItem.displayName = 'Menu.RadioItem';
125
+ /** A submenu: a `Menu.SubTrigger` row and the `Menu.SubContent` it opens. */
126
+ function MenuSub(props) {
127
+ return _jsx(BaseMenu.SubmenuRoot, { ...props });
128
+ }
129
+ MenuSub.displayName = 'Menu.Sub';
130
+ /** The row that opens a submenu, with a chevron at its end. It opens on hover as well as on press. */
131
+ function MenuSubTrigger({ className, children, end, openOnHover = true, delay = 80, ...props }) {
132
+ const slots = menuVariants();
133
+ return (_jsxs(BaseMenu.SubmenuTrigger, { "data-slot": "menu-sub-trigger", openOnHover: openOnHover, delay: delay, className: slots.item({ className }), ...props, children: [children, _jsxs("span", { className: slots.end(), children: [end, _jsx("svg", { "aria-hidden": "true", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className: slots.chevron({ className: end ? 'ms-2' : undefined }), children: _jsx("path", { d: "m9 18 6-6-6-6" }) })] })] }));
134
+ }
135
+ MenuSubTrigger.displayName = 'Menu.SubTrigger';
136
+ /** The submenu's list, opened beside its row with the first item level with it. */
137
+ function MenuSubContent({ side = 'inline-end', align = 'start', sideOffset = 6, alignOffset = -5, ...props }) {
138
+ return _jsx(MenuContent, { side: side, align: align, sideOffset: sideOffset, alignOffset: alignOffset, ...props });
139
+ }
140
+ MenuSubContent.displayName = 'Menu.SubContent';
141
+ /** Items that belong together, under a `Menu.GroupLabel`. */
142
+ function MenuGroup({ className, ...props }) {
143
+ return _jsx(BaseMenu.Group, { "data-slot": "menu-group", className: menuVariants().group({ className }), ...props });
144
+ }
145
+ MenuGroup.displayName = 'Menu.Group';
146
+ /** The heading of a group. Screen readers announce the group by it. */
147
+ function MenuGroupLabel({ className, ...props }) {
148
+ return _jsx(BaseMenu.GroupLabel, { "data-slot": "menu-group-label", className: menuVariants().groupLabel({ className }), ...props });
149
+ }
150
+ MenuGroupLabel.displayName = 'Menu.GroupLabel';
151
+ /** A rule between groups of items. */
152
+ function MenuSeparator({ className, ...props }) {
153
+ return _jsx("div", { role: "separator", "data-slot": "menu-separator", className: menuVariants().separator({ className }), ...props });
154
+ }
155
+ MenuSeparator.displayName = 'Menu.Separator';
156
+ /** What an item does, in quieter words after its label. */
157
+ function MenuDescription({ className, ...props }) {
158
+ return _jsx("span", { "data-slot": "menu-description", className: menuVariants().description({ className }), ...props });
159
+ }
160
+ MenuDescription.displayName = 'Menu.Description';
161
+ /** The keys for an item, at its end. It only shows them: wire the keys up where the action lives. */
162
+ function MenuShortcut({ className, ...props }) {
163
+ return _jsx("kbd", { "data-slot": "menu-shortcut", className: menuVariants().shortcut({ className }), ...props });
164
+ }
165
+ MenuShortcut.displayName = 'Menu.Shortcut';
166
+ /** A small count or flag at an item's end: how many need attention, or that something is new. */
167
+ function MenuBadge({ className, ...props }) {
168
+ return _jsx("span", { "data-slot": "menu-badge", className: menuVariants().badge({ className }), ...props });
169
+ }
170
+ MenuBadge.displayName = 'Menu.Badge';
171
+ export const Menu = Object.assign(MenuRoot, {
172
+ Trigger: MenuTrigger,
173
+ Content: MenuContent,
174
+ Item: MenuItem,
175
+ CheckboxItem: MenuCheckboxItem,
176
+ RadioGroup: MenuRadioGroup,
177
+ RadioItem: MenuRadioItem,
178
+ Sub: MenuSub,
179
+ SubTrigger: MenuSubTrigger,
180
+ SubContent: MenuSubContent,
181
+ Group: MenuGroup,
182
+ GroupLabel: MenuGroupLabel,
183
+ Separator: MenuSeparator,
184
+ Description: MenuDescription,
185
+ Shortcut: MenuShortcut,
186
+ Badge: MenuBadge,
187
+ });
188
+ export { MenuTrigger, MenuContent, MenuItem, MenuCheckboxItem, MenuRadioGroup, MenuRadioItem, MenuSub, MenuSubTrigger, MenuSubContent, MenuGroup, MenuGroupLabel, MenuSeparator, MenuDescription, MenuShortcut, MenuBadge, };
@@ -0,0 +1,26 @@
1
+ /**
2
+ * The shapes NotFound draws, as grids of cells.
3
+ *
4
+ * A digit is drawn at 5 by 7 and scaled up three times, so every stroke is
5
+ * three cells wide. Kept whole that is a solid pixel digit; reduced to the
6
+ * cells on its edge it is a digit drawn in outline, two rows of dots with a
7
+ * gap between, which is the look a stroked numeral has at this size.
8
+ *
9
+ * Digits are drawn without diagonal strokes. Two blocks that meet only at a
10
+ * corner come apart when reduced to their edges, so a slanted stroke would
11
+ * outline as a stair of separate rings.
12
+ *
13
+ * The icons Scramble turns a digit into are drawn straight onto the same 15 by
14
+ * 21 grid, from circles and lines, so they arrive at exactly the size of the
15
+ * digit they replace and need no scaling of their own.
16
+ */
17
+ /** Cells across one glyph. */
18
+ export declare const GLYPH_COLUMNS = 15;
19
+ /** Cells down one glyph. */
20
+ export declare const GLYPH_ROWS = 21;
21
+ /** A glyph: which cells are on, row by row. */
22
+ export type Glyph = boolean[][];
23
+ /** A digit, solid or as its outline. */
24
+ export declare function digitGlyph(character: string, fill: 'outline' | 'solid'): Glyph | null;
25
+ /** The icons Scramble cycles through, in order. */
26
+ export declare const ICONS: readonly Glyph[];
@@ -0,0 +1,141 @@
1
+ /**
2
+ * The shapes NotFound draws, as grids of cells.
3
+ *
4
+ * A digit is drawn at 5 by 7 and scaled up three times, so every stroke is
5
+ * three cells wide. Kept whole that is a solid pixel digit; reduced to the
6
+ * cells on its edge it is a digit drawn in outline, two rows of dots with a
7
+ * gap between, which is the look a stroked numeral has at this size.
8
+ *
9
+ * Digits are drawn without diagonal strokes. Two blocks that meet only at a
10
+ * corner come apart when reduced to their edges, so a slanted stroke would
11
+ * outline as a stair of separate rings.
12
+ *
13
+ * The icons Scramble turns a digit into are drawn straight onto the same 15 by
14
+ * 21 grid, from circles and lines, so they arrive at exactly the size of the
15
+ * digit they replace and need no scaling of their own.
16
+ */
17
+ /** Cells across one glyph. */
18
+ export const GLYPH_COLUMNS = 15;
19
+ /** Cells down one glyph. */
20
+ export const GLYPH_ROWS = 21;
21
+ const SCALE = 3;
22
+ const DIGITS = {
23
+ '0': ['.###.', '#...#', '#...#', '#...#', '#...#', '#...#', '.###.'],
24
+ '1': ['.##..', '..#..', '..#..', '..#..', '..#..', '..#..', '.###.'],
25
+ '2': ['.###.', '#...#', '....#', '.###.', '#....', '#....', '#####'],
26
+ '3': ['####.', '....#', '....#', '.###.', '....#', '....#', '####.'],
27
+ '4': ['#..#.', '#..#.', '#..#.', '#####', '...#.', '...#.', '...#.'],
28
+ '5': ['#####', '#....', '####.', '....#', '....#', '....#', '####.'],
29
+ '6': ['.###.', '#....', '#....', '####.', '#...#', '#...#', '.###.'],
30
+ '7': ['#####', '....#', '....#', '...##', '...#.', '...#.', '...#.'],
31
+ '8': ['.###.', '#...#', '#...#', '.###.', '#...#', '#...#', '.###.'],
32
+ '9': ['.###.', '#...#', '#...#', '.####', '....#', '....#', '.###.'],
33
+ };
34
+ function empty() {
35
+ return Array.from({ length: GLYPH_ROWS }, () => Array(GLYPH_COLUMNS).fill(false));
36
+ }
37
+ /** A digit as a solid grid of cells, or `null` for a character there is no drawing of. */
38
+ function solidDigit(character) {
39
+ const rows = DIGITS[character];
40
+ if (!rows)
41
+ return null;
42
+ const glyph = empty();
43
+ for (let y = 0; y < GLYPH_ROWS; y += 1) {
44
+ for (let x = 0; x < GLYPH_COLUMNS; x += 1) {
45
+ glyph[y][x] = rows[Math.floor(y / SCALE)][Math.floor(x / SCALE)] === '#';
46
+ }
47
+ }
48
+ return glyph;
49
+ }
50
+ /**
51
+ * Only the cells of a glyph that touch a cell outside it, corners included.
52
+ * Counting corners keeps a diagonal stroke as a continuous band; counting
53
+ * only sides leaves it as a scatter of single dots.
54
+ */
55
+ function outlineOf(glyph) {
56
+ const on = (x, y) => glyph[y]?.[x] === true;
57
+ return glyph.map((row, y) => row.map((cell, x) => {
58
+ if (!cell)
59
+ return false;
60
+ for (let dy = -1; dy <= 1; dy += 1) {
61
+ for (let dx = -1; dx <= 1; dx += 1)
62
+ if (!on(x + dx, y + dy))
63
+ return true;
64
+ }
65
+ return false;
66
+ }));
67
+ }
68
+ /** A digit, solid or as its outline. */
69
+ export function digitGlyph(character, fill) {
70
+ const solid = STROKED[character] ?? solidDigit(character);
71
+ if (!solid)
72
+ return null;
73
+ return fill === 'solid' ? solid : outlineOf(solid);
74
+ }
75
+ /* -------------------------------------------------------------------------- */
76
+ /* Icons */
77
+ /* -------------------------------------------------------------------------- */
78
+ /** Distance from a point to the segment from a to b. */
79
+ function toSegment(px, py, ax, ay, bx, by) {
80
+ const dx = bx - ax;
81
+ const dy = by - ay;
82
+ const t = Math.max(0, Math.min(1, ((px - ax) * dx + (py - ay) * dy) / (dx * dx + dy * dy || 1)));
83
+ return Math.hypot(px - (ax + dx * t), py - (ay + dy * t));
84
+ }
85
+ /** A glyph from a test of each cell's centre. */
86
+ function drawn(test) {
87
+ const glyph = empty();
88
+ for (let y = 0; y < GLYPH_ROWS; y += 1) {
89
+ for (let x = 0; x < GLYPH_COLUMNS; x += 1)
90
+ glyph[y][x] = test(x + 0.5, y + 0.5);
91
+ }
92
+ return glyph;
93
+ }
94
+ /** A ring: cells whose centre is within half a stroke of the circle. */
95
+ const ring = (x, y, cx, cy, r, stroke) => Math.abs(Math.hypot(x - cx, y - cy) - r) <= stroke / 2;
96
+ /** A question mark: a hook, a short stem, a dot. */
97
+ const question = drawn((x, y) => {
98
+ const angle = Math.atan2(y - 6.5, x - 7.5);
99
+ // The hook runs from the left of the circle, over the top, round to the
100
+ // lower right, and stops there so the stem can drop from it.
101
+ const onHook = ring(x, y, 7.5, 6.5, 4.6, 2) && (y < 6.5 || angle < Math.PI * 0.35);
102
+ const stem = toSegment(x, y, 9.4, 10.6, 7.5, 13.2) <= 1 || toSegment(x, y, 7.5, 13.2, 7.5, 14.5) <= 1;
103
+ const dot = Math.hypot(x - 7.5, y - 18) <= 1.3;
104
+ return onHook || stem || dot;
105
+ });
106
+ /** A magnifying glass, the lens high and the handle running to the lower right. */
107
+ const magnifier = drawn((x, y) => ring(x, y, 6.5, 7.5, 4.8, 2) || toSegment(x, y, 10.2, 11.6, 13.4, 17.6) <= 1.2);
108
+ /** A map pin: a ring with a dot in it, narrowing to a point. */
109
+ const pin = drawn((x, y) => {
110
+ const head = ring(x, y, 7.5, 7.5, 5, 2) && y < 10.5;
111
+ // The two sides run from the widest part of the ring down to the point.
112
+ const sides = toSegment(x, y, 3, 9.6, 7.5, 18.6) <= 1 || toSegment(x, y, 12, 9.6, 7.5, 18.6) <= 1;
113
+ const dot = Math.hypot(x - 7.5, y - 7.5) <= 1.5;
114
+ return head || sides || dot;
115
+ });
116
+ /*
117
+ * Digits drawn from strokes rather than scaled up, for the ones whose shape
118
+ * needs a diagonal. Each stroke is three cells wide, the same as a scaled-up
119
+ * pixel, so they sit with the others.
120
+ */
121
+ const stroke = (x, y, ax, ay, bx, by) => toSegment(x, y, ax, ay, bx, by) <= 1.5;
122
+ /** Distance from a point to an ellipse's outline, near enough for drawing at this size. */
123
+ const toEllipse = (x, y, cx, cy, rx, ry) => {
124
+ const nx = (x - cx) / rx;
125
+ const ny = (y - cy) / ry;
126
+ const r = Math.hypot(nx, ny);
127
+ // Scale the radial error back to cells by the radius in that direction.
128
+ const along = r === 0 ? Math.min(rx, ry) : Math.hypot(nx * rx, ny * ry) / r;
129
+ return Math.abs(r - 1) * along;
130
+ };
131
+ const STROKED = {
132
+ // An oval nought, not a box with its corners cut.
133
+ '0': drawn((x, y) => toEllipse(x, y, 7.5, 10.5, 6, 9) <= 1.5),
134
+ // A closed four: the diagonal runs from the top of the stem down to the
135
+ // crossbar, and the crossbar reaches a little past the stem.
136
+ '4': drawn((x, y) => stroke(x, y, 10.5, 1.5, 10.5, 19.5) ||
137
+ stroke(x, y, 10.5, 1.5, 1.5, 14.5) ||
138
+ stroke(x, y, 1.5, 14.5, 13.5, 14.5)),
139
+ };
140
+ /** The icons Scramble cycles through, in order. */
141
+ export const ICONS = [question, magnifier, pin];
@@ -0,0 +1,118 @@
1
+ /**
2
+ * NotFound — an error code drawn in pixels that come apart when the pointer
3
+ * reaches them, with a title, a line of help and a way out underneath.
4
+ *
5
+ * ```tsx
6
+ * <NotFound>
7
+ * <NotFound.Code code="404" effect="scatter" />
8
+ * <NotFound.Title>Nothing lives here</NotFound.Title>
9
+ * <NotFound.Description>The address may be mistyped, or the page has moved.</NotFound.Description>
10
+ * <NotFound.Actions>…</NotFound.Actions>
11
+ * </NotFound>
12
+ * ```
13
+ *
14
+ * ## A grid of cells, not a font
15
+ *
16
+ * Each digit is a small bitmap laid on a grid of cells, and every cell is
17
+ * drawn as a dot or a rounded square of its own size and strength. The sizes
18
+ * and strengths are fixed per cell from a seed, so the code has the uneven,
19
+ * hand-placed texture of pixel art and still looks the same on every render.
20
+ * Nothing depends on a typeface loading.
21
+ *
22
+ * ## One digit reacts at a time
23
+ *
24
+ * The digit nearest the pointer is the one that reacts, and it is put back
25
+ * when the pointer moves away. `effect` chooses how: its cells fly apart
26
+ * (`scatter`), drop out one by one (`dissolve`), or travel into a small icon
27
+ * and back (`scramble`). On a touch screen a tap does the same, and the digit
28
+ * is put back a moment after the finger lifts.
29
+ *
30
+ * ## Two colours, mixed by light
31
+ *
32
+ * Every cell is either the text colour, faint, or the accent. Which one it is
33
+ * depends on how much light falls on it: the highlighted digit is always
34
+ * partly lit, a band of light goes slowly round the whole code, and the
35
+ * pointer lights the cells near it and leaves them fading as it moves on. Each
36
+ * cell turns accent at its own level of light, so a shape in half light is
37
+ * dithered between the two colours, the way pixel art is shaded.
38
+ *
39
+ * ## What it costs
40
+ *
41
+ * The cells are drawn on a canvas, a few hundred of them in about sixteen
42
+ * fills. The band asks for 30 frames a second while the code is on screen and
43
+ * none once it is scrolled away; with `ambient={false}` a frame is only drawn
44
+ * while something is moving. The canvas reads its colours from the theme each
45
+ * time it draws, so a change of theme needs nothing from the page.
46
+ */
47
+ import { type ComponentProps } from 'react';
48
+ /** How the digit near the pointer reacts. */
49
+ export type NotFoundEffect = 'scatter' | 'dissolve' | 'scramble';
50
+ /** Which of the theme's roles the highlighted digit is drawn in. */
51
+ export type NotFoundColor = 'primary' | 'info' | 'success' | 'warning' | 'destructive';
52
+ export interface NotFoundProps extends ComponentProps<'div'> {
53
+ /** Draws a faint grid behind everything, fading out towards the edges. On by default. */
54
+ backdrop?: boolean;
55
+ }
56
+ /** The column the code, the title, the description and the actions stand in, centred. */
57
+ declare function NotFoundRoot({ backdrop, className, children, ...props }: NotFoundProps): import("react").JSX.Element;
58
+ declare namespace NotFoundRoot {
59
+ var displayName: string;
60
+ }
61
+ export interface NotFoundCodeProps extends Omit<ComponentProps<'div'>, 'children' | 'color'> {
62
+ /** The code to draw. Digits only; any other character leaves a blank the width of a digit. */
63
+ code?: string;
64
+ /** How the digit nearest the pointer reacts. */
65
+ effect?: NotFoundEffect;
66
+ /** Digits drawn with solid strokes three cells wide, or as their outline. */
67
+ fill?: 'outline' | 'solid';
68
+ /**
69
+ * A band of light that goes slowly round the code on its own. While it is
70
+ * on, the canvas draws about 30 frames a second when the code is on screen;
71
+ * turn it off and nothing is drawn until the pointer comes near.
72
+ */
73
+ ambient?: boolean;
74
+ /** The theme role of the accent: the highlighted digit, the band of light and the pointer's light. */
75
+ color?: NotFoundColor;
76
+ /**
77
+ * Which digit is lit in `color` even with nothing else lighting it, counting
78
+ * from 0. Defaults to the middle one. `null` lights none of them.
79
+ */
80
+ highlight?: number | null;
81
+ /** What a screen reader says for the code. Defaults to "Error" and the code. */
82
+ label?: string;
83
+ }
84
+ /** The code, drawn in cells on a canvas. Its box keeps the code's shape before anything is drawn. */
85
+ declare function NotFoundCode({ code, effect, fill, color, ambient, highlight, label, className, style, ...props }: NotFoundCodeProps): import("react").JSX.Element;
86
+ declare namespace NotFoundCode {
87
+ var displayName: string;
88
+ }
89
+ export interface NotFoundTitleProps extends ComponentProps<'h1'> {
90
+ /** The heading level. A page that is only this one message wants `h1`. */
91
+ as?: 'h1' | 'h2' | 'h3';
92
+ }
93
+ /** What happened, in a few words. */
94
+ declare function NotFoundTitle({ as: Tag, className, ...props }: NotFoundTitleProps): import("react").JSX.Element;
95
+ declare namespace NotFoundTitle {
96
+ var displayName: string;
97
+ }
98
+ export interface NotFoundDescriptionProps extends ComponentProps<'p'> {
99
+ }
100
+ /** A line on what to do next. */
101
+ declare function NotFoundDescription({ className, ...props }: NotFoundDescriptionProps): import("react").JSX.Element;
102
+ declare namespace NotFoundDescription {
103
+ var displayName: string;
104
+ }
105
+ export interface NotFoundActionsProps extends ComponentProps<'div'> {
106
+ }
107
+ /** The ways out: links or buttons in a row that wraps on a narrow screen. */
108
+ declare function NotFoundActions({ className, ...props }: NotFoundActionsProps): import("react").JSX.Element;
109
+ declare namespace NotFoundActions {
110
+ var displayName: string;
111
+ }
112
+ export declare const NotFound: typeof NotFoundRoot & {
113
+ Code: typeof NotFoundCode;
114
+ Title: typeof NotFoundTitle;
115
+ Description: typeof NotFoundDescription;
116
+ Actions: typeof NotFoundActions;
117
+ };
118
+ export { NotFoundCode, NotFoundTitle, NotFoundDescription, NotFoundActions };