@misoto22/design 0.3.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/LICENSE +21 -0
- package/dist/components/Accordion/Accordion.d.ts +34 -0
- package/dist/components/Accordion/Accordion.js +37 -0
- package/dist/components/Accordion/Accordion.js.map +1 -0
- package/dist/components/Alert/Alert.d.ts +32 -0
- package/dist/components/Alert/Alert.js +49 -0
- package/dist/components/Alert/Alert.js.map +1 -0
- package/dist/components/AppShell/AppShell.d.ts +38 -0
- package/dist/components/AppShell/AppShell.js +92 -0
- package/dist/components/AppShell/AppShell.js.map +1 -0
- package/dist/components/Avatar/Avatar.d.ts +29 -0
- package/dist/components/Avatar/Avatar.js +39 -0
- package/dist/components/Avatar/Avatar.js.map +1 -0
- package/dist/components/Badge/Badge.d.ts +30 -0
- package/dist/components/Badge/Badge.js +29 -0
- package/dist/components/Badge/Badge.js.map +1 -0
- package/dist/components/Breadcrumb/Breadcrumb.d.ts +31 -0
- package/dist/components/Breadcrumb/Breadcrumb.js +31 -0
- package/dist/components/Breadcrumb/Breadcrumb.js.map +1 -0
- package/dist/components/Button/Button.d.ts +70 -0
- package/dist/components/Button/Button.js +81 -0
- package/dist/components/Button/Button.js.map +1 -0
- package/dist/components/Calendar/Calendar.d.ts +28 -0
- package/dist/components/Calendar/Calendar.js +87 -0
- package/dist/components/Calendar/Calendar.js.map +1 -0
- package/dist/components/Card/Card.d.ts +49 -0
- package/dist/components/Card/Card.js +64 -0
- package/dist/components/Card/Card.js.map +1 -0
- package/dist/components/Checkbox/Checkbox.d.ts +23 -0
- package/dist/components/Checkbox/Checkbox.js +24 -0
- package/dist/components/Checkbox/Checkbox.js.map +1 -0
- package/dist/components/Collapsible/Collapsible.d.ts +31 -0
- package/dist/components/Collapsible/Collapsible.js +35 -0
- package/dist/components/Collapsible/Collapsible.js.map +1 -0
- package/dist/components/Combobox/Combobox.d.ts +63 -0
- package/dist/components/Combobox/Combobox.js +151 -0
- package/dist/components/Combobox/Combobox.js.map +1 -0
- package/dist/components/Command/Command.d.ts +98 -0
- package/dist/components/Command/Command.js +166 -0
- package/dist/components/Command/Command.js.map +1 -0
- package/dist/components/ContextMenu/ContextMenu.d.ts +38 -0
- package/dist/components/ContextMenu/ContextMenu.js +79 -0
- package/dist/components/ContextMenu/ContextMenu.js.map +1 -0
- package/dist/components/DatePicker/DatePicker.d.ts +98 -0
- package/dist/components/DatePicker/DatePicker.js +214 -0
- package/dist/components/DatePicker/DatePicker.js.map +1 -0
- package/dist/components/Dialog/Dialog.d.ts +48 -0
- package/dist/components/Dialog/Dialog.js +72 -0
- package/dist/components/Dialog/Dialog.js.map +1 -0
- package/dist/components/DropdownMenu/DropdownMenu.d.ts +49 -0
- package/dist/components/DropdownMenu/DropdownMenu.js +89 -0
- package/dist/components/DropdownMenu/DropdownMenu.js.map +1 -0
- package/dist/components/EmptyState/EmptyState.d.ts +31 -0
- package/dist/components/EmptyState/EmptyState.js +30 -0
- package/dist/components/EmptyState/EmptyState.js.map +1 -0
- package/dist/components/ErrorState/ErrorState.d.ts +34 -0
- package/dist/components/ErrorState/ErrorState.js +32 -0
- package/dist/components/ErrorState/ErrorState.js.map +1 -0
- package/dist/components/Field/Field.d.ts +44 -0
- package/dist/components/Field/Field.js +54 -0
- package/dist/components/Field/Field.js.map +1 -0
- package/dist/components/FigureBand/FigureBand.d.ts +51 -0
- package/dist/components/FigureBand/FigureBand.js +52 -0
- package/dist/components/FigureBand/FigureBand.js.map +1 -0
- package/dist/components/FloatingIconButton/FloatingIconButton.d.ts +29 -0
- package/dist/components/FloatingIconButton/FloatingIconButton.js +32 -0
- package/dist/components/FloatingIconButton/FloatingIconButton.js.map +1 -0
- package/dist/components/Input/Input.d.ts +22 -0
- package/dist/components/Input/Input.js +21 -0
- package/dist/components/Input/Input.js.map +1 -0
- package/dist/components/Kbd/Kbd.d.ts +20 -0
- package/dist/components/Kbd/Kbd.js +21 -0
- package/dist/components/Kbd/Kbd.js.map +1 -0
- package/dist/components/LinkArrow/LinkArrow.d.ts +24 -0
- package/dist/components/LinkArrow/LinkArrow.js +24 -0
- package/dist/components/LinkArrow/LinkArrow.js.map +1 -0
- package/dist/components/NativeSelect/NativeSelect.d.ts +27 -0
- package/dist/components/NativeSelect/NativeSelect.js +46 -0
- package/dist/components/NativeSelect/NativeSelect.js.map +1 -0
- package/dist/components/NavItem/NavItem.d.ts +39 -0
- package/dist/components/NavItem/NavItem.js +31 -0
- package/dist/components/NavItem/NavItem.js.map +1 -0
- package/dist/components/Pagination/Pagination.d.ts +44 -0
- package/dist/components/Pagination/Pagination.js +109 -0
- package/dist/components/Pagination/Pagination.js.map +1 -0
- package/dist/components/Popover/Popover.d.ts +39 -0
- package/dist/components/Popover/Popover.js +57 -0
- package/dist/components/Popover/Popover.js.map +1 -0
- package/dist/components/Progress/Progress.d.ts +31 -0
- package/dist/components/Progress/Progress.js +45 -0
- package/dist/components/Progress/Progress.js.map +1 -0
- package/dist/components/RadioGroup/RadioGroup.d.ts +43 -0
- package/dist/components/RadioGroup/RadioGroup.js +43 -0
- package/dist/components/RadioGroup/RadioGroup.js.map +1 -0
- package/dist/components/ScrollArea/ScrollArea.d.ts +31 -0
- package/dist/components/ScrollArea/ScrollArea.js +54 -0
- package/dist/components/ScrollArea/ScrollArea.js.map +1 -0
- package/dist/components/SearchableMenu/SearchableMenu.d.ts +59 -0
- package/dist/components/SearchableMenu/SearchableMenu.js +79 -0
- package/dist/components/SearchableMenu/SearchableMenu.js.map +1 -0
- package/dist/components/Select/Select.d.ts +71 -0
- package/dist/components/Select/Select.js +114 -0
- package/dist/components/Select/Select.js.map +1 -0
- package/dist/components/Separator/Separator.d.ts +30 -0
- package/dist/components/Separator/Separator.js +35 -0
- package/dist/components/Separator/Separator.js.map +1 -0
- package/dist/components/Sheet/Sheet.d.ts +38 -0
- package/dist/components/Sheet/Sheet.js +68 -0
- package/dist/components/Sheet/Sheet.js.map +1 -0
- package/dist/components/Skeleton/Skeleton.d.ts +63 -0
- package/dist/components/Skeleton/Skeleton.js +44 -0
- package/dist/components/Skeleton/Skeleton.js.map +1 -0
- package/dist/components/Slider/Slider.d.ts +35 -0
- package/dist/components/Slider/Slider.js +52 -0
- package/dist/components/Slider/Slider.js.map +1 -0
- package/dist/components/Spinner/Spinner.d.ts +50 -0
- package/dist/components/Spinner/Spinner.js +48 -0
- package/dist/components/Spinner/Spinner.js.map +1 -0
- package/dist/components/StatusDot/StatusDot.d.ts +29 -0
- package/dist/components/StatusDot/StatusDot.js +44 -0
- package/dist/components/StatusDot/StatusDot.js.map +1 -0
- package/dist/components/StatusPill/StatusPill.d.ts +25 -0
- package/dist/components/StatusPill/StatusPill.js +31 -0
- package/dist/components/StatusPill/StatusPill.js.map +1 -0
- package/dist/components/Switch/Switch.d.ts +23 -0
- package/dist/components/Switch/Switch.js +23 -0
- package/dist/components/Switch/Switch.js.map +1 -0
- package/dist/components/Table/Table.d.ts +96 -0
- package/dist/components/Table/Table.js +136 -0
- package/dist/components/Table/Table.js.map +1 -0
- package/dist/components/Tabs/Tabs.d.ts +35 -0
- package/dist/components/Tabs/Tabs.js +48 -0
- package/dist/components/Tabs/Tabs.js.map +1 -0
- package/dist/components/Tag/Tag.d.ts +28 -0
- package/dist/components/Tag/Tag.js +22 -0
- package/dist/components/Tag/Tag.js.map +1 -0
- package/dist/components/Textarea/Textarea.d.ts +20 -0
- package/dist/components/Textarea/Textarea.js +32 -0
- package/dist/components/Textarea/Textarea.js.map +1 -0
- package/dist/components/Toast/Toast.d.ts +24 -0
- package/dist/components/Toast/Toast.js +41 -0
- package/dist/components/Toast/Toast.js.map +1 -0
- package/dist/components/ToggleGroup/ToggleGroup.d.ts +41 -0
- package/dist/components/ToggleGroup/ToggleGroup.js +78 -0
- package/dist/components/ToggleGroup/ToggleGroup.js.map +1 -0
- package/dist/components/Tooltip/Tooltip.d.ts +43 -0
- package/dist/components/Tooltip/Tooltip.js +40 -0
- package/dist/components/Tooltip/Tooltip.js.map +1 -0
- package/dist/fonts/hanken-grotesk-300.woff2 +0 -0
- package/dist/fonts/hanken-grotesk-400.woff2 +0 -0
- package/dist/fonts/hanken-grotesk-500.woff2 +0 -0
- package/dist/fonts/hanken-grotesk-600.woff2 +0 -0
- package/dist/fonts/hanken-grotesk-700.woff2 +0 -0
- package/dist/fonts/ibm-plex-mono-400.woff2 +0 -0
- package/dist/fonts/ibm-plex-mono-500.woff2 +0 -0
- package/dist/fonts/ibm-plex-mono-600.woff2 +0 -0
- package/dist/fonts/newsreader-300.woff2 +0 -0
- package/dist/fonts/newsreader-400.woff2 +0 -0
- package/dist/fonts/newsreader-500.woff2 +0 -0
- package/dist/fonts/newsreader-600.woff2 +0 -0
- package/dist/fonts.css +96 -0
- package/dist/index.d.ts +77 -0
- package/dist/index.js +63 -0
- package/dist/index.js.map +1 -0
- package/dist/keyframes.css +212 -0
- package/dist/lib/cn.d.ts +21 -0
- package/dist/lib/cn.js +9 -0
- package/dist/lib/cn.js.map +1 -0
- package/dist/lib/control.d.ts +31 -0
- package/dist/lib/control.js +14 -0
- package/dist/lib/control.js.map +1 -0
- package/dist/lib/index.d.ts +4 -0
- package/dist/lib/index.js +11 -0
- package/dist/lib/index.js.map +1 -0
- package/dist/lib/overlay-container.d.ts +38 -0
- package/dist/lib/overlay-container.js +15 -0
- package/dist/lib/overlay-container.js.map +1 -0
- package/dist/lib/useSelectionIndicator.d.ts +29 -0
- package/dist/lib/useSelectionIndicator.js +45 -0
- package/dist/lib/useSelectionIndicator.js.map +1 -0
- package/dist/semantic.css +127 -0
- package/dist/styles.css +98 -0
- package/dist/themes.css +115 -0
- package/dist/tokens/brand.d.ts +47 -0
- package/dist/tokens/brand.js +38 -0
- package/dist/tokens/brand.js.map +1 -0
- package/dist/tokens.css +323 -0
- package/dist/tokens.d.ts +153 -0
- package/dist/tokens.js +766 -0
- package/dist/tokens.json +765 -0
- package/package.json +130 -0
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import * as react from 'react';
|
|
2
|
+
import { HTMLAttributes, ReactNode } from 'react';
|
|
3
|
+
|
|
4
|
+
type SkeletonProps = HTMLAttributes<HTMLDivElement>;
|
|
5
|
+
/**
|
|
6
|
+
* The fill every skeleton part is drawn from: `--stone`, and nothing else.
|
|
7
|
+
*
|
|
8
|
+
* Deliberately NOT a shimmer. A shimmer sweeps a highlight across the shape,
|
|
9
|
+
* which implies a light source; the White Reset has none, so the sweep reads as
|
|
10
|
+
* a bug rather than as loading. The whole page breathes together instead —
|
|
11
|
+
* see `SkeletonPage`, which owns the single pulse.
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* <Skeleton className="h-3 w-40 rounded-(--radius-pill)" />
|
|
15
|
+
*/
|
|
16
|
+
declare function Skeleton({ className, ...rest }: SkeletonProps): react.JSX.Element;
|
|
17
|
+
/** A line of type. Height is the line's; width is how far it runs. */
|
|
18
|
+
declare function SkeletonLine({ className, ...rest }: SkeletonProps): react.JSX.Element;
|
|
19
|
+
/** A block: an image mount, a plate, a chart. */
|
|
20
|
+
declare function SkeletonBlock({ className, ...rest }: SkeletonProps): react.JSX.Element;
|
|
21
|
+
/** A circle: an avatar, a marker. */
|
|
22
|
+
declare function SkeletonCircle({ className, ...rest }: SkeletonProps): react.JSX.Element;
|
|
23
|
+
interface SkeletonTextProps extends SkeletonProps {
|
|
24
|
+
/** How many lines of prose to stand in for. */
|
|
25
|
+
lines?: number;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* A paragraph.
|
|
29
|
+
*
|
|
30
|
+
* The last line is short, because real prose ends mid-measure. A block of equal
|
|
31
|
+
* full-width bars reads as a table, and the reader then flinches when a
|
|
32
|
+
* paragraph arrives instead.
|
|
33
|
+
*/
|
|
34
|
+
declare function SkeletonText({ lines, className, ...rest }: SkeletonTextProps): react.JSX.Element;
|
|
35
|
+
interface SkeletonPageProps extends HTMLAttributes<HTMLDivElement> {
|
|
36
|
+
/**
|
|
37
|
+
* What is loading, in words. Announced once; the shapes themselves are
|
|
38
|
+
* `aria-hidden`, so this sentence is the entire experience for a screen
|
|
39
|
+
* reader and "Loading" alone tells it nothing.
|
|
40
|
+
*/
|
|
41
|
+
label: string;
|
|
42
|
+
children: ReactNode;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* The frame a skeleton sits in: the live region, and the single pulse.
|
|
46
|
+
*
|
|
47
|
+
* One `animate-pulse` on the wrapper rather than one per part, so the page
|
|
48
|
+
* breathes together instead of shimmering out of phase — twenty independently
|
|
49
|
+
* animated bars is a visual noise floor, not a loading state.
|
|
50
|
+
*
|
|
51
|
+
* A skeleton whose shape does not match what replaces it is worse than none: it
|
|
52
|
+
* promises a layout and then the page jumps out from under the reader. Compose
|
|
53
|
+
* the parts to match the real thing, and change both together.
|
|
54
|
+
*
|
|
55
|
+
* @example
|
|
56
|
+
* <SkeletonPage label="Loading projects">
|
|
57
|
+
* <SkeletonLine className="h-2.5 w-28" />
|
|
58
|
+
* <SkeletonBlock className="mt-6 h-40" />
|
|
59
|
+
* </SkeletonPage>
|
|
60
|
+
*/
|
|
61
|
+
declare function SkeletonPage({ label, children, className, ...rest }: SkeletonPageProps): react.JSX.Element;
|
|
62
|
+
|
|
63
|
+
export { Skeleton, SkeletonBlock, SkeletonCircle, SkeletonLine, SkeletonPage, type SkeletonPageProps, type SkeletonProps, SkeletonText, type SkeletonTextProps, Skeleton as default };
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { cn } from "../../lib/cn.js";
|
|
3
|
+
function Skeleton({ className, ...rest }) {
|
|
4
|
+
return /* @__PURE__ */ jsx("div", { "aria-hidden": "true", className: cn("bg-(--stone)", className), ...rest });
|
|
5
|
+
}
|
|
6
|
+
function SkeletonLine({ className, ...rest }) {
|
|
7
|
+
return /* @__PURE__ */ jsx(Skeleton, { className: cn("h-3 rounded-(--radius-pill)", className), ...rest });
|
|
8
|
+
}
|
|
9
|
+
function SkeletonBlock({ className, ...rest }) {
|
|
10
|
+
return /* @__PURE__ */ jsx(Skeleton, { className: cn("rounded-(--radius-sm)", className), ...rest });
|
|
11
|
+
}
|
|
12
|
+
function SkeletonCircle({ className, ...rest }) {
|
|
13
|
+
return /* @__PURE__ */ jsx(Skeleton, { className: cn("size-9 rounded-full", className), ...rest });
|
|
14
|
+
}
|
|
15
|
+
function SkeletonText({ lines = 3, className, ...rest }) {
|
|
16
|
+
return /* @__PURE__ */ jsx("div", { className: cn("flex flex-col gap-2.5", className), ...rest, children: Array.from({ length: lines }, (_, index) => /* @__PURE__ */ jsx(SkeletonLine, { className: index === lines - 1 ? "w-[62%]" : "w-full" }, index)) });
|
|
17
|
+
}
|
|
18
|
+
function SkeletonPage({ label, children, className, ...rest }) {
|
|
19
|
+
return /* @__PURE__ */ jsxs(
|
|
20
|
+
"div",
|
|
21
|
+
{
|
|
22
|
+
role: "status",
|
|
23
|
+
"aria-busy": "true",
|
|
24
|
+
"data-m22-animated": true,
|
|
25
|
+
className: cn("animate-pulse motion-reduce:animate-none", className),
|
|
26
|
+
...rest,
|
|
27
|
+
children: [
|
|
28
|
+
/* @__PURE__ */ jsx("span", { className: "sr-only", children: label }),
|
|
29
|
+
children
|
|
30
|
+
]
|
|
31
|
+
}
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
var Skeleton_default = Skeleton;
|
|
35
|
+
export {
|
|
36
|
+
Skeleton,
|
|
37
|
+
SkeletonBlock,
|
|
38
|
+
SkeletonCircle,
|
|
39
|
+
SkeletonLine,
|
|
40
|
+
SkeletonPage,
|
|
41
|
+
SkeletonText,
|
|
42
|
+
Skeleton_default as default
|
|
43
|
+
};
|
|
44
|
+
//# sourceMappingURL=Skeleton.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/components/Skeleton/Skeleton.tsx"],"sourcesContent":["import type { HTMLAttributes, ReactNode } from 'react'\nimport { cn } from '../../lib/cn'\n\nexport type SkeletonProps = HTMLAttributes<HTMLDivElement>\n\n/**\n * The fill every skeleton part is drawn from: `--stone`, and nothing else.\n *\n * Deliberately NOT a shimmer. A shimmer sweeps a highlight across the shape,\n * which implies a light source; the White Reset has none, so the sweep reads as\n * a bug rather than as loading. The whole page breathes together instead —\n * see `SkeletonPage`, which owns the single pulse.\n *\n * @example\n * <Skeleton className=\"h-3 w-40 rounded-(--radius-pill)\" />\n */\nexport function Skeleton({ className, ...rest }: SkeletonProps) {\n return <div aria-hidden=\"true\" className={cn('bg-(--stone)', className)} {...rest} />\n}\n\n/** A line of type. Height is the line's; width is how far it runs. */\nexport function SkeletonLine({ className, ...rest }: SkeletonProps) {\n return <Skeleton className={cn('h-3 rounded-(--radius-pill)', className)} {...rest} />\n}\n\n/** A block: an image mount, a plate, a chart. */\nexport function SkeletonBlock({ className, ...rest }: SkeletonProps) {\n return <Skeleton className={cn('rounded-(--radius-sm)', className)} {...rest} />\n}\n\n/** A circle: an avatar, a marker. */\nexport function SkeletonCircle({ className, ...rest }: SkeletonProps) {\n return <Skeleton className={cn('size-9 rounded-full', className)} {...rest} />\n}\n\nexport interface SkeletonTextProps extends SkeletonProps {\n /** How many lines of prose to stand in for. */\n lines?: number\n}\n\n/**\n * A paragraph.\n *\n * The last line is short, because real prose ends mid-measure. A block of equal\n * full-width bars reads as a table, and the reader then flinches when a\n * paragraph arrives instead.\n */\nexport function SkeletonText({ lines = 3, className, ...rest }: SkeletonTextProps) {\n return (\n <div className={cn('flex flex-col gap-2.5', className)} {...rest}>\n {Array.from({ length: lines }, (_, index) => (\n <SkeletonLine key={index} className={index === lines - 1 ? 'w-[62%]' : 'w-full'} />\n ))}\n </div>\n )\n}\n\nexport interface SkeletonPageProps extends HTMLAttributes<HTMLDivElement> {\n /**\n * What is loading, in words. Announced once; the shapes themselves are\n * `aria-hidden`, so this sentence is the entire experience for a screen\n * reader and \"Loading\" alone tells it nothing.\n */\n label: string\n children: ReactNode\n}\n\n/**\n * The frame a skeleton sits in: the live region, and the single pulse.\n *\n * One `animate-pulse` on the wrapper rather than one per part, so the page\n * breathes together instead of shimmering out of phase — twenty independently\n * animated bars is a visual noise floor, not a loading state.\n *\n * A skeleton whose shape does not match what replaces it is worse than none: it\n * promises a layout and then the page jumps out from under the reader. Compose\n * the parts to match the real thing, and change both together.\n *\n * @example\n * <SkeletonPage label=\"Loading projects\">\n * <SkeletonLine className=\"h-2.5 w-28\" />\n * <SkeletonBlock className=\"mt-6 h-40\" />\n * </SkeletonPage>\n */\nexport function SkeletonPage({ label, children, className, ...rest }: SkeletonPageProps) {\n return (\n <div\n role=\"status\"\n aria-busy=\"true\"\n data-m22-animated\n className={cn('animate-pulse motion-reduce:animate-none', className)}\n {...rest}\n >\n <span className=\"sr-only\">{label}</span>\n {children}\n </div>\n )\n}\n\nexport default Skeleton\n"],"mappings":"AAiBS,cAqEL,YArEK;AAhBT,SAAS,UAAU;AAeZ,SAAS,SAAS,EAAE,WAAW,GAAG,KAAK,GAAkB;AAC9D,SAAO,oBAAC,SAAI,eAAY,QAAO,WAAW,GAAG,gBAAgB,SAAS,GAAI,GAAG,MAAM;AACrF;AAGO,SAAS,aAAa,EAAE,WAAW,GAAG,KAAK,GAAkB;AAClE,SAAO,oBAAC,YAAS,WAAW,GAAG,+BAA+B,SAAS,GAAI,GAAG,MAAM;AACtF;AAGO,SAAS,cAAc,EAAE,WAAW,GAAG,KAAK,GAAkB;AACnE,SAAO,oBAAC,YAAS,WAAW,GAAG,yBAAyB,SAAS,GAAI,GAAG,MAAM;AAChF;AAGO,SAAS,eAAe,EAAE,WAAW,GAAG,KAAK,GAAkB;AACpE,SAAO,oBAAC,YAAS,WAAW,GAAG,uBAAuB,SAAS,GAAI,GAAG,MAAM;AAC9E;AAcO,SAAS,aAAa,EAAE,QAAQ,GAAG,WAAW,GAAG,KAAK,GAAsB;AACjF,SACE,oBAAC,SAAI,WAAW,GAAG,yBAAyB,SAAS,GAAI,GAAG,MACzD,gBAAM,KAAK,EAAE,QAAQ,MAAM,GAAG,CAAC,GAAG,UACjC,oBAAC,gBAAyB,WAAW,UAAU,QAAQ,IAAI,YAAY,YAApD,KAA8D,CAClF,GACH;AAEJ;AA6BO,SAAS,aAAa,EAAE,OAAO,UAAU,WAAW,GAAG,KAAK,GAAsB;AACvF,SACE;AAAA,IAAC;AAAA;AAAA,MACC,MAAK;AAAA,MACL,aAAU;AAAA,MACV,qBAAiB;AAAA,MACjB,WAAW,GAAG,4CAA4C,SAAS;AAAA,MAClE,GAAG;AAAA,MAEJ;AAAA,4BAAC,UAAK,WAAU,WAAW,iBAAM;AAAA,QAChC;AAAA;AAAA;AAAA,EACH;AAEJ;AAEA,IAAO,mBAAQ;","names":[]}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import * as react from 'react';
|
|
2
|
+
import { ComponentProps } from 'react';
|
|
3
|
+
import * as SliderPrimitive from '@radix-ui/react-slider';
|
|
4
|
+
|
|
5
|
+
interface SliderProps extends ComponentProps<typeof SliderPrimitive.Root> {
|
|
6
|
+
/**
|
|
7
|
+
* Names the control. Required: a slider with no name announces only a number,
|
|
8
|
+
* and a number with no noun is not information.
|
|
9
|
+
*
|
|
10
|
+
* A range slider (two thumbs) needs one name per thumb — pass an array.
|
|
11
|
+
*/
|
|
12
|
+
label: string | [string, string];
|
|
13
|
+
/** Prints the current value beside the label. */
|
|
14
|
+
showValue?: boolean;
|
|
15
|
+
/** Renders the value with a unit or a currency, e.g. `(n) => n + '%'`. */
|
|
16
|
+
format?: (value: number) => string;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* A value chosen along a range.
|
|
20
|
+
*
|
|
21
|
+
* Radix owns the keyboard contract — arrows step, Page keys jump, Home and End
|
|
22
|
+
* reach the ends — and the ARIA that reports the value. What is here is the
|
|
23
|
+
* look, and the labelling, which is the part a slider most often gets wrong: a
|
|
24
|
+
* thumb that announces "42" and nothing else leaves a screen reader user with a
|
|
25
|
+
* number and no idea what it measures.
|
|
26
|
+
*
|
|
27
|
+
* A 44px hit area sits invisibly around the 16px thumb, because a thumb sized
|
|
28
|
+
* for the design is well under any pointer-target guideline.
|
|
29
|
+
*
|
|
30
|
+
* @example
|
|
31
|
+
* <Slider label="Quality" defaultValue={[80]} max={100} step={5} showValue format={(n) => `${n}%`} />
|
|
32
|
+
*/
|
|
33
|
+
declare function Slider({ label, showValue, format, className, onValueChange, ...props }: SliderProps): react.JSX.Element;
|
|
34
|
+
|
|
35
|
+
export { Slider, type SliderProps, Slider as default };
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import * as SliderPrimitive from "@radix-ui/react-slider";
|
|
4
|
+
import { useState } from "react";
|
|
5
|
+
import { cn } from "../../lib/cn.js";
|
|
6
|
+
function Slider({
|
|
7
|
+
label,
|
|
8
|
+
showValue = false,
|
|
9
|
+
format = String,
|
|
10
|
+
className,
|
|
11
|
+
onValueChange,
|
|
12
|
+
...props
|
|
13
|
+
}) {
|
|
14
|
+
const names = Array.isArray(label) ? label : [label];
|
|
15
|
+
const [uncontrolled, setUncontrolled] = useState(props.defaultValue ?? []);
|
|
16
|
+
const current = props.value ?? uncontrolled;
|
|
17
|
+
const handleChange = (next) => {
|
|
18
|
+
if (props.value === void 0) setUncontrolled(next);
|
|
19
|
+
onValueChange?.(next);
|
|
20
|
+
};
|
|
21
|
+
return /* @__PURE__ */ jsxs("div", { className: cn("flex w-full flex-col gap-3", className), children: [
|
|
22
|
+
showValue && /* @__PURE__ */ jsxs("div", { className: "flex items-baseline justify-between mono-meta text-(--ink-3-aa)", children: [
|
|
23
|
+
/* @__PURE__ */ jsx("span", { children: names[0] }),
|
|
24
|
+
/* @__PURE__ */ jsx("span", { className: "tabular-nums text-(--ink)", children: current.map((value) => format(value)).join(" \u2013 ") })
|
|
25
|
+
] }),
|
|
26
|
+
/* @__PURE__ */ jsxs(
|
|
27
|
+
SliderPrimitive.Root,
|
|
28
|
+
{
|
|
29
|
+
className: "group relative flex w-full touch-none select-none items-center py-3",
|
|
30
|
+
onValueChange: handleChange,
|
|
31
|
+
...props,
|
|
32
|
+
children: [
|
|
33
|
+
/* @__PURE__ */ jsx(SliderPrimitive.Track, { className: "relative h-1 w-full grow overflow-hidden rounded-(--radius-pill) bg-(--stone) transition-[height] duration-(--duration-fast) group-hover:h-1.5", children: /* @__PURE__ */ jsx(SliderPrimitive.Range, { className: "absolute h-full bg-(--accent)" }) }),
|
|
34
|
+
current.map((_, index) => /* @__PURE__ */ jsx(
|
|
35
|
+
SliderPrimitive.Thumb,
|
|
36
|
+
{
|
|
37
|
+
"aria-label": names[index] ?? names[0],
|
|
38
|
+
className: "relative block size-4 rounded-full border border-(--accent) bg-(--paper) transition-[transform,background-color] duration-(--duration-fast) ease-(--ease-out-expo) before:absolute before:left-1/2 before:top-1/2 before:size-11 before:-translate-x-1/2 before:-translate-y-1/2 before:content-[''] hover:scale-110 hover:bg-(--stone) active:scale-125 data-[state=active]:scale-125 disabled:opacity-(--disabled-opacity) motion-reduce:transition-none"
|
|
39
|
+
},
|
|
40
|
+
index
|
|
41
|
+
))
|
|
42
|
+
]
|
|
43
|
+
}
|
|
44
|
+
)
|
|
45
|
+
] });
|
|
46
|
+
}
|
|
47
|
+
var Slider_default = Slider;
|
|
48
|
+
export {
|
|
49
|
+
Slider,
|
|
50
|
+
Slider_default as default
|
|
51
|
+
};
|
|
52
|
+
//# sourceMappingURL=Slider.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/components/Slider/Slider.tsx"],"sourcesContent":["'use client'\n\nimport * as SliderPrimitive from '@radix-ui/react-slider'\nimport { useState, type ComponentProps } from 'react'\nimport { cn } from '../../lib/cn'\n\nexport interface SliderProps extends ComponentProps<typeof SliderPrimitive.Root> {\n /**\n * Names the control. Required: a slider with no name announces only a number,\n * and a number with no noun is not information.\n *\n * A range slider (two thumbs) needs one name per thumb — pass an array.\n */\n label: string | [string, string]\n /** Prints the current value beside the label. */\n showValue?: boolean\n /** Renders the value with a unit or a currency, e.g. `(n) => n + '%'`. */\n format?: (value: number) => string\n}\n\n/**\n * A value chosen along a range.\n *\n * Radix owns the keyboard contract — arrows step, Page keys jump, Home and End\n * reach the ends — and the ARIA that reports the value. What is here is the\n * look, and the labelling, which is the part a slider most often gets wrong: a\n * thumb that announces \"42\" and nothing else leaves a screen reader user with a\n * number and no idea what it measures.\n *\n * A 44px hit area sits invisibly around the 16px thumb, because a thumb sized\n * for the design is well under any pointer-target guideline.\n *\n * @example\n * <Slider label=\"Quality\" defaultValue={[80]} max={100} step={5} showValue format={(n) => `${n}%`} />\n */\nexport function Slider({\n label,\n showValue = false,\n format = String,\n className,\n onValueChange,\n ...props\n}: SliderProps) {\n const names = Array.isArray(label) ? label : [label]\n\n // Tracked here, not read off the props. An uncontrolled slider's\n // `defaultValue` never changes, so the printed figure sat at its starting\n // number while the thumb moved — the one thing `showValue` exists to avoid.\n const [uncontrolled, setUncontrolled] = useState<number[]>(props.defaultValue ?? [])\n const current = props.value ?? uncontrolled\n\n const handleChange = (next: number[]) => {\n if (props.value === undefined) setUncontrolled(next)\n onValueChange?.(next)\n }\n\n return (\n <div className={cn('flex w-full flex-col gap-3', className)}>\n {showValue && (\n <div className=\"flex items-baseline justify-between mono-meta text-(--ink-3-aa)\">\n <span>{names[0]}</span>\n <span className=\"tabular-nums text-(--ink)\">\n {current.map((value) => format(value)).join(' – ')}\n </span>\n </div>\n )}\n <SliderPrimitive.Root\n className=\"group relative flex w-full touch-none select-none items-center py-3\"\n onValueChange={handleChange}\n {...props}\n >\n <SliderPrimitive.Track className=\"relative h-1 w-full grow overflow-hidden rounded-(--radius-pill) bg-(--stone) transition-[height] duration-(--duration-fast) group-hover:h-1.5\">\n <SliderPrimitive.Range className=\"absolute h-full bg-(--accent)\" />\n </SliderPrimitive.Track>\n {current.map((_, index) => (\n <SliderPrimitive.Thumb\n key={index}\n aria-label={names[index] ?? names[0]}\n // The visible thumb is 16px; the `before` pseudo-element widens the\n // hit area to 44px without changing what is drawn.\n // `transition-transform` and not `all`: the thumb's own position is\n // set by Radix as a `left` percentage, and transitioning that would\n // make it lag the pointer. Only the grow-on-grab is animated.\n className=\"relative block size-4 rounded-full border border-(--accent) bg-(--paper) transition-[transform,background-color] duration-(--duration-fast) ease-(--ease-out-expo) before:absolute before:left-1/2 before:top-1/2 before:size-11 before:-translate-x-1/2 before:-translate-y-1/2 before:content-[''] hover:scale-110 hover:bg-(--stone) active:scale-125 data-[state=active]:scale-125 disabled:opacity-(--disabled-opacity) motion-reduce:transition-none\"\n />\n ))}\n </SliderPrimitive.Root>\n </div>\n )\n}\n\nexport default Slider\n"],"mappings":";AA2DQ,SACE,KADF;AAzDR,YAAY,qBAAqB;AACjC,SAAS,gBAAqC;AAC9C,SAAS,UAAU;AA+BZ,SAAS,OAAO;AAAA,EACrB;AAAA,EACA,YAAY;AAAA,EACZ,SAAS;AAAA,EACT;AAAA,EACA;AAAA,EACA,GAAG;AACL,GAAgB;AACd,QAAM,QAAQ,MAAM,QAAQ,KAAK,IAAI,QAAQ,CAAC,KAAK;AAKnD,QAAM,CAAC,cAAc,eAAe,IAAI,SAAmB,MAAM,gBAAgB,CAAC,CAAC;AACnF,QAAM,UAAU,MAAM,SAAS;AAE/B,QAAM,eAAe,CAAC,SAAmB;AACvC,QAAI,MAAM,UAAU,OAAW,iBAAgB,IAAI;AACnD,oBAAgB,IAAI;AAAA,EACtB;AAEA,SACE,qBAAC,SAAI,WAAW,GAAG,8BAA8B,SAAS,GACvD;AAAA,iBACC,qBAAC,SAAI,WAAU,mEACb;AAAA,0BAAC,UAAM,gBAAM,CAAC,GAAE;AAAA,MAChB,oBAAC,UAAK,WAAU,6BACb,kBAAQ,IAAI,CAAC,UAAU,OAAO,KAAK,CAAC,EAAE,KAAK,UAAK,GACnD;AAAA,OACF;AAAA,IAEF;AAAA,MAAC,gBAAgB;AAAA,MAAhB;AAAA,QACC,WAAU;AAAA,QACV,eAAe;AAAA,QACd,GAAG;AAAA,QAEJ;AAAA,8BAAC,gBAAgB,OAAhB,EAAsB,WAAU,kJAC/B,8BAAC,gBAAgB,OAAhB,EAAsB,WAAU,iCAAgC,GACnE;AAAA,UACC,QAAQ,IAAI,CAAC,GAAG,UACf;AAAA,YAAC,gBAAgB;AAAA,YAAhB;AAAA,cAEC,cAAY,MAAM,KAAK,KAAK,MAAM,CAAC;AAAA,cAMnC,WAAU;AAAA;AAAA,YAPL;AAAA,UAQP,CACD;AAAA;AAAA;AAAA,IACH;AAAA,KACF;AAEJ;AAEA,IAAO,iBAAQ;","names":[]}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import * as react from 'react';
|
|
2
|
+
import { HTMLAttributes } from 'react';
|
|
3
|
+
|
|
4
|
+
interface SpinnerProps extends HTMLAttributes<HTMLSpanElement> {
|
|
5
|
+
/** 14px / 18px / 26px. Match the size of the type it sits beside. */
|
|
6
|
+
size?: 'sm' | 'md' | 'lg';
|
|
7
|
+
/**
|
|
8
|
+
* `default` draws the ring against the page — a hairline track in the rule
|
|
9
|
+
* colour with the leading quarter in ink. `current` draws it in the inherited
|
|
10
|
+
* text colour, which is what a spinner inside a filled button needs: on an
|
|
11
|
+
* ink ground an ink ring is invisible.
|
|
12
|
+
*/
|
|
13
|
+
tone?: 'default' | 'current';
|
|
14
|
+
/**
|
|
15
|
+
* Announced to assistive tech. Pass the specific thing being waited on
|
|
16
|
+
* ("Loading projects"), not the generic word — a screen reader user hearing
|
|
17
|
+
* "Loading" three times cannot tell which three things.
|
|
18
|
+
*
|
|
19
|
+
* Pass `null` for a spinner that sits inside a control which already names
|
|
20
|
+
* the operation (a button whose own label changes to "Saving…"), so the two
|
|
21
|
+
* are not read out twice.
|
|
22
|
+
*/
|
|
23
|
+
label?: string | null;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* The system's one "working" indicator.
|
|
27
|
+
*
|
|
28
|
+
* A ring, not a blurred glow and not a shimmer: the White Reset has no light
|
|
29
|
+
* source, so depth and softness are not available to it. The leading quarter is
|
|
30
|
+
* the only thing that distinguishes the ring from a plain circle, which is why
|
|
31
|
+
* the track stays a hairline — a spinner is a hint that time is passing, not a
|
|
32
|
+
* feature of the page.
|
|
33
|
+
*
|
|
34
|
+
* It spins under `motion-safe` only. A reader who has asked for less motion
|
|
35
|
+
* gets a static ring, which still reads as "not finished" because the leading
|
|
36
|
+
* quarter is darker than the track.
|
|
37
|
+
*
|
|
38
|
+
* For a wait long enough that the reader would otherwise wonder whether the
|
|
39
|
+
* page is broken, prefer `Skeleton` — a shape that describes what is coming
|
|
40
|
+
* beats a dot that describes nothing.
|
|
41
|
+
*
|
|
42
|
+
* @example
|
|
43
|
+
* <Spinner size="lg" label="Loading projects" />
|
|
44
|
+
* @example
|
|
45
|
+
* // Inside a filled button, where the ground is ink:
|
|
46
|
+
* <Spinner size="sm" tone="current" label={null} />
|
|
47
|
+
*/
|
|
48
|
+
declare function Spinner({ size, tone, className, label, ...rest }: SpinnerProps): react.JSX.Element;
|
|
49
|
+
|
|
50
|
+
export { Spinner, type SpinnerProps, Spinner as default };
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { cn } from "../../lib/cn.js";
|
|
3
|
+
const SIZE = {
|
|
4
|
+
sm: "size-3.5 border-[1.5px]",
|
|
5
|
+
md: "size-[18px] border-2",
|
|
6
|
+
lg: "size-6.5 border-2"
|
|
7
|
+
};
|
|
8
|
+
const TONE = {
|
|
9
|
+
default: "border-(--rule-2) border-t-(--ink)",
|
|
10
|
+
current: "border-current/25 border-t-current"
|
|
11
|
+
};
|
|
12
|
+
function Spinner({
|
|
13
|
+
size = "md",
|
|
14
|
+
tone = "default",
|
|
15
|
+
className,
|
|
16
|
+
label = "Loading",
|
|
17
|
+
...rest
|
|
18
|
+
}) {
|
|
19
|
+
return /* @__PURE__ */ jsxs(
|
|
20
|
+
"span",
|
|
21
|
+
{
|
|
22
|
+
role: label === null ? void 0 : "status",
|
|
23
|
+
"aria-hidden": label === null ? true : void 0,
|
|
24
|
+
className: cn("inline-flex shrink-0 items-center justify-center", className),
|
|
25
|
+
...rest,
|
|
26
|
+
children: [
|
|
27
|
+
/* @__PURE__ */ jsx(
|
|
28
|
+
"span",
|
|
29
|
+
{
|
|
30
|
+
"data-m22-animated": true,
|
|
31
|
+
className: cn(
|
|
32
|
+
"inline-block rounded-full border-solid motion-safe:animate-[m22-spin_0.7s_linear_infinite]",
|
|
33
|
+
SIZE[size],
|
|
34
|
+
TONE[tone]
|
|
35
|
+
)
|
|
36
|
+
}
|
|
37
|
+
),
|
|
38
|
+
label !== null && /* @__PURE__ */ jsx("span", { className: "sr-only", children: label })
|
|
39
|
+
]
|
|
40
|
+
}
|
|
41
|
+
);
|
|
42
|
+
}
|
|
43
|
+
var Spinner_default = Spinner;
|
|
44
|
+
export {
|
|
45
|
+
Spinner,
|
|
46
|
+
Spinner_default as default
|
|
47
|
+
};
|
|
48
|
+
//# sourceMappingURL=Spinner.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/components/Spinner/Spinner.tsx"],"sourcesContent":["import type { HTMLAttributes } from 'react'\nimport { cn } from '../../lib/cn'\n\nexport interface SpinnerProps extends HTMLAttributes<HTMLSpanElement> {\n /** 14px / 18px / 26px. Match the size of the type it sits beside. */\n size?: 'sm' | 'md' | 'lg'\n /**\n * `default` draws the ring against the page — a hairline track in the rule\n * colour with the leading quarter in ink. `current` draws it in the inherited\n * text colour, which is what a spinner inside a filled button needs: on an\n * ink ground an ink ring is invisible.\n */\n tone?: 'default' | 'current'\n /**\n * Announced to assistive tech. Pass the specific thing being waited on\n * (\"Loading projects\"), not the generic word — a screen reader user hearing\n * \"Loading\" three times cannot tell which three things.\n *\n * Pass `null` for a spinner that sits inside a control which already names\n * the operation (a button whose own label changes to \"Saving…\"), so the two\n * are not read out twice.\n */\n label?: string | null\n}\n\nconst SIZE = {\n sm: 'size-3.5 border-[1.5px]',\n md: 'size-[18px] border-2',\n lg: 'size-6.5 border-2',\n} as const\n\nconst TONE = {\n default: 'border-(--rule-2) border-t-(--ink)',\n current: 'border-current/25 border-t-current',\n} as const\n\n/**\n * The system's one \"working\" indicator.\n *\n * A ring, not a blurred glow and not a shimmer: the White Reset has no light\n * source, so depth and softness are not available to it. The leading quarter is\n * the only thing that distinguishes the ring from a plain circle, which is why\n * the track stays a hairline — a spinner is a hint that time is passing, not a\n * feature of the page.\n *\n * It spins under `motion-safe` only. A reader who has asked for less motion\n * gets a static ring, which still reads as \"not finished\" because the leading\n * quarter is darker than the track.\n *\n * For a wait long enough that the reader would otherwise wonder whether the\n * page is broken, prefer `Skeleton` — a shape that describes what is coming\n * beats a dot that describes nothing.\n *\n * @example\n * <Spinner size=\"lg\" label=\"Loading projects\" />\n * @example\n * // Inside a filled button, where the ground is ink:\n * <Spinner size=\"sm\" tone=\"current\" label={null} />\n */\nexport function Spinner({\n size = 'md',\n tone = 'default',\n className,\n label = 'Loading',\n ...rest\n}: SpinnerProps) {\n return (\n <span\n role={label === null ? undefined : 'status'}\n aria-hidden={label === null ? true : undefined}\n className={cn('inline-flex shrink-0 items-center justify-center', className)}\n {...rest}\n >\n <span\n data-m22-animated\n className={cn(\n 'inline-block rounded-full border-solid motion-safe:animate-[m22-spin_0.7s_linear_infinite]',\n SIZE[size],\n TONE[tone],\n )}\n />\n {label !== null && <span className=\"sr-only\">{label}</span>}\n </span>\n )\n}\n\nexport default Spinner\n"],"mappings":"AAmEI,SAME,KANF;AAlEJ,SAAS,UAAU;AAwBnB,MAAM,OAAO;AAAA,EACX,IAAI;AAAA,EACJ,IAAI;AAAA,EACJ,IAAI;AACN;AAEA,MAAM,OAAO;AAAA,EACX,SAAS;AAAA,EACT,SAAS;AACX;AAyBO,SAAS,QAAQ;AAAA,EACtB,OAAO;AAAA,EACP,OAAO;AAAA,EACP;AAAA,EACA,QAAQ;AAAA,EACR,GAAG;AACL,GAAiB;AACf,SACE;AAAA,IAAC;AAAA;AAAA,MACC,MAAM,UAAU,OAAO,SAAY;AAAA,MACnC,eAAa,UAAU,OAAO,OAAO;AAAA,MACrC,WAAW,GAAG,oDAAoD,SAAS;AAAA,MAC1E,GAAG;AAAA,MAEJ;AAAA;AAAA,UAAC;AAAA;AAAA,YACC,qBAAiB;AAAA,YACjB,WAAW;AAAA,cACT;AAAA,cACA,KAAK,IAAI;AAAA,cACT,KAAK,IAAI;AAAA,YACX;AAAA;AAAA,QACF;AAAA,QACC,UAAU,QAAQ,oBAAC,UAAK,WAAU,WAAW,iBAAM;AAAA;AAAA;AAAA,EACtD;AAEJ;AAEA,IAAO,kBAAQ;","names":[]}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import * as react from 'react';
|
|
2
|
+
import { HTMLAttributes } from 'react';
|
|
3
|
+
|
|
4
|
+
type StatusTone = 'success' | 'warning' | 'danger' | 'neutral';
|
|
5
|
+
interface StatusDotProps extends HTMLAttributes<HTMLSpanElement> {
|
|
6
|
+
/** 7px / 8px. `md` is the default. */
|
|
7
|
+
size?: 'sm' | 'md';
|
|
8
|
+
tone?: StatusTone;
|
|
9
|
+
/** A halo that grows and dissolves, for "live right now". Off for a settled state. */
|
|
10
|
+
pulse?: boolean;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* The dot beside a status word.
|
|
14
|
+
*
|
|
15
|
+
* `aria-hidden` without exception: the dot repeats a state the adjacent label
|
|
16
|
+
* already names, and a screen reader announcing "available" twice is worse than
|
|
17
|
+
* not announcing the decoration at all. If a call site has no visible label,
|
|
18
|
+
* the fix is a label, not an `aria-label` on the dot.
|
|
19
|
+
*
|
|
20
|
+
* The halo is a separate absolutely-positioned ring rather than a box-shadow,
|
|
21
|
+
* because Law 2 of this system is that a shadow is never blurred — and it is
|
|
22
|
+
* motion-safe, so a reader who asked for less motion gets a static dot.
|
|
23
|
+
*
|
|
24
|
+
* @example
|
|
25
|
+
* <span className="inline-flex items-center gap-2"><StatusDot /> Available</span>
|
|
26
|
+
*/
|
|
27
|
+
declare function StatusDot({ size, tone, pulse, className, ...rest }: StatusDotProps): react.JSX.Element;
|
|
28
|
+
|
|
29
|
+
export { StatusDot, type StatusDotProps, type StatusTone, StatusDot as default };
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { cn } from "../../lib/cn.js";
|
|
3
|
+
const SIZE = { sm: "size-[7px]", md: "size-2" };
|
|
4
|
+
const TONE = {
|
|
5
|
+
success: "bg-(--ok)",
|
|
6
|
+
warning: "bg-(--warn)",
|
|
7
|
+
danger: "bg-(--danger)",
|
|
8
|
+
neutral: "bg-(--ink-3-aa)"
|
|
9
|
+
};
|
|
10
|
+
function StatusDot({
|
|
11
|
+
size = "md",
|
|
12
|
+
tone = "success",
|
|
13
|
+
pulse = true,
|
|
14
|
+
className,
|
|
15
|
+
...rest
|
|
16
|
+
}) {
|
|
17
|
+
return /* @__PURE__ */ jsxs(
|
|
18
|
+
"span",
|
|
19
|
+
{
|
|
20
|
+
"aria-hidden": "true",
|
|
21
|
+
className: cn("relative inline-grid shrink-0 place-items-center", SIZE[size], className),
|
|
22
|
+
...rest,
|
|
23
|
+
children: [
|
|
24
|
+
/* @__PURE__ */ jsx("span", { className: cn("absolute inset-0 rounded-full", TONE[tone]) }),
|
|
25
|
+
pulse && /* @__PURE__ */ jsx(
|
|
26
|
+
"span",
|
|
27
|
+
{
|
|
28
|
+
"data-m22-animated": true,
|
|
29
|
+
className: cn(
|
|
30
|
+
"absolute inset-0 rounded-full motion-safe:animate-[m22-halo_2.4s_ease-out_infinite]",
|
|
31
|
+
TONE[tone]
|
|
32
|
+
)
|
|
33
|
+
}
|
|
34
|
+
)
|
|
35
|
+
]
|
|
36
|
+
}
|
|
37
|
+
);
|
|
38
|
+
}
|
|
39
|
+
var StatusDot_default = StatusDot;
|
|
40
|
+
export {
|
|
41
|
+
StatusDot,
|
|
42
|
+
StatusDot_default as default
|
|
43
|
+
};
|
|
44
|
+
//# sourceMappingURL=StatusDot.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/components/StatusDot/StatusDot.tsx"],"sourcesContent":["import type { HTMLAttributes } from 'react'\nimport { cn } from '../../lib/cn'\n\nexport type StatusTone = 'success' | 'warning' | 'danger' | 'neutral'\n\nexport interface StatusDotProps extends HTMLAttributes<HTMLSpanElement> {\n /** 7px / 8px. `md` is the default. */\n size?: 'sm' | 'md'\n tone?: StatusTone\n /** A halo that grows and dissolves, for \"live right now\". Off for a settled state. */\n pulse?: boolean\n}\n\nconst SIZE = { sm: 'size-[7px]', md: 'size-2' } as const\n\nconst TONE: Record<StatusTone, string> = {\n success: 'bg-(--ok)',\n warning: 'bg-(--warn)',\n danger: 'bg-(--danger)',\n neutral: 'bg-(--ink-3-aa)',\n}\n\n/**\n * The dot beside a status word.\n *\n * `aria-hidden` without exception: the dot repeats a state the adjacent label\n * already names, and a screen reader announcing \"available\" twice is worse than\n * not announcing the decoration at all. If a call site has no visible label,\n * the fix is a label, not an `aria-label` on the dot.\n *\n * The halo is a separate absolutely-positioned ring rather than a box-shadow,\n * because Law 2 of this system is that a shadow is never blurred — and it is\n * motion-safe, so a reader who asked for less motion gets a static dot.\n *\n * @example\n * <span className=\"inline-flex items-center gap-2\"><StatusDot /> Available</span>\n */\nexport function StatusDot({\n size = 'md',\n tone = 'success',\n pulse = true,\n className,\n ...rest\n}: StatusDotProps) {\n return (\n <span\n aria-hidden=\"true\"\n className={cn('relative inline-grid shrink-0 place-items-center', SIZE[size], className)}\n {...rest}\n >\n <span className={cn('absolute inset-0 rounded-full', TONE[tone])} />\n {pulse && (\n <span\n data-m22-animated\n className={cn(\n 'absolute inset-0 rounded-full motion-safe:animate-[m22-halo_2.4s_ease-out_infinite]',\n TONE[tone],\n )}\n />\n )}\n </span>\n )\n}\n\nexport default StatusDot\n"],"mappings":"AA6CI,SAKE,KALF;AA5CJ,SAAS,UAAU;AAYnB,MAAM,OAAO,EAAE,IAAI,cAAc,IAAI,SAAS;AAE9C,MAAM,OAAmC;AAAA,EACvC,SAAS;AAAA,EACT,SAAS;AAAA,EACT,QAAQ;AAAA,EACR,SAAS;AACX;AAiBO,SAAS,UAAU;AAAA,EACxB,OAAO;AAAA,EACP,OAAO;AAAA,EACP,QAAQ;AAAA,EACR;AAAA,EACA,GAAG;AACL,GAAmB;AACjB,SACE;AAAA,IAAC;AAAA;AAAA,MACC,eAAY;AAAA,MACZ,WAAW,GAAG,oDAAoD,KAAK,IAAI,GAAG,SAAS;AAAA,MACtF,GAAG;AAAA,MAEJ;AAAA,4BAAC,UAAK,WAAW,GAAG,iCAAiC,KAAK,IAAI,CAAC,GAAG;AAAA,QACjE,SACC;AAAA,UAAC;AAAA;AAAA,YACC,qBAAiB;AAAA,YACjB,WAAW;AAAA,cACT;AAAA,cACA,KAAK,IAAI;AAAA,YACX;AAAA;AAAA,QACF;AAAA;AAAA;AAAA,EAEJ;AAEJ;AAEA,IAAO,oBAAQ;","names":[]}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import * as react from 'react';
|
|
2
|
+
import { HTMLAttributes, ReactNode } from 'react';
|
|
3
|
+
import { StatusTone } from '../StatusDot/StatusDot.js';
|
|
4
|
+
|
|
5
|
+
interface StatusPillProps extends HTMLAttributes<HTMLSpanElement> {
|
|
6
|
+
children: ReactNode;
|
|
7
|
+
tone?: StatusTone;
|
|
8
|
+
/** Pulsing live dot (default) vs a settled one. */
|
|
9
|
+
pulse?: boolean;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* A live state, named: a dot plus an uppercase mono label in an outlined pill.
|
|
13
|
+
*
|
|
14
|
+
* One component rather than a dot and a pill assembled per call site, which is
|
|
15
|
+
* how the same "available for work" chip ended up with three different dot
|
|
16
|
+
* sizes and two different pulse timings on one site.
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
* <StatusPill>Available for work</StatusPill>
|
|
20
|
+
* @example
|
|
21
|
+
* <StatusPill tone="warning" pulse={false}>Degraded</StatusPill>
|
|
22
|
+
*/
|
|
23
|
+
declare function StatusPill({ children, tone, pulse, className, ...rest }: StatusPillProps): react.JSX.Element;
|
|
24
|
+
|
|
25
|
+
export { StatusPill, type StatusPillProps, StatusPill as default };
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { cn } from "../../lib/cn.js";
|
|
3
|
+
import { StatusDot } from "../StatusDot/StatusDot.js";
|
|
4
|
+
function StatusPill({
|
|
5
|
+
children,
|
|
6
|
+
tone = "success",
|
|
7
|
+
pulse = true,
|
|
8
|
+
className,
|
|
9
|
+
...rest
|
|
10
|
+
}) {
|
|
11
|
+
return /* @__PURE__ */ jsxs(
|
|
12
|
+
"span",
|
|
13
|
+
{
|
|
14
|
+
className: cn(
|
|
15
|
+
"inline-flex items-center gap-2.5 rounded-(--radius-pill) border border-(--rule-2) bg-(--paper) py-1.5 ps-2.5 pe-3 eyebrow tracking-[0.12em] text-(--ink-2)",
|
|
16
|
+
className
|
|
17
|
+
),
|
|
18
|
+
...rest,
|
|
19
|
+
children: [
|
|
20
|
+
/* @__PURE__ */ jsx(StatusDot, { tone, pulse }),
|
|
21
|
+
children
|
|
22
|
+
]
|
|
23
|
+
}
|
|
24
|
+
);
|
|
25
|
+
}
|
|
26
|
+
var StatusPill_default = StatusPill;
|
|
27
|
+
export {
|
|
28
|
+
StatusPill,
|
|
29
|
+
StatusPill_default as default
|
|
30
|
+
};
|
|
31
|
+
//# sourceMappingURL=StatusPill.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/components/StatusPill/StatusPill.tsx"],"sourcesContent":["import type { HTMLAttributes, ReactNode } from 'react'\nimport { cn } from '../../lib/cn'\nimport { StatusDot, type StatusTone } from '../StatusDot/StatusDot'\n\nexport interface StatusPillProps extends HTMLAttributes<HTMLSpanElement> {\n children: ReactNode\n tone?: StatusTone\n /** Pulsing live dot (default) vs a settled one. */\n pulse?: boolean\n}\n\n/**\n * A live state, named: a dot plus an uppercase mono label in an outlined pill.\n *\n * One component rather than a dot and a pill assembled per call site, which is\n * how the same \"available for work\" chip ended up with three different dot\n * sizes and two different pulse timings on one site.\n *\n * @example\n * <StatusPill>Available for work</StatusPill>\n * @example\n * <StatusPill tone=\"warning\" pulse={false}>Degraded</StatusPill>\n */\nexport function StatusPill({\n children,\n tone = 'success',\n pulse = true,\n className,\n ...rest\n}: StatusPillProps) {\n return (\n <span\n className={cn(\n 'inline-flex items-center gap-2.5 rounded-(--radius-pill) border border-(--rule-2) bg-(--paper) py-1.5 ps-2.5 pe-3 eyebrow tracking-[0.12em] text-(--ink-2)',\n className,\n )}\n {...rest}\n >\n <StatusDot tone={tone} pulse={pulse} />\n {children}\n </span>\n )\n}\n\nexport default StatusPill\n"],"mappings":"AA+BI,SAOE,KAPF;AA9BJ,SAAS,UAAU;AACnB,SAAS,iBAAkC;AAqBpC,SAAS,WAAW;AAAA,EACzB;AAAA,EACA,OAAO;AAAA,EACP,QAAQ;AAAA,EACR;AAAA,EACA,GAAG;AACL,GAAoB;AAClB,SACE;AAAA,IAAC;AAAA;AAAA,MACC,WAAW;AAAA,QACT;AAAA,QACA;AAAA,MACF;AAAA,MACC,GAAG;AAAA,MAEJ;AAAA,4BAAC,aAAU,MAAY,OAAc;AAAA,QACpC;AAAA;AAAA;AAAA,EACH;AAEJ;AAEA,IAAO,qBAAQ;","names":[]}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import * as react from 'react';
|
|
2
|
+
import { ComponentProps } from 'react';
|
|
3
|
+
import * as SwitchPrimitive from '@radix-ui/react-switch';
|
|
4
|
+
|
|
5
|
+
type SwitchProps = ComponentProps<typeof SwitchPrimitive.Root>;
|
|
6
|
+
/**
|
|
7
|
+
* A setting that takes effect immediately.
|
|
8
|
+
*
|
|
9
|
+
* Distinct from `Checkbox`, and the distinction is not cosmetic: a switch
|
|
10
|
+
* applies on flip, a checkbox applies on submit. A switch inside a form with a
|
|
11
|
+
* Save button is a lie about when the change happened.
|
|
12
|
+
*
|
|
13
|
+
* The track fills with ink when on and the thumb is paper with a hairline —
|
|
14
|
+
* rather than a white thumb floating on a drop shadow, which this system does
|
|
15
|
+
* not have. The off state is a filled rule-coloured track, so the control still
|
|
16
|
+
* reads as a control on a white page.
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
* <Field label="Email notifications"><Switch defaultChecked /></Field>
|
|
20
|
+
*/
|
|
21
|
+
declare function Switch({ className, ...props }: SwitchProps): react.JSX.Element;
|
|
22
|
+
|
|
23
|
+
export { Switch, type SwitchProps, Switch as default };
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx } from "react/jsx-runtime";
|
|
3
|
+
import * as SwitchPrimitive from "@radix-ui/react-switch";
|
|
4
|
+
import { cn } from "../../lib/cn.js";
|
|
5
|
+
function Switch({ className, ...props }) {
|
|
6
|
+
return /* @__PURE__ */ jsx(
|
|
7
|
+
SwitchPrimitive.Root,
|
|
8
|
+
{
|
|
9
|
+
className: cn(
|
|
10
|
+
"group inline-flex h-5 w-9 shrink-0 cursor-pointer items-center rounded-(--radius-pill) border border-(--rule-2) bg-(--stone) px-0.5 transition-colors duration-(--duration-fast) hover:border-(--rule-hard) data-[state=checked]:border-(--accent) data-[state=checked]:bg-(--accent) disabled:opacity-(--disabled-opacity) disabled:pointer-events-none",
|
|
11
|
+
className
|
|
12
|
+
),
|
|
13
|
+
...props,
|
|
14
|
+
children: /* @__PURE__ */ jsx(SwitchPrimitive.Thumb, { className: "block size-3.5 translate-x-0 rounded-full bg-(--paper) transition-[transform,width] duration-(--duration-base) ease-(--ease-out-expo) group-active:w-5 data-[state=checked]:translate-x-4 rtl:data-[state=checked]:-translate-x-4 motion-reduce:transition-none" })
|
|
15
|
+
}
|
|
16
|
+
);
|
|
17
|
+
}
|
|
18
|
+
var Switch_default = Switch;
|
|
19
|
+
export {
|
|
20
|
+
Switch,
|
|
21
|
+
Switch_default as default
|
|
22
|
+
};
|
|
23
|
+
//# sourceMappingURL=Switch.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/components/Switch/Switch.tsx"],"sourcesContent":["'use client'\n\nimport * as SwitchPrimitive from '@radix-ui/react-switch'\nimport type { ComponentProps } from 'react'\nimport { cn } from '../../lib/cn'\n\nexport type SwitchProps = ComponentProps<typeof SwitchPrimitive.Root>\n\n/**\n * A setting that takes effect immediately.\n *\n * Distinct from `Checkbox`, and the distinction is not cosmetic: a switch\n * applies on flip, a checkbox applies on submit. A switch inside a form with a\n * Save button is a lie about when the change happened.\n *\n * The track fills with ink when on and the thumb is paper with a hairline —\n * rather than a white thumb floating on a drop shadow, which this system does\n * not have. The off state is a filled rule-coloured track, so the control still\n * reads as a control on a white page.\n *\n * @example\n * <Field label=\"Email notifications\"><Switch defaultChecked /></Field>\n */\nexport function Switch({ className, ...props }: SwitchProps) {\n return (\n <SwitchPrimitive.Root\n className={cn(\n 'group inline-flex h-5 w-9 shrink-0 cursor-pointer items-center rounded-(--radius-pill) border border-(--rule-2) bg-(--stone) px-0.5 transition-colors duration-(--duration-fast) hover:border-(--rule-hard) data-[state=checked]:border-(--accent) data-[state=checked]:bg-(--accent) disabled:opacity-(--disabled-opacity) disabled:pointer-events-none',\n className,\n )}\n {...props}\n >\n {/* The thumb narrows as it starts moving and rounds out again when it\n lands, which is what makes a 16px slide read as a deliberate flip\n rather than a jump. `transition-[transform,width]` and not `all`, so\n the track's own colour change keeps its shorter duration. */}\n <SwitchPrimitive.Thumb className=\"block size-3.5 translate-x-0 rounded-full bg-(--paper) transition-[transform,width] duration-(--duration-base) ease-(--ease-out-expo) group-active:w-5 data-[state=checked]:translate-x-4 rtl:data-[state=checked]:-translate-x-4 motion-reduce:transition-none\" />\n </SwitchPrimitive.Root>\n )\n}\n\nexport default Switch\n"],"mappings":";AAoCM;AAlCN,YAAY,qBAAqB;AAEjC,SAAS,UAAU;AAmBZ,SAAS,OAAO,EAAE,WAAW,GAAG,MAAM,GAAgB;AAC3D,SACE;AAAA,IAAC,gBAAgB;AAAA,IAAhB;AAAA,MACC,WAAW;AAAA,QACT;AAAA,QACA;AAAA,MACF;AAAA,MACC,GAAG;AAAA,MAMJ,8BAAC,gBAAgB,OAAhB,EAAsB,WAAU,mQAAkQ;AAAA;AAAA,EACrS;AAEJ;AAEA,IAAO,iBAAQ;","names":[]}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import * as react from 'react';
|
|
2
|
+
import { HTMLAttributes, TdHTMLAttributes, ThHTMLAttributes, TableHTMLAttributes } from 'react';
|
|
3
|
+
|
|
4
|
+
type TableAlign = 'start' | 'center' | 'end';
|
|
5
|
+
/**
|
|
6
|
+
* Which rules the table draws.
|
|
7
|
+
*
|
|
8
|
+
* `rows` is the default and the right answer for reading down a column: one
|
|
9
|
+
* hairline between records and nothing else, so the eye tracks a line across
|
|
10
|
+
* without a grid competing for it. `grid` adds the vertical rules a dense
|
|
11
|
+
* numeric table needs to keep columns apart. `bordered` puts an edge around the
|
|
12
|
+
* whole thing, for a table that sits loose on a page rather than inside a card
|
|
13
|
+
* that already has one. `none` is for a table inside something that draws its
|
|
14
|
+
* own structure.
|
|
15
|
+
*/
|
|
16
|
+
type TableBorders = 'rows' | 'grid' | 'bordered' | 'bordered-grid' | 'none';
|
|
17
|
+
interface TableProps extends TableHTMLAttributes<HTMLTableElement> {
|
|
18
|
+
/**
|
|
19
|
+
* Describes the table for a screen reader, which cannot see the heading above
|
|
20
|
+
* it. Rendered as a visually-hidden `<caption>` — required, because an
|
|
21
|
+
* unnamed table in a page with three tables is unnavigable.
|
|
22
|
+
*/
|
|
23
|
+
caption: string;
|
|
24
|
+
/** Prints the caption instead of hiding it. */
|
|
25
|
+
showCaption?: boolean;
|
|
26
|
+
/**
|
|
27
|
+
* Pins the header row while the body scrolls. Needs a bounded height on the
|
|
28
|
+
* container — otherwise the page scrolls, not the table, and nothing sticks.
|
|
29
|
+
*/
|
|
30
|
+
stickyHeader?: boolean;
|
|
31
|
+
/** Which rules to draw. See {@link TableBorders}. */
|
|
32
|
+
borders?: TableBorders;
|
|
33
|
+
/** Tightens the row padding, for a table that is mostly numbers. */
|
|
34
|
+
density?: 'comfortable' | 'compact';
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* A ruled data table.
|
|
38
|
+
*
|
|
39
|
+
* Wrapped in its own horizontally scrolling container, because a table is the
|
|
40
|
+
* one block that legitimately exceeds the measure — and a page that scrolls
|
|
41
|
+
* sideways as a whole is a layout bug, while a table that does is a table.
|
|
42
|
+
*
|
|
43
|
+
* That container is focusable, and it has to be. A scrollable region whose
|
|
44
|
+
* contents are not themselves focusable is unreachable by keyboard: there is no
|
|
45
|
+
* element to Tab to and therefore no way to press an arrow key at it, so the
|
|
46
|
+
* columns past the fold simply do not exist for anyone not using a mouse.
|
|
47
|
+
*
|
|
48
|
+
* No zebra striping at any border setting. In a monochrome system a striped row
|
|
49
|
+
* is a second surface competing with the page ground, and the hairline between
|
|
50
|
+
* rows is already enough to track a line across.
|
|
51
|
+
*
|
|
52
|
+
* @example
|
|
53
|
+
* <Table caption="Deploy history">
|
|
54
|
+
* <THead><TR><TH>Commit</TH><TH align="end" sortable sortDirection={dir} onSort={sort}>Duration</TH></TR></THead>
|
|
55
|
+
* <TBody><TR><TD>a1b2c3d</TD><TD align="end">2m 14s</TD></TR></TBody>
|
|
56
|
+
* </Table>
|
|
57
|
+
* @example
|
|
58
|
+
* <Table caption="Inventory" borders="bordered-grid" density="compact">…</Table>
|
|
59
|
+
*/
|
|
60
|
+
declare function Table({ caption, showCaption, stickyHeader, borders, density, className, children, ...rest }: TableProps): react.JSX.Element;
|
|
61
|
+
/** Header group. The rule under it is drawn by the table's border setting. */
|
|
62
|
+
declare function THead({ className, ...rest }: HTMLAttributes<HTMLTableSectionElement>): react.JSX.Element;
|
|
63
|
+
declare function TBody({ className, ...rest }: HTMLAttributes<HTMLTableSectionElement>): react.JSX.Element;
|
|
64
|
+
declare function TR({ className, ...rest }: HTMLAttributes<HTMLTableRowElement>): react.JSX.Element;
|
|
65
|
+
type SortDirection = 'ascending' | 'descending' | 'none';
|
|
66
|
+
interface THProps extends Omit<ThHTMLAttributes<HTMLTableCellElement>, 'align'> {
|
|
67
|
+
/** Which edge the column's contents sit against. Numbers belong at `end`. */
|
|
68
|
+
align?: TableAlign;
|
|
69
|
+
/** Makes the label a button and shows the sort marker. */
|
|
70
|
+
sortable?: boolean;
|
|
71
|
+
/**
|
|
72
|
+
* Which way this column is sorted. Also set as `aria-sort`, which is the
|
|
73
|
+
* only way a screen reader learns a table is ordered at all — a caret drawn
|
|
74
|
+
* in the header tells it nothing.
|
|
75
|
+
*/
|
|
76
|
+
sortDirection?: SortDirection;
|
|
77
|
+
onSort?: () => void;
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* A column label. Mono and uppercase, so it never reads as data.
|
|
81
|
+
*
|
|
82
|
+
* When sortable, the label becomes a `<button>` INSIDE the `<th>` rather than
|
|
83
|
+
* the `<th>` becoming clickable: a cell with a click handler is not focusable
|
|
84
|
+
* and not announced as a control, so the sort exists only for a mouse.
|
|
85
|
+
*
|
|
86
|
+
* Sorting is per column and opt-in. A table where every header is a button
|
|
87
|
+
* invites the reader to try sorting a column the data cannot be ordered by.
|
|
88
|
+
*/
|
|
89
|
+
declare function TH({ align, sortable, sortDirection, onSort, className, children, ...rest }: THProps): react.JSX.Element;
|
|
90
|
+
interface TDProps extends Omit<TdHTMLAttributes<HTMLTableCellElement>, 'align'> {
|
|
91
|
+
/** Which edge the cell's contents sit against. Match the column's header. */
|
|
92
|
+
align?: TableAlign;
|
|
93
|
+
}
|
|
94
|
+
declare function TD({ align, className, ...rest }: TDProps): react.JSX.Element;
|
|
95
|
+
|
|
96
|
+
export { type SortDirection, TBody, TD, type TDProps, TH, type THProps, THead, TR, Table, type TableAlign, type TableBorders, type TableProps, Table as default };
|