atlas-react-kit 0.6.0 → 0.6.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/DESIGN_RULES.md +5 -3
- package/README.md +3 -3
- package/dist/accordion.js +2 -1
- package/dist/badge.js +7 -2
- package/dist/button.d.ts +1 -1
- package/dist/button.js +12 -2
- package/dist/checkbox.js +2 -1
- package/dist/combobox.js +2 -2
- package/dist/dropdown-menu.js +2 -1
- package/dist/form/markdown-field.js +3 -2
- package/dist/info-tip.js +2 -1
- package/dist/input.js +2 -1
- package/dist/internal/form-control.d.ts +1 -1
- package/dist/internal/form-control.js +1 -1
- package/dist/internal/styles.d.ts +50 -0
- package/dist/internal/styles.js +50 -0
- package/dist/internal/tabs-styles.js +5 -2
- package/dist/pagination.js +2 -1
- package/dist/radio-group.js +2 -1
- package/dist/search-picker.js +2 -1
- package/dist/select.js +3 -2
- package/dist/slider.js +2 -1
- package/dist/switch.js +2 -1
- package/dist/table.js +2 -1
- package/dist/textarea.js +2 -1
- package/dist/toggle.d.ts +4 -8
- package/dist/toggle.js +13 -9
- package/package.json +3 -1
package/DESIGN_RULES.md
CHANGED
|
@@ -32,9 +32,11 @@ belong to the application.
|
|
|
32
32
|
## Accessibility
|
|
33
33
|
|
|
34
34
|
The target is WCAG 2.2 AA. CI runs axe-core's WCAG 2.2 A and AA rules against
|
|
35
|
-
every Storybook story
|
|
36
|
-
|
|
37
|
-
|
|
35
|
+
every Storybook story and every open overlay (menus, listboxes, dialogs,
|
|
36
|
+
popovers, tooltips, toasts) in light and dark mode, checks that focus never
|
|
37
|
+
lands in content an open overlay hides, and checks the theme's token pairs
|
|
38
|
+
for text and non-text contrast in both themes (`npm run test:a11y --prefix
|
|
39
|
+
site`). A violation fails the build. Automated checks do not replace
|
|
38
40
|
keyboard and screen-reader testing.
|
|
39
41
|
|
|
40
42
|
- Text meets 4.5:1 against its background. The token pairs are tuned for it,
|
package/README.md
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
# Atlas React Kit
|
|
2
2
|
|
|
3
3
|
Shared React components and a Tailwind theme with square corners, semantic
|
|
4
|
-
colors, and distinct selected states. 0.6.0
|
|
4
|
+
colors, and distinct selected states. 0.6.0 added generic pieces apps were
|
|
5
5
|
keeping locally (`InfoTip`, `ColorLegend`, `SearchPicker`, `Markdown`,
|
|
6
6
|
`StatTile`, `Meter`, `Slider`) plus the ToggleGroup selected-state fix.
|
|
7
7
|
React 19 and Tailwind 4 are required.
|
|
8
8
|
|
|
9
9
|
```sh
|
|
10
|
-
npm install --save-exact atlas-react-kit@0.6.
|
|
10
|
+
npm install --save-exact atlas-react-kit@0.6.1
|
|
11
11
|
```
|
|
12
12
|
|
|
13
13
|
Import components by subpath:
|
|
@@ -116,7 +116,7 @@ artifact with the package owner's npm login:
|
|
|
116
116
|
```sh
|
|
117
117
|
mkdir -p release-artifacts
|
|
118
118
|
npm pack --pack-destination release-artifacts
|
|
119
|
-
npm publish ./release-artifacts/atlas-react-kit-0.6.
|
|
119
|
+
npm publish ./release-artifacts/atlas-react-kit-0.6.1.tgz --access public
|
|
120
120
|
```
|
|
121
121
|
|
|
122
122
|
See [CONTRIBUTING.md](./CONTRIBUTING.md) for adding components, the browser
|
package/dist/accordion.js
CHANGED
|
@@ -3,6 +3,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
3
3
|
import * as React from "react";
|
|
4
4
|
import { Accordion as AccordionPrimitive } from "radix-ui";
|
|
5
5
|
import { ChevronDownIcon } from "lucide-react";
|
|
6
|
+
import { disabledState, focusRing } from "./internal/styles.js";
|
|
6
7
|
import { cn } from "./utils.js";
|
|
7
8
|
/** Stacked sections that expand in place (FAQs, settings groups). */
|
|
8
9
|
function Accordion({ ...props }) {
|
|
@@ -12,7 +13,7 @@ function AccordionItem({ className, ...props }) {
|
|
|
12
13
|
return (_jsx(AccordionPrimitive.Item, { "data-slot": "accordion-item", className: cn("border-b last:border-b-0", className), ...props }));
|
|
13
14
|
}
|
|
14
15
|
function AccordionTrigger({ className, children, ...props }) {
|
|
15
|
-
return (_jsx(AccordionPrimitive.Header, { className: "flex", children: _jsxs(AccordionPrimitive.Trigger, { "data-slot": "accordion-trigger", className: cn("flex flex-1 items-start justify-between gap-4 rounded-none py-4 text-left text-sm font-medium transition-colors
|
|
16
|
+
return (_jsx(AccordionPrimitive.Header, { className: "flex", children: _jsxs(AccordionPrimitive.Trigger, { "data-slot": "accordion-trigger", className: cn(focusRing, disabledState, "flex flex-1 items-start justify-between gap-4 rounded-none py-4 text-left text-sm font-medium transition-colors hover:text-selected-foreground focus-visible:text-selected-foreground [&[data-state=open]>svg]:rotate-180", className), ...props, children: [children, _jsx(ChevronDownIcon, { className: "pointer-events-none size-4 shrink-0 translate-y-0.5 text-muted-foreground transition-transform duration-200" })] }) }));
|
|
16
17
|
}
|
|
17
18
|
function AccordionContent({ className, children, ...props }) {
|
|
18
19
|
return (_jsx(AccordionPrimitive.Content, { "data-slot": "accordion-content", className: "overflow-hidden text-sm data-[state=closed]:animate-accordion-up data-[state=open]:animate-accordion-down", ...props, children: _jsx("div", { className: cn("pt-0 pb-4", className), children: children }) }));
|
package/dist/badge.js
CHANGED
|
@@ -2,13 +2,18 @@ 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 { focusRing, invalidState } from "./internal/styles.js";
|
|
5
6
|
import { cn } from "./utils.js";
|
|
6
|
-
const badgeVariants = cva(
|
|
7
|
+
const badgeVariants = cva([
|
|
8
|
+
focusRing,
|
|
9
|
+
invalidState,
|
|
10
|
+
"group/badge inline-flex w-fit shrink-0 items-center justify-center gap-1.5 overflow-hidden rounded-none border-0 bg-transparent px-0 py-0 text-[0.625rem] font-semibold tracking-widest whitespace-nowrap uppercase transition-colors has-data-[icon=inline-end]:pr-0 has-data-[icon=inline-start]:pl-0 [&>svg]:pointer-events-none [&>svg]:size-3!",
|
|
11
|
+
], {
|
|
7
12
|
variants: {
|
|
8
13
|
variant: {
|
|
9
14
|
default: "text-foreground [a]:hover:text-foreground/70",
|
|
10
15
|
secondary: "text-muted-foreground [a]:hover:text-foreground",
|
|
11
|
-
destructive: "text-destructive
|
|
16
|
+
destructive: "text-destructive [a]:hover:text-destructive/70",
|
|
12
17
|
outline: "text-foreground [a]:hover:text-foreground/70",
|
|
13
18
|
ghost: "text-muted-foreground hover:text-foreground",
|
|
14
19
|
link: "text-foreground underline-offset-4 hover:underline",
|
package/dist/button.d.ts
CHANGED
|
@@ -6,7 +6,7 @@ import { type VariantProps } from "class-variance-authority";
|
|
|
6
6
|
* stays readable on dark), no muted grey fill. Used by quiet actions such as
|
|
7
7
|
* "Clear selection" sitting on a selected row.
|
|
8
8
|
*/
|
|
9
|
-
declare const quietButtonInteraction
|
|
9
|
+
declare const quietButtonInteraction: string;
|
|
10
10
|
declare const buttonVariants: (props?: ({
|
|
11
11
|
variant?: "default" | "link" | "destructive" | "secondary" | "outline" | "ghost" | null | undefined;
|
|
12
12
|
size?: "default" | "sm" | "lg" | "xs" | "icon" | "icon-xs" | "icon-sm" | "icon-lg" | null | undefined;
|
package/dist/button.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 { disabledState, focusRing, invalidState, selectedState, } from "./internal/styles.js";
|
|
5
6
|
import { cn } from "./utils.js";
|
|
6
7
|
/**
|
|
7
8
|
* Outline and ghost hover/focus: selected-foreground border, selected wash, and
|
|
@@ -9,8 +10,17 @@ import { cn } from "./utils.js";
|
|
|
9
10
|
* stays readable on dark), no muted grey fill. Used by quiet actions such as
|
|
10
11
|
* "Clear selection" sitting on a selected row.
|
|
11
12
|
*/
|
|
12
|
-
const quietButtonInteraction =
|
|
13
|
-
|
|
13
|
+
const quietButtonInteraction = [
|
|
14
|
+
selectedState.hover,
|
|
15
|
+
"focus-visible:text-selected-foreground",
|
|
16
|
+
selectedState.expanded,
|
|
17
|
+
].join(" ");
|
|
18
|
+
const buttonVariants = cva([
|
|
19
|
+
focusRing,
|
|
20
|
+
invalidState,
|
|
21
|
+
disabledState,
|
|
22
|
+
"group/button inline-flex shrink-0 items-center justify-center rounded-none border border-transparent bg-clip-padding text-xs font-semibold tracking-widest whitespace-nowrap uppercase transition-all select-none active:not-aria-[haspopup]:translate-y-px [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-3.5",
|
|
23
|
+
], {
|
|
14
24
|
variants: {
|
|
15
25
|
variant: {
|
|
16
26
|
default: "bg-primary text-primary-foreground hover:bg-primary/80",
|
package/dist/checkbox.js
CHANGED
|
@@ -2,9 +2,10 @@
|
|
|
2
2
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
3
|
import * as React from "react";
|
|
4
4
|
import { Checkbox as CheckboxPrimitive } from "radix-ui";
|
|
5
|
+
import { disabledState, focusRing, invalidState } from "./internal/styles.js";
|
|
5
6
|
import { cn } from "./utils.js";
|
|
6
7
|
import { CheckIcon } from "lucide-react";
|
|
7
8
|
function Checkbox({ className, ...props }) {
|
|
8
|
-
return (_jsx(CheckboxPrimitive.Root, { "data-slot": "checkbox", className: cn("peer relative flex size-4.5 shrink-0 items-center justify-center rounded-none border border-input bg-transparent transition-shadow
|
|
9
|
+
return (_jsx(CheckboxPrimitive.Root, { "data-slot": "checkbox", className: cn(focusRing, invalidState, disabledState, "peer relative flex size-4.5 shrink-0 items-center justify-center rounded-none border border-input bg-transparent transition-shadow group-has-disabled/field:opacity-50 after:absolute after:-inset-x-3 after:-inset-y-2 aria-invalid:aria-checked:border-primary data-checked:border-primary data-checked:bg-primary data-checked:text-primary-foreground dark:data-checked:bg-primary", className), ...props, children: _jsx(CheckboxPrimitive.Indicator, { "data-slot": "checkbox-indicator", className: "grid place-content-center text-current transition-none [&>svg]:size-3.5", children: _jsx(CheckIcon, {}) }) }));
|
|
9
10
|
}
|
|
10
11
|
export { Checkbox };
|
package/dist/combobox.js
CHANGED
|
@@ -6,6 +6,7 @@ import { Button } from "./button.js";
|
|
|
6
6
|
import { Input } from "./input.js";
|
|
7
7
|
import { formControlTriggerClassName } from "./internal/form-control.js";
|
|
8
8
|
import { Popover, PopoverContent, PopoverTrigger } from "./popover.js";
|
|
9
|
+
import { optionState } from "./internal/styles.js";
|
|
9
10
|
import { cn, onInputChange } from "./utils.js";
|
|
10
11
|
/**
|
|
11
12
|
* PageUp/PageDown jump size. The option list is `max-h-60`; ten rows is a
|
|
@@ -112,7 +113,6 @@ export function Combobox({ options, value, onValueChange, placeholder = "Select"
|
|
|
112
113
|
return (_jsxs(Popover, { open: open, onOpenChange: onOpenChange, children: [_jsx(PopoverTrigger, { asChild: true, children: _jsxs(Button, { id: id, type: "button", variant: "outline", role: "combobox", "aria-expanded": open, "aria-controls": listId, "aria-invalid": ariaInvalid, "aria-label": ariaLabel, "aria-labelledby": ariaLabelledBy, disabled: disabled, className: cn(formControlTriggerClassName, "justify-between", !selected && "text-muted-foreground", className), children: [_jsx("span", { className: "truncate", children: selected?.label ?? placeholder }), _jsx(ChevronDownIcon, { "data-icon": "inline-end", className: "opacity-50" })] }) }), _jsxs(PopoverContent, { align: "start", className: "w-[var(--radix-popover-trigger-width)] gap-0 rounded-none p-0", children: [_jsx("div", { className: "border-b p-2", children: _jsx(Input, { ref: inputRef, value: query, onChange: onQueryChange, onKeyDown: onSearchKeyDown, placeholder: searchPlaceholder, "aria-autocomplete": "list", "aria-controls": listId, "aria-activedescendant": activeOption ? optionId(highlight) : undefined }) }), _jsx("ul", { id: listId, role: "listbox", className: "max-h-60 overflow-y-auto p-1", children: filtered.length === 0 ? (_jsx("li", { className: "px-3 py-4 text-center text-sm text-muted-foreground", children: emptyText })) : (filtered.map((option, index) => {
|
|
113
114
|
const isSelected = option.value === value;
|
|
114
115
|
const isActive = index === highlight;
|
|
115
|
-
return (_jsx("li", { role: "none", children: _jsxs("button", { ref: isActive ? scrollHighlightedOption : undefined, id: optionId(index), type: "button", role: "option", "aria-selected": isSelected, className: cn("relative flex w-full cursor-default items-center gap-2 rounded-none py-2 pr-8 pl-3 text-left text-sm outline-hidden select-none", isSelected && "
|
|
116
|
-
"bg-accent text-accent-foreground shadow-[inset_2px_0_0_var(--color-ring)]"), onMouseEnter: () => setHighlight(index), onClick: () => selectValue(option.value), children: [_jsx("span", { className: "truncate", children: option.label }), isSelected ? (_jsx(CheckIcon, { className: "pointer-events-none absolute right-2 size-3.5" })) : null] }) }, option.value));
|
|
116
|
+
return (_jsx("li", { role: "none", children: _jsxs("button", { ref: isActive ? scrollHighlightedOption : undefined, id: optionId(index), type: "button", role: "option", "aria-selected": isSelected, className: cn("relative flex w-full cursor-default items-center gap-2 rounded-none py-2 pr-8 pl-3 text-left text-sm outline-hidden select-none", isSelected && optionState.selected, isActive && optionState.active), onMouseEnter: () => setHighlight(index), onClick: () => selectValue(option.value), children: [_jsx("span", { className: "truncate", children: option.label }), isSelected ? (_jsx(CheckIcon, { className: "pointer-events-none absolute right-2 size-3.5" })) : null] }) }, option.value));
|
|
117
117
|
})) })] })] }));
|
|
118
118
|
}
|
package/dist/dropdown-menu.js
CHANGED
|
@@ -3,9 +3,10 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
3
3
|
import * as React from "react";
|
|
4
4
|
import { DropdownMenu as DropdownMenuPrimitive } from "radix-ui";
|
|
5
5
|
import { CheckIcon, ChevronRightIcon } from "lucide-react";
|
|
6
|
+
import { dataDisabledState, optionState } from "./internal/styles.js";
|
|
6
7
|
import { cn } from "./utils.js";
|
|
7
8
|
/** Item highlight and selected chrome shared with `SelectItem`. */
|
|
8
|
-
const menuItemClassName = "relative flex cursor-default items-center gap-2.5 rounded-none px-3 py-2 text-sm outline-hidden select-none
|
|
9
|
+
const menuItemClassName = cn(optionState.focus, dataDisabledState, "relative flex cursor-default items-center gap-2.5 rounded-none px-3 py-2 text-sm outline-hidden select-none data-inset:pl-9 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-3.5");
|
|
9
10
|
const menuContentClassName = "z-50 min-w-40 overflow-x-hidden overflow-y-auto rounded-none bg-popover p-1 text-popover-foreground shadow-md ring-1 ring-foreground/10 duration-100 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 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95";
|
|
10
11
|
/** Action menu for commands (row actions, overflow menus). Use `Select` to pick a value. */
|
|
11
12
|
function DropdownMenu({ ...props }) {
|
|
@@ -5,7 +5,8 @@ import { Label } from "../label.js";
|
|
|
5
5
|
import { Markdown } from "../markdown.js";
|
|
6
6
|
import { Textarea } from "../textarea.js";
|
|
7
7
|
import { ToggleGroup, ToggleGroupItem } from "../toggle-group.js";
|
|
8
|
-
import {
|
|
8
|
+
import { fieldSurface } from "../internal/styles.js";
|
|
9
|
+
import { cn, onInputChange } from "../utils.js";
|
|
9
10
|
import { FieldError, getFieldErrorMessage } from "./field-error.js";
|
|
10
11
|
/**
|
|
11
12
|
* GFM body field: Write is a textarea, Preview is the sanitized Markdown
|
|
@@ -22,5 +23,5 @@ export function FormMarkdownField({ field, id = field.name, label, placeholder =
|
|
|
22
23
|
if (value === "write" || value === "preview")
|
|
23
24
|
setMode(value);
|
|
24
25
|
}
|
|
25
|
-
return (_jsxs("div", { className: "flex flex-col gap-1.5", children: [_jsxs("div", { className: "flex items-center justify-between gap-2", children: [_jsx(Label, { htmlFor: id, children: label }), _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" })] })] }), mode === "write" ? (_jsx(Textarea, { id: id, value: field.state.value, onChange: onChange, onBlur: field.handleBlur, placeholder: placeholder, rows: rows, "aria-invalid": errorText ? true : undefined })) : (_jsx("div", { className: "min-h-16
|
|
26
|
+
return (_jsxs("div", { className: "flex flex-col gap-1.5", children: [_jsxs("div", { className: "flex items-center justify-between gap-2", children: [_jsx(Label, { htmlFor: id, children: label }), _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" })] })] }), mode === "write" ? (_jsx(Textarea, { id: id, value: field.state.value, onChange: onChange, onBlur: field.handleBlur, placeholder: placeholder, rows: rows, "aria-invalid": errorText ? true : undefined })) : (_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" })) })), showError ? _jsx(FieldError, { text: errorText }) : null] }));
|
|
26
27
|
}
|
package/dist/info-tip.js
CHANGED
|
@@ -3,6 +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
7
|
import { cn } from "./utils.js";
|
|
7
8
|
/**
|
|
8
9
|
* True when a mouse can hover. Touch and coarse pointers must use click;
|
|
@@ -52,7 +53,7 @@ function InfoTip({ label, title, description, children, layout = "box", side = "
|
|
|
52
53
|
}
|
|
53
54
|
hoverOpened.current = false;
|
|
54
55
|
setOpen(next);
|
|
55
|
-
}, children: [_jsx(PopoverTrigger, { asChild: true, children: _jsx("button", { type: "button", "aria-label": label, "data-slot": "info-tip-trigger", "data-layout": layout, className: cn("relative inline-flex shrink-0 items-center justify-center rounded-none text-muted-foreground
|
|
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
|
|
56
57
|
? "size-5 align-middle after:absolute after:top-1/2 after:left-1/2 after:size-11 after:-translate-x-1/2 after:-translate-y-1/2"
|
|
57
58
|
: "size-11", className), onMouseEnter: openFromHover, onMouseLeave: closeFromHover, children: icon ?? (_jsx(CircleHelpIcon, { className: "size-5", "data-icon": "info-tip", "aria-hidden": true })) }) }), _jsxs(PopoverContent, { side: side, align: align, className: "w-72", onMouseEnter: openFromHover, onMouseLeave: closeFromHover, children: [_jsxs(PopoverHeader, { children: [_jsxs("div", { className: "flex items-start justify-between gap-2", children: [title ? _jsx(PopoverTitle, { children: title }) : null, badge] }), description ? (_jsx(PopoverDescription, { children: description })) : null] }), children] })] }));
|
|
58
59
|
}
|
package/dist/input.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import * as React from "react";
|
|
3
|
+
import { disabledState, fieldSurface, focusRing, invalidState, } from "./internal/styles.js";
|
|
3
4
|
import { cn } from "./utils.js";
|
|
4
5
|
function Input({ className, type, ...props }) {
|
|
5
|
-
return (_jsx("input", { type: type, "data-slot": "input", className: cn(
|
|
6
|
+
return (_jsx("input", { type: type, "data-slot": "input", className: cn(fieldSurface, focusRing, invalidState, disabledState, "h-10 file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground", className), ...props }));
|
|
6
7
|
}
|
|
7
8
|
export { Input };
|
|
@@ -6,4 +6,4 @@
|
|
|
6
6
|
* control next to real form fields. Hover stays Input-like and cancels the
|
|
7
7
|
* outline Button's purple selected wash.
|
|
8
8
|
*/
|
|
9
|
-
export declare const formControlTriggerClassName = "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:
|
|
9
|
+
export declare const formControlTriggerClassName = "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";
|
|
@@ -6,4 +6,4 @@
|
|
|
6
6
|
* control next to real form fields. Hover stays Input-like and cancels the
|
|
7
7
|
* outline Button's purple selected wash.
|
|
8
8
|
*/
|
|
9
|
-
export const formControlTriggerClassName = "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:
|
|
9
|
+
export const formControlTriggerClassName = "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";
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* State classes shared by the kit's controls. Components add these with
|
|
3
|
+
* `cn()` instead of retyping them, so a state looks the same everywhere and
|
|
4
|
+
* a change (such as the WCAG focus ring) happens here once. Tailwind only
|
|
5
|
+
* generates class names it finds as literals, so each state attribute gets
|
|
6
|
+
* its own complete string. `test/dry.test.ts` fails when a component types
|
|
7
|
+
* one of these by hand.
|
|
8
|
+
*/
|
|
9
|
+
/** Keyboard focus: a ring-colored border plus a solid 2px ring (WCAG 1.4.11). */
|
|
10
|
+
export declare const focusRing = "outline-none focus-visible:border-ring focus-visible:ring-2 focus-visible:ring-ring";
|
|
11
|
+
/** `aria-invalid`: destructive border and a soft glow; the error text carries the message. */
|
|
12
|
+
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";
|
|
13
|
+
/** Native `disabled`: dimmed, with no hover or click. */
|
|
14
|
+
export declare const disabledState = "disabled:pointer-events-none disabled:opacity-50";
|
|
15
|
+
/** Radix `data-disabled` (menu and list items, slider). */
|
|
16
|
+
export declare const dataDisabledState = "data-disabled:pointer-events-none data-disabled:opacity-50";
|
|
17
|
+
/** The text-field surface, so Input, Textarea, and SelectTrigger line up. */
|
|
18
|
+
export declare const fieldSurface = "w-full min-w-0 rounded-none border border-input bg-background px-3 py-2 text-base shadow-xs transition-[color,box-shadow] placeholder:text-muted-foreground md:text-sm";
|
|
19
|
+
/**
|
|
20
|
+
* Selected chrome: the wash, selected text, and a selected border, because
|
|
21
|
+
* the wash alone is 1.2:1 against the page. One string per state attribute.
|
|
22
|
+
*/
|
|
23
|
+
export declare const selectedState: {
|
|
24
|
+
/**
|
|
25
|
+
* Toggle and ToggleGroupItem. Radix Toggle sets `data-state=on` and
|
|
26
|
+
* `aria-pressed`; group items always set `data-state=on`, add
|
|
27
|
+
* `aria-pressed` in multiple mode, and are radios with `aria-checked` in
|
|
28
|
+
* single mode. All three get the same chrome.
|
|
29
|
+
*/
|
|
30
|
+
toggle: string;
|
|
31
|
+
/** Tab trigger and TabsNav link (`data-state="active"`). */
|
|
32
|
+
active: string;
|
|
33
|
+
/** Current page (`data-active="true"`). */
|
|
34
|
+
current: string;
|
|
35
|
+
/** Trigger whose menu or popover is open. */
|
|
36
|
+
expanded: string;
|
|
37
|
+
/** Quiet buttons and toggles on hover. */
|
|
38
|
+
hover: string;
|
|
39
|
+
};
|
|
40
|
+
/** Options in menus and listboxes. */
|
|
41
|
+
export declare const optionState: {
|
|
42
|
+
/** Highlight as focus moves: grey fill plus a 2px ring bar (the fill alone is 1.1:1). */
|
|
43
|
+
focus: string;
|
|
44
|
+
/** The same highlight for lists that track the active option in state. */
|
|
45
|
+
active: string;
|
|
46
|
+
/** The chosen value (`data-state="checked"`); a check icon marks it too. */
|
|
47
|
+
checked: string;
|
|
48
|
+
/** The chosen value in lists that track selection in state. */
|
|
49
|
+
selected: string;
|
|
50
|
+
};
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* State classes shared by the kit's controls. Components add these with
|
|
3
|
+
* `cn()` instead of retyping them, so a state looks the same everywhere and
|
|
4
|
+
* a change (such as the WCAG focus ring) happens here once. Tailwind only
|
|
5
|
+
* generates class names it finds as literals, so each state attribute gets
|
|
6
|
+
* its own complete string. `test/dry.test.ts` fails when a component types
|
|
7
|
+
* one of these by hand.
|
|
8
|
+
*/
|
|
9
|
+
/** Keyboard focus: a ring-colored border plus a solid 2px ring (WCAG 1.4.11). */
|
|
10
|
+
export const focusRing = "outline-none focus-visible:border-ring focus-visible:ring-2 focus-visible:ring-ring";
|
|
11
|
+
/** `aria-invalid`: destructive border and a soft glow; the error text carries the message. */
|
|
12
|
+
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";
|
|
13
|
+
/** Native `disabled`: dimmed, with no hover or click. */
|
|
14
|
+
export const disabledState = "disabled:pointer-events-none disabled:opacity-50";
|
|
15
|
+
/** Radix `data-disabled` (menu and list items, slider). */
|
|
16
|
+
export const dataDisabledState = "data-disabled:pointer-events-none data-disabled:opacity-50";
|
|
17
|
+
/** The text-field surface, so Input, Textarea, and SelectTrigger line up. */
|
|
18
|
+
export const fieldSurface = "w-full min-w-0 rounded-none border border-input bg-background px-3 py-2 text-base shadow-xs transition-[color,box-shadow] placeholder:text-muted-foreground md:text-sm";
|
|
19
|
+
/**
|
|
20
|
+
* Selected chrome: the wash, selected text, and a selected border, because
|
|
21
|
+
* the wash alone is 1.2:1 against the page. One string per state attribute.
|
|
22
|
+
*/
|
|
23
|
+
export const selectedState = {
|
|
24
|
+
/**
|
|
25
|
+
* Toggle and ToggleGroupItem. Radix Toggle sets `data-state=on` and
|
|
26
|
+
* `aria-pressed`; group items always set `data-state=on`, add
|
|
27
|
+
* `aria-pressed` in multiple mode, and are radios with `aria-checked` in
|
|
28
|
+
* single mode. All three get the same chrome.
|
|
29
|
+
*/
|
|
30
|
+
toggle: "data-on:border-selected-foreground data-on:bg-selected data-on:text-selected-foreground aria-pressed:border-selected-foreground aria-pressed:bg-selected aria-pressed:text-selected-foreground aria-checked:border-selected-foreground aria-checked:bg-selected aria-checked:text-selected-foreground",
|
|
31
|
+
/** Tab trigger and TabsNav link (`data-state="active"`). */
|
|
32
|
+
active: "data-[state=active]:border-selected-foreground data-[state=active]:bg-selected data-[state=active]:text-selected-foreground",
|
|
33
|
+
/** Current page (`data-active="true"`). */
|
|
34
|
+
current: "data-[active=true]:border-selected-foreground data-[active=true]:bg-selected data-[active=true]:text-selected-foreground",
|
|
35
|
+
/** Trigger whose menu or popover is open. */
|
|
36
|
+
expanded: "aria-expanded:border-selected-foreground aria-expanded:bg-selected aria-expanded:text-selected-foreground",
|
|
37
|
+
/** Quiet buttons and toggles on hover. */
|
|
38
|
+
hover: "hover:border-selected-foreground hover:bg-selected hover:text-selected-foreground",
|
|
39
|
+
};
|
|
40
|
+
/** Options in menus and listboxes. */
|
|
41
|
+
export const optionState = {
|
|
42
|
+
/** Highlight as focus moves: grey fill plus a 2px ring bar (the fill alone is 1.1:1). */
|
|
43
|
+
focus: "focus:bg-accent focus:text-accent-foreground focus:shadow-[inset_2px_0_0_var(--color-ring)]",
|
|
44
|
+
/** The same highlight for lists that track the active option in state. */
|
|
45
|
+
active: "bg-accent text-accent-foreground shadow-[inset_2px_0_0_var(--color-ring)]",
|
|
46
|
+
/** The chosen value (`data-state="checked"`); a check icon marks it too. */
|
|
47
|
+
checked: "data-[state=checked]:bg-selected data-[state=checked]:text-selected-foreground",
|
|
48
|
+
/** The chosen value in lists that track selection in state. */
|
|
49
|
+
selected: "bg-selected text-selected-foreground",
|
|
50
|
+
};
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { cva } from "class-variance-authority";
|
|
2
|
+
import { disabledState, focusRing, selectedState } from "./styles.js";
|
|
2
3
|
const tabsListVariants = cva('group/tabs-list inline-flex w-fit items-stretch justify-center p-0 text-muted-foreground group-data-[orientation="horizontal"]/tabs:h-11 group-data-[orientation="vertical"]/tabs:h-fit group-data-[orientation="vertical"]/tabs:flex-col', {
|
|
3
4
|
variants: {
|
|
4
5
|
variant: {
|
|
@@ -11,9 +12,11 @@ const tabsListVariants = cva('group/tabs-list inline-flex w-fit items-stretch ju
|
|
|
11
12
|
},
|
|
12
13
|
});
|
|
13
14
|
const tabsTriggerVariants = cva([
|
|
14
|
-
|
|
15
|
+
focusRing,
|
|
16
|
+
disabledState,
|
|
17
|
+
'relative inline-flex h-full min-w-0 flex-1 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 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*=\'size-\'])]:size-3.5',
|
|
15
18
|
'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',
|
|
16
|
-
|
|
19
|
+
selectedState.active,
|
|
17
20
|
'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',
|
|
18
21
|
]);
|
|
19
22
|
export { tabsListVariants, tabsTriggerVariants };
|
package/dist/pagination.js
CHANGED
|
@@ -2,6 +2,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
2
2
|
import * as React from "react";
|
|
3
3
|
import { ChevronLeftIcon, ChevronRightIcon, MoreHorizontalIcon, } from "lucide-react";
|
|
4
4
|
import { buttonVariants } from "./button.js";
|
|
5
|
+
import { selectedState } from "./internal/styles.js";
|
|
5
6
|
import { cn } from "./utils.js";
|
|
6
7
|
/**
|
|
7
8
|
* Page links for long lists and tables. Links are plain anchors so the page
|
|
@@ -21,7 +22,7 @@ function PaginationLink({ className, isActive, size = "icon", ...props }) {
|
|
|
21
22
|
return (_jsx("a", { "aria-current": isActive ? "page" : undefined, "data-slot": "pagination-link", "data-active": isActive, className: cn(buttonVariants({
|
|
22
23
|
variant: isActive ? "outline" : "ghost",
|
|
23
24
|
size: size === "icon" ? "icon-sm" : "sm",
|
|
24
|
-
}), "font-medium tracking-normal normal-case tabular-nums
|
|
25
|
+
}), selectedState.current, "font-medium tracking-normal normal-case tabular-nums", className), ...props }));
|
|
25
26
|
}
|
|
26
27
|
function PaginationPrevious({ className, children = "Previous", ...props }) {
|
|
27
28
|
return (_jsxs(PaginationLink, { "aria-label": "Go to previous page", size: "default", className: cn("gap-1 px-2.5 sm:pl-2", className), ...props, children: [_jsx(ChevronLeftIcon, { "data-icon": "inline-start" }), _jsx("span", { className: "hidden sm:block", children: children })] }));
|
package/dist/radio-group.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
3
|
import * as React from "react";
|
|
4
4
|
import { RadioGroup as RadioGroupPrimitive } from "radix-ui";
|
|
5
|
+
import { disabledState, focusRing, invalidState } from "./internal/styles.js";
|
|
5
6
|
import { cn } from "./utils.js";
|
|
6
7
|
/**
|
|
7
8
|
* One choice from a short, visible list. Arrow keys move the selection.
|
|
@@ -15,6 +16,6 @@ function RadioGroup({ className, ...props }) {
|
|
|
15
16
|
* differently from square checkboxes.
|
|
16
17
|
*/
|
|
17
18
|
function RadioGroupItem({ className, ...props }) {
|
|
18
|
-
return (_jsx(RadioGroupPrimitive.Item, { "data-slot": "radio-group-item", className: cn("peer relative flex aspect-square size-4.5 shrink-0 items-center justify-center rounded-full border border-input bg-transparent shadow-xs transition-shadow
|
|
19
|
+
return (_jsx(RadioGroupPrimitive.Item, { "data-slot": "radio-group-item", className: cn(focusRing, invalidState, disabledState, "peer relative flex aspect-square size-4.5 shrink-0 items-center justify-center rounded-full border border-input bg-transparent shadow-xs transition-shadow after:absolute after:-inset-x-3 after:-inset-y-2 data-checked:border-primary", className), ...props, children: _jsx(RadioGroupPrimitive.Indicator, { "data-slot": "radio-group-indicator", className: "flex items-center justify-center", children: _jsx("span", { className: "size-2 rounded-full bg-primary" }) }) }));
|
|
19
20
|
}
|
|
20
21
|
export { RadioGroup, RadioGroupItem };
|
package/dist/search-picker.js
CHANGED
|
@@ -3,6 +3,7 @@ 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 { optionState } from "./internal/styles.js";
|
|
6
7
|
import { cn } from "./utils.js";
|
|
7
8
|
const LISTBOX_PAGE_SIZE = 10;
|
|
8
9
|
/**
|
|
@@ -92,7 +93,7 @@ function SearchPicker({ items, getValue, getLabel, filter = defaultSearchFilter,
|
|
|
92
93
|
const itemValue = getValue(item);
|
|
93
94
|
const isSelected = itemValue === value;
|
|
94
95
|
const isActive = index === highlight;
|
|
95
|
-
return (_jsx("li", { role: "none", children: _jsx("button", { ref: isActive ? scrollHighlightedOption : undefined, id: optionId(index), type: "button", role: "option", "aria-selected": isSelected, disabled: disabled, className: cn("relative flex w-full cursor-default items-center rounded-none py-2 pr-3 pl-3 text-left text-sm outline-hidden select-none", isSelected &&
|
|
96
|
+
return (_jsx("li", { role: "none", children: _jsx("button", { ref: isActive ? scrollHighlightedOption : undefined, id: optionId(index), type: "button", role: "option", "aria-selected": isSelected, disabled: disabled, className: cn("relative flex w-full cursor-default items-center rounded-none py-2 pr-3 pl-3 text-left text-sm outline-hidden select-none", isSelected && optionState.selected, isActive && optionState.active, isActive &&
|
|
96
97
|
!isSelected &&
|
|
97
98
|
"bg-accent text-accent-foreground"), onMouseEnter: () => setHighlight(index), onClick: () => selectItem(item), children: _jsx("span", { className: "truncate", children: getLabel(item) }) }) }, itemValue));
|
|
98
99
|
})) })] }));
|
package/dist/select.js
CHANGED
|
@@ -2,6 +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 { Select as SelectPrimitive } from "radix-ui";
|
|
5
|
+
import { dataDisabledState, disabledState, fieldSurface, focusRing, invalidState, optionState, } from "./internal/styles.js";
|
|
5
6
|
import { cn } from "./utils.js";
|
|
6
7
|
import { ChevronDownIcon, CheckIcon, ChevronUpIcon } from "lucide-react";
|
|
7
8
|
function Select({ ...props }) {
|
|
@@ -14,7 +15,7 @@ function SelectValue({ ...props }) {
|
|
|
14
15
|
return _jsx(SelectPrimitive.Value, { "data-slot": "select-value", ...props });
|
|
15
16
|
}
|
|
16
17
|
function SelectTrigger({ className, size = "default", children, ...props }) {
|
|
17
|
-
return (_jsxs(SelectPrimitive.Trigger, { "data-slot": "select-trigger", "data-size": size, className: cn("flex
|
|
18
|
+
return (_jsxs(SelectPrimitive.Trigger, { "data-slot": "select-trigger", "data-size": size, className: cn(fieldSurface, focusRing, invalidState, disabledState, "flex items-center justify-between gap-1.5 whitespace-nowrap data-placeholder:text-muted-foreground data-[size=default]:h-10 data-[size=sm]:h-9 *: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 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-3.5", className), ...props, children: [children, _jsx(SelectPrimitive.Icon, { asChild: true, children: _jsx(ChevronDownIcon, { className: "pointer-events-none size-3.5 text-muted-foreground" }) })] }));
|
|
18
19
|
}
|
|
19
20
|
function SelectContent({ className, children, position = "popper", align = "center", sideOffset = 4, collisionPadding = 8, ...props }) {
|
|
20
21
|
return (_jsx(SelectPrimitive.Portal, { children: _jsxs(SelectPrimitive.Content, { "data-slot": "select-content", "data-align-trigger": position === "item-aligned", className: cn("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 rounded-none bg-popover text-popover-foreground shadow-md ring-1 ring-foreground/10 duration-100 data-[align-trigger=true]:animate-none 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 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95", position === "popper" &&
|
|
@@ -24,7 +25,7 @@ function SelectLabel({ className, ...props }) {
|
|
|
24
25
|
return (_jsx(SelectPrimitive.Label, { "data-slot": "select-label", className: cn("px-3 py-2 text-xs font-semibold tracking-wider text-muted-foreground uppercase", className), ...props }));
|
|
25
26
|
}
|
|
26
27
|
function SelectItem({ className, children, ...props }) {
|
|
27
|
-
return (_jsxs(SelectPrimitive.Item, { "data-slot": "select-item", className: cn("relative flex w-full cursor-default items-center gap-2.5 rounded-none py-2 pr-8 pl-3 text-sm outline-hidden select-none
|
|
28
|
+
return (_jsxs(SelectPrimitive.Item, { "data-slot": "select-item", className: cn(optionState.focus, optionState.checked, dataDisabledState, "relative flex w-full cursor-default items-center gap-2.5 rounded-none py-2 pr-8 pl-3 text-sm outline-hidden select-none not-data-[variant=destructive]:focus:**:text-accent-foreground [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-3.5 *:[span]:last:flex *:[span]:last:items-center *:[span]:last:gap-2", className), ...props, children: [_jsx("span", { className: "pointer-events-none absolute right-2 flex size-4 items-center justify-center", children: _jsx(SelectPrimitive.ItemIndicator, { children: _jsx(CheckIcon, { className: "pointer-events-none" }) }) }), _jsx(SelectPrimitive.ItemText, { children: children })] }));
|
|
28
29
|
}
|
|
29
30
|
function SelectSeparator({ className, ...props }) {
|
|
30
31
|
return (_jsx(SelectPrimitive.Separator, { "data-slot": "select-separator", className: cn("pointer-events-none -mx-1.5 my-1.5 h-px bg-border/50", className), ...props }));
|
package/dist/slider.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
3
|
import { Slider as SliderPrimitive } from "radix-ui";
|
|
4
|
+
import { dataDisabledState, focusRing } from "./internal/styles.js";
|
|
4
5
|
import { cn } from "./utils.js";
|
|
5
6
|
/**
|
|
6
7
|
* Single-thumb range on Radix Slider. Square track and thumb, primary fill,
|
|
@@ -15,6 +16,6 @@ function Slider({ value, onValueChange, onValueCommit, min = 0, max = 100, step
|
|
|
15
16
|
const amount = next[0];
|
|
16
17
|
if (amount !== undefined)
|
|
17
18
|
onValueCommit?.(amount);
|
|
18
|
-
}, className: cn("relative flex w-full touch-none items-center select-none
|
|
19
|
+
}, className: cn(dataDisabledState, "relative flex w-full touch-none items-center select-none", className), children: [_jsx(SliderPrimitive.Track, { "data-slot": "slider-track", className: "relative h-1.5 w-full grow overflow-hidden rounded-none bg-muted", children: _jsx(SliderPrimitive.Range, { "data-slot": "slider-range", className: "absolute h-full bg-primary" }) }), _jsx(SliderPrimitive.Thumb, { id: id, "aria-label": ariaLabel, "aria-labelledby": ariaLabelledBy, "data-slot": "slider-thumb", className: cn(focusRing, "relative block size-4 shrink-0 rounded-none border border-primary bg-background shadow-xs after:absolute after:top-1/2 after:left-1/2 after:size-11 after:-translate-x-1/2 after:-translate-y-1/2") })] }));
|
|
19
20
|
}
|
|
20
21
|
export { Slider };
|
package/dist/switch.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
3
|
import * as React from "react";
|
|
4
4
|
import { Switch as SwitchPrimitive } from "radix-ui";
|
|
5
|
+
import { disabledState, focusRing, invalidState } from "./internal/styles.js";
|
|
5
6
|
import { cn } from "./utils.js";
|
|
6
7
|
/**
|
|
7
8
|
* On/off setting that applies immediately (notifications, dark mode). Use
|
|
@@ -9,6 +10,6 @@ import { cn } from "./utils.js";
|
|
|
9
10
|
* primitives; checked uses the primary fill, as `Checkbox` does.
|
|
10
11
|
*/
|
|
11
12
|
function Switch({ className, ...props }) {
|
|
12
|
-
return (_jsx(SwitchPrimitive.Root, { "data-slot": "switch", className: cn("peer inline-flex h-5 w-9 shrink-0 items-center rounded-none border border-transparent bg-input p-0.5 shadow-xs transition-colors
|
|
13
|
+
return (_jsx(SwitchPrimitive.Root, { "data-slot": "switch", className: cn(focusRing, invalidState, disabledState, "peer inline-flex h-5 w-9 shrink-0 items-center rounded-none border border-transparent bg-input p-0.5 shadow-xs transition-colors data-checked:bg-primary dark:data-checked:bg-primary", className), ...props, children: _jsx(SwitchPrimitive.Thumb, { "data-slot": "switch-thumb", className: "pointer-events-none block size-3.5 rounded-none bg-background shadow-sm ring-0 transition-transform data-[state=checked]:translate-x-4 data-[state=unchecked]:translate-x-0 dark:data-[state=checked]:bg-primary-foreground" }) }));
|
|
13
14
|
}
|
|
14
15
|
export { Switch };
|
package/dist/table.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 { focusRing } from "./internal/styles.js";
|
|
4
5
|
import { cn } from "./utils.js";
|
|
5
6
|
/**
|
|
6
7
|
* True while the element's content is wider than its box. Re-checked when
|
|
@@ -32,7 +33,7 @@ function useHorizontalOverflow(ref) {
|
|
|
32
33
|
function Table({ className, ...props }) {
|
|
33
34
|
const containerRef = React.useRef(null);
|
|
34
35
|
const scrollable = useHorizontalOverflow(containerRef);
|
|
35
|
-
return (_jsx("div", { ref: containerRef, "data-slot": "table-container", tabIndex: scrollable ? 0 : undefined, className: "relative w-full min-w-0 max-w-full overflow-x-auto overscroll-x-contain
|
|
36
|
+
return (_jsx("div", { ref: containerRef, "data-slot": "table-container", tabIndex: scrollable ? 0 : undefined, className: cn(focusRing, "relative w-full min-w-0 max-w-full overflow-x-auto overscroll-x-contain"), children: _jsx("table", { "data-slot": "table", className: cn("w-full min-w-full caption-bottom text-sm", className), ...props }) }));
|
|
36
37
|
}
|
|
37
38
|
function TableHeader({ className, ...props }) {
|
|
38
39
|
return (_jsx("thead", { "data-slot": "table-header", className: cn("[&_tr]:border-b", className), ...props }));
|
package/dist/textarea.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import * as React from "react";
|
|
3
|
+
import { disabledState, fieldSurface, focusRing, invalidState, } from "./internal/styles.js";
|
|
3
4
|
import { cn } from "./utils.js";
|
|
4
5
|
function Textarea({ className, ...props }) {
|
|
5
|
-
return (_jsx("textarea", { "data-slot": "textarea", className: cn("flex field-sizing-content min-h-16
|
|
6
|
+
return (_jsx("textarea", { "data-slot": "textarea", className: cn(fieldSurface, focusRing, invalidState, disabledState, "flex field-sizing-content min-h-16 resize-none", className), ...props }));
|
|
6
7
|
}
|
|
7
8
|
export { Textarea };
|
package/dist/toggle.d.ts
CHANGED
|
@@ -2,15 +2,11 @@ import * as React from "react";
|
|
|
2
2
|
import { type VariantProps } from "class-variance-authority";
|
|
3
3
|
import { Toggle as TogglePrimitive } from "radix-ui";
|
|
4
4
|
/**
|
|
5
|
-
* Selected chrome shared by
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* always set `data-state=on`; in multiple mode they also set `aria-pressed`,
|
|
9
|
-
* but in single mode they are radios with `aria-checked` and no
|
|
10
|
-
* `aria-pressed`. All three selectors apply the same wash, text, and
|
|
11
|
-
* selected-foreground border so the state meets WCAG 1.4.11 in every mode.
|
|
5
|
+
* Selected chrome shared by Toggle and ToggleGroupItem (see
|
|
6
|
+
* `selectedState.toggle`): the wash, selected text, and a selected border
|
|
7
|
+
* for `data-state=on`, `aria-pressed`, and `aria-checked`.
|
|
12
8
|
*/
|
|
13
|
-
declare const selectedToggleChrome
|
|
9
|
+
declare const selectedToggleChrome: string;
|
|
14
10
|
declare const toggleVariants: (props?: ({
|
|
15
11
|
variant?: "default" | "outline" | null | undefined;
|
|
16
12
|
size?: "default" | "sm" | "lg" | null | undefined;
|
package/dist/toggle.js
CHANGED
|
@@ -3,18 +3,22 @@ import { jsx as _jsx } from "react/jsx-runtime";
|
|
|
3
3
|
import * as React from "react";
|
|
4
4
|
import { cva } from "class-variance-authority";
|
|
5
5
|
import { Toggle as TogglePrimitive } from "radix-ui";
|
|
6
|
+
import { disabledState, focusRing, invalidState, selectedState, } from "./internal/styles.js";
|
|
6
7
|
import { cn } from "./utils.js";
|
|
7
8
|
/**
|
|
8
|
-
* Selected chrome shared by
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
* always set `data-state=on`; in multiple mode they also set `aria-pressed`,
|
|
12
|
-
* but in single mode they are radios with `aria-checked` and no
|
|
13
|
-
* `aria-pressed`. All three selectors apply the same wash, text, and
|
|
14
|
-
* selected-foreground border so the state meets WCAG 1.4.11 in every mode.
|
|
9
|
+
* Selected chrome shared by Toggle and ToggleGroupItem (see
|
|
10
|
+
* `selectedState.toggle`): the wash, selected text, and a selected border
|
|
11
|
+
* for `data-state=on`, `aria-pressed`, and `aria-checked`.
|
|
15
12
|
*/
|
|
16
|
-
const selectedToggleChrome =
|
|
17
|
-
const toggleVariants = cva(
|
|
13
|
+
const selectedToggleChrome = selectedState.toggle;
|
|
14
|
+
const toggleVariants = cva([
|
|
15
|
+
focusRing,
|
|
16
|
+
selectedState.hover,
|
|
17
|
+
selectedToggleChrome,
|
|
18
|
+
invalidState,
|
|
19
|
+
disabledState,
|
|
20
|
+
"group/toggle inline-flex items-center justify-center gap-1.5 rounded-none text-xs font-semibold tracking-widest whitespace-nowrap uppercase transition-colors focus-visible:text-selected-foreground [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-3.5",
|
|
21
|
+
], {
|
|
18
22
|
variants: {
|
|
19
23
|
variant: {
|
|
20
24
|
default: "border border-transparent bg-transparent",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "atlas-react-kit",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.1",
|
|
4
4
|
"description": "Shared React components and Tailwind theme for Atlas",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -245,6 +245,7 @@
|
|
|
245
245
|
"build": "tsc",
|
|
246
246
|
"check": "npm run build && prettier --check .",
|
|
247
247
|
"test": "npm run build && vitest run",
|
|
248
|
+
"test:coverage": "npm run build && vitest run --coverage",
|
|
248
249
|
"prepack": "npm run build"
|
|
249
250
|
},
|
|
250
251
|
"engines": {
|
|
@@ -286,6 +287,7 @@
|
|
|
286
287
|
"@types/node": "^25.5.0",
|
|
287
288
|
"@types/react": "^19.2.14",
|
|
288
289
|
"@types/react-dom": "^19.2.3",
|
|
290
|
+
"@vitest/coverage-v8": "4.1.11",
|
|
289
291
|
"jsdom": "^29.1.1",
|
|
290
292
|
"next": "16.2.6",
|
|
291
293
|
"prettier": "^3.8.1",
|