atlas-react-kit 1.2.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 +19 -23
- package/DESIGN_RULES.md +14 -40
- package/README.md +7 -6
- package/dist/alert.js +6 -5
- package/dist/calendar.js +5 -4
- package/dist/combobox.js +6 -11
- package/dist/date-time-picker.js +2 -1
- package/dist/dropdown-menu.js +2 -2
- 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 +9 -4
- 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-option.d.ts +16 -3
- package/dist/internal/listbox-option.js +22 -5
- package/dist/internal/overlay-styles.d.ts +10 -5
- package/dist/internal/overlay-styles.js +10 -5
- package/dist/internal/search.d.ts +4 -0
- package/dist/internal/search.js +6 -0
- package/dist/internal/styles.d.ts +16 -0
- package/dist/internal/styles.js +16 -0
- package/dist/kanban.js +2 -1
- 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.js +5 -9
- package/dist/select.js +4 -4
- package/dist/skip-link.js +2 -1
- package/dist/tabs.js +3 -2
- package/dist/toast.js +11 -10
- package/dist/tooltip.js +2 -2
- package/package.json +1 -1
- package/skills/atlas-react-kit/SKILL.md +46 -42
package/dist/internal/search.js
CHANGED
|
@@ -12,7 +12,13 @@ export function normalizeSearchQuery(query) {
|
|
|
12
12
|
export function createSearchIndex(items, getLabel) {
|
|
13
13
|
return items.map((item) => ({ item, label: foldSearchText(getLabel(item)) }));
|
|
14
14
|
}
|
|
15
|
+
/**
|
|
16
|
+
* The items whose folded label includes `needle` (a `normalizeSearchQuery`
|
|
17
|
+
* result), in order; every item when `needle` is empty.
|
|
18
|
+
*/
|
|
15
19
|
export function filterSearchIndex(index, needle) {
|
|
20
|
+
if (!needle)
|
|
21
|
+
return index.map(({ item }) => item);
|
|
16
22
|
return index
|
|
17
23
|
.filter(({ label }) => label.includes(needle))
|
|
18
24
|
.map(({ item }) => item);
|
|
@@ -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,6 +41,16 @@ 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";
|
|
42
56
|
/**
|
|
@@ -83,6 +97,8 @@ export declare const controlSize: {
|
|
|
83
97
|
* tailwind-merge then drops the `text-xs`.
|
|
84
98
|
*/
|
|
85
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";
|
|
86
102
|
/**
|
|
87
103
|
* Icons inside a control or row: no pointer events, no shrinking, 14px
|
|
88
104
|
* unless the icon sets its own `size-*`. `sm` lowers that default to 12px
|
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,6 +41,16 @@ 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";
|
|
42
56
|
/**
|
|
@@ -83,6 +97,8 @@ export const controlSize = {
|
|
|
83
97
|
* tailwind-merge then drops the `text-xs`.
|
|
84
98
|
*/
|
|
85
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";
|
|
86
102
|
/**
|
|
87
103
|
* Icons inside a control or row: no pointer events, no shrinking, 14px
|
|
88
104
|
* unless the icon sets its own `size-*`. `sm` lowers that default to 12px
|
package/dist/kanban.js
CHANGED
|
@@ -7,6 +7,7 @@ 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. */
|
|
@@ -146,7 +147,7 @@ function KanbanCardViewImpl({ item, index, column, onCardClick, className, rende
|
|
|
146
147
|
// The overlay moves; the source keeps its slot.
|
|
147
148
|
"data-dragging:pointer-events-none data-dragging:invisible",
|
|
148
149
|
// One ring on the card for the grip and the body alike.
|
|
149
|
-
|
|
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 }))] }));
|
|
150
151
|
}
|
|
151
152
|
/**
|
|
152
153
|
* Memoized so a board update re-renders only the cards whose item, index or
|
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
|
}
|
package/dist/nav-link.js
CHANGED
|
@@ -2,6 +2,7 @@ import { jsx as _jsx } from "react/jsx-runtime";
|
|
|
2
2
|
import * as React from "react";
|
|
3
3
|
import { cva } from "class-variance-authority";
|
|
4
4
|
import { Slot } from "radix-ui";
|
|
5
|
+
import { activeLinkAttributes } from "./internal/active-link.js";
|
|
5
6
|
import { focusRing, selectedState } from "./internal/styles.js";
|
|
6
7
|
import { cn } from "./utils.js";
|
|
7
8
|
/**
|
|
@@ -36,6 +37,6 @@ function isNavActive(pathname, href) {
|
|
|
36
37
|
*/
|
|
37
38
|
function NavLink({ asChild, active, className, ...props }) {
|
|
38
39
|
const Comp = asChild ? Slot.Root : "a";
|
|
39
|
-
return (_jsx(Comp, { "data-slot": "nav-link",
|
|
40
|
+
return (_jsx(Comp, { "data-slot": "nav-link", ...activeLinkAttributes(active), className: cn(navLinkVariants(), className), ...props }));
|
|
40
41
|
}
|
|
41
42
|
export { NavLink, isNavActive, navLinkVariants };
|
package/dist/popover.js
CHANGED
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
3
|
import * as React from "react";
|
|
4
4
|
import { Popover as PopoverPrimitive } from "radix-ui";
|
|
5
|
-
import { overlayFadeZoom, overlaySideSlide, overlaySurface, } from "./internal/overlay-styles.js";
|
|
5
|
+
import { OVERLAY_COLLISION_PADDING, OVERLAY_SIDE_OFFSET, overlayFadeZoom, overlaySideSlide, overlaySurface, } from "./internal/overlay-styles.js";
|
|
6
|
+
import { labelText } from "./internal/styles.js";
|
|
6
7
|
import { cn } from "./utils.js";
|
|
7
8
|
function Popover({ ...props }) {
|
|
8
9
|
return _jsx(PopoverPrimitive.Root, { "data-slot": "popover", ...props });
|
|
@@ -10,7 +11,7 @@ function Popover({ ...props }) {
|
|
|
10
11
|
function PopoverTrigger({ ...props }) {
|
|
11
12
|
return _jsx(PopoverPrimitive.Trigger, { "data-slot": "popover-trigger", ...props });
|
|
12
13
|
}
|
|
13
|
-
function PopoverContent({ className, align = "center", sideOffset =
|
|
14
|
+
function PopoverContent({ className, align = "center", sideOffset = OVERLAY_SIDE_OFFSET, collisionPadding = OVERLAY_COLLISION_PADDING, ...props }) {
|
|
14
15
|
return (_jsx(PopoverPrimitive.Portal, { children: _jsx(PopoverPrimitive.Content, { "data-slot": "popover-content", align: align, sideOffset: sideOffset, collisionPadding: collisionPadding, className: cn(overlaySurface, overlaySideSlide, overlayFadeZoom, "z-50 flex max-w-(--radix-popover-content-available-width) origin-(--radix-popover-content-transform-origin) flex-col gap-4 p-4 text-sm outline-hidden", className), ...props }) }));
|
|
15
16
|
}
|
|
16
17
|
function PopoverAnchor({ ...props }) {
|
|
@@ -20,7 +21,7 @@ function PopoverHeader({ className, ...props }) {
|
|
|
20
21
|
return (_jsx("div", { "data-slot": "popover-header", className: cn("flex flex-col gap-1 text-sm", className), ...props }));
|
|
21
22
|
}
|
|
22
23
|
function PopoverTitle({ className, ...props }) {
|
|
23
|
-
return (_jsx("h2", { "data-slot": "popover-title", className: cn(
|
|
24
|
+
return (_jsx("h2", { "data-slot": "popover-title", className: cn(labelText, className), ...props }));
|
|
24
25
|
}
|
|
25
26
|
function PopoverDescription({ className, ...props }) {
|
|
26
27
|
return (_jsx("p", { "data-slot": "popover-description", className: cn("mt-0.5 text-sm leading-relaxed text-muted-foreground", className), ...props }));
|
package/dist/search-picker.js
CHANGED
|
@@ -3,10 +3,9 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
3
3
|
import * as React from "react";
|
|
4
4
|
import { Input } from "./input.js";
|
|
5
5
|
import { Label } from "./label.js";
|
|
6
|
-
import { handleListboxKey
|
|
7
|
-
import { ListboxEmptyStatus, PickerOption } from "./internal/listbox-option.js";
|
|
6
|
+
import { handleListboxKey } from "./internal/listbox-navigation.js";
|
|
7
|
+
import { Listbox, ListboxEmptyStatus, listboxInputAria, PickerOption, } from "./internal/listbox-option.js";
|
|
8
8
|
import { createSearchIndex, filterSearchIndex, foldSearchText, normalizeSearchQuery, } from "./internal/search.js";
|
|
9
|
-
import { listboxScroll } from "./internal/styles.js";
|
|
10
9
|
import { cn } from "./utils.js";
|
|
11
10
|
export { foldSearchText };
|
|
12
11
|
/**
|
|
@@ -25,7 +24,7 @@ const listFrame = "border border-input bg-background";
|
|
|
25
24
|
* the list is not in a popover. Keyboard: arrows, Home/End, PageUp/PageDown,
|
|
26
25
|
* Enter, Escape.
|
|
27
26
|
*/
|
|
28
|
-
function SearchPicker({ items, getValue, getLabel, filter = defaultSearchFilter, value, onSelect, label, placeholder = "Search", emptyText
|
|
27
|
+
function SearchPicker({ items, getValue, getLabel, filter = defaultSearchFilter, value, onSelect, label, placeholder = "Search", emptyText, listLabel, disabled, className, listClassName, ...props }) {
|
|
29
28
|
const [query, setQuery] = React.useState("");
|
|
30
29
|
const listId = React.useId();
|
|
31
30
|
const inputId = React.useId();
|
|
@@ -34,11 +33,8 @@ function SearchPicker({ items, getValue, getLabel, filter = defaultSearchFilter,
|
|
|
34
33
|
: null, [filter, getLabel, items]);
|
|
35
34
|
const foldedQuery = filter === defaultSearchFilter ? normalizeSearchQuery(query) : "";
|
|
36
35
|
const filtered = React.useMemo(() => {
|
|
37
|
-
if (normalizedItems)
|
|
38
|
-
if (!foldedQuery)
|
|
39
|
-
return items;
|
|
36
|
+
if (normalizedItems)
|
|
40
37
|
return filterSearchIndex(normalizedItems, foldedQuery);
|
|
41
|
-
}
|
|
42
38
|
return items.filter((item) => filter(item, query, getLabel));
|
|
43
39
|
}, [items, filter, foldedQuery, getLabel, normalizedItems, query]);
|
|
44
40
|
const selectedIndex = React.useMemo(() => {
|
|
@@ -85,6 +81,6 @@ function SearchPicker({ items, getValue, getLabel, filter = defaultSearchFilter,
|
|
|
85
81
|
}
|
|
86
82
|
event.currentTarget.blur();
|
|
87
83
|
};
|
|
88
|
-
return (_jsxs("div", { "data-slot": "search-picker", className: cn("flex flex-col gap-1.5", className), ...props, children: [_jsx(Label, { htmlFor: inputId, children: label }), _jsx(Input, { id: inputId, role: "combobox", "aria-expanded": true, "aria-controls": listId,
|
|
84
|
+
return (_jsxs("div", { "data-slot": "search-picker", className: cn("flex flex-col gap-1.5", className), ...props, children: [_jsx(Label, { htmlFor: inputId, children: label }), _jsx(Input, { id: inputId, role: "combobox", "aria-expanded": true, "aria-controls": listId, ...listboxInputAria(listId, active ? highlight : undefined), value: query, disabled: disabled, placeholder: placeholder, onChange: (event) => setQuery(event.target.value), onKeyDown: onKeyDown }), _jsx(Listbox, { id: listId, empty: empty, "aria-label": listLabel ?? label, className: cn(listFrame, listClassName), children: rows.map(({ item, value: itemValue }, index) => (_jsx(PickerOption, { "data-slot": "search-picker-option", listId: listId, index: index, active: index === highlight, selected: itemValue === value, disabled: disabled, item: item, getLabel: getLabel, onHighlight: setHighlight, onSelect: selectItem }, itemValue))) }), _jsx(ListboxEmptyStatus, { "data-slot": "search-picker-empty", empty: empty, className: listFrame, children: emptyText })] }));
|
|
89
85
|
}
|
|
90
86
|
export { SearchPicker };
|
package/dist/select.js
CHANGED
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
3
|
import * as React from "react";
|
|
4
4
|
import { Select as SelectPrimitive } from "radix-ui";
|
|
5
|
-
import { dataDisabledState, disabledState, fieldSurface, focusRing, inlineIcon, invalidState, optionState, } from "./internal/styles.js";
|
|
6
|
-
import { menuLabel, optionRow, overlayFadeZoom, overlaySideSlide, overlaySurface, } from "./internal/overlay-styles.js";
|
|
5
|
+
import { dataDisabledState, disabledState, emptyValueText, fieldSurface, focusRing, inlineIcon, invalidState, optionState, } from "./internal/styles.js";
|
|
6
|
+
import { menuLabel, optionRow, OVERLAY_COLLISION_PADDING, OVERLAY_SIDE_OFFSET, overlayFadeZoom, overlaySideSlide, overlaySurface, } from "./internal/overlay-styles.js";
|
|
7
7
|
import { cn } from "./utils.js";
|
|
8
8
|
import { ChevronDownIcon, CheckIcon, ChevronUpIcon } from "lucide-react";
|
|
9
9
|
/**
|
|
@@ -22,9 +22,9 @@ function SelectValue({ ...props }) {
|
|
|
22
22
|
return _jsx(SelectPrimitive.Value, { "data-slot": "select-value", ...props });
|
|
23
23
|
}
|
|
24
24
|
function SelectTrigger({ className, size = "default", children, ...props }) {
|
|
25
|
-
return (_jsxs(SelectPrimitive.Trigger, { "data-slot": "select-trigger", "data-size": size, className: cn(fieldSurface, focusRing, invalidState, disabledState, inlineIcon.default, triggerHeight[size], "flex items-center justify-between gap-1.5 whitespace-nowrap
|
|
25
|
+
return (_jsxs(SelectPrimitive.Trigger, { "data-slot": "select-trigger", "data-size": size, className: cn(fieldSurface, focusRing, invalidState, disabledState, emptyValueText, emptyValueText, inlineIcon.default, triggerHeight[size], "flex items-center justify-between gap-1.5 whitespace-nowrap *:data-[slot=select-value]:line-clamp-1 *:data-[slot=select-value]:flex *:data-[slot=select-value]:items-center *:data-[slot=select-value]:gap-1.5", className), ...props, children: [children, _jsx(SelectPrimitive.Icon, { "data-slot": "select-icon", asChild: true, children: _jsx(ChevronDownIcon, { className: "pointer-events-none size-3.5 text-muted-foreground" }) })] }));
|
|
26
26
|
}
|
|
27
|
-
function SelectContent({ className, children, position = "popper", align = "center", sideOffset =
|
|
27
|
+
function SelectContent({ className, children, position = "popper", align = "center", sideOffset = OVERLAY_SIDE_OFFSET, collisionPadding = OVERLAY_COLLISION_PADDING, ...props }) {
|
|
28
28
|
return (_jsx(SelectPrimitive.Portal, { children: _jsxs(SelectPrimitive.Content, { "data-slot": "select-content", "data-align-trigger": position === "item-aligned", className: cn(overlaySurface, overlaySideSlide, overlayFadeZoom, "relative z-50 max-h-(--radix-select-content-available-height) min-w-(--radix-select-trigger-width) origin-(--radix-select-content-transform-origin) overflow-x-hidden overflow-y-auto data-[align-trigger=true]:animate-none", position === "popper" &&
|
|
29
29
|
"data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1", className), position: position, align: align, sideOffset: sideOffset, collisionPadding: collisionPadding, ...props, children: [_jsx(SelectScrollUpButton, {}), _jsx(SelectPrimitive.Viewport, { "data-slot": "select-viewport", "data-position": position, className: "data-[position=popper]:h-(--radix-select-trigger-height) data-[position=popper]:w-full data-[position=popper]:min-w-(--radix-select-trigger-width)", children: children }), _jsx(SelectScrollDownButton, {})] }) }));
|
|
30
30
|
}
|
package/dist/skip-link.js
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { focusRing } from "./internal/styles.js";
|
|
2
3
|
import { cn } from "./utils.js";
|
|
3
4
|
/**
|
|
4
5
|
* First focusable link on the page: hidden until a keyboard user tabs to
|
|
5
6
|
* it, then it jumps past the navigation to `href` (the main landmark's id).
|
|
6
7
|
*/
|
|
7
8
|
function SkipLink({ href = "#main-content", className, children = "Skip to content", ...props }) {
|
|
8
|
-
return (_jsx("a", { "data-slot": "skip-link", href: href, className: cn("sr-only focus:not-sr-only focus:absolute focus:top-4 focus:left-4 focus:z-50 focus:bg-background focus:px-3 focus:py-2 focus:text-sm
|
|
9
|
+
return (_jsx("a", { "data-slot": "skip-link", href: href, className: cn("sr-only focus:not-sr-only focus:absolute focus:top-4 focus:left-4 focus:z-50 focus:bg-background focus:px-3 focus:py-2 focus:text-sm", focusRing, className), ...props, children: children }));
|
|
9
10
|
}
|
|
10
11
|
export { SkipLink };
|
package/dist/tabs.js
CHANGED
|
@@ -3,6 +3,7 @@ import { jsx as _jsx } from "react/jsx-runtime";
|
|
|
3
3
|
import * as React from "react";
|
|
4
4
|
import {} from "class-variance-authority";
|
|
5
5
|
import { Slot, Tabs as TabsPrimitive } from "radix-ui";
|
|
6
|
+
import { activeLinkAttributes, activeLinkSelector, } from "./internal/active-link.js";
|
|
6
7
|
import { focusRing } from "./internal/styles.js";
|
|
7
8
|
import { tabsListVariants, tabsScrollerVariants, tabsTriggerVariants, } from "./internal/tabs-styles.js";
|
|
8
9
|
import { useDragScroll } from "./internal/use-drag-scroll.js";
|
|
@@ -37,7 +38,7 @@ function TabsScroller({ variant = "default", orientation = "horizontal", classNa
|
|
|
37
38
|
const scroller = ref.current;
|
|
38
39
|
if (!scroller)
|
|
39
40
|
return;
|
|
40
|
-
const active = scroller.querySelector(
|
|
41
|
+
const active = scroller.querySelector(activeLinkSelector);
|
|
41
42
|
if (!active || active === revealed.current)
|
|
42
43
|
return;
|
|
43
44
|
revealed.current = active;
|
|
@@ -74,7 +75,7 @@ function TabsContent({ className, ...props }) {
|
|
|
74
75
|
*/
|
|
75
76
|
function TabsNavLink({ asChild, active, className, ...props }) {
|
|
76
77
|
const Comp = asChild ? Slot.Root : "a";
|
|
77
|
-
return (_jsx(Comp, { "data-slot": "tabs-nav-link",
|
|
78
|
+
return (_jsx(Comp, { "data-slot": "tabs-nav-link", ...activeLinkAttributes(active), className: cn(tabsTriggerVariants(), className), ...props }));
|
|
78
79
|
}
|
|
79
80
|
/**
|
|
80
81
|
* Tab-styled links for a view switcher that changes the URL. Use this, not
|
package/dist/toast.js
CHANGED
|
@@ -5,6 +5,7 @@ import { CircleAlertIcon, CircleCheckIcon, InfoIcon, LoaderCircleIcon, XIcon, }
|
|
|
5
5
|
import { useTheme } from "next-themes";
|
|
6
6
|
import { Toaster as Sonner, toast as sonnerToast, } from "sonner";
|
|
7
7
|
import { buttonVariants } from "./button.js";
|
|
8
|
+
import { statusTone } from "./internal/styles.js";
|
|
8
9
|
import { cn } from "./utils.js";
|
|
9
10
|
/**
|
|
10
11
|
* Copies only the kit's options, so plain JavaScript callers cannot hand the
|
|
@@ -42,7 +43,7 @@ const toast = Object.assign(show(sonnerToast), {
|
|
|
42
43
|
*
|
|
43
44
|
* Icon policy: a plain `toast()` has no icon. `success`, `info`, `warning`,
|
|
44
45
|
* `error`, and `loading` each show a status icon. Tone colors the icon, title,
|
|
45
|
-
* and border (
|
|
46
|
+
* and border (`statusTone`, shared with Alert), not the body text.
|
|
46
47
|
*
|
|
47
48
|
* Close button: a circular badge centred on the top-right corner (`-top-2
|
|
48
49
|
* -right-2`, half outside the border). Ghost / icon-xs still supplies the
|
|
@@ -70,22 +71,22 @@ const classNames = {
|
|
|
70
71
|
// falls back to the title's width, so a one-word toast stays compact.
|
|
71
72
|
// 1.625rem = 16px icon + 10px gap-x-2.5.
|
|
72
73
|
content: "flex min-w-0 flex-1 basis-full flex-col gap-1 [[data-icon]+&]:basis-[calc(100%-1.625rem)]",
|
|
73
|
-
title: "font-medium leading-5",
|
|
74
|
+
title: "font-medium leading-5 text-(--status-tone)",
|
|
74
75
|
description: "leading-5 text-muted-foreground",
|
|
75
76
|
icon: "flex size-4 shrink-0 items-center justify-center [&_svg]:size-4",
|
|
76
77
|
actionButton: cn(buttonVariants({ size: "xs" }), "shrink-0"),
|
|
77
78
|
cancelButton: cn(buttonVariants({ variant: "outline", size: "xs" }), "shrink-0"),
|
|
78
79
|
closeButton: cn(buttonVariants({ variant: "ghost", size: "icon-xs" }), "!absolute !-top-2 !-right-2 !left-auto !z-10 !size-6 !rounded-full !border !border-border !bg-popover !p-0 !text-muted-foreground !shadow-xs !translate-x-0 !translate-y-0 hover:!border-border hover:!bg-popover hover:!text-foreground focus-visible:!text-foreground active:!translate-y-0 [&_svg]:!size-3"),
|
|
79
|
-
success:
|
|
80
|
-
info:
|
|
81
|
-
warning:
|
|
82
|
-
error:
|
|
80
|
+
success: statusTone.success,
|
|
81
|
+
info: statusTone.neutral,
|
|
82
|
+
warning: statusTone.warning,
|
|
83
|
+
error: statusTone.destructive,
|
|
83
84
|
};
|
|
84
85
|
const icons = {
|
|
85
|
-
success: _jsx(CircleCheckIcon, { "aria-hidden": true, className: "text-
|
|
86
|
-
info: _jsx(InfoIcon, { "aria-hidden": true, className: "text-
|
|
87
|
-
warning: _jsx(CircleAlertIcon, { "aria-hidden": true, className: "text-
|
|
88
|
-
error: _jsx(CircleAlertIcon, { "aria-hidden": true, className: "text-
|
|
86
|
+
success: _jsx(CircleCheckIcon, { "aria-hidden": true, className: "text-(--status-tone)" }),
|
|
87
|
+
info: _jsx(InfoIcon, { "aria-hidden": true, className: "text-(--status-tone)" }),
|
|
88
|
+
warning: _jsx(CircleAlertIcon, { "aria-hidden": true, className: "text-(--status-tone)" }),
|
|
89
|
+
error: _jsx(CircleAlertIcon, { "aria-hidden": true, className: "text-(--status-tone)" }),
|
|
89
90
|
loading: (_jsx(LoaderCircleIcon, { "aria-hidden": true, className: "animate-spin text-muted-foreground" })),
|
|
90
91
|
close: _jsx(XIcon, { "aria-hidden": true, className: "size-3" }),
|
|
91
92
|
};
|
package/dist/tooltip.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
3
|
import * as React from "react";
|
|
4
4
|
import { Tooltip as TooltipPrimitive } from "radix-ui";
|
|
5
|
-
import { overlaySideSlide } from "./internal/overlay-styles.js";
|
|
5
|
+
import { OVERLAY_SIDE_OFFSET, overlaySideSlide, } from "./internal/overlay-styles.js";
|
|
6
6
|
import { cn } from "./utils.js";
|
|
7
7
|
/** True inside a TooltipProvider, so `Tooltip` does not add its own. */
|
|
8
8
|
const HasTooltipProvider = React.createContext(false);
|
|
@@ -28,7 +28,7 @@ function Tooltip({ ...props }) {
|
|
|
28
28
|
function TooltipTrigger({ ...props }) {
|
|
29
29
|
return _jsx(TooltipPrimitive.Trigger, { "data-slot": "tooltip-trigger", ...props });
|
|
30
30
|
}
|
|
31
|
-
function TooltipContent({ className, arrowClassName, sideOffset =
|
|
31
|
+
function TooltipContent({ className, arrowClassName, sideOffset = OVERLAY_SIDE_OFFSET, children, ...props }) {
|
|
32
32
|
return (_jsx(TooltipPrimitive.Portal, { children: _jsxs(TooltipPrimitive.Content, { "data-slot": "tooltip-content", sideOffset: sideOffset, className: cn(overlaySideSlide,
|
|
33
33
|
// Radix tooltips open as "delayed-open" or "instant-open", which
|
|
34
34
|
// the shared data-open fade does not match.
|
package/package.json
CHANGED
|
@@ -28,23 +28,21 @@ than guessing.
|
|
|
28
28
|
|
|
29
29
|
## 2. Check the project setup once
|
|
30
30
|
|
|
31
|
-
Components render unstyled unless the app's global stylesheet imports the
|
|
32
|
-
and tells Tailwind to scan the kit's compiled files
|
|
31
|
+
Components render unstyled unless the app's global stylesheet imports the
|
|
32
|
+
theme and tells Tailwind to scan the kit's compiled files. Compare the app
|
|
33
|
+
with the setup in the package's README (the stylesheet lines and dark mode):
|
|
33
34
|
|
|
34
|
-
```
|
|
35
|
-
|
|
36
|
-
@import "atlas-react-kit/theme.css";
|
|
37
|
-
@source "../node_modules/atlas-react-kit/dist"; /* relative to this CSS file */
|
|
35
|
+
```sh
|
|
36
|
+
cat "$KIT/README.md"
|
|
38
37
|
```
|
|
39
38
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
Fix missing setup rather than working around unstyled components.
|
|
39
|
+
Toasts need one `<Toaster />` from `atlas-react-kit/toast`; show them with
|
|
40
|
+
`toast()` from the same module, using only the options it declares. Never
|
|
41
|
+
import a toast library such as `sonner` directly: the kit owns the toast API,
|
|
42
|
+
and a second copy never reaches its toaster. Some entry points need optional
|
|
43
|
+
peers; install the extras from the "Optional peer dependencies" table in
|
|
44
|
+
`COMPONENTS.md` when you import them. Fix missing setup rather than working
|
|
45
|
+
around unstyled components.
|
|
48
46
|
|
|
49
47
|
## 3. Pick the kit component for the job
|
|
50
48
|
|
|
@@ -54,48 +52,54 @@ Import each component from its subpath, e.g.
|
|
|
54
52
|
|
|
55
53
|
| Need | Use |
|
|
56
54
|
| ---------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------- |
|
|
57
|
-
| Short fixed list (a handful of options) | `Select`
|
|
58
|
-
| Long or searchable list (timezones, currencies) | `Combobox` from `combobox`
|
|
55
|
+
| Short fixed list (a handful of options) | `Select` from `select` |
|
|
56
|
+
| Long or searchable list (timezones, currencies) | `Combobox` from `combobox` |
|
|
59
57
|
| Always-visible searchable list (places) | `SearchPicker` from `search-picker` |
|
|
60
|
-
| One date, or a from/to range | `DatePickerButton` from `date-picker`
|
|
58
|
+
| One date, or a from/to range | `DatePickerButton` from `date-picker` |
|
|
61
59
|
| Date and time (not `datetime-local`) | `DateTimePickerButton` from `date-time-picker` |
|
|
62
60
|
| Labelled field with error text | `FormTextField`, `FormSelectField`, `FormDatePickerField`, `FormDateTimePickerField`, `FormMarkdownField` from `form/*` |
|
|
63
61
|
| Checkbox, checkbox group, toggle group, combobox in a form | `FormCheckboxField`, `FormCheckboxGroupField`, `FormToggleGroupField`, `FormComboboxField` from `form/*` |
|
|
64
62
|
| Custom control the kit has no form field for | `FormControlField` from `form/control-field` |
|
|
65
63
|
| Form-level error (save failed) | `FormFeedbackField` from `form/feedback-field` |
|
|
66
|
-
|
|
|
67
|
-
|
|
|
68
|
-
|
|
|
69
|
-
|
|
|
64
|
+
| Focus the first invalid field when a submit fails | `focusFirstInvalid` from `form/focus-first-invalid` |
|
|
65
|
+
| Content panels on one page | `Tabs` from `tabs` |
|
|
66
|
+
| View switch that changes the URL | `TabsNav` and `TabsNavLink` from `tabs`; in Next, `next/tabs-nav-link` |
|
|
67
|
+
| App shell nav (Calendar, Tasks, Settings) | `NavLink` from `nav-link`; in Next, `next/nav-link` |
|
|
70
68
|
| Skip-to-content link | `SkipLink` from `skip-link` |
|
|
71
69
|
| Page title row with actions | `PageHeader` from `page-header` |
|
|
72
|
-
| Previous / today / next period | `PeriodStepper` from `period-stepper`; `next/period-stepper`
|
|
73
|
-
| Cards in columns that drag | `KanbanBoard` from `kanban
|
|
74
|
-
| SVG chart axes, grid, tooltip, legend | `
|
|
75
|
-
| Status chip (paid, pending, failed) | `Badge
|
|
76
|
-
| Data table | `Table`
|
|
70
|
+
| Previous / today / next period | `PeriodStepper` from `period-stepper`; for URL periods in Next, `next/period-stepper` |
|
|
71
|
+
| Cards in columns that drag | `KanbanBoard` from `kanban` |
|
|
72
|
+
| SVG chart axes, grid, tooltip, legend | `chartFrame` and the other helpers from `chart` |
|
|
73
|
+
| Status chip (paid, pending, failed) | `Badge` from `badge` |
|
|
74
|
+
| Data table | `Table` from `table` |
|
|
75
|
+
| Sortable column header | `SortableTableHead` from `sortable-table-head` |
|
|
76
|
+
| Table body while loading | `TableBodySkeleton` from `table-body-skeleton` |
|
|
77
77
|
| Nothing to show yet | `EmptyPanel` from `empty-panel` |
|
|
78
|
-
| Grouped content | `Card`
|
|
79
|
-
| Modal | `Dialog`
|
|
80
|
-
| Label for an icon button, or a short hint | `Tooltip` from `tooltip
|
|
81
|
-
| Longer help from a glyph | `InfoTip`
|
|
78
|
+
| Grouped content | `Card` from `card` |
|
|
79
|
+
| Modal | `Dialog` from `dialog` |
|
|
80
|
+
| Label for an icon button, or a short hint | `Tooltip` from `tooltip` |
|
|
81
|
+
| Longer help from a glyph | `InfoTip` from `info-tip` |
|
|
82
82
|
| Color scale key | `ColorLegend` from `color-legend` |
|
|
83
|
-
| GitHub-flavoured Markdown | `Markdown` from `markdown
|
|
84
|
-
| Summary figure in a strip | `StatTile` from `stat-tile`
|
|
85
|
-
| Value against a scale, optional ticks | `Meter` from `meter`
|
|
83
|
+
| GitHub-flavoured Markdown | `Markdown` from `markdown` |
|
|
84
|
+
| Summary figure in a strip | `StatTile` from `stat-tile` |
|
|
85
|
+
| Value against a scale, optional ticks | `Meter` from `meter` |
|
|
86
86
|
| Number on a continuous scale | `Slider` from `slider` |
|
|
87
|
-
| Row actions or an overflow menu of commands | `DropdownMenu`
|
|
88
|
-
| Setting that applies immediately | `Switch` from `switch
|
|
89
|
-
|
|
|
90
|
-
|
|
|
91
|
-
|
|
|
92
|
-
|
|
|
93
|
-
|
|
|
94
|
-
|
|
|
95
|
-
|
|
|
87
|
+
| Row actions or an overflow menu of commands | `DropdownMenu` from `dropdown-menu` |
|
|
88
|
+
| Setting that applies immediately | `Switch` from `switch` |
|
|
89
|
+
| On/off choice submitted with a form | `Checkbox` from `checkbox` |
|
|
90
|
+
| One choice from a few visible options | `RadioGroup` from `radio-group` |
|
|
91
|
+
| Inline message about a page or section | `Alert` from `alert` |
|
|
92
|
+
| Person or workspace image | `Avatar` from `avatar` |
|
|
93
|
+
| Sections that expand in place | `Accordion` from `accordion` |
|
|
94
|
+
| Pages of a long list | `Pagination` from `pagination` |
|
|
95
|
+
| Where the page sits in a hierarchy | `Breadcrumb` from `breadcrumb` |
|
|
96
|
+
| Job with a known percentage | `Progress` from `progress` |
|
|
97
|
+
| Wait of unknown length | `Spinner` from `spinner` |
|
|
96
98
|
| Crash recovery around a section | `ErrorBoundary` from `error-boundary` |
|
|
97
99
|
| Conditional classes | `cn` from `utils` |
|
|
98
100
|
|
|
101
|
+
The parts, props, and options of each are in its `COMPONENTS.md` row.
|
|
102
|
+
|
|
99
103
|
If nothing fits, build the piece in the app from kit primitives and semantic
|
|
100
104
|
tokens. Do not copy kit source into the app; a missing shared component belongs
|
|
101
105
|
in the atlas-design-system repository.
|