atlas-react-kit 1.1.0 → 1.2.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/COMPONENTS.md +22 -26
- package/DESIGN_RULES.md +14 -40
- package/README.md +7 -6
- package/dist/accordion.js +2 -2
- package/dist/alert.js +6 -5
- package/dist/badge.js +5 -2
- package/dist/breadcrumb.js +2 -2
- package/dist/button.js +7 -4
- package/dist/calendar.js +5 -4
- package/dist/combobox.js +7 -13
- package/dist/date-time-picker.js +2 -1
- package/dist/dropdown-menu.js +4 -4
- package/dist/field.d.ts +6 -3
- package/dist/field.js +3 -2
- package/dist/form/checkbox-field.d.ts +1 -1
- package/dist/form/checkbox-field.js +8 -11
- package/dist/form/combobox-field.js +2 -2
- package/dist/form/control-field.js +2 -7
- package/dist/form/date-picker-field.js +5 -2
- package/dist/form/date-time-picker-field.js +5 -2
- package/dist/form/field-error.d.ts +3 -3
- package/dist/form/field-shell.d.ts +36 -31
- package/dist/form/field-shell.js +41 -34
- package/dist/form/markdown-field.js +2 -2
- package/dist/form/select-field.js +2 -2
- package/dist/form/text-field.js +2 -4
- package/dist/form/toggle-group-field.js +2 -2
- package/dist/info-tip.d.ts +5 -4
- package/dist/info-tip.js +15 -14
- package/dist/internal/active-link.d.ts +7 -0
- package/dist/internal/active-link.js +9 -0
- package/dist/internal/calendar-size.d.ts +5 -0
- package/dist/internal/calendar-size.js +5 -0
- package/dist/internal/form-control.d.ts +1 -1
- package/dist/internal/form-control.js +2 -1
- package/dist/internal/listbox-navigation.d.ts +0 -5
- package/dist/internal/listbox-navigation.js +0 -7
- package/dist/internal/listbox-option.d.ts +16 -3
- package/dist/internal/listbox-option.js +22 -5
- package/dist/internal/overlay-styles.d.ts +10 -7
- package/dist/internal/overlay-styles.js +10 -7
- package/dist/internal/search.d.ts +9 -1
- package/dist/internal/search.js +14 -5
- package/dist/internal/styles.d.ts +43 -0
- package/dist/internal/styles.js +43 -0
- package/dist/internal/tabs-styles.d.ts +8 -7
- package/dist/internal/tabs-styles.js +15 -13
- package/dist/internal/use-media-query.d.ts +5 -0
- package/dist/internal/use-media-query.js +5 -0
- package/dist/kanban.d.ts +1 -1
- package/dist/kanban.js +6 -4
- package/dist/label.js +2 -1
- package/dist/lazy-calendar.js +3 -2
- package/dist/nav-link.js +2 -1
- package/dist/popover.js +4 -3
- package/dist/search-picker.d.ts +1 -1
- package/dist/search-picker.js +7 -12
- package/dist/select.js +6 -6
- package/dist/skip-link.js +2 -1
- package/dist/tabs.d.ts +3 -3
- package/dist/tabs.js +7 -6
- package/dist/toast.js +11 -10
- package/dist/toggle.d.ts +2 -0
- package/dist/toggle.js +8 -3
- package/dist/tooltip.js +2 -2
- package/package.json +1 -1
- package/skills/atlas-react-kit/SKILL.md +46 -42
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/** How NavLink and TabsNavLink mark the current page (`selectedState.active`). */
|
|
2
|
+
export declare function activeLinkAttributes(active: boolean): {
|
|
3
|
+
"data-state": string;
|
|
4
|
+
"aria-current": "page" | undefined;
|
|
5
|
+
};
|
|
6
|
+
/** A link marked by `activeLinkAttributes`, or an active Radix tab. */
|
|
7
|
+
export declare const activeLinkSelector = "[aria-current=\"page\"], [data-state=\"active\"]";
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/** How NavLink and TabsNavLink mark the current page (`selectedState.active`). */
|
|
2
|
+
export function activeLinkAttributes(active) {
|
|
3
|
+
return {
|
|
4
|
+
"data-state": active ? "active" : "inactive",
|
|
5
|
+
"aria-current": active ? "page" : undefined,
|
|
6
|
+
};
|
|
7
|
+
}
|
|
8
|
+
/** A link marked by `activeLinkAttributes`, or an active Radix tab. */
|
|
9
|
+
export const activeLinkSelector = '[aria-current="page"], [data-state="active"]';
|
|
@@ -12,7 +12,7 @@ export declare const formControlTriggerClassName = "h-10 w-full border-input bg-
|
|
|
12
12
|
* with muted text while empty (`data-empty`). Intrinsic padding makes one
|
|
13
13
|
* line 40px tall while longer values wrap and consumer height classes work.
|
|
14
14
|
*/
|
|
15
|
-
export declare const pickerTriggerClassName = "h-10 w-full border-input bg-background px-3 font-normal text-base tracking-normal shadow-xs normal-case md:text-sm hover:border-input hover:bg-background hover:text-foreground focus-visible:text-foreground aria-expanded:border-input aria-expanded:bg-background aria-expanded:text-foreground has-data-[icon=inline-start]:pl-3 has-data-[icon=inline-end]:pr-3 dark:hover:bg-background dark:aria-expanded:bg-background h-auto justify-start py-[calc((--spacing(10)_-_1lh_-_2px)/2)] data-[empty=true]:text-muted-foreground";
|
|
15
|
+
export declare const pickerTriggerClassName = "h-10 w-full border-input bg-background px-3 font-normal text-base tracking-normal shadow-xs normal-case md:text-sm hover:border-input hover:bg-background hover:text-foreground focus-visible:text-foreground aria-expanded:border-input aria-expanded:bg-background aria-expanded:text-foreground has-data-[icon=inline-start]:pl-3 has-data-[icon=inline-end]:pr-3 dark:hover:bg-background dark:aria-expanded:bg-background h-auto justify-start py-[calc((--spacing(10)_-_1lh_-_2px)/2)] data-placeholder:text-muted-foreground data-[empty=true]:text-muted-foreground";
|
|
16
16
|
/** Calendar popover body: fits the viewport height and scrolls inside it. */
|
|
17
17
|
export declare const pickerContentClassName = "max-h-(--radix-popover-content-available-height) overflow-y-auto rounded-none p-0";
|
|
18
18
|
/** Gutter used when a calendar popover collides with the viewport. */
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { emptyValueText } from "./styles.js";
|
|
1
2
|
/**
|
|
2
3
|
* Chrome that makes a Button-based picker trigger match Input / SelectTrigger:
|
|
3
4
|
* 40px height, px-3, body text (16px below md so iOS does not zoom), border-input,
|
|
@@ -12,7 +13,7 @@ export const formControlTriggerClassName = "h-10 w-full border-input bg-backgrou
|
|
|
12
13
|
* with muted text while empty (`data-empty`). Intrinsic padding makes one
|
|
13
14
|
* line 40px tall while longer values wrap and consumer height classes work.
|
|
14
15
|
*/
|
|
15
|
-
export const pickerTriggerClassName = `${formControlTriggerClassName} h-auto justify-start py-[calc((--spacing(10)_-_1lh_-_2px)/2)]
|
|
16
|
+
export const pickerTriggerClassName = `${formControlTriggerClassName} h-auto justify-start py-[calc((--spacing(10)_-_1lh_-_2px)/2)] ${emptyValueText}`;
|
|
16
17
|
/** Calendar popover body: fits the viewport height and scrolls inside it. */
|
|
17
18
|
export const pickerContentClassName = "max-h-(--radix-popover-content-available-height) overflow-y-auto rounded-none p-0";
|
|
18
19
|
/** Gutter used when a calendar popover collides with the viewport. */
|
|
@@ -28,8 +28,3 @@ export declare function handleListboxKey(event: {
|
|
|
28
28
|
setHighlight: (index: number) => void;
|
|
29
29
|
onEnter: (index: number) => void;
|
|
30
30
|
}): boolean;
|
|
31
|
-
/**
|
|
32
|
-
* Case-fold and strip combining marks so "nino" matches "Niño". Shared by
|
|
33
|
-
* Combobox and SearchPicker (which re-exports it).
|
|
34
|
-
*/
|
|
35
|
-
export declare function foldSearchText(value: string): string;
|
|
@@ -66,10 +66,3 @@ export function handleListboxKey(event, { highlight, length, setHighlight, onEnt
|
|
|
66
66
|
}
|
|
67
67
|
return false;
|
|
68
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
|
-
}
|
|
@@ -1,4 +1,16 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
|
+
/**
|
|
3
|
+
* ARIA for the search input of a listbox; the input sets `aria-controls`
|
|
4
|
+
* itself, where the linter can see it.
|
|
5
|
+
*/
|
|
6
|
+
export declare function listboxInputAria(listId: string, activeIndex: number | undefined): {
|
|
7
|
+
"aria-autocomplete": "list";
|
|
8
|
+
"aria-activedescendant": string | undefined;
|
|
9
|
+
};
|
|
10
|
+
/** The scrolling listbox; sr-only while `ListboxEmptyStatus` shows. */
|
|
11
|
+
export declare function Listbox({ empty, className, ...props }: Omit<React.ComponentProps<"ul">, "role"> & {
|
|
12
|
+
empty: boolean;
|
|
13
|
+
}): React.JSX.Element;
|
|
2
14
|
type PickerOptionProps<T> = {
|
|
3
15
|
item: T;
|
|
4
16
|
getLabel: (item: T) => string;
|
|
@@ -16,9 +28,10 @@ type PickerOptionProps<T> = {
|
|
|
16
28
|
declare function PickerOptionInner<T>({ item, getLabel, onSelect, onHighlight, showCheck, ...props }: PickerOptionProps<T>): React.JSX.Element;
|
|
17
29
|
export declare const PickerOption: typeof PickerOptionInner;
|
|
18
30
|
/**
|
|
19
|
-
* The "No results" status
|
|
20
|
-
*
|
|
21
|
-
* announce the empty state as
|
|
31
|
+
* The "No results" status (`children`, default "No results"). It sits
|
|
32
|
+
* outside the listbox, which may own only options, and stays mounted
|
|
33
|
+
* (sr-only when not empty) so screen readers announce the empty state as
|
|
34
|
+
* the query changes.
|
|
22
35
|
*/
|
|
23
36
|
export declare function ListboxEmptyStatus({ empty, className, children, ...props }: Omit<React.ComponentProps<"div">, "role"> & {
|
|
24
37
|
empty: boolean;
|
|
@@ -4,7 +4,23 @@ import { CheckIcon } from "lucide-react";
|
|
|
4
4
|
import { cn } from "../utils.js";
|
|
5
5
|
import { listboxOptionId, scrollOptionIntoView } from "./listbox-navigation.js";
|
|
6
6
|
import { optionRow } from "./overlay-styles.js";
|
|
7
|
-
import { listboxEmpty, optionState } from "./styles.js";
|
|
7
|
+
import { listboxEmpty, listboxScroll, optionState } from "./styles.js";
|
|
8
|
+
/**
|
|
9
|
+
* ARIA for the search input of a listbox; the input sets `aria-controls`
|
|
10
|
+
* itself, where the linter can see it.
|
|
11
|
+
*/
|
|
12
|
+
export function listboxInputAria(listId, activeIndex) {
|
|
13
|
+
return {
|
|
14
|
+
"aria-autocomplete": "list",
|
|
15
|
+
"aria-activedescendant": activeIndex === undefined
|
|
16
|
+
? undefined
|
|
17
|
+
: listboxOptionId(listId, activeIndex),
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
/** The scrolling listbox; sr-only while `ListboxEmptyStatus` shows. */
|
|
21
|
+
export function Listbox({ empty, className, ...props }) {
|
|
22
|
+
return (_jsx("ul", { role: "listbox", className: empty ? "sr-only" : cn(listboxScroll, className), ...props }));
|
|
23
|
+
}
|
|
8
24
|
/**
|
|
9
25
|
* One option of a search-driven listbox (Combobox, SearchPicker): a
|
|
10
26
|
* non-focusable button in a presentational `li`, highlighted by state and
|
|
@@ -20,10 +36,11 @@ function PickerOptionInner({ item, getLabel, onSelect, onHighlight, showCheck, .
|
|
|
20
36
|
}
|
|
21
37
|
export const PickerOption = React.memo(PickerOptionInner);
|
|
22
38
|
/**
|
|
23
|
-
* The "No results" status
|
|
24
|
-
*
|
|
25
|
-
* announce the empty state as
|
|
39
|
+
* The "No results" status (`children`, default "No results"). It sits
|
|
40
|
+
* outside the listbox, which may own only options, and stays mounted
|
|
41
|
+
* (sr-only when not empty) so screen readers announce the empty state as
|
|
42
|
+
* the query changes.
|
|
26
43
|
*/
|
|
27
|
-
export function ListboxEmptyStatus({ empty, className, children, ...props }) {
|
|
44
|
+
export function ListboxEmptyStatus({ empty, className, children = "No results", ...props }) {
|
|
28
45
|
return (_jsx("div", { role: "status", className: empty ? cn(listboxEmpty, className) : "sr-only", ...props, children: empty ? children : null }));
|
|
29
46
|
}
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Classes shared by the kit's floating surfaces
|
|
3
|
-
* Select, Dialog, Tooltip) and the option rows
|
|
4
|
-
*
|
|
5
|
-
* and move the same everywhere. Tailwind only
|
|
6
|
-
* finds as literals, so every class appears here
|
|
2
|
+
* Classes and placement defaults shared by the kit's floating surfaces
|
|
3
|
+
* (Popover, DropdownMenu, Select, Dialog, Tooltip) and the option rows
|
|
4
|
+
* inside them. Components use these instead of retyping them, so the
|
|
5
|
+
* panels and rows look, sit, and move the same everywhere. Tailwind only
|
|
6
|
+
* generates class names it finds as literals, so every class appears here
|
|
7
|
+
* in full.
|
|
7
8
|
*/
|
|
8
9
|
/** The panel: square corners, popover colors, shadow, and a hairline ring. */
|
|
9
10
|
export declare const overlaySurface = "rounded-none bg-popover text-popover-foreground shadow-md ring-1 ring-foreground/10";
|
|
@@ -22,7 +23,9 @@ export declare const overlayFadeZoom = "duration-100 data-open:animate-in data-o
|
|
|
22
23
|
* indicator, plus the highlight from `optionState`.
|
|
23
24
|
*/
|
|
24
25
|
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
26
|
/** Group label in a menu or listbox (DropdownMenuLabel, SelectLabel). */
|
|
28
27
|
export declare const menuLabel = "px-3 py-2 text-xs font-semibold tracking-wider text-muted-foreground uppercase";
|
|
28
|
+
/** Default gap in px between a trigger and its panel (Radix `sideOffset`). */
|
|
29
|
+
export declare const OVERLAY_SIDE_OFFSET = 4;
|
|
30
|
+
/** Default px a panel keeps from the viewport edge (Radix `collisionPadding`). */
|
|
31
|
+
export declare const OVERLAY_COLLISION_PADDING = 8;
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Classes shared by the kit's floating surfaces
|
|
3
|
-
* Select, Dialog, Tooltip) and the option rows
|
|
4
|
-
*
|
|
5
|
-
* and move the same everywhere. Tailwind only
|
|
6
|
-
* finds as literals, so every class appears here
|
|
2
|
+
* Classes and placement defaults shared by the kit's floating surfaces
|
|
3
|
+
* (Popover, DropdownMenu, Select, Dialog, Tooltip) and the option rows
|
|
4
|
+
* inside them. Components use these instead of retyping them, so the
|
|
5
|
+
* panels and rows look, sit, and move the same everywhere. Tailwind only
|
|
6
|
+
* generates class names it finds as literals, so every class appears here
|
|
7
|
+
* in full.
|
|
7
8
|
*/
|
|
8
9
|
/** The panel: square corners, popover colors, shadow, and a hairline ring. */
|
|
9
10
|
export const overlaySurface = "rounded-none bg-popover text-popover-foreground shadow-md ring-1 ring-foreground/10";
|
|
@@ -22,7 +23,9 @@ export const overlayFadeZoom = `${overlayFade} data-open:zoom-in-95 data-closed:
|
|
|
22
23
|
* indicator, plus the highlight from `optionState`.
|
|
23
24
|
*/
|
|
24
25
|
export 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 const optionIcon = "[&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-3.5";
|
|
27
26
|
/** Group label in a menu or listbox (DropdownMenuLabel, SelectLabel). */
|
|
28
27
|
export const menuLabel = "px-3 py-2 text-xs font-semibold tracking-wider text-muted-foreground uppercase";
|
|
28
|
+
/** Default gap in px between a trigger and its panel (Radix `sideOffset`). */
|
|
29
|
+
export const OVERLAY_SIDE_OFFSET = 4;
|
|
30
|
+
/** Default px a panel keeps from the viewport edge (Radix `collisionPadding`). */
|
|
31
|
+
export const OVERLAY_COLLISION_PADDING = 8;
|
|
@@ -1,8 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Case-fold and strip combining marks so "nino" matches "Niño". Shared by
|
|
3
|
+
* Combobox and SearchPicker (which re-exports it).
|
|
4
|
+
*/
|
|
5
|
+
export declare function foldSearchText(value: string): string;
|
|
1
6
|
/** Fold the query once; item labels are folded when their catalog changes. */
|
|
2
7
|
export declare function normalizeSearchQuery(query: string): string;
|
|
3
|
-
export declare function matchesSearchLabel(label: string, needle: string): boolean;
|
|
4
8
|
export declare function createSearchIndex<T>(items: T[], getLabel: (item: T) => string): {
|
|
5
9
|
item: T;
|
|
6
10
|
label: string;
|
|
7
11
|
}[];
|
|
12
|
+
/**
|
|
13
|
+
* The items whose folded label includes `needle` (a `normalizeSearchQuery`
|
|
14
|
+
* result), in order; every item when `needle` is empty.
|
|
15
|
+
*/
|
|
8
16
|
export declare function filterSearchIndex<T>(index: ReturnType<typeof createSearchIndex<T>>, needle: string): T[];
|
package/dist/internal/search.js
CHANGED
|
@@ -1,16 +1,25 @@
|
|
|
1
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Case-fold and strip combining marks so "nino" matches "Niño". Shared by
|
|
3
|
+
* Combobox and SearchPicker (which re-exports it).
|
|
4
|
+
*/
|
|
5
|
+
export function foldSearchText(value) {
|
|
6
|
+
return value.normalize("NFD").replace(/\p{M}/gu, "").toLowerCase();
|
|
7
|
+
}
|
|
2
8
|
/** Fold the query once; item labels are folded when their catalog changes. */
|
|
3
9
|
export function normalizeSearchQuery(query) {
|
|
4
10
|
return foldSearchText(query.trim());
|
|
5
11
|
}
|
|
6
|
-
export function matchesSearchLabel(label, needle) {
|
|
7
|
-
return label.includes(needle);
|
|
8
|
-
}
|
|
9
12
|
export function createSearchIndex(items, getLabel) {
|
|
10
13
|
return items.map((item) => ({ item, label: foldSearchText(getLabel(item)) }));
|
|
11
14
|
}
|
|
15
|
+
/**
|
|
16
|
+
* The items whose folded label includes `needle` (a `normalizeSearchQuery`
|
|
17
|
+
* result), in order; every item when `needle` is empty.
|
|
18
|
+
*/
|
|
12
19
|
export function filterSearchIndex(index, needle) {
|
|
20
|
+
if (!needle)
|
|
21
|
+
return index.map(({ item }) => item);
|
|
13
22
|
return index
|
|
14
|
-
.filter(({ label }) =>
|
|
23
|
+
.filter(({ label }) => label.includes(needle))
|
|
15
24
|
.map(({ item }) => item);
|
|
16
25
|
}
|
|
@@ -8,8 +8,12 @@
|
|
|
8
8
|
*/
|
|
9
9
|
/** Keyboard focus: a solid 2px ring separated from filled controls (WCAG 1.4.11). */
|
|
10
10
|
export declare const focusRing = "outline-none focus-visible:border-ring focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background";
|
|
11
|
+
/** The same ring on a wrapper with keyboard focus inside it. */
|
|
12
|
+
export declare const focusRingWithin = "has-focus-visible:ring-2 has-focus-visible:ring-ring has-focus-visible:ring-offset-2 has-focus-visible:ring-offset-background";
|
|
11
13
|
/** `aria-invalid`: destructive border and a soft glow; the error text carries the message. */
|
|
12
14
|
export declare const invalidState = "aria-invalid:border-destructive aria-invalid:ring-2 aria-invalid:ring-destructive/20 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40";
|
|
15
|
+
/** Muted trigger text while empty (`data-placeholder` or `data-empty`). */
|
|
16
|
+
export declare const emptyValueText = "data-placeholder:text-muted-foreground data-[empty=true]:text-muted-foreground";
|
|
13
17
|
/** Native `disabled`: dimmed, with no hover or click. */
|
|
14
18
|
export declare const disabledState = "disabled:pointer-events-none disabled:opacity-50";
|
|
15
19
|
/** Radix `data-disabled` (menu and list items, slider). */
|
|
@@ -37,8 +41,23 @@ export declare const selectedState: {
|
|
|
37
41
|
/** Quiet buttons and toggles on hover. */
|
|
38
42
|
hover: string;
|
|
39
43
|
};
|
|
44
|
+
/**
|
|
45
|
+
* Alert and Toast tones: a border, and `--status-tone` for the title and
|
|
46
|
+
* icon (`text-(--status-tone)`). `neutral` unsets it, so they inherit.
|
|
47
|
+
*/
|
|
48
|
+
export declare const statusTone: {
|
|
49
|
+
neutral: string;
|
|
50
|
+
success: string;
|
|
51
|
+
warning: string;
|
|
52
|
+
destructive: string;
|
|
53
|
+
};
|
|
40
54
|
/** The "No results" row of a listbox. */
|
|
41
55
|
export declare const listboxEmpty = "px-3 py-4 text-center text-sm text-muted-foreground";
|
|
56
|
+
/**
|
|
57
|
+
* The scrolling frame of a listbox (Combobox, SearchPicker): about ten rows
|
|
58
|
+
* tall, then scrolls. A consumer `listClassName` replaces the height.
|
|
59
|
+
*/
|
|
60
|
+
export declare const listboxScroll = "max-h-60 overflow-y-auto";
|
|
42
61
|
/** Options in menus and listboxes. */
|
|
43
62
|
export declare const optionState: {
|
|
44
63
|
/** Highlight as focus moves: grey fill plus a 2px ring bar (the fill alone is 1.1:1). */
|
|
@@ -72,6 +91,30 @@ export declare const controlSize: {
|
|
|
72
91
|
sm: string;
|
|
73
92
|
lg: string;
|
|
74
93
|
};
|
|
94
|
+
/**
|
|
95
|
+
* Label text of a control (Button, Toggle, Badge): small, bold, wide-tracked
|
|
96
|
+
* uppercase on one line. Badge adds its own `text-[0.625rem]` after it;
|
|
97
|
+
* tailwind-merge then drops the `text-xs`.
|
|
98
|
+
*/
|
|
99
|
+
export declare const controlText = "text-xs font-semibold tracking-widest whitespace-nowrap uppercase";
|
|
100
|
+
/** Field-label text (Label, FieldLegend, FieldTitle, PopoverTitle). */
|
|
101
|
+
export declare const labelText = "text-xs font-semibold tracking-wide uppercase";
|
|
102
|
+
/**
|
|
103
|
+
* Icons inside a control or row: no pointer events, no shrinking, 14px
|
|
104
|
+
* unless the icon sets its own `size-*`. `sm` lowers that default to 12px
|
|
105
|
+
* for the xs controls; it goes after `default`, and tailwind-merge keeps
|
|
106
|
+
* the later size.
|
|
107
|
+
*/
|
|
108
|
+
export declare const inlineIcon: {
|
|
109
|
+
default: string;
|
|
110
|
+
sm: string;
|
|
111
|
+
};
|
|
112
|
+
/**
|
|
113
|
+
* Brand-colored text on hover and keyboard focus for quiet links and
|
|
114
|
+
* triggers (BreadcrumbLink, AccordionTrigger, InfoTip). DESIGN_RULES.md,
|
|
115
|
+
* "Tokens": brand text is `text-selected-foreground`, never `text-primary`.
|
|
116
|
+
*/
|
|
117
|
+
export declare const quietTextHover = "hover:text-selected-foreground focus-visible:text-selected-foreground";
|
|
75
118
|
/**
|
|
76
119
|
* Uppercase muted label used on tiles, legends, and thin section chrome.
|
|
77
120
|
* Re-exported publicly from `atlas-react-kit/stat-tile`.
|
package/dist/internal/styles.js
CHANGED
|
@@ -8,8 +8,12 @@
|
|
|
8
8
|
*/
|
|
9
9
|
/** Keyboard focus: a solid 2px ring separated from filled controls (WCAG 1.4.11). */
|
|
10
10
|
export const focusRing = "outline-none focus-visible:border-ring focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background";
|
|
11
|
+
/** The same ring on a wrapper with keyboard focus inside it. */
|
|
12
|
+
export const focusRingWithin = "has-focus-visible:ring-2 has-focus-visible:ring-ring has-focus-visible:ring-offset-2 has-focus-visible:ring-offset-background";
|
|
11
13
|
/** `aria-invalid`: destructive border and a soft glow; the error text carries the message. */
|
|
12
14
|
export const invalidState = "aria-invalid:border-destructive aria-invalid:ring-2 aria-invalid:ring-destructive/20 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40";
|
|
15
|
+
/** Muted trigger text while empty (`data-placeholder` or `data-empty`). */
|
|
16
|
+
export const emptyValueText = "data-placeholder:text-muted-foreground data-[empty=true]:text-muted-foreground";
|
|
13
17
|
/** Native `disabled`: dimmed, with no hover or click. */
|
|
14
18
|
export const disabledState = "disabled:pointer-events-none disabled:opacity-50";
|
|
15
19
|
/** Radix `data-disabled` (menu and list items, slider). */
|
|
@@ -37,8 +41,23 @@ export const selectedState = {
|
|
|
37
41
|
/** Quiet buttons and toggles on hover. */
|
|
38
42
|
hover: "hover:border-selected-foreground hover:bg-selected hover:text-selected-foreground",
|
|
39
43
|
};
|
|
44
|
+
/**
|
|
45
|
+
* Alert and Toast tones: a border, and `--status-tone` for the title and
|
|
46
|
+
* icon (`text-(--status-tone)`). `neutral` unsets it, so they inherit.
|
|
47
|
+
*/
|
|
48
|
+
export const statusTone = {
|
|
49
|
+
neutral: "[--status-tone:initial]",
|
|
50
|
+
success: "border-success/40 [--status-tone:var(--color-success)]",
|
|
51
|
+
warning: "border-warning/40 [--status-tone:var(--color-warning)]",
|
|
52
|
+
destructive: "border-destructive/40 [--status-tone:var(--color-destructive)]",
|
|
53
|
+
};
|
|
40
54
|
/** The "No results" row of a listbox. */
|
|
41
55
|
export const listboxEmpty = "px-3 py-4 text-center text-sm text-muted-foreground";
|
|
56
|
+
/**
|
|
57
|
+
* The scrolling frame of a listbox (Combobox, SearchPicker): about ten rows
|
|
58
|
+
* tall, then scrolls. A consumer `listClassName` replaces the height.
|
|
59
|
+
*/
|
|
60
|
+
export const listboxScroll = "max-h-60 overflow-y-auto";
|
|
42
61
|
/** Options in menus and listboxes. */
|
|
43
62
|
export const optionState = {
|
|
44
63
|
/** Highlight as focus moves: grey fill plus a 2px ring bar (the fill alone is 1.1:1). */
|
|
@@ -72,6 +91,30 @@ export const controlSize = {
|
|
|
72
91
|
sm: "h-9 px-4 has-data-[icon=inline-end]:pr-3 has-data-[icon=inline-start]:pl-3",
|
|
73
92
|
lg: "h-11 px-8 has-data-[icon=inline-end]:pr-5 has-data-[icon=inline-start]:pl-5",
|
|
74
93
|
};
|
|
94
|
+
/**
|
|
95
|
+
* Label text of a control (Button, Toggle, Badge): small, bold, wide-tracked
|
|
96
|
+
* uppercase on one line. Badge adds its own `text-[0.625rem]` after it;
|
|
97
|
+
* tailwind-merge then drops the `text-xs`.
|
|
98
|
+
*/
|
|
99
|
+
export const controlText = "text-xs font-semibold tracking-widest whitespace-nowrap uppercase";
|
|
100
|
+
/** Field-label text (Label, FieldLegend, FieldTitle, PopoverTitle). */
|
|
101
|
+
export const labelText = "text-xs font-semibold tracking-wide uppercase";
|
|
102
|
+
/**
|
|
103
|
+
* Icons inside a control or row: no pointer events, no shrinking, 14px
|
|
104
|
+
* unless the icon sets its own `size-*`. `sm` lowers that default to 12px
|
|
105
|
+
* for the xs controls; it goes after `default`, and tailwind-merge keeps
|
|
106
|
+
* the later size.
|
|
107
|
+
*/
|
|
108
|
+
export const inlineIcon = {
|
|
109
|
+
default: "[&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-3.5",
|
|
110
|
+
sm: "[&_svg:not([class*='size-'])]:size-3",
|
|
111
|
+
};
|
|
112
|
+
/**
|
|
113
|
+
* Brand-colored text on hover and keyboard focus for quiet links and
|
|
114
|
+
* triggers (BreadcrumbLink, AccordionTrigger, InfoTip). DESIGN_RULES.md,
|
|
115
|
+
* "Tokens": brand text is `text-selected-foreground`, never `text-primary`.
|
|
116
|
+
*/
|
|
117
|
+
export const quietTextHover = "hover:text-selected-foreground focus-visible:text-selected-foreground";
|
|
75
118
|
/**
|
|
76
119
|
* Uppercase muted label used on tiles, legends, and thin section chrome.
|
|
77
120
|
* Re-exported publicly from `atlas-react-kit/stat-tile`.
|
|
@@ -10,13 +10,14 @@ declare const tabsListVariants: (props?: ({
|
|
|
10
10
|
} & import("class-variance-authority/types").ClassProp) | undefined) => string;
|
|
11
11
|
/**
|
|
12
12
|
* Horizontal scroller around a tab row. `p-1` leaves room for the active
|
|
13
|
-
* border and focus ring; the height is the `h-
|
|
14
|
-
* (`h-
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
* `overflow-y` is not hidden. Line
|
|
18
|
-
* (`h-
|
|
19
|
-
* `w-full`. `theme.css` hides the
|
|
13
|
+
* border and focus ring; the height is the `h-7` list plus that padding
|
|
14
|
+
* (`h-9` = 28px + 8px) so the list fits the content box and the outer
|
|
15
|
+
* row matches `Button` `size="sm"`. Putting padding on an overflow box
|
|
16
|
+
* without this height makes `scrollHeight` taller than `clientHeight`
|
|
17
|
+
* and a vertical scrollbar appears if `overflow-y` is not hidden. Line
|
|
18
|
+
* variant adds `pb-2` for the underline (`h-10` = 28px + 4px + 8px). A
|
|
19
|
+
* block, so it fills its parent without `w-full`. `theme.css` hides the
|
|
20
|
+
* scrollbar and draws the edge fades.
|
|
20
21
|
*/
|
|
21
22
|
declare const tabsScrollerVariants: (props?: ({
|
|
22
23
|
variant?: "default" | "line" | null | undefined;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { cva } from "class-variance-authority";
|
|
2
|
-
import { disabledState, focusRing, selectedState } from "./styles.js";
|
|
2
|
+
import { disabledState, focusRing, inlineIcon, selectedState, } from "./styles.js";
|
|
3
3
|
/**
|
|
4
4
|
* Tab row. `TabsList` and `TabsNav` pass `orientation`, which picks plain
|
|
5
5
|
* size classes so a consumer `className` (`h-9`) replaces them. Called
|
|
@@ -13,8 +13,8 @@ const tabsListVariants = cva("group/tabs-list inline-flex w-max min-w-full items
|
|
|
13
13
|
line: "gap-1 bg-transparent",
|
|
14
14
|
},
|
|
15
15
|
orientation: {
|
|
16
|
-
auto: 'group-data-[orientation="horizontal"]/tabs:h-
|
|
17
|
-
horizontal: "h-
|
|
16
|
+
auto: 'group-data-[orientation="horizontal"]/tabs:h-7 group-data-[orientation="vertical"]/tabs:h-fit group-data-[orientation="vertical"]/tabs:flex-col',
|
|
17
|
+
horizontal: "h-7",
|
|
18
18
|
vertical: "h-fit flex-col",
|
|
19
19
|
},
|
|
20
20
|
},
|
|
@@ -25,13 +25,14 @@ const tabsListVariants = cva("group/tabs-list inline-flex w-max min-w-full items
|
|
|
25
25
|
});
|
|
26
26
|
/**
|
|
27
27
|
* Horizontal scroller around a tab row. `p-1` leaves room for the active
|
|
28
|
-
* border and focus ring; the height is the `h-
|
|
29
|
-
* (`h-
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
* `overflow-y` is not hidden. Line
|
|
33
|
-
* (`h-
|
|
34
|
-
* `w-full`. `theme.css` hides the
|
|
28
|
+
* border and focus ring; the height is the `h-7` list plus that padding
|
|
29
|
+
* (`h-9` = 28px + 8px) so the list fits the content box and the outer
|
|
30
|
+
* row matches `Button` `size="sm"`. Putting padding on an overflow box
|
|
31
|
+
* without this height makes `scrollHeight` taller than `clientHeight`
|
|
32
|
+
* and a vertical scrollbar appears if `overflow-y` is not hidden. Line
|
|
33
|
+
* variant adds `pb-2` for the underline (`h-10` = 28px + 4px + 8px). A
|
|
34
|
+
* block, so it fills its parent without `w-full`. `theme.css` hides the
|
|
35
|
+
* scrollbar and draws the edge fades.
|
|
35
36
|
*/
|
|
36
37
|
const tabsScrollerVariants = cva("relative box-border min-w-0 max-w-full overflow-x-auto overflow-y-hidden overscroll-x-contain p-1", {
|
|
37
38
|
variants: {
|
|
@@ -45,8 +46,8 @@ const tabsScrollerVariants = cva("relative box-border min-w-0 max-w-full overflo
|
|
|
45
46
|
},
|
|
46
47
|
},
|
|
47
48
|
compoundVariants: [
|
|
48
|
-
{ variant: "default", orientation: "horizontal", className: "h-
|
|
49
|
-
{ variant: "line", orientation: "horizontal", className: "h-
|
|
49
|
+
{ variant: "default", orientation: "horizontal", className: "h-9" },
|
|
50
|
+
{ variant: "line", orientation: "horizontal", className: "h-10" },
|
|
50
51
|
],
|
|
51
52
|
defaultVariants: {
|
|
52
53
|
variant: "default",
|
|
@@ -59,7 +60,8 @@ const tabsTriggerVariants = cva([
|
|
|
59
60
|
// flex-none + px-4: every trigger and TabsNav link keeps the same
|
|
60
61
|
// horizontal padding. flex-1 used to grow short labels and make the
|
|
61
62
|
// longest (e.g. Apple Reminders) look tighter. No last-child padding.
|
|
62
|
-
'relative inline-flex h-full min-w-max flex-none items-center justify-center gap-2 border border-transparent px-4 py-1.5 text-xs font-semibold tracking-wider whitespace-nowrap text-foreground/60 uppercase transition-all group-data-[orientation="vertical"]/tabs:w-full group-data-[orientation="vertical"]/tabs:justify-start group-data-[orientation="vertical"]/tabs:px-4 group-data-[orientation="vertical"]/tabs:py-2 hover:text-foreground has-data-[icon=inline-end]:pr-2.5 has-data-[icon=inline-start]:pl-2.5 dark:text-muted-foreground dark:hover:text-foreground
|
|
63
|
+
'relative inline-flex h-full min-w-max flex-none items-center justify-center gap-2 border border-transparent px-4 py-1.5 text-xs font-semibold tracking-wider whitespace-nowrap text-foreground/60 uppercase transition-all group-data-[orientation="vertical"]/tabs:w-full group-data-[orientation="vertical"]/tabs:justify-start group-data-[orientation="vertical"]/tabs:px-4 group-data-[orientation="vertical"]/tabs:py-2 hover:text-foreground has-data-[icon=inline-end]:pr-2.5 has-data-[icon=inline-start]:pl-2.5 dark:text-muted-foreground dark:hover:text-foreground',
|
|
64
|
+
inlineIcon.default,
|
|
63
65
|
'group-data-[variant=line]/tabs-list:bg-transparent group-data-[variant=line]/tabs-list:data-[state="active"]:border-transparent group-data-[variant=line]/tabs-list:data-[state="active"]:bg-transparent dark:group-data-[variant=line]/tabs-list:data-[state="active"]:bg-transparent',
|
|
64
66
|
selectedState.active,
|
|
65
67
|
'after:absolute after:bg-foreground after:opacity-0 after:transition-opacity group-data-[orientation="horizontal"]/tabs:after:inset-x-0 group-data-[orientation="horizontal"]/tabs:after:bottom-[-5px] group-data-[orientation="horizontal"]/tabs:after:h-0.5 group-data-[orientation="vertical"]/tabs:after:inset-y-0 group-data-[orientation="vertical"]/tabs:after:-right-1 group-data-[orientation="vertical"]/tabs:after:w-0.5 group-data-[variant=line]/tabs-list:data-[state="active"]:after:opacity-100',
|
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
/** Tailwind `md` breakpoint. Used for range calendars that stack on small screens. */
|
|
2
2
|
export declare const MD_MEDIA_QUERY = "(min-width: 768px)";
|
|
3
|
+
/**
|
|
4
|
+
* A pointer that can hover (a mouse or trackpad). Touch and coarse pointers
|
|
5
|
+
* must use click; a hover-open popover would trap them after the first tap.
|
|
6
|
+
*/
|
|
7
|
+
export declare const HOVER_MEDIA_QUERY = "(hover: hover) and (pointer: fine)";
|
|
3
8
|
/**
|
|
4
9
|
* Subscribe to a CSS media query with `useSyncExternalStore`. The server
|
|
5
10
|
* render and hydration use `defaultMatches`, so the markup always agrees;
|
|
@@ -2,6 +2,11 @@
|
|
|
2
2
|
import * as React from "react";
|
|
3
3
|
/** Tailwind `md` breakpoint. Used for range calendars that stack on small screens. */
|
|
4
4
|
export const MD_MEDIA_QUERY = "(min-width: 768px)";
|
|
5
|
+
/**
|
|
6
|
+
* A pointer that can hover (a mouse or trackpad). Touch and coarse pointers
|
|
7
|
+
* must use click; a hover-open popover would trap them after the first tap.
|
|
8
|
+
*/
|
|
9
|
+
export const HOVER_MEDIA_QUERY = "(hover: hover) and (pointer: fine)";
|
|
5
10
|
function hasMatchMedia() {
|
|
6
11
|
return (typeof window !== "undefined" && typeof window.matchMedia === "function");
|
|
7
12
|
}
|
package/dist/kanban.d.ts
CHANGED
|
@@ -59,7 +59,7 @@ export type KanbanBoardProps<T extends {
|
|
|
59
59
|
deleteLabel?: string;
|
|
60
60
|
/** Classes for the horizontal scroller that holds the columns. */
|
|
61
61
|
scrollerClassName?: string;
|
|
62
|
-
/** Classes for every column, e.g.
|
|
62
|
+
/** Classes for every column, e.g. to pin a width instead of growing. */
|
|
63
63
|
columnClassName?: string;
|
|
64
64
|
/** Classes for every card, including the one under the pointer while dragging. */
|
|
65
65
|
cardClassName?: string;
|
package/dist/kanban.js
CHANGED
|
@@ -7,13 +7,15 @@ import { CheckCircle2, ChevronLeft, ChevronRight, GripVertical, Trash2, } from "
|
|
|
7
7
|
import { Button } from "./button.js";
|
|
8
8
|
import { KANBAN_COMPLETE_ID, KANBAN_DELETE_ID, buildKanbanGroups, kanbanOverflowEdges, kanbanScrollBy, resolveKanbanDrop, } from "./internal/kanban-layout.js";
|
|
9
9
|
import { configureCardSortablePlugins, configureKanbanPlugins, } from "./internal/kanban-plugins.js";
|
|
10
|
+
import { focusRingWithin } from "./internal/styles.js";
|
|
10
11
|
import { Skeleton } from "./skeleton.js";
|
|
11
12
|
import { cn } from "./utils.js";
|
|
12
13
|
/** Column droppables lose to cards, so a drop lands between cards. */
|
|
13
14
|
const COLUMN_DROP_PRIORITY = 1;
|
|
14
15
|
/** Rail zones beat columns when both are under the pointer. */
|
|
15
16
|
const ACTION_ZONE_PRIORITY = 10;
|
|
16
|
-
|
|
17
|
+
/** Grow to fill the board; 18rem floor so a long row still scrolls. */
|
|
18
|
+
const columnSize = "flex-1 basis-72 min-w-72 min-h-[min(70vh,32rem)]";
|
|
17
19
|
/**
|
|
18
20
|
* Drag-and-drop board built on dnd-kit. The consumer owns `items` and maps
|
|
19
21
|
* each to a column; the board derives its layout from them, so an optimistic
|
|
@@ -123,7 +125,7 @@ function KanbanColumnView({ column, count, emptyLabel, loading, className, child
|
|
|
123
125
|
collisionPriority: COLUMN_DROP_PRIORITY,
|
|
124
126
|
});
|
|
125
127
|
const empty = count === 0;
|
|
126
|
-
return (_jsxs("div", { ref: ref, "data-slot": "kanban-column", "data-drop-target": isDropTarget || undefined, className: cn("flex
|
|
128
|
+
return (_jsxs("div", { ref: ref, "data-slot": "kanban-column", "data-drop-target": isDropTarget || undefined, className: cn("flex snap-start flex-col gap-3 p-3", "bg-muted/40 ring-1 ring-foreground/5", "data-drop-target:bg-muted/60 data-drop-target:ring-2 data-drop-target:ring-ring/60", columnSize, className), children: [_jsxs("header", { "data-slot": "kanban-column-header", className: "flex items-start justify-between gap-2", children: [_jsxs("span", { className: "flex items-center gap-2 text-xs font-semibold tracking-widest uppercase", children: [column.color ? (_jsx("span", { "aria-hidden": true, className: "size-2 shrink-0 rounded-full", style: { backgroundColor: column.color } })) : null, _jsx("span", { className: "leading-tight", children: column.label })] }), _jsxs("span", { className: "flex shrink-0 flex-col items-end gap-0.5 text-muted-foreground tabular-nums", children: [_jsx("span", { className: "text-xs", children: count }), column.badge ? (_jsx("span", { className: "text-[10px] font-medium", children: column.badge })) : null] })] }), _jsx("div", { className: "flex min-h-32 flex-1 flex-col gap-2 overflow-y-auto pr-1", children: loading ? (_jsxs(_Fragment, { children: [_jsx(Skeleton, { className: "h-20" }), _jsx(Skeleton, { className: "h-16" })] })) : (_jsxs(_Fragment, { children: [children, _jsx("p", { "data-slot": "kanban-column-empty", "aria-hidden": !empty, className: cn("border border-dashed px-3 py-6 text-center text-xs text-muted-foreground", !empty && "hidden"), children: emptyLabel })] })) })] }));
|
|
127
129
|
}
|
|
128
130
|
const cardChrome = cn("flex border border-border bg-card text-card-foreground");
|
|
129
131
|
const handleChrome = cn("flex shrink-0 items-center self-stretch border-r border-border", "bg-muted/30 px-1.5 text-muted-foreground");
|
|
@@ -145,7 +147,7 @@ function KanbanCardViewImpl({ item, index, column, onCardClick, className, rende
|
|
|
145
147
|
// The overlay moves; the source keeps its slot.
|
|
146
148
|
"data-dragging:pointer-events-none data-dragging:invisible",
|
|
147
149
|
// One ring on the card for the grip and the body alike.
|
|
148
|
-
|
|
150
|
+
focusRingWithin, onCardClick && "hover:border-foreground/15", className), children: [_jsx("button", { type: "button", ref: handleRef, "data-slot": "kanban-card-handle", "aria-label": "Drag to move", onClick: stopClick, className: cn(handleChrome, "cursor-grab outline-none hover:bg-muted/50 active:cursor-grabbing"), children: _jsx(GripVertical, { className: "size-4", "aria-hidden": true }) }), onCardClick ? (_jsx("button", { type: "button", "data-slot": "kanban-card-body", onClick: () => onCardClick(item), className: "min-w-0 flex-1 cursor-pointer p-3 text-left outline-none hover:bg-muted/20", children: children })) : (_jsx("div", { "data-slot": "kanban-card-body", className: "min-w-0 flex-1 p-3", children: children }))] }));
|
|
149
151
|
}
|
|
150
152
|
/**
|
|
151
153
|
* Memoized so a board update re-renders only the cards whose item, index or
|
|
@@ -192,6 +194,6 @@ function OverflowFade({ side, visible, }) {
|
|
|
192
194
|
}
|
|
193
195
|
/** Board-shaped placeholder for a route `loading` file. */
|
|
194
196
|
function KanbanSkeleton({ columnCount = 4, className, columnClassName, ...props }) {
|
|
195
|
-
return (_jsx("div", { "data-slot": "kanban-skeleton", className: cn("flex gap-4 overflow-hidden", className), ...props, children: Array.from({ length: columnCount }, (_, index) => (_jsx(Skeleton, { className: cn(
|
|
197
|
+
return (_jsx("div", { "data-slot": "kanban-skeleton", className: cn("flex gap-4 overflow-hidden", className), ...props, children: Array.from({ length: columnCount }, (_, index) => (_jsx(Skeleton, { className: cn(columnSize, columnClassName) }, index))) }));
|
|
196
198
|
}
|
|
197
199
|
export { KanbanBoard, KanbanSkeleton };
|
package/dist/label.js
CHANGED
|
@@ -2,12 +2,13 @@
|
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
3
|
import * as React from "react";
|
|
4
4
|
import { Label as LabelPrimitive } from "radix-ui";
|
|
5
|
+
import { labelText } from "./internal/styles.js";
|
|
5
6
|
import { cn } from "./utils.js";
|
|
6
7
|
/**
|
|
7
8
|
* Form label. `required` adds a visible marker; style it with
|
|
8
9
|
* `requiredClassName` or the `label-required` slot.
|
|
9
10
|
*/
|
|
10
11
|
function Label({ className, required, requiredClassName, children, ...props }) {
|
|
11
|
-
return (_jsxs(LabelPrimitive.Root, { "data-slot": "label", className: cn("flex items-center gap-2
|
|
12
|
+
return (_jsxs(LabelPrimitive.Root, { "data-slot": "label", className: cn(labelText, "flex items-center gap-2 select-none group-data-[disabled=true]:pointer-events-none group-data-[disabled=true]:opacity-50 peer-disabled:cursor-not-allowed peer-disabled:opacity-50 peer-data-[slot=checkbox]:text-sm peer-data-[slot=checkbox]:font-normal peer-data-[slot=checkbox]:tracking-normal peer-data-[slot=checkbox]:normal-case peer-data-[slot=radio-group-item]:text-sm peer-data-[slot=radio-group-item]:font-normal peer-data-[slot=radio-group-item]:tracking-normal peer-data-[slot=radio-group-item]:normal-case peer-data-[slot=switch]:text-sm peer-data-[slot=switch]:font-normal peer-data-[slot=switch]:tracking-normal peer-data-[slot=switch]:normal-case", className), ...props, children: [children, required ? (_jsx("span", { "data-slot": "label-required", "aria-hidden": "true", className: cn("-ml-1.5 text-destructive", requiredClassName), children: "*" })) : null] }));
|
|
12
13
|
}
|
|
13
14
|
export { Label };
|
package/dist/lazy-calendar.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
3
|
import * as React from "react";
|
|
4
|
+
import { calendarSize } from "./internal/calendar-size.js";
|
|
4
5
|
import { Spinner } from "./spinner.js";
|
|
5
6
|
import { cn } from "./utils.js";
|
|
6
7
|
/**
|
|
@@ -16,11 +17,11 @@ export function preloadCalendar() {
|
|
|
16
17
|
const LazyCalendarInner = React.lazy(() => loadCalendar().then((module) => ({ default: module.Calendar })));
|
|
17
18
|
/**
|
|
18
19
|
* While loading, a placeholder the size of a one-month calendar, measured in
|
|
19
|
-
* the calendar's own
|
|
20
|
+
* the calendar's own `calendarSize` (7 columns plus padding; about 9 cells
|
|
20
21
|
* tall), so the popover does not jump. It takes the calendar's `className`,
|
|
21
22
|
* so a `--cell-size` override sizes both.
|
|
22
23
|
*/
|
|
23
|
-
const fallbackClassName = "flex h-[calc(var(--cell-size)*9)] w-[calc(var(--cell-size)*7
|
|
24
|
+
const fallbackClassName = cn("flex h-[calc(var(--cell-size)*9)] w-[calc(var(--cell-size)*7+var(--calendar-padding)*2)] items-center justify-center", calendarSize);
|
|
24
25
|
export function LazyCalendar(props) {
|
|
25
26
|
return (_jsx(React.Suspense, { fallback: _jsx("div", { className: cn(fallbackClassName, props.className), children: _jsx(Spinner, { "aria-label": "Loading calendar" }) }), children: _jsx(LazyCalendarInner, { ...props }) }));
|
|
26
27
|
}
|