@sableui/react 0.8.2 → 0.9.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/AGENTS.md +1 -1
- package/CHANGELOG.md +83 -0
- package/bin/sable.mjs +26 -11
- package/dist/blocks/index.d.ts +1 -1
- package/dist/components/app-shell/app-shell.js +2 -12
- package/dist/components/app-shell/app-shell.meta.js +22 -0
- package/dist/components/app-shell/index.js +2 -0
- package/dist/components/app-shell/skip-link.js +28 -0
- package/dist/components/button/button.js +35 -8
- package/dist/components/button/button.meta.js +6 -1
- package/dist/components/card/card.meta.js +1 -1
- package/dist/components/command-menu/command-menu.js +27 -2
- package/dist/components/date-tile/date-tile.meta.js +1 -0
- package/dist/components/feedback/feedback.meta.js +1 -0
- package/dist/components/fieldset/fieldset.js +22 -13
- package/dist/components/fieldset/fieldset.meta.js +25 -1
- package/dist/components/form/form.js +45 -9
- package/dist/components/form/form.meta.js +12 -3
- package/dist/components/media-tile/media-tile.js +16 -2
- package/dist/components/media-tile/media-tile.meta.js +8 -0
- package/dist/components/radio-card/radio-card.js +6 -2
- package/dist/components/radio-card/radio-card.meta.js +5 -0
- package/dist/components/sheet/sheet.js +1 -0
- package/dist/components/stepper/stepper.js +7 -2
- package/dist/components/stepper/stepper.meta.js +1 -1
- package/dist/components/toast/toast.js +2 -0
- package/dist/file-hashes.json +28 -27
- package/dist/index.d.ts +109 -22
- package/dist/index.js +1 -1
- package/dist/{meta-CcE_SeFu.d.ts → meta-DVG8ceze.d.ts} +9 -0
- package/dist/patterns/index.d.ts +1 -1
- package/dist/styles.css +30 -0
- package/llms/components/app-shell.md +26 -0
- package/llms/components/button.md +2 -1
- package/llms/components/card.md +1 -1
- package/llms/components/fieldset.md +21 -1
- package/llms/components/form.md +11 -2
- package/llms/components/media-tile.md +32 -0
- package/llms/components/radio-card.md +1 -0
- package/llms/components/stepper.md +1 -1
- package/llms/tasks/creating-a-preset.md +4 -1
- package/llms-full.txt +99 -7
- package/llms.txt +2 -2
- package/package.json +1 -1
- package/registry.json +89 -10
- package/scripts/grade.mjs +14 -3
- package/scripts/sources.mjs +10 -2
- package/skills/sable/SKILL.md +23 -7
|
@@ -23,16 +23,18 @@ function Form({
|
|
|
23
23
|
...props
|
|
24
24
|
}) {
|
|
25
25
|
const blockedRef = useRef(false);
|
|
26
|
+
const formRef = useRef(null);
|
|
26
27
|
const handleSubmit = (event) => {
|
|
27
28
|
blockedRef.current = false;
|
|
28
29
|
const form = event.currentTarget;
|
|
30
|
+
formRef.current = form;
|
|
29
31
|
if (!form.checkValidity()) {
|
|
30
32
|
event.preventDefault();
|
|
31
33
|
blockedRef.current = true;
|
|
32
34
|
form.reportValidity();
|
|
33
35
|
return;
|
|
34
36
|
}
|
|
35
|
-
onSubmit?.(event);
|
|
37
|
+
afterSubmit(form, onSubmit?.(event));
|
|
36
38
|
};
|
|
37
39
|
if (noValidate) {
|
|
38
40
|
return /* @__PURE__ */ jsx(
|
|
@@ -40,7 +42,10 @@ function Form({
|
|
|
40
42
|
{
|
|
41
43
|
noValidate: true,
|
|
42
44
|
className: cn("flex flex-col gap-6", className),
|
|
43
|
-
onSubmit
|
|
45
|
+
onSubmit: (event) => {
|
|
46
|
+
const form = event.currentTarget;
|
|
47
|
+
afterSubmit(form, onSubmit?.(event));
|
|
48
|
+
},
|
|
44
49
|
...props
|
|
45
50
|
}
|
|
46
51
|
);
|
|
@@ -54,13 +59,33 @@ function Form({
|
|
|
54
59
|
actionsRef,
|
|
55
60
|
onSubmit: handleSubmit,
|
|
56
61
|
onFormSubmit: onFormSubmit ? (values, details) => {
|
|
57
|
-
if (
|
|
62
|
+
if (blockedRef.current) return;
|
|
63
|
+
const result = onFormSubmit(values, details);
|
|
64
|
+
if (formRef.current && isPromise(result)) afterSubmit(formRef.current, result);
|
|
58
65
|
} : void 0,
|
|
59
66
|
...props
|
|
60
67
|
}
|
|
61
68
|
);
|
|
62
69
|
}
|
|
63
70
|
Form.displayName = "Form";
|
|
71
|
+
var isPromise = (value) => typeof value?.then === "function";
|
|
72
|
+
function afterSubmit(form, result) {
|
|
73
|
+
const focus = () => requestAnimationFrame(() => {
|
|
74
|
+
if (!form.isConnected) return;
|
|
75
|
+
const active = document.activeElement;
|
|
76
|
+
if (active && form.contains(active) && active.getAttribute("aria-invalid") === "true") return;
|
|
77
|
+
const first = [...form.querySelectorAll('[aria-invalid="true"]')].find(
|
|
78
|
+
(el) => !(el instanceof HTMLInputElement && el.type === "hidden") && !el.closest("[aria-hidden='true'],[inert]")
|
|
79
|
+
);
|
|
80
|
+
if (!first) return;
|
|
81
|
+
first.focus();
|
|
82
|
+
if (first instanceof HTMLInputElement && /^(?:text|search|email|tel|url|password|number)?$/.test(first.type)) {
|
|
83
|
+
first.select();
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
if (isPromise(result)) result.finally(focus);
|
|
87
|
+
else focus();
|
|
88
|
+
}
|
|
64
89
|
function Field({
|
|
65
90
|
label,
|
|
66
91
|
description,
|
|
@@ -73,8 +98,10 @@ function Field({
|
|
|
73
98
|
validationMode,
|
|
74
99
|
validationDebounceTime,
|
|
75
100
|
orientation = "vertical",
|
|
101
|
+
trailing,
|
|
76
102
|
className,
|
|
77
|
-
children
|
|
103
|
+
children,
|
|
104
|
+
...props
|
|
78
105
|
}) {
|
|
79
106
|
const generatedId = useId();
|
|
80
107
|
const child = Children.only(children);
|
|
@@ -137,22 +164,31 @@ function Field({
|
|
|
137
164
|
validate,
|
|
138
165
|
validationMode,
|
|
139
166
|
validationDebounceTime,
|
|
167
|
+
...props,
|
|
140
168
|
children: orientation === "horizontal" ? (
|
|
141
169
|
// Control beside a text column. The control sits in a box one LABEL
|
|
142
170
|
// line tall (`h-[1lh]` in the label's own type step), so it centres
|
|
143
171
|
// on the first line and stays there when the label wraps. Helper and
|
|
144
172
|
// error text live in the label's column, not under the control.
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
173
|
+
// A Switch (the tallest, --spacing(6)) is taller than that line, so
|
|
174
|
+
// the row is padded by the difference: the control stays inside the
|
|
175
|
+
// Field's box, where an AccordionPanel or any clipping parent keeps
|
|
176
|
+
// it whole.
|
|
177
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-start gap-2 py-[max(0px,calc((--spacing(6)_-_1lh)/2))] text-label-lg", children: [
|
|
178
|
+
/* @__PURE__ */ jsx("div", { className: "flex h-[1lh] shrink-0 items-center", children: control }),
|
|
179
|
+
/* @__PURE__ */ jsxs("div", { className: "flex min-w-0 flex-1 flex-col gap-1.5", children: [
|
|
148
180
|
labelNode,
|
|
149
181
|
descriptionNode,
|
|
150
182
|
errorNode
|
|
151
|
-
] })
|
|
183
|
+
] }),
|
|
184
|
+
trailing != null ? /* @__PURE__ */ jsx("div", { className: "flex h-[1lh] shrink-0 items-center", children: trailing }) : null
|
|
152
185
|
] })
|
|
153
186
|
) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
154
187
|
labelNode,
|
|
155
|
-
|
|
188
|
+
trailing != null ? /* @__PURE__ */ jsxs("div", { className: "flex items-start gap-2", children: [
|
|
189
|
+
/* @__PURE__ */ jsx("div", { className: "min-w-0 flex-1", children: control }),
|
|
190
|
+
trailing
|
|
191
|
+
] }) : control,
|
|
156
192
|
descriptionNode,
|
|
157
193
|
errorNode
|
|
158
194
|
] })
|
|
@@ -72,7 +72,12 @@ var formMeta = {
|
|
|
72
72
|
{
|
|
73
73
|
name: "Form",
|
|
74
74
|
type: '{ validationMode?: "onSubmit" | "onBlur" | "onChange"; errors?: Record<string, string | string[]>; onFormSubmit?: (values, details) => void; actionsRef?: RefObject<Form.Actions> } + ComponentPropsWithRef<"form">',
|
|
75
|
-
description: 'A <form> laid out as a vertical column with comfortable field spacing (override with className, e.g. gap-4). On submit it validates every Field (native constraints plus Field validate), shows each message under its field, focuses the first invalid control, and only then calls onSubmit. validationMode defaults to "onSubmit" (then live re-validation on change). errors shows external (e.g. server) messages keyed by Field name and clears one when its field is edited. onFormSubmit receives the named Field values and prevents the native submit. A control outside any Field is still checked by the browser before onSubmit runs. Validating in your own code (react-hook-form, a schema, state)? Pass noValidate: onSubmit always runs and each Field shows the error you set; without it an empty required Field stops the submit before your handler can set that error.'
|
|
75
|
+
description: 'A <form> laid out as a vertical column with comfortable field spacing (override with className, e.g. gap-4). On submit it validates every Field (native constraints plus Field validate), shows each message under its field, focuses the first invalid control, and only then calls onSubmit. validationMode defaults to "onSubmit" (then live re-validation on change). errors shows external (e.g. server) messages keyed by Field name and clears one when its field is edited. onFormSubmit receives the named Field values and prevents the native submit. A control outside any Field is still checked by the browser before onSubmit runs. Validating in your own code (react-hook-form, a schema, state)? Pass noValidate: onSubmit always runs and each Field shows the error you set; without it an empty required Field stops the submit before your handler can set that error. Either way, once onSubmit returns (or the promise it returns settles), the first control a Field marks invalid takes focus.'
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
name: "onSubmit",
|
|
79
|
+
type: "(event: FormEvent<HTMLFormElement>) => unknown",
|
|
80
|
+
description: "The native submit handler. It may return a promise. When it returns, or its promise settles, the first control a Field marks invalid takes focus, so errors your own code sets get the same treatment as Sable's validation."
|
|
76
81
|
},
|
|
77
82
|
{
|
|
78
83
|
name: "validationMode",
|
|
@@ -105,8 +110,8 @@ var formMeta = {
|
|
|
105
110
|
subcomponents: [
|
|
106
111
|
{
|
|
107
112
|
name: "Field",
|
|
108
|
-
type: '{ label?: ReactNode; description?: ReactNode; error?: ReactNode; required?: boolean; messages?: Partial<Record<"valueMissing" | "typeMismatch" | "patternMismatch" | "tooShort" | "tooLong" | "rangeUnderflow" | "rangeOverflow" | "stepMismatch" | "badInput", ReactNode>>; disabled?: boolean; name?: string; validate?: (value, formValues) => string | string[] | null | Promise<\u2026>; validationMode?: "onSubmit" | "onBlur" | "onChange"; validationDebounceTime?: number; orientation?: "vertical" | "horizontal"; className?: string; children: ReactElement }',
|
|
109
|
-
description: `Wraps a single control element (e.g. <Input />) on Base UI Field and wires label (htmlFor + aria-labelledby), helper text and error (aria-describedby), aria-invalid, and required onto it automatically. Base UI-backed controls (Input, Textarea, Select, Combobox, NumberField, Checkbox, Switch, Slider, OtpField\u2026) register through context; the child is also cloned so plain elements keep the same wiring. error renders a destructive message and takes precedence; without it the slot shows the field's messages entry for the native constraint that failed (messages={{ valueMissing: "Enter a subject." }}), else Base UI's validation message (the browser's wording for a native constraint, validate, or Form errors). required adds the native attribute plus a visual asterisk (defaults to false). disabled disables the control and dims the label. name keys the field in Form errors / onFormSubmit. validationMode overrides the Form's. orientation defaults to "vertical" (label \u2192 control); "horizontal" places control then label inline for a single checkbox / switch / radio \u2014 the control centres on the label's first line (it stays there when the label wraps) and description/error sit in the label's column. The root, label, description, error and control carry data-invalid / data-valid, data-touched, data-dirty, data-filled, data-focused and data-disabled for state styling.`
|
|
113
|
+
type: '{ label?: ReactNode; description?: ReactNode; error?: ReactNode; required?: boolean; messages?: Partial<Record<"valueMissing" | "typeMismatch" | "patternMismatch" | "tooShort" | "tooLong" | "rangeUnderflow" | "rangeOverflow" | "stepMismatch" | "badInput", ReactNode>>; disabled?: boolean; name?: string; validate?: (value, formValues) => string | string[] | null | Promise<\u2026>; validationMode?: "onSubmit" | "onBlur" | "onChange"; validationDebounceTime?: number; orientation?: "vertical" | "horizontal"; trailing?: ReactNode; className?: string; children: ReactElement } + other div props on the root',
|
|
114
|
+
description: `Wraps a single control element (e.g. <Input />) on Base UI Field and wires label (htmlFor + aria-labelledby), helper text and error (aria-describedby), aria-invalid, and required onto it automatically. Base UI-backed controls (Input, Textarea, Select, Combobox, NumberField, Checkbox, Switch, Slider, OtpField\u2026) register through context; the child is also cloned so plain elements keep the same wiring. error renders a destructive message and takes precedence; without it the slot shows the field's messages entry for the native constraint that failed (messages={{ valueMissing: "Enter a subject." }}), else Base UI's validation message (the browser's wording for a native constraint, validate, or Form errors). required adds the native attribute plus a visual asterisk (defaults to false). disabled disables the control and dims the label. name keys the field in Form errors / onFormSubmit. validationMode overrides the Form's. orientation defaults to "vertical" (label \u2192 control); "horizontal" places control then label inline for a single checkbox / switch / radio \u2014 the control centres on the label's first line (it stays there when the label wraps) and description/error sit in the label's column. trailing puts something at the end of the control's row: vertically, beside the control (a promo code's Apply button, the control taking the rest); horizontally, at the end of the label's first line (a facet count). Other props (data-*, id, aria-*) land on the root. The root, label, description, error and control carry data-invalid / data-valid, data-touched, data-dirty, data-filled, data-focused and data-disabled for state styling.`
|
|
110
115
|
}
|
|
111
116
|
],
|
|
112
117
|
tokensUsed: ["muted-foreground", "destructive"],
|
|
@@ -125,6 +130,10 @@ var formMeta = {
|
|
|
125
130
|
label: "Field with an error",
|
|
126
131
|
code: '<Field label="Password" error="Must be at least 8 characters."><Input type="password" /></Field>'
|
|
127
132
|
},
|
|
133
|
+
{
|
|
134
|
+
label: "An input with its action",
|
|
135
|
+
code: '<Field label="Promo code" error={promoError} trailing={<Button variant="secondary" onClick={applyPromo}>Apply</Button>}><Input name="promo" autoCapitalize="characters" /></Field>'
|
|
136
|
+
},
|
|
128
137
|
{
|
|
129
138
|
label: "Your own wording for a missing value",
|
|
130
139
|
code: '<Form onSubmit={handleSubmit}><Field label="Subject" required messages={{ valueMissing: "Enter a subject." }}><Input name="subject" /></Field><Button type="submit">Send</Button></Form>'
|
|
@@ -28,7 +28,10 @@ function TileLink({
|
|
|
28
28
|
function MediaTile({
|
|
29
29
|
title,
|
|
30
30
|
subtitle,
|
|
31
|
+
meta,
|
|
31
32
|
src,
|
|
33
|
+
srcSet,
|
|
34
|
+
sizes,
|
|
32
35
|
alt,
|
|
33
36
|
seed,
|
|
34
37
|
aspect = "square",
|
|
@@ -84,7 +87,18 @@ function MediaTile({
|
|
|
84
87
|
),
|
|
85
88
|
children: subtitle
|
|
86
89
|
}
|
|
87
|
-
) : /* @__PURE__ */ jsx("p", { className: mediaTileSubtitleVariants({ size }), children: subtitle }) : null
|
|
90
|
+
) : /* @__PURE__ */ jsx("p", { className: mediaTileSubtitleVariants({ size }), children: subtitle }) : null,
|
|
91
|
+
meta != null ? /* @__PURE__ */ jsx(
|
|
92
|
+
"div",
|
|
93
|
+
{
|
|
94
|
+
"data-slot": "media-tile-meta",
|
|
95
|
+
className: cn(
|
|
96
|
+
mediaTileSubtitleVariants({ size }),
|
|
97
|
+
"flex flex-wrap items-center gap-x-2 gap-y-1 overflow-visible whitespace-normal"
|
|
98
|
+
),
|
|
99
|
+
children: meta
|
|
100
|
+
}
|
|
101
|
+
) : null
|
|
88
102
|
] }),
|
|
89
103
|
/* @__PURE__ */ jsxs(
|
|
90
104
|
AspectBox,
|
|
@@ -93,7 +107,7 @@ function MediaTile({
|
|
|
93
107
|
"data-slot": "media-tile-cover",
|
|
94
108
|
className: "order-first rounded-(--media-tile-radius)",
|
|
95
109
|
children: [
|
|
96
|
-
/* @__PURE__ */ jsx(MediaImage, { src, alt, seed: seed ?? titleText, loading }),
|
|
110
|
+
/* @__PURE__ */ jsx(MediaImage, { src, srcSet, sizes, alt, seed: seed ?? titleText, loading }),
|
|
97
111
|
linked || action != null ? (
|
|
98
112
|
// The tile answers the pointer and the keyboard alike: a light scrim
|
|
99
113
|
// deepens the cover so the action reads over any artwork. Hover only
|
|
@@ -3,6 +3,7 @@ var mediaTileMeta = {
|
|
|
3
3
|
name: "MediaTile",
|
|
4
4
|
summary: "A cover-first tile \u2014 square, 16:9 or poster artwork with an optional play-style action over it, then a title and a subtitle that can each be links \u2014 that is one link as a whole without nesting its other controls inside one.",
|
|
5
5
|
status: "beta",
|
|
6
|
+
composition: 'AspectBox, MediaImage, a stretched link (or Card render), Badge appearance="overlay", Button',
|
|
6
7
|
keywords: [
|
|
7
8
|
"album",
|
|
8
9
|
"album art",
|
|
@@ -63,7 +64,10 @@ var mediaTileMeta = {
|
|
|
63
64
|
props: [
|
|
64
65
|
{ name: "title", type: "ReactNode", description: "The item's name. Seeds the fallback art when it is a string." },
|
|
65
66
|
{ name: "subtitle", type: "ReactNode", description: "The second line \u2014 an artist, a channel, a year." },
|
|
67
|
+
{ name: "meta", type: "ReactNode", description: "Lines under the subtitle, in its muted type: a price beside its sale price, a count of finishes, a status Badge. A wrapping block, where the subtitle is one truncated line. Use it for a product tile instead of building one." },
|
|
66
68
|
{ name: "src", type: "string", description: "Cover image URL. Without one, or when it fails, the cover is GradientArt seeded by the item." },
|
|
69
|
+
{ name: "srcSet", type: "string", description: "Responsive cover candidates, passed to the `<img>`." },
|
|
70
|
+
{ name: "sizes", type: "string", description: 'The cover\'s layout width for `srcSet`, e.g. `"(min-width: 64rem) 25vw, 50vw"`.' },
|
|
67
71
|
{ name: "alt", type: "string", default: '""', description: "Cover alt text. Empty by default: the title below already names the item." },
|
|
68
72
|
{ name: "seed", type: "string | number", description: "Seeds the fallback art. Defaults to `title` when it is a string." },
|
|
69
73
|
{
|
|
@@ -110,6 +114,10 @@ var mediaTileMeta = {
|
|
|
110
114
|
"Truncated titles keep their full text in the link's accessible name and in a `title` tooltip."
|
|
111
115
|
],
|
|
112
116
|
examples: [
|
|
117
|
+
{
|
|
118
|
+
label: "Product tile",
|
|
119
|
+
code: '<Grid minItemWidth="14rem" gap={4}>\n {products.map((product) => (\n <MediaTile\n key={product.id}\n aspect={4 / 5}\n title={product.name}\n subtitle={product.material}\n src={product.image}\n srcSet={product.imageSet}\n sizes="(min-width: 64rem) 25vw, 50vw"\n render={<Link to={`/products/${product.slug}`} />}\n badge={product.isNew ? <Badge appearance="overlay">New</Badge> : undefined}\n meta={\n <>\n <span className="text-foreground">{formatPrice(product.price)}</span>\n <span>{product.finishes.length} finishes</span>\n </>\n }\n />\n ))}\n</Grid>'
|
|
120
|
+
},
|
|
113
121
|
{
|
|
114
122
|
label: "Album grid",
|
|
115
123
|
code: '<Grid minItemWidth="10rem" gap={3}>\n {albums.map((album) => (\n <MediaTile\n key={album.id}\n title={album.title}\n subtitle={album.artist}\n src={album.cover}\n render={<Link to={`/albums/${album.id}`} />}\n subtitleRender={<Link to={`/artists/${album.artistId}`} />}\n action={\n <Button size="icon" aria-label={`Play ${album.title}`} onClick={() => play(album)}>\n <Icon name="play" variant="solid" />\n </Button>\n }\n />\n ))}\n</Grid>'
|
|
@@ -15,6 +15,7 @@ function RadioCard({
|
|
|
15
15
|
className,
|
|
16
16
|
title,
|
|
17
17
|
description,
|
|
18
|
+
trailing,
|
|
18
19
|
icon,
|
|
19
20
|
tone = "neutral",
|
|
20
21
|
titleClassName,
|
|
@@ -40,8 +41,11 @@ function RadioCard({
|
|
|
40
41
|
)
|
|
41
42
|
}
|
|
42
43
|
);
|
|
43
|
-
const content = /* @__PURE__ */ jsxs("span", { className: "flex min-w-0 flex-col gap-0.5", children: [
|
|
44
|
-
/* @__PURE__ */
|
|
44
|
+
const content = /* @__PURE__ */ jsxs("span", { className: "flex min-w-0 flex-1 flex-col gap-0.5", children: [
|
|
45
|
+
trailing != null ? /* @__PURE__ */ jsxs("span", { className: "flex items-baseline justify-between gap-3", children: [
|
|
46
|
+
/* @__PURE__ */ jsx("span", { className: cn("min-w-0 text-title-sm", titleClassName), children: title }),
|
|
47
|
+
/* @__PURE__ */ jsx("span", { className: "shrink-0 text-body-md text-foreground tabular-nums", children: trailing })
|
|
48
|
+
] }) : /* @__PURE__ */ jsx("span", { className: cn("text-title-sm", titleClassName), children: title }),
|
|
45
49
|
description ? /* @__PURE__ */ jsx(
|
|
46
50
|
"span",
|
|
47
51
|
{
|
|
@@ -65,6 +65,11 @@ var radioCardMeta = {
|
|
|
65
65
|
type: "ReactNode",
|
|
66
66
|
description: "Optional supporting copy beneath the title (text-body-sm, muted)."
|
|
67
67
|
},
|
|
68
|
+
{
|
|
69
|
+
name: "trailing",
|
|
70
|
+
type: "ReactNode",
|
|
71
|
+
description: "End-aligned on the title's line: a price, a delivery date, a count. The title takes the rest of the row."
|
|
72
|
+
},
|
|
68
73
|
{
|
|
69
74
|
name: "icon",
|
|
70
75
|
type: "ReactNode",
|
|
@@ -50,6 +50,7 @@ function SheetContent({
|
|
|
50
50
|
{
|
|
51
51
|
ref: mergedRef,
|
|
52
52
|
initialFocus: initialFocus === void 0 ? (type) => type === "touch" ? popupRef.current : true : initialFocus,
|
|
53
|
+
"data-sable-sheet": resolvedSide,
|
|
53
54
|
className: cn(sheetVariants({ side }), className),
|
|
54
55
|
...props,
|
|
55
56
|
children: /* @__PURE__ */ jsx(Base.Content, { className: "contents", children: /* @__PURE__ */ jsx(PresentationProvider, { value: resolvedSide === "bottom" ? "sheet" : "page", children }) })
|
|
@@ -41,7 +41,8 @@ function Stepper({
|
|
|
41
41
|
// isolate: the indicators' z-10 (above the connector) stays inside the
|
|
42
42
|
// stepper instead of rising over sticky headers and nav bars.
|
|
43
43
|
"isolate flex w-full",
|
|
44
|
-
|
|
44
|
+
// A container, so the steps compact on the stepper's own width.
|
|
45
|
+
orientation === "horizontal" ? "@container/stepper flex-row items-start gap-3" : "flex-col",
|
|
45
46
|
className
|
|
46
47
|
),
|
|
47
48
|
...props,
|
|
@@ -94,7 +95,11 @@ function StepperItem({
|
|
|
94
95
|
{
|
|
95
96
|
className: cn(
|
|
96
97
|
"flex min-w-0 flex-col gap-0.5 text-start",
|
|
97
|
-
!dot && "min-h-(--control-xs) justify-center"
|
|
98
|
+
!dot && "min-h-(--control-xs) justify-center",
|
|
99
|
+
// Narrow and horizontal: three two-word titles wrapped under their
|
|
100
|
+
// connectors at 390px. The current step keeps its title; the rest
|
|
101
|
+
// are discs on screen and full titles to a screen reader.
|
|
102
|
+
orientation === "horizontal" && !isCurrent && "@max-md/stepper:sr-only"
|
|
98
103
|
),
|
|
99
104
|
children: [
|
|
100
105
|
/* @__PURE__ */ jsxs(
|
|
@@ -73,7 +73,7 @@ var stepperMeta = {
|
|
|
73
73
|
name: "orientation",
|
|
74
74
|
type: '"horizontal" | "vertical"',
|
|
75
75
|
default: '"horizontal"',
|
|
76
|
-
description: "Horizontal for a short flow across the top of a form; vertical for long flows, narrow columns or steps with descriptions."
|
|
76
|
+
description: "Horizontal for a short flow across the top of a form; vertical for long flows, narrow columns or steps with descriptions. A horizontal stepper narrower than 28rem (a phone) compacts itself: every disc stays, only the current step's title shows, and the other titles stay for screen readers. No breakpoint code needed."
|
|
77
77
|
},
|
|
78
78
|
{
|
|
79
79
|
name: "indicator",
|
|
@@ -103,6 +103,8 @@ function ToastViewport({
|
|
|
103
103
|
return /* @__PURE__ */ jsx(ToastViewportContext.Provider, { value: { position, stacked }, children: /* @__PURE__ */ jsx(ToastPortal, { children: /* @__PURE__ */ jsx(
|
|
104
104
|
Base.Viewport,
|
|
105
105
|
{
|
|
106
|
+
"data-sable-toast-viewport": "",
|
|
107
|
+
"data-position": position,
|
|
106
108
|
className: cn(
|
|
107
109
|
"fixed z-(--z-toast) w-95 max-w-[90vw] outline-none",
|
|
108
110
|
// Stacked toasts are positioned against the viewport, not laid out in it.
|
package/dist/file-hashes.json
CHANGED
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
"dist/blocks/goal-progress-block.meta.js": "3e5b376bf58bdb4b",
|
|
25
25
|
"dist/blocks/hero-block.js": "ac74e80d4d7d2ad9",
|
|
26
26
|
"dist/blocks/hero-block.meta.js": "2dd2e5be2472a563",
|
|
27
|
-
"dist/blocks/index.d.ts": "
|
|
27
|
+
"dist/blocks/index.d.ts": "7dc3a0506334de38",
|
|
28
28
|
"dist/blocks/index.js": "8116f4581aad860e",
|
|
29
29
|
"dist/blocks/invite-team-block.js": "be4df15518252535",
|
|
30
30
|
"dist/blocks/invite-team-block.meta.js": "fd57b632f0f32276",
|
|
@@ -89,9 +89,10 @@
|
|
|
89
89
|
"dist/components/app-header/app-header.meta.js": "708f75abae1bb376",
|
|
90
90
|
"dist/components/app-header/index.js": "012804273e56f06d",
|
|
91
91
|
"dist/components/app-shell/app-shell-dock.js": "1038582b11567a8a",
|
|
92
|
-
"dist/components/app-shell/app-shell.js": "
|
|
93
|
-
"dist/components/app-shell/app-shell.meta.js": "
|
|
94
|
-
"dist/components/app-shell/index.js": "
|
|
92
|
+
"dist/components/app-shell/app-shell.js": "88d2d0d1f4e11374",
|
|
93
|
+
"dist/components/app-shell/app-shell.meta.js": "77c4bc3e2fa16ec0",
|
|
94
|
+
"dist/components/app-shell/index.js": "976c14a186f290ec",
|
|
95
|
+
"dist/components/app-shell/skip-link.js": "59f149e155942e69",
|
|
95
96
|
"dist/components/area-chart/area-chart.js": "ddcc0a0f65328dea",
|
|
96
97
|
"dist/components/area-chart/area-chart.meta.js": "ba9ac76647282b0c",
|
|
97
98
|
"dist/components/area-chart/index.js": "51abe421742a4b41",
|
|
@@ -127,15 +128,15 @@
|
|
|
127
128
|
"dist/components/bubble/bubble.meta.js": "2202d4226cf79eb6",
|
|
128
129
|
"dist/components/bubble/bubble.variants.js": "b1eefabd9f0bb425",
|
|
129
130
|
"dist/components/bubble/index.js": "dfdc5326d5050d12",
|
|
130
|
-
"dist/components/button/button.js": "
|
|
131
|
-
"dist/components/button/button.meta.js": "
|
|
131
|
+
"dist/components/button/button.js": "85d996d9309e9e18",
|
|
132
|
+
"dist/components/button/button.meta.js": "aa62fe875a12ac8a",
|
|
132
133
|
"dist/components/button/button.variants.js": "d3054d6389644df0",
|
|
133
134
|
"dist/components/button/index.js": "1ef5e4068111496a",
|
|
134
135
|
"dist/components/calendar/calendar.js": "91321f1ea8843a11",
|
|
135
136
|
"dist/components/calendar/calendar.meta.js": "46565db6017d2706",
|
|
136
137
|
"dist/components/calendar/index.js": "24c1d513b60c972a",
|
|
137
138
|
"dist/components/card/card.js": "0b864f24d922a0c9",
|
|
138
|
-
"dist/components/card/card.meta.js": "
|
|
139
|
+
"dist/components/card/card.meta.js": "2cee6606ed8720cc",
|
|
139
140
|
"dist/components/card/index.js": "d6b4b33cd2a0c862",
|
|
140
141
|
"dist/components/carousel/carousel.js": "09aef818b8130d57",
|
|
141
142
|
"dist/components/carousel/carousel.meta.js": "d45f0086b2e40632",
|
|
@@ -169,7 +170,7 @@
|
|
|
169
170
|
"dist/components/combobox/combobox.meta.js": "38ef70bf96a3cf87",
|
|
170
171
|
"dist/components/combobox/index.js": "f8f9863cf4f7e59b",
|
|
171
172
|
"dist/components/command-menu/command-menu-trigger.js": "30b1606fb611c758",
|
|
172
|
-
"dist/components/command-menu/command-menu.js": "
|
|
173
|
+
"dist/components/command-menu/command-menu.js": "6991ea75f72a0188",
|
|
173
174
|
"dist/components/command-menu/command-menu.meta.js": "dde6c79e36a55f25",
|
|
174
175
|
"dist/components/command-menu/index.js": "c49dd188ea788ba5",
|
|
175
176
|
"dist/components/context-card/context-card.js": "ab28de9684e32eea",
|
|
@@ -185,7 +186,7 @@
|
|
|
185
186
|
"dist/components/date-picker/date-picker.meta.js": "a56f356cf633d7e8",
|
|
186
187
|
"dist/components/date-picker/index.js": "44e1227972891934",
|
|
187
188
|
"dist/components/date-tile/date-tile.js": "7458bf733bc64ab2",
|
|
188
|
-
"dist/components/date-tile/date-tile.meta.js": "
|
|
189
|
+
"dist/components/date-tile/date-tile.meta.js": "0bc0081c8cb73535",
|
|
189
190
|
"dist/components/date-tile/index.js": "684eef3a72b11651",
|
|
190
191
|
"dist/components/destructive-action-modal/destructive-action-modal.js": "3806d39cb3fb0cdf",
|
|
191
192
|
"dist/components/destructive-action-modal/destructive-action-modal.meta.js": "e81a94de90f8eccf",
|
|
@@ -204,10 +205,10 @@
|
|
|
204
205
|
"dist/components/entity/entity.variants.js": "8838fb5cf6f304f1",
|
|
205
206
|
"dist/components/entity/index.js": "1397602aa7068da8",
|
|
206
207
|
"dist/components/feedback/feedback.js": "af27a7be7d8e0552",
|
|
207
|
-
"dist/components/feedback/feedback.meta.js": "
|
|
208
|
+
"dist/components/feedback/feedback.meta.js": "a37f92831e6e24f2",
|
|
208
209
|
"dist/components/feedback/index.js": "fd1ab40c9d879286",
|
|
209
|
-
"dist/components/fieldset/fieldset.js": "
|
|
210
|
-
"dist/components/fieldset/fieldset.meta.js": "
|
|
210
|
+
"dist/components/fieldset/fieldset.js": "08d02302b6866f8e",
|
|
211
|
+
"dist/components/fieldset/fieldset.meta.js": "64be6d633de27e04",
|
|
211
212
|
"dist/components/fieldset/index.js": "9def974be7511a5c",
|
|
212
213
|
"dist/components/file-dropzone/file-dropzone.js": "668f2325feba6b6d",
|
|
213
214
|
"dist/components/file-dropzone/file-dropzone.meta.js": "6f7f0601319576ae",
|
|
@@ -216,8 +217,8 @@
|
|
|
216
217
|
"dist/components/font-picker/font-picker.js": "1d76108197a03759",
|
|
217
218
|
"dist/components/font-picker/font-picker.meta.js": "846401404a0e03c5",
|
|
218
219
|
"dist/components/font-picker/index.js": "eb8b97fcda8ff592",
|
|
219
|
-
"dist/components/form/form.js": "
|
|
220
|
-
"dist/components/form/form.meta.js": "
|
|
220
|
+
"dist/components/form/form.js": "3a724e6891a6a3df",
|
|
221
|
+
"dist/components/form/form.meta.js": "1e377418406a019d",
|
|
221
222
|
"dist/components/form/index.js": "6c3047e437b10fba",
|
|
222
223
|
"dist/components/gliding-highlight/gliding-highlight.js": "c424c85af1cc5665",
|
|
223
224
|
"dist/components/gliding-highlight/gliding-highlight.meta.js": "4aecad0e1aabea90",
|
|
@@ -253,8 +254,8 @@
|
|
|
253
254
|
"dist/components/media-image/media-image.js": "f68499e9f84b6e64",
|
|
254
255
|
"dist/components/media-image/media-image.meta.js": "0778fd790511a701",
|
|
255
256
|
"dist/components/media-tile/index.js": "00281f6f8cc246d1",
|
|
256
|
-
"dist/components/media-tile/media-tile.js": "
|
|
257
|
-
"dist/components/media-tile/media-tile.meta.js": "
|
|
257
|
+
"dist/components/media-tile/media-tile.js": "faf79c27ec15c8d4",
|
|
258
|
+
"dist/components/media-tile/media-tile.meta.js": "6a21970f9b463812",
|
|
258
259
|
"dist/components/media-tile/media-tile.variants.js": "e22c08004b4a55ad",
|
|
259
260
|
"dist/components/menu/index.js": "44a0f949be1272fb",
|
|
260
261
|
"dist/components/menu/menu.js": "55d24cd48752830a",
|
|
@@ -333,8 +334,8 @@
|
|
|
333
334
|
"dist/components/radio/radio.js": "dce8ff6dd0deaa5e",
|
|
334
335
|
"dist/components/radio/radio.meta.js": "0b0419bde53f857d",
|
|
335
336
|
"dist/components/radio-card/index.js": "ff815b13ce46e2bb",
|
|
336
|
-
"dist/components/radio-card/radio-card.js": "
|
|
337
|
-
"dist/components/radio-card/radio-card.meta.js": "
|
|
337
|
+
"dist/components/radio-card/radio-card.js": "29c459d88339f035",
|
|
338
|
+
"dist/components/radio-card/radio-card.meta.js": "7370c85b8f32ffad",
|
|
338
339
|
"dist/components/radio-card/radio-card.variants.js": "86778fe9e4888cd1",
|
|
339
340
|
"dist/components/relative-time/index.js": "02e9b7b91ccad470",
|
|
340
341
|
"dist/components/relative-time/relative-time.js": "db5cd286b36dee74",
|
|
@@ -366,7 +367,7 @@
|
|
|
366
367
|
"dist/components/settings-menu/settings-menu.meta.js": "46bdda712ce13d85",
|
|
367
368
|
"dist/components/settings-menu/settings-menu.variants.js": "1eefc629793ed7b3",
|
|
368
369
|
"dist/components/sheet/index.js": "b19aacba55e7de80",
|
|
369
|
-
"dist/components/sheet/sheet.js": "
|
|
370
|
+
"dist/components/sheet/sheet.js": "851b9623a38e1e6c",
|
|
370
371
|
"dist/components/sheet/sheet.meta.js": "28cd51d5ba9872db",
|
|
371
372
|
"dist/components/sheet/sheet.variants.js": "672d2fd7f746bd57",
|
|
372
373
|
"dist/components/show-more/index.js": "e7c7e37ffedde2c2",
|
|
@@ -399,8 +400,8 @@
|
|
|
399
400
|
"dist/components/status-dot/status-dot.meta.js": "90200e97feb25c97",
|
|
400
401
|
"dist/components/status-dot/status-dot.variants.js": "d65605a0376e7d3b",
|
|
401
402
|
"dist/components/stepper/index.js": "2346537b50c95017",
|
|
402
|
-
"dist/components/stepper/stepper.js": "
|
|
403
|
-
"dist/components/stepper/stepper.meta.js": "
|
|
403
|
+
"dist/components/stepper/stepper.js": "543b52e8d429fb3e",
|
|
404
|
+
"dist/components/stepper/stepper.meta.js": "6d17c7521e2aa31e",
|
|
404
405
|
"dist/components/stepper/stepper.variants.js": "3c343c6e38e39d80",
|
|
405
406
|
"dist/components/streaming-text/index.js": "c46856aa7c0718f5",
|
|
406
407
|
"dist/components/streaming-text/streaming-text.js": "e450c4d5e312a6c5",
|
|
@@ -428,7 +429,7 @@
|
|
|
428
429
|
"dist/components/thinking-orb/thinking-orb.meta.js": "dcc1982c0f9e9d01",
|
|
429
430
|
"dist/components/thinking-orb/thinking-orb.render.js": "1d561ac1ffcd1e97",
|
|
430
431
|
"dist/components/toast/index.js": "d13be0a6c335931d",
|
|
431
|
-
"dist/components/toast/toast.js": "
|
|
432
|
+
"dist/components/toast/toast.js": "75052e65f5589fd2",
|
|
432
433
|
"dist/components/toast/toast.meta.js": "493dec13b784b6bc",
|
|
433
434
|
"dist/components/toggle/index.js": "84a0a4bf596a1b7d",
|
|
434
435
|
"dist/components/toggle/toggle.js": "90d0deb9c6af0d61",
|
|
@@ -470,8 +471,8 @@
|
|
|
470
471
|
"dist/icon-sets/brands.js": "9a37d8739f14f2a9",
|
|
471
472
|
"dist/icon-sets/reicon.d.ts": "52f6e41dc8137b4e",
|
|
472
473
|
"dist/icon-sets/reicon.js": "89a930f17cc90cde",
|
|
473
|
-
"dist/index.d.ts": "
|
|
474
|
-
"dist/index.js": "
|
|
474
|
+
"dist/index.d.ts": "5bd49d44680db545",
|
|
475
|
+
"dist/index.js": "aef044650352430a",
|
|
475
476
|
"dist/ios/chrome.css": "a7ca9e3735d8658e",
|
|
476
477
|
"dist/ios/chrome.js": "37091fef7b68bd28",
|
|
477
478
|
"dist/ios/frame.css": "dd2703c944ce575a",
|
|
@@ -540,7 +541,7 @@
|
|
|
540
541
|
"dist/lib/use-resize-observer.js": "4f08e280d64be22c",
|
|
541
542
|
"dist/lib/use-scroll-activity.js": "793394a8c952e697",
|
|
542
543
|
"dist/lib/use-wheel-to-horizontal.js": "5f886b1033db9129",
|
|
543
|
-
"dist/meta-
|
|
544
|
+
"dist/meta-DVG8ceze.d.ts": "60cdb0adc726bc03",
|
|
544
545
|
"dist/motion/MotionProvider.js": "feeab4e6ce816a5e",
|
|
545
546
|
"dist/motion/Presence.js": "21fc6f1c68207718",
|
|
546
547
|
"dist/motion/Reveal.js": "b80396095aa2de96",
|
|
@@ -559,7 +560,7 @@
|
|
|
559
560
|
"dist/patterns/dashboard-page.meta.js": "da27758c17fe15a2",
|
|
560
561
|
"dist/patterns/feed-page.js": "0fabf456c32dbbad",
|
|
561
562
|
"dist/patterns/feed-page.meta.js": "ab079074b87ad23b",
|
|
562
|
-
"dist/patterns/index.d.ts": "
|
|
563
|
+
"dist/patterns/index.d.ts": "7511ee8632e26bbf",
|
|
563
564
|
"dist/patterns/index.js": "3ff5fabb1afbf8dc",
|
|
564
565
|
"dist/patterns/landing-page.js": "4f3294839ac36cd6",
|
|
565
566
|
"dist/patterns/landing-page.meta.js": "95d34273ee999eea",
|
|
@@ -570,7 +571,7 @@
|
|
|
570
571
|
"dist/presets/index.d.ts": "143528fa9cbec492",
|
|
571
572
|
"dist/presets/index.js": "51babe07a2424380",
|
|
572
573
|
"dist/registry-CyL28OzJ.d.ts": "b068fc89571517d8",
|
|
573
|
-
"dist/styles.css": "
|
|
574
|
+
"dist/styles.css": "05ff241061103966",
|
|
574
575
|
"dist/theme-provider-S0NVJs2Y.d.ts": "ae7728dddd10f563",
|
|
575
576
|
"dist/themes/_contract.js": "cacc81c9295d55a2",
|
|
576
577
|
"dist/themes/atelier/atelier.css": "e693fec4a6ae71a8",
|