atlas-react-kit 0.9.4 → 0.11.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/COMPONENTS.md +52 -10
- package/DESIGN_RULES.md +26 -0
- package/README.md +2 -2
- package/dist/accordion.d.ts +8 -1
- package/dist/accordion.js +8 -3
- package/dist/alert.js +1 -1
- package/dist/avatar.d.ts +5 -1
- package/dist/avatar.js +7 -2
- package/dist/badge.js +1 -1
- package/dist/breadcrumb.js +3 -2
- package/dist/button.js +6 -6
- package/dist/calendar.js +19 -12
- package/dist/card.d.ts +1 -1
- package/dist/checkbox.js +2 -2
- package/dist/color-legend.d.ts +6 -4
- package/dist/color-legend.js +7 -4
- package/dist/combobox.d.ts +10 -2
- package/dist/combobox.js +35 -25
- package/dist/date-picker.d.ts +7 -13
- package/dist/date-picker.js +14 -20
- package/dist/date-time-picker.d.ts +5 -13
- package/dist/date-time-picker.js +26 -30
- package/dist/dialog.d.ts +5 -1
- package/dist/dialog.js +4 -3
- package/dist/dropdown-menu.js +5 -4
- package/dist/empty-panel.d.ts +4 -3
- package/dist/empty-panel.js +3 -3
- package/dist/error-boundary.d.ts +5 -3
- package/dist/error-boundary.js +9 -4
- package/dist/field.d.ts +14 -1
- package/dist/field.js +16 -3
- package/dist/form/date-picker-field.d.ts +8 -2
- package/dist/form/date-picker-field.js +4 -4
- package/dist/form/date-time-picker-field.d.ts +8 -1
- package/dist/form/date-time-picker-field.js +3 -3
- package/dist/form/feedback-field.d.ts +2 -1
- package/dist/form/feedback-field.js +3 -2
- package/dist/form/field-error.d.ts +4 -1
- package/dist/form/field-error.js +3 -2
- package/dist/form/field-shell.d.ts +25 -1
- package/dist/form/field-shell.js +36 -3
- package/dist/form/markdown-field.d.ts +7 -1
- package/dist/form/markdown-field.js +10 -5
- package/dist/form/select-field.d.ts +7 -1
- package/dist/form/select-field.js +3 -3
- package/dist/form/text-field.d.ts +5 -1
- package/dist/form/text-field.js +6 -3
- package/dist/info-tip.d.ts +5 -2
- package/dist/info-tip.js +34 -16
- package/dist/internal/form-control.d.ts +5 -0
- package/dist/internal/form-control.js +7 -0
- package/dist/internal/listbox-navigation.d.ts +32 -0
- package/dist/internal/listbox-navigation.js +38 -0
- package/dist/internal/listbox-option.d.ts +25 -0
- package/dist/internal/listbox-option.js +23 -0
- package/dist/internal/math.d.ts +5 -0
- package/dist/internal/math.js +7 -0
- package/dist/internal/overlay-styles.d.ts +28 -0
- package/dist/internal/overlay-styles.js +28 -0
- package/dist/internal/styles.d.ts +27 -0
- package/dist/internal/styles.js +27 -0
- package/dist/internal/tabs-styles.d.ts +10 -1
- package/dist/internal/tabs-styles.js +27 -5
- package/dist/lazy-calendar.d.ts +6 -0
- package/dist/lazy-calendar.js +26 -0
- package/dist/markdown.js +10 -4
- package/dist/meter.d.ts +3 -2
- package/dist/meter.js +6 -3
- package/dist/pagination.d.ts +8 -1
- package/dist/pagination.js +15 -9
- package/dist/picker-shell.d.ts +59 -0
- package/dist/picker-shell.js +21 -0
- package/dist/popover.js +2 -1
- package/dist/progress.d.ts +7 -3
- package/dist/progress.js +10 -4
- package/dist/radio-group.js +2 -2
- package/dist/search-picker.d.ts +6 -6
- package/dist/search-picker.js +28 -33
- package/dist/select.js +12 -6
- package/dist/separator.d.ts +4 -0
- package/dist/separator.js +5 -1
- package/dist/slider.d.ts +8 -3
- package/dist/slider.js +7 -5
- package/dist/sortable-table-head.d.ts +3 -1
- package/dist/sortable-table-head.js +3 -2
- package/dist/stat-tile.d.ts +6 -4
- package/dist/stat-tile.js +6 -4
- package/dist/switch.js +2 -2
- package/dist/table-body-skeleton.d.ts +8 -2
- package/dist/table-body-skeleton.js +7 -3
- package/dist/table.d.ts +7 -1
- package/dist/table.js +6 -2
- package/dist/tabs.d.ts +13 -3
- package/dist/tabs.js +29 -10
- package/dist/theme-provider.d.ts +2 -1
- package/dist/theme-provider.js +1 -1
- package/dist/toggle.js +4 -4
- package/dist/tooltip.d.ts +7 -3
- package/dist/tooltip.js +17 -6
- package/package.json +1 -1
- package/theme.css +43 -50
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { DatePickerCalendarProps } from "../date-picker.js";
|
|
1
2
|
import type { FieldMetaState } from "./field-error.js";
|
|
2
3
|
type Props = {
|
|
3
4
|
value: Date | null;
|
|
@@ -10,7 +11,13 @@ type Props = {
|
|
|
10
11
|
timeStep?: number;
|
|
11
12
|
/** Forwarded to the date-time picker trigger. */
|
|
12
13
|
className?: string;
|
|
14
|
+
/** Classes for the field wrapper (label, trigger, and error). */
|
|
15
|
+
fieldClassName?: string;
|
|
16
|
+
/** Forwarded to the date-time picker popover. */
|
|
17
|
+
contentClassName?: string;
|
|
18
|
+
/** Day Picker options forwarded to the calendar (disabled days, etc.). */
|
|
19
|
+
calendarProps?: DatePickerCalendarProps;
|
|
13
20
|
};
|
|
14
21
|
/** Date-time picker with built-in field error text and invalid styling. */
|
|
15
|
-
export declare function FormDateTimePickerField({
|
|
22
|
+
export declare function FormDateTimePickerField({ onValueChange, label, meta, fieldClassName, ...picker }: Props): import("react").JSX.Element;
|
|
16
23
|
export {};
|
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
3
|
import { useId } from "react";
|
|
4
4
|
import { DateTimePickerButton } from "../date-time-picker.js";
|
|
5
|
-
import { FormFieldShell,
|
|
5
|
+
import { FormFieldShell, metaErrorText, pickerFieldAria, } from "./field-shell.js";
|
|
6
6
|
/** Date-time picker with built-in field error text and invalid styling. */
|
|
7
|
-
export function FormDateTimePickerField({
|
|
7
|
+
export function FormDateTimePickerField({ onValueChange, label, meta, fieldClassName, ...picker }) {
|
|
8
8
|
const errorText = metaErrorText(meta);
|
|
9
9
|
const id = useId();
|
|
10
|
-
return (_jsx(FormFieldShell, { id: id, label: label, errorText: errorText, children: _jsx(DateTimePickerButton, { id: id,
|
|
10
|
+
return (_jsx(FormFieldShell, { id: id, label: label, errorText: errorText, className: fieldClassName, children: _jsx(DateTimePickerButton, { ...picker, id: id, onChange: onValueChange, ...pickerFieldAria(id, errorText) }) }));
|
|
11
11
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
/** Form-level submit or mutation error (not tied to a single field). */
|
|
2
|
-
export declare function FormFeedbackField({ message }: {
|
|
2
|
+
export declare function FormFeedbackField({ message, className, }: {
|
|
3
3
|
message: string | null;
|
|
4
|
+
className?: string;
|
|
4
5
|
}): import("react").JSX.Element | null;
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { cn } from "../utils.js";
|
|
2
3
|
/** Form-level submit or mutation error (not tied to a single field). */
|
|
3
|
-
export function FormFeedbackField({ message }) {
|
|
4
|
+
export function FormFeedbackField({ message, className, }) {
|
|
4
5
|
if (!message)
|
|
5
6
|
return null;
|
|
6
|
-
return (_jsx("p", { className: "text-sm text-destructive", role: "alert", children: message }));
|
|
7
|
+
return (_jsx("p", { "data-slot": "form-feedback", className: cn("text-sm text-destructive", className), role: "alert", children: message }));
|
|
7
8
|
}
|
|
@@ -6,7 +6,10 @@ export type FieldMetaState = {
|
|
|
6
6
|
};
|
|
7
7
|
/** First TanStack Form error message for a field, if any. */
|
|
8
8
|
export declare function getFieldErrorMessage(message: FieldErrorMessage): string | undefined;
|
|
9
|
-
export declare function FieldError({ message, text, }: {
|
|
9
|
+
export declare function FieldError({ id, message, text, className, }: {
|
|
10
|
+
/** Point the control's `aria-describedby` here so the error is read with it. */
|
|
11
|
+
id?: string;
|
|
10
12
|
message?: FieldErrorMessage;
|
|
11
13
|
text?: string;
|
|
14
|
+
className?: string;
|
|
12
15
|
}): import("react").JSX.Element | null;
|
package/dist/form/field-error.js
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { cn } from "../utils.js";
|
|
2
3
|
/** First TanStack Form error message for a field, if any. */
|
|
3
4
|
export function getFieldErrorMessage(message) {
|
|
4
5
|
if (typeof message === "string")
|
|
5
6
|
return message;
|
|
6
7
|
return message?.message;
|
|
7
8
|
}
|
|
8
|
-
export function FieldError({ message, text, }) {
|
|
9
|
+
export function FieldError({ id, message, text, className, }) {
|
|
9
10
|
const resolved = text ?? getFieldErrorMessage(message);
|
|
10
11
|
if (!resolved)
|
|
11
12
|
return null;
|
|
12
|
-
return (_jsx("p", { className: "text-xs text-destructive", role: "alert", children: resolved }));
|
|
13
|
+
return (_jsx("p", { id: id, "data-slot": "form-field-error", className: cn("text-xs text-destructive", className), role: "alert", children: resolved }));
|
|
13
14
|
}
|
|
@@ -8,14 +8,38 @@ import { type FieldMetaState } from "./field-error.js";
|
|
|
8
8
|
export declare function metaErrorText(meta: FieldMetaState | undefined): string | undefined;
|
|
9
9
|
/** `aria-invalid` for the control: `true` while there is error text. */
|
|
10
10
|
export declare function invalidWhen(errorText: string | undefined): true | undefined;
|
|
11
|
+
/** Id of the label `FormFieldShell` renders for control `id`. */
|
|
12
|
+
export declare function labelIdFor(id: string): string;
|
|
13
|
+
/** Id of the error text `FormFieldShell` renders for control `id`. */
|
|
14
|
+
export declare function errorIdFor(id: string): string;
|
|
15
|
+
/**
|
|
16
|
+
* `aria-describedby` for the control: the given hint ids, plus the error
|
|
17
|
+
* text while there is one, or `undefined` when there is nothing to read.
|
|
18
|
+
*/
|
|
19
|
+
export declare function describedBy(id: string, errorText: string | undefined, ...hintIds: Array<string | undefined>): string | undefined;
|
|
20
|
+
/**
|
|
21
|
+
* Trigger ARIA for the date and date-time fields: invalid while there is
|
|
22
|
+
* error text, named "label, value" (a `<label for>` alone would hide the
|
|
23
|
+
* picked value), and described by the error.
|
|
24
|
+
*/
|
|
25
|
+
export declare function pickerFieldAria(id: string, errorText: string | undefined): {
|
|
26
|
+
"aria-invalid": boolean | undefined;
|
|
27
|
+
"aria-labelledby": string;
|
|
28
|
+
"aria-describedby": string | undefined;
|
|
29
|
+
};
|
|
11
30
|
/**
|
|
12
31
|
* Label, control, and error text stacked with the kit's field spacing.
|
|
13
32
|
* `labelAction` sits on the label's row (the Markdown field's mode toggle).
|
|
33
|
+
* Set `labelFor` to false when the control is not rendered (Markdown
|
|
34
|
+
* preview), so the label does not point at a missing element.
|
|
35
|
+
* `className` styles the wrapper (`data-slot="form-field"`), e.g. a width.
|
|
14
36
|
*/
|
|
15
|
-
export declare function FormFieldShell({ id, label, labelAction, errorText, children, }: {
|
|
37
|
+
export declare function FormFieldShell({ id, label, labelAction, errorText, labelFor, className, children, }: {
|
|
16
38
|
id: string;
|
|
17
39
|
label: string;
|
|
18
40
|
labelAction?: ReactNode;
|
|
19
41
|
errorText: string | undefined;
|
|
42
|
+
labelFor?: boolean;
|
|
43
|
+
className?: string;
|
|
20
44
|
children: ReactNode;
|
|
21
45
|
}): import("react").JSX.Element;
|
package/dist/form/field-shell.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { pickerValueId } from "../internal/form-control.js";
|
|
2
3
|
import { Label } from "../label.js";
|
|
4
|
+
import { cn } from "../utils.js";
|
|
3
5
|
import { FieldError, getFieldErrorMessage, } from "./field-error.js";
|
|
4
6
|
/**
|
|
5
7
|
* Not a public entry. The layout and error wiring every `form/*` field
|
|
@@ -13,11 +15,42 @@ export function metaErrorText(meta) {
|
|
|
13
15
|
export function invalidWhen(errorText) {
|
|
14
16
|
return errorText ? true : undefined;
|
|
15
17
|
}
|
|
18
|
+
/** Id of the label `FormFieldShell` renders for control `id`. */
|
|
19
|
+
export function labelIdFor(id) {
|
|
20
|
+
return `${id}-label`;
|
|
21
|
+
}
|
|
22
|
+
/** Id of the error text `FormFieldShell` renders for control `id`. */
|
|
23
|
+
export function errorIdFor(id) {
|
|
24
|
+
return `${id}-error`;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* `aria-describedby` for the control: the given hint ids, plus the error
|
|
28
|
+
* text while there is one, or `undefined` when there is nothing to read.
|
|
29
|
+
*/
|
|
30
|
+
export function describedBy(id, errorText, ...hintIds) {
|
|
31
|
+
const ids = [...hintIds, errorText ? errorIdFor(id) : undefined].filter(Boolean);
|
|
32
|
+
return ids.length ? ids.join(" ") : undefined;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Trigger ARIA for the date and date-time fields: invalid while there is
|
|
36
|
+
* error text, named "label, value" (a `<label for>` alone would hide the
|
|
37
|
+
* picked value), and described by the error.
|
|
38
|
+
*/
|
|
39
|
+
export function pickerFieldAria(id, errorText) {
|
|
40
|
+
return {
|
|
41
|
+
"aria-invalid": invalidWhen(errorText),
|
|
42
|
+
"aria-labelledby": `${labelIdFor(id)} ${pickerValueId(id)}`,
|
|
43
|
+
"aria-describedby": describedBy(id, errorText),
|
|
44
|
+
};
|
|
45
|
+
}
|
|
16
46
|
/**
|
|
17
47
|
* Label, control, and error text stacked with the kit's field spacing.
|
|
18
48
|
* `labelAction` sits on the label's row (the Markdown field's mode toggle).
|
|
49
|
+
* Set `labelFor` to false when the control is not rendered (Markdown
|
|
50
|
+
* preview), so the label does not point at a missing element.
|
|
51
|
+
* `className` styles the wrapper (`data-slot="form-field"`), e.g. a width.
|
|
19
52
|
*/
|
|
20
|
-
export function FormFieldShell({ id, label, labelAction, errorText, children, }) {
|
|
21
|
-
const labelNode = _jsx(Label, { htmlFor: id, children: label });
|
|
22
|
-
return (_jsxs("div", { className: "flex flex-col gap-1.5", children: [labelAction ? (_jsxs("div", { className: "flex items-center justify-between gap-2", children: [labelNode, labelAction] })) : (labelNode), children, _jsx(FieldError, { text: errorText })] }));
|
|
53
|
+
export function FormFieldShell({ id, label, labelAction, errorText, labelFor = true, className, children, }) {
|
|
54
|
+
const labelNode = (_jsx(Label, { id: labelIdFor(id), htmlFor: labelFor ? id : undefined, children: label }));
|
|
55
|
+
return (_jsxs("div", { "data-slot": "form-field", className: cn("flex flex-col gap-1.5", className), children: [labelAction ? (_jsxs("div", { "data-slot": "form-field-header", className: "flex items-center justify-between gap-2", children: [labelNode, labelAction] })) : (labelNode), children, _jsx(FieldError, { id: errorIdFor(id), text: errorText })] }));
|
|
23
56
|
}
|
|
@@ -3,7 +3,7 @@ import type { TextFieldApi } from "./text-field.js";
|
|
|
3
3
|
* GFM body field: Write is a textarea, Preview is the sanitized Markdown
|
|
4
4
|
* renderer. String-in/string-out — not a WYSIWYG document model.
|
|
5
5
|
*/
|
|
6
|
-
export declare function FormMarkdownField({ field, id, label, placeholder, rows, showError, }: {
|
|
6
|
+
export declare function FormMarkdownField({ field, id, label, placeholder, rows, showError, className, fieldClassName, previewClassName, }: {
|
|
7
7
|
field: TextFieldApi;
|
|
8
8
|
/**
|
|
9
9
|
* Control id the label points at. Defaults to `field.name`; pass a unique
|
|
@@ -14,4 +14,10 @@ export declare function FormMarkdownField({ field, id, label, placeholder, rows,
|
|
|
14
14
|
placeholder?: string;
|
|
15
15
|
rows?: number;
|
|
16
16
|
showError?: boolean;
|
|
17
|
+
/** Forwarded to the Write textarea. */
|
|
18
|
+
className?: string;
|
|
19
|
+
/** Classes for the field wrapper (label row, body, and error). */
|
|
20
|
+
fieldClassName?: string;
|
|
21
|
+
/** Classes for the Preview surface, e.g. a taller `min-h-40`. */
|
|
22
|
+
previewClassName?: string;
|
|
17
23
|
}): import("react").JSX.Element;
|
|
@@ -1,17 +1,22 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
-
import { useState } from "react";
|
|
4
|
-
import {
|
|
3
|
+
import { lazy, Suspense, useState } from "react";
|
|
4
|
+
import { Spinner } from "../spinner.js";
|
|
5
5
|
import { Textarea } from "../textarea.js";
|
|
6
6
|
import { ToggleGroup, ToggleGroupItem } from "../toggle-group.js";
|
|
7
7
|
import { fieldSurface } from "../internal/styles.js";
|
|
8
8
|
import { cn, onInputChange } from "../utils.js";
|
|
9
|
-
import { FormFieldShell, invalidWhen, metaErrorText } from "./field-shell.js";
|
|
9
|
+
import { describedBy, FormFieldShell, invalidWhen, metaErrorText, } from "./field-shell.js";
|
|
10
|
+
// The renderer (react-markdown + remark-gfm) is fetched the first time
|
|
11
|
+
// Preview is shown or hovered, so a form in Write mode stays light.
|
|
12
|
+
const loadMarkdown = () => import("../markdown.js");
|
|
13
|
+
const preloadMarkdown = () => void loadMarkdown();
|
|
14
|
+
const Markdown = lazy(() => loadMarkdown().then((module) => ({ default: module.Markdown })));
|
|
10
15
|
/**
|
|
11
16
|
* GFM body field: Write is a textarea, Preview is the sanitized Markdown
|
|
12
17
|
* renderer. String-in/string-out — not a WYSIWYG document model.
|
|
13
18
|
*/
|
|
14
|
-
export function FormMarkdownField({ field, id = field.name, label, placeholder = "Markdown supported", rows = 5, showError = true, }) {
|
|
19
|
+
export function FormMarkdownField({ field, id = field.name, label, placeholder = "Markdown supported", rows = 5, showError = true, className, fieldClassName, previewClassName, }) {
|
|
15
20
|
const [mode, setMode] = useState("write");
|
|
16
21
|
const onChange = onInputChange(field.handleChange);
|
|
17
22
|
const errorText = showError ? metaErrorText(field.state.meta) : undefined;
|
|
@@ -20,5 +25,5 @@ export function FormMarkdownField({ field, id = field.name, label, placeholder =
|
|
|
20
25
|
if (value === "write" || value === "preview")
|
|
21
26
|
setMode(value);
|
|
22
27
|
}
|
|
23
|
-
return (_jsx(FormFieldShell, { id: id, label: label, errorText: errorText, labelAction: _jsxs(ToggleGroup, { type: "single", value: mode, onValueChange: handleModeChange, variant: "outline", size: "sm", spacing: 0, "aria-label": `${label} mode`, children: [_jsx(ToggleGroupItem, { value: "write", children: "Write" }), _jsx(ToggleGroupItem, { value: "preview", children: "Preview" })] }), children: mode === "write" ? (_jsx(Textarea, { id: id, value: field.state.value, onChange: onChange, onBlur: field.handleBlur, placeholder: placeholder, rows: rows, "aria-invalid": invalidWhen(errorText) })) : (_jsx("div", { className: cn(fieldSurface, "min-h-16"), "aria-live": "polite", children: trimmed ? (_jsx(Markdown, { content: trimmed })) : (_jsx("p", { className: "text-sm text-muted-foreground", children: "Nothing to preview" })) })) }));
|
|
28
|
+
return (_jsx(FormFieldShell, { id: id, label: label, errorText: errorText, labelFor: mode === "write", className: fieldClassName, labelAction: _jsxs(ToggleGroup, { type: "single", value: mode, onValueChange: handleModeChange, variant: "outline", size: "sm", spacing: 0, "aria-label": `${label} mode`, children: [_jsx(ToggleGroupItem, { value: "write", children: "Write" }), _jsx(ToggleGroupItem, { value: "preview", onPointerEnter: preloadMarkdown, onFocus: preloadMarkdown, children: "Preview" })] }), children: mode === "write" ? (_jsx(Textarea, { id: id, value: field.state.value, onChange: onChange, onBlur: field.handleBlur, placeholder: placeholder, rows: rows, className: className, "aria-invalid": invalidWhen(errorText), "aria-describedby": describedBy(id, errorText) })) : (_jsx("div", { "data-slot": "form-markdown-preview", className: cn(fieldSurface, "min-h-16", previewClassName), "aria-live": "polite", children: trimmed ? (_jsx(Suspense, { fallback: _jsx(Spinner, { "aria-label": "Loading preview" }), children: _jsx(Markdown, { content: trimmed }) })) : (_jsx("p", { className: "text-sm text-muted-foreground", children: "Nothing to preview" })) })) }));
|
|
24
29
|
}
|
|
@@ -6,8 +6,14 @@ type Props = {
|
|
|
6
6
|
label: string;
|
|
7
7
|
onValueChange: (value: string) => void;
|
|
8
8
|
placeholder?: string;
|
|
9
|
+
/** Forwarded to the select trigger. */
|
|
10
|
+
className?: string;
|
|
11
|
+
/** Classes for the field wrapper (label, trigger, and error). */
|
|
12
|
+
fieldClassName?: string;
|
|
13
|
+
/** Forwarded to the select's option list (`SelectContent`). */
|
|
14
|
+
contentClassName?: string;
|
|
9
15
|
children: ReactNode;
|
|
10
16
|
};
|
|
11
17
|
/** Select control with built-in field error text and invalid styling. */
|
|
12
|
-
export declare function FormSelectField({ value, meta, label, onValueChange, placeholder, children, }: Props): import("react").JSX.Element;
|
|
18
|
+
export declare function FormSelectField({ value, meta, label, onValueChange, placeholder, className, fieldClassName, contentClassName, children, }: Props): import("react").JSX.Element;
|
|
13
19
|
export {};
|
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
3
|
import { useId } from "react";
|
|
4
4
|
import { Select, SelectContent, SelectTrigger, SelectValue } from "../select.js";
|
|
5
|
-
import { FormFieldShell, invalidWhen, metaErrorText } from "./field-shell.js";
|
|
5
|
+
import { describedBy, FormFieldShell, invalidWhen, metaErrorText, } from "./field-shell.js";
|
|
6
6
|
/** Select control with built-in field error text and invalid styling. */
|
|
7
|
-
export function FormSelectField({ value, meta, label, onValueChange, placeholder, children, }) {
|
|
7
|
+
export function FormSelectField({ value, meta, label, onValueChange, placeholder, className, fieldClassName, contentClassName, children, }) {
|
|
8
8
|
const errorText = metaErrorText(meta);
|
|
9
9
|
const id = useId();
|
|
10
|
-
return (_jsx(FormFieldShell, { id: id, label: label, errorText: errorText, children: _jsxs(Select, { value: value, onValueChange: onValueChange, children: [_jsx(SelectTrigger, { id: id, "aria-invalid": invalidWhen(errorText), children: _jsx(SelectValue, { placeholder: placeholder }) }), _jsx(SelectContent, { children: children })] }) }));
|
|
10
|
+
return (_jsx(FormFieldShell, { id: id, label: label, errorText: errorText, className: fieldClassName, children: _jsxs(Select, { value: value, onValueChange: onValueChange, children: [_jsx(SelectTrigger, { id: id, className: className, "aria-invalid": invalidWhen(errorText), "aria-describedby": describedBy(id, errorText), children: _jsx(SelectValue, { placeholder: placeholder }) }), _jsx(SelectContent, { className: contentClassName, children: children })] }) }));
|
|
11
11
|
}
|
|
@@ -8,7 +8,7 @@ export type TextFieldApi = {
|
|
|
8
8
|
handleChange: (value: string) => void;
|
|
9
9
|
handleBlur: () => void;
|
|
10
10
|
};
|
|
11
|
-
export declare function FormTextField({ field, id, label, placeholder, description, autoFocus, multiline, rows, inputType, showError, autoComplete, required, minLength, }: {
|
|
11
|
+
export declare function FormTextField({ field, id, label, placeholder, description, autoFocus, multiline, rows, inputType, showError, autoComplete, required, minLength, className, fieldClassName, }: {
|
|
12
12
|
field: TextFieldApi;
|
|
13
13
|
/**
|
|
14
14
|
* Control id the label points at. Defaults to `field.name`; pass a unique
|
|
@@ -30,4 +30,8 @@ export declare function FormTextField({ field, id, label, placeholder, descripti
|
|
|
30
30
|
autoComplete?: string;
|
|
31
31
|
required?: boolean;
|
|
32
32
|
minLength?: number;
|
|
33
|
+
/** Forwarded to the input or textarea. */
|
|
34
|
+
className?: string;
|
|
35
|
+
/** Classes for the field wrapper (label, hint, control, and error). */
|
|
36
|
+
fieldClassName?: string;
|
|
33
37
|
}): import("react").JSX.Element;
|
package/dist/form/text-field.js
CHANGED
|
@@ -3,10 +3,11 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
3
3
|
import { Input } from "../input.js";
|
|
4
4
|
import { Textarea } from "../textarea.js";
|
|
5
5
|
import { onInputChange } from "../utils.js";
|
|
6
|
-
import { FormFieldShell, invalidWhen, metaErrorText } from "./field-shell.js";
|
|
7
|
-
export function FormTextField({ field, id = field.name, label, placeholder, description, autoFocus, multiline, rows = 3, inputType = "text", showError = true, autoComplete, required, minLength, }) {
|
|
6
|
+
import { describedBy, FormFieldShell, invalidWhen, metaErrorText, } from "./field-shell.js";
|
|
7
|
+
export function FormTextField({ field, id = field.name, label, placeholder, description, autoFocus, multiline, rows = 3, inputType = "text", showError = true, autoComplete, required, minLength, className, fieldClassName, }) {
|
|
8
8
|
const onChange = onInputChange(field.handleChange);
|
|
9
9
|
const errorText = showError ? metaErrorText(field.state.meta) : undefined;
|
|
10
|
+
const descriptionId = description ? `${id}-description` : undefined;
|
|
10
11
|
const controlProps = {
|
|
11
12
|
id,
|
|
12
13
|
value: field.state.value,
|
|
@@ -16,7 +17,9 @@ export function FormTextField({ field, id = field.name, label, placeholder, desc
|
|
|
16
17
|
autoComplete,
|
|
17
18
|
required,
|
|
18
19
|
minLength,
|
|
20
|
+
className,
|
|
19
21
|
"aria-invalid": invalidWhen(errorText),
|
|
22
|
+
"aria-describedby": describedBy(id, errorText, descriptionId),
|
|
20
23
|
};
|
|
21
|
-
return (_jsxs(FormFieldShell, { id: id, label: label, errorText: errorText, children: [description ? (_jsx("p", { className: "text-xs text-muted-foreground", children: description })) : null, multiline ? (_jsx(Textarea, { ...controlProps, rows: rows })) : (_jsx(Input, { ...controlProps, type: inputType, autoFocus: autoFocus }))] }));
|
|
24
|
+
return (_jsxs(FormFieldShell, { id: id, label: label, errorText: errorText, className: fieldClassName, children: [description ? (_jsx("p", { id: descriptionId, "data-slot": "form-field-description", className: "text-xs text-muted-foreground", children: description })) : null, multiline ? (_jsx(Textarea, { ...controlProps, rows: rows })) : (_jsx(Input, { ...controlProps, type: inputType, autoFocus: autoFocus }))] }));
|
|
22
25
|
}
|
package/dist/info-tip.d.ts
CHANGED
|
@@ -14,14 +14,17 @@ export type InfoTipProps = {
|
|
|
14
14
|
align?: React.ComponentProps<typeof PopoverContent>["align"];
|
|
15
15
|
badge?: React.ReactNode;
|
|
16
16
|
icon?: React.ReactNode;
|
|
17
|
+
/** Classes for the trigger button. */
|
|
17
18
|
className?: string;
|
|
18
|
-
|
|
19
|
+
/** Classes for the panel, e.g. its width. Defaults to `w-72`. */
|
|
20
|
+
contentClassName?: string;
|
|
21
|
+
} & Omit<React.ComponentProps<"button">, "title" | "children" | "type" | "onMouseEnter" | "onMouseLeave" | "onClick">;
|
|
19
22
|
/**
|
|
20
23
|
* Longer help than a Tooltip: a Popover with a 20px glyph and a 44px hit
|
|
21
24
|
* area. Hover opens only on a fine pointer; click, tap, and keyboard always
|
|
22
25
|
* toggle it. Not a glossary or "coming soon" chip — pass that copy in.
|
|
23
26
|
*/
|
|
24
|
-
declare function InfoTip({ label, title, description, children, layout, side, align, badge, icon, className, }: InfoTipProps): React.JSX.Element;
|
|
27
|
+
declare function InfoTip({ label, title, description, children, layout, side, align, badge, icon, className, contentClassName, ...props }: InfoTipProps): React.JSX.Element;
|
|
25
28
|
/** InfoTip that sits in a line of text without stretching the line box. */
|
|
26
29
|
declare function InlineTip(props: Omit<InfoTipProps, "layout">): React.JSX.Element;
|
|
27
30
|
export { InfoTip, InlineTip };
|
package/dist/info-tip.js
CHANGED
|
@@ -3,7 +3,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
3
3
|
import * as React from "react";
|
|
4
4
|
import { CircleHelpIcon } from "lucide-react";
|
|
5
5
|
import { Popover, PopoverContent, PopoverDescription, PopoverHeader, PopoverTitle, PopoverTrigger, } from "./popover.js";
|
|
6
|
-
import { focusRing } from "./internal/styles.js";
|
|
6
|
+
import { focusRing, hitArea44 } from "./internal/styles.js";
|
|
7
7
|
import { cn } from "./utils.js";
|
|
8
8
|
/**
|
|
9
9
|
* True when a mouse can hover. Touch and coarse pointers must use click;
|
|
@@ -17,10 +17,13 @@ function canHoverOpen() {
|
|
|
17
17
|
* area. Hover opens only on a fine pointer; click, tap, and keyboard always
|
|
18
18
|
* toggle it. Not a glossary or "coming soon" chip — pass that copy in.
|
|
19
19
|
*/
|
|
20
|
-
function InfoTip({ label, title, description, children, layout = "box", side = "top", align = "center", badge, icon, className, }) {
|
|
20
|
+
function InfoTip({ label, title, description, children, layout = "box", side = "top", align = "center", badge, icon, className, contentClassName, ...props }) {
|
|
21
21
|
const [open, setOpen] = React.useState(false);
|
|
22
22
|
const closeTimer = React.useRef(null);
|
|
23
|
-
|
|
23
|
+
// How the panel last opened. A hover-open leaves focus where it was and
|
|
24
|
+
// closes when the pointer leaves; a click (or keyboard) open pins it.
|
|
25
|
+
const openedBy = React.useRef(null);
|
|
26
|
+
const titleId = React.useId();
|
|
24
27
|
const inline = layout === "inline";
|
|
25
28
|
const cancelClose = () => {
|
|
26
29
|
if (closeTimer.current !== null) {
|
|
@@ -32,11 +35,13 @@ function InfoTip({ label, title, description, children, layout = "box", side = "
|
|
|
32
35
|
if (!canHoverOpen())
|
|
33
36
|
return;
|
|
34
37
|
cancelClose();
|
|
35
|
-
|
|
38
|
+
if (open)
|
|
39
|
+
return;
|
|
40
|
+
openedBy.current = "hover";
|
|
36
41
|
setOpen(true);
|
|
37
42
|
};
|
|
38
43
|
const closeFromHover = () => {
|
|
39
|
-
if (!canHoverOpen() ||
|
|
44
|
+
if (!canHoverOpen() || openedBy.current !== "hover")
|
|
40
45
|
return;
|
|
41
46
|
cancelClose();
|
|
42
47
|
// Leave a beat so the pointer can travel from the glyph into the panel.
|
|
@@ -44,18 +49,31 @@ function InfoTip({ label, title, description, children, layout = "box", side = "
|
|
|
44
49
|
};
|
|
45
50
|
React.useEffect(() => () => cancelClose(), []);
|
|
46
51
|
return (_jsxs(Popover, { open: open, onOpenChange: (next) => {
|
|
47
|
-
//
|
|
48
|
-
//
|
|
49
|
-
|
|
50
|
-
if (
|
|
51
|
-
|
|
52
|
-
return;
|
|
53
|
-
}
|
|
54
|
-
hoverOpened.current = false;
|
|
52
|
+
// Escape and outside clicks close however the panel opened. Keep
|
|
53
|
+
// `openedBy` on close so onCloseAutoFocus can read it.
|
|
54
|
+
cancelClose();
|
|
55
|
+
if (next)
|
|
56
|
+
openedBy.current = "click";
|
|
55
57
|
setOpen(next);
|
|
56
|
-
}, children: [_jsx(PopoverTrigger, { asChild: true, children: _jsx("button", { type: "button", "aria-label": label, "data-slot": "info-tip-trigger", "data-layout": layout, className: cn(focusRing, "relative inline-flex shrink-0 items-center justify-center rounded-none text-muted-foreground hover:text-selected-foreground focus-visible:text-selected-foreground", inline
|
|
57
|
-
|
|
58
|
-
|
|
58
|
+
}, children: [_jsx(PopoverTrigger, { asChild: true, children: _jsx("button", { ...props, type: "button", "aria-label": label, "data-slot": "info-tip-trigger", "data-layout": layout, className: cn(focusRing, "relative inline-flex shrink-0 items-center justify-center rounded-none text-muted-foreground hover:text-selected-foreground focus-visible:text-selected-foreground", inline ? cn("size-5 align-middle", hitArea44) : "size-11", className), onMouseEnter: openFromHover, onMouseLeave: closeFromHover, onClick: (event) => {
|
|
59
|
+
// Hover already opened the panel; this click pins it instead of
|
|
60
|
+
// toggling it closed (Playwright and real mice both hover before
|
|
61
|
+
// they click). Preventing default skips Radix's toggle.
|
|
62
|
+
if (open && openedBy.current === "hover") {
|
|
63
|
+
event.preventDefault();
|
|
64
|
+
cancelClose();
|
|
65
|
+
openedBy.current = "click";
|
|
66
|
+
}
|
|
67
|
+
}, children: icon ?? (_jsx(CircleHelpIcon, { className: "size-5", "data-icon": "info-tip", "aria-hidden": true })) }) }), _jsxs(PopoverContent, { side: side, align: align, className: cn("w-72", contentClassName), "aria-labelledby": title ? titleId : undefined, "aria-label": title ? undefined : label,
|
|
68
|
+
// A pointer passing over the glyph must not take focus from where
|
|
69
|
+
// the user is typing, nor hand it to the trigger on close.
|
|
70
|
+
onOpenAutoFocus: (event) => {
|
|
71
|
+
if (openedBy.current === "hover")
|
|
72
|
+
event.preventDefault();
|
|
73
|
+
}, onCloseAutoFocus: (event) => {
|
|
74
|
+
if (openedBy.current === "hover")
|
|
75
|
+
event.preventDefault();
|
|
76
|
+
}, onMouseEnter: openFromHover, onMouseLeave: closeFromHover, children: [_jsxs(PopoverHeader, { children: [_jsxs("div", { className: "flex items-start justify-between gap-2", children: [title ? _jsx(PopoverTitle, { id: titleId, children: title }) : null, badge] }), description ? (_jsx(PopoverDescription, { children: description })) : null] }), children] })] }));
|
|
59
77
|
}
|
|
60
78
|
/** InfoTip that sits in a line of text without stretching the line box. */
|
|
61
79
|
function InlineTip(props) {
|
|
@@ -16,3 +16,8 @@ export declare const pickerTriggerClassName = "h-10 w-full border-input bg-backg
|
|
|
16
16
|
export declare const pickerContentClassName = "max-h-(--radix-popover-content-available-height) overflow-y-auto rounded-none p-0";
|
|
17
17
|
/** Gutter used when a calendar popover collides with the viewport. */
|
|
18
18
|
export declare const PICKER_COLLISION_PADDING = 16;
|
|
19
|
+
/**
|
|
20
|
+
* Id of a picker trigger's value text. A form field lists it after the label
|
|
21
|
+
* id in `aria-labelledby` so the trigger reads "label, value".
|
|
22
|
+
*/
|
|
23
|
+
export declare function pickerValueId(id: string): string;
|
|
@@ -16,3 +16,10 @@ export const pickerTriggerClassName = `${formControlTriggerClassName} justify-st
|
|
|
16
16
|
export const pickerContentClassName = "max-h-(--radix-popover-content-available-height) overflow-y-auto rounded-none p-0";
|
|
17
17
|
/** Gutter used when a calendar popover collides with the viewport. */
|
|
18
18
|
export const PICKER_COLLISION_PADDING = 16;
|
|
19
|
+
/**
|
|
20
|
+
* Id of a picker trigger's value text. A form field lists it after the label
|
|
21
|
+
* id in `aria-labelledby` so the trigger reads "label, value".
|
|
22
|
+
*/
|
|
23
|
+
export function pickerValueId(id) {
|
|
24
|
+
return `${id}-value`;
|
|
25
|
+
}
|
|
@@ -12,3 +12,35 @@ export declare function listboxOptionId(listId: string, index: number): string;
|
|
|
12
12
|
* so the ref is stable without `useCallback`.
|
|
13
13
|
*/
|
|
14
14
|
export declare function scrollOptionIntoView(node: HTMLElement | null): void;
|
|
15
|
+
/**
|
|
16
|
+
* True while an IME is composing (Japanese, Chinese, Korean input). Enter
|
|
17
|
+
* then commits the composition and must not select an option. `keyCode`
|
|
18
|
+
* 229 covers Safari, which ends composition before the keydown.
|
|
19
|
+
*/
|
|
20
|
+
export declare function isComposingKey(event: {
|
|
21
|
+
nativeEvent: KeyboardEvent;
|
|
22
|
+
keyCode: number;
|
|
23
|
+
}): boolean;
|
|
24
|
+
/**
|
|
25
|
+
* Shared keydown for a search input that drives a listbox: navigation keys
|
|
26
|
+
* move the highlight, Enter picks the highlighted index. Returns `true` when
|
|
27
|
+
* the key is handled (or an IME is composing, when every key belongs to the
|
|
28
|
+
* IME), so the caller can stop; `false` leaves the key to the caller (for
|
|
29
|
+
* example SearchPicker's Escape).
|
|
30
|
+
*/
|
|
31
|
+
export declare function handleListboxKey(event: {
|
|
32
|
+
key: string;
|
|
33
|
+
keyCode: number;
|
|
34
|
+
nativeEvent: KeyboardEvent;
|
|
35
|
+
preventDefault: () => void;
|
|
36
|
+
}, { highlight, length, setHighlight, onEnter, }: {
|
|
37
|
+
highlight: number;
|
|
38
|
+
length: number;
|
|
39
|
+
setHighlight: (index: number) => void;
|
|
40
|
+
onEnter: (index: number) => void;
|
|
41
|
+
}): boolean;
|
|
42
|
+
/**
|
|
43
|
+
* Case-fold and strip combining marks so "nino" matches "Niño". Shared by
|
|
44
|
+
* Combobox and SearchPicker (which re-exports it).
|
|
45
|
+
*/
|
|
46
|
+
export declare function foldSearchText(value: string): string;
|
|
@@ -35,3 +35,41 @@ export function listboxOptionId(listId, index) {
|
|
|
35
35
|
export function scrollOptionIntoView(node) {
|
|
36
36
|
node?.scrollIntoView({ block: "nearest" });
|
|
37
37
|
}
|
|
38
|
+
/**
|
|
39
|
+
* True while an IME is composing (Japanese, Chinese, Korean input). Enter
|
|
40
|
+
* then commits the composition and must not select an option. `keyCode`
|
|
41
|
+
* 229 covers Safari, which ends composition before the keydown.
|
|
42
|
+
*/
|
|
43
|
+
export function isComposingKey(event) {
|
|
44
|
+
return event.nativeEvent.isComposing || event.keyCode === 229;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Shared keydown for a search input that drives a listbox: navigation keys
|
|
48
|
+
* move the highlight, Enter picks the highlighted index. Returns `true` when
|
|
49
|
+
* the key is handled (or an IME is composing, when every key belongs to the
|
|
50
|
+
* IME), so the caller can stop; `false` leaves the key to the caller (for
|
|
51
|
+
* example SearchPicker's Escape).
|
|
52
|
+
*/
|
|
53
|
+
export function handleListboxKey(event, { highlight, length, setHighlight, onEnter, }) {
|
|
54
|
+
if (isComposingKey(event))
|
|
55
|
+
return true;
|
|
56
|
+
const next = moveListboxHighlight(event.key, highlight, length);
|
|
57
|
+
if (next !== undefined) {
|
|
58
|
+
event.preventDefault();
|
|
59
|
+
setHighlight(next);
|
|
60
|
+
return true;
|
|
61
|
+
}
|
|
62
|
+
if (event.key === "Enter") {
|
|
63
|
+
event.preventDefault();
|
|
64
|
+
onEnter(highlight);
|
|
65
|
+
return true;
|
|
66
|
+
}
|
|
67
|
+
return false;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Case-fold and strip combining marks so "nino" matches "Niño". Shared by
|
|
71
|
+
* Combobox and SearchPicker (which re-exports it).
|
|
72
|
+
*/
|
|
73
|
+
export function foldSearchText(value) {
|
|
74
|
+
return value.normalize("NFD").replace(/\p{M}/gu, "").toLowerCase();
|
|
75
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
type ListboxOptionProps = Omit<React.ComponentProps<"button">, "id" | "role" | "type" | "tabIndex" | "aria-selected"> & {
|
|
3
|
+
/** The listbox id; the option id derives from it for aria-activedescendant. */
|
|
4
|
+
listId: string;
|
|
5
|
+
index: number;
|
|
6
|
+
/** Keyboard or pointer highlight (tracked in state, not DOM focus). */
|
|
7
|
+
active: boolean;
|
|
8
|
+
selected: boolean;
|
|
9
|
+
};
|
|
10
|
+
/**
|
|
11
|
+
* One option of a search-driven listbox (Combobox, SearchPicker): a
|
|
12
|
+
* non-focusable button in a presentational `li`, highlighted by state and
|
|
13
|
+
* scrolled into view while active. Pass `data-slot`, handlers, and the
|
|
14
|
+
* component's right padding and indicator.
|
|
15
|
+
*/
|
|
16
|
+
export declare function ListboxOption({ listId, index, active, selected, className, ...props }: ListboxOptionProps): React.JSX.Element;
|
|
17
|
+
/**
|
|
18
|
+
* The "No results" status. It sits outside the listbox, which may own only
|
|
19
|
+
* options, and stays mounted (sr-only when not empty) so screen readers
|
|
20
|
+
* announce the empty state as the query changes.
|
|
21
|
+
*/
|
|
22
|
+
export declare function ListboxEmptyStatus({ empty, className, children, ...props }: Omit<React.ComponentProps<"div">, "role"> & {
|
|
23
|
+
empty: boolean;
|
|
24
|
+
}): React.JSX.Element;
|
|
25
|
+
export {};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import * as React from "react";
|
|
3
|
+
import { cn } from "../utils.js";
|
|
4
|
+
import { listboxOptionId, scrollOptionIntoView } from "./listbox-navigation.js";
|
|
5
|
+
import { optionRow } from "./overlay-styles.js";
|
|
6
|
+
import { listboxEmpty, optionState } from "./styles.js";
|
|
7
|
+
/**
|
|
8
|
+
* One option of a search-driven listbox (Combobox, SearchPicker): a
|
|
9
|
+
* non-focusable button in a presentational `li`, highlighted by state and
|
|
10
|
+
* scrolled into view while active. Pass `data-slot`, handlers, and the
|
|
11
|
+
* component's right padding and indicator.
|
|
12
|
+
*/
|
|
13
|
+
export function ListboxOption({ listId, index, active, selected, className, ...props }) {
|
|
14
|
+
return (_jsx("li", { role: "none", children: _jsx("button", { ref: active ? scrollOptionIntoView : undefined, id: listboxOptionId(listId, index), type: "button", role: "option", tabIndex: -1, "aria-selected": selected, className: cn(optionRow, selected && optionState.selected, active && optionState.active, className), ...props }) }));
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* The "No results" status. It sits outside the listbox, which may own only
|
|
18
|
+
* options, and stays mounted (sr-only when not empty) so screen readers
|
|
19
|
+
* announce the empty state as the query changes.
|
|
20
|
+
*/
|
|
21
|
+
export function ListboxEmptyStatus({ empty, className, children, ...props }) {
|
|
22
|
+
return (_jsx("div", { role: "status", className: empty ? cn(listboxEmpty, className) : "sr-only", ...props, children: empty ? children : null }));
|
|
23
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Classes shared by the kit's floating surfaces (Popover, DropdownMenu,
|
|
3
|
+
* Select, Dialog, Tooltip) and the option rows inside them. Components add
|
|
4
|
+
* these with `cn()` instead of retyping them, so the panels and rows look
|
|
5
|
+
* and move the same everywhere. Tailwind only generates class names it
|
|
6
|
+
* finds as literals, so every class appears here in full.
|
|
7
|
+
*/
|
|
8
|
+
/** The panel: square corners, popover colors, shadow, and a hairline ring. */
|
|
9
|
+
export declare const overlaySurface = "rounded-none bg-popover text-popover-foreground shadow-md ring-1 ring-foreground/10";
|
|
10
|
+
/** Slide in from the trigger, on whichever side Radix placed the panel. */
|
|
11
|
+
export declare const overlaySideSlide = "data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2";
|
|
12
|
+
/**
|
|
13
|
+
* Fade on open and close. `data-open` and `data-closed` (theme.css) match
|
|
14
|
+
* Radix's `data-state="open"` and `"closed"`.
|
|
15
|
+
*/
|
|
16
|
+
export declare const overlayFade = "duration-100 data-open:animate-in data-open:fade-in-0 data-closed:animate-out data-closed:fade-out-0";
|
|
17
|
+
/** Fade plus a slight zoom, for panels (the backdrop only fades). */
|
|
18
|
+
export declare const overlayFadeZoom = "duration-100 data-open:animate-in data-open:fade-in-0 data-closed:animate-out data-closed:fade-out-0 data-open:zoom-in-95 data-closed:zoom-out-95";
|
|
19
|
+
/**
|
|
20
|
+
* Base of a menu or listbox row (SelectItem, DropdownMenuItem, Combobox and
|
|
21
|
+
* SearchPicker options). Each component adds its own right padding and
|
|
22
|
+
* indicator, plus the highlight from `optionState`.
|
|
23
|
+
*/
|
|
24
|
+
export declare const optionRow = "relative flex w-full cursor-default items-center gap-2.5 rounded-none py-2 pl-3 text-left text-sm outline-hidden select-none";
|
|
25
|
+
/** Icons in a row: no pointer events, no shrinking, 14px unless sized. */
|
|
26
|
+
export declare const optionIcon = "[&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-3.5";
|
|
27
|
+
/** Group label in a menu or listbox (DropdownMenuLabel, SelectLabel). */
|
|
28
|
+
export declare const menuLabel = "px-3 py-2 text-xs font-semibold tracking-wider text-muted-foreground uppercase";
|