atlas-react-kit 0.5.0 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/COMPONENTS.md +63 -52
- package/DESIGN_RULES.md +30 -15
- package/README.md +6 -3
- package/dist/accordion.js +1 -1
- package/dist/badge.js +1 -1
- package/dist/button.d.ts +2 -2
- package/dist/button.js +4 -4
- package/dist/calendar.js +1 -1
- package/dist/checkbox.js +1 -1
- package/dist/color-legend.d.ts +22 -0
- package/dist/color-legend.js +11 -0
- package/dist/combobox.js +2 -1
- package/dist/dropdown-menu.js +1 -1
- package/dist/form/markdown-field.d.ts +17 -0
- package/dist/form/markdown-field.js +26 -0
- package/dist/info-tip.d.ts +27 -0
- package/dist/info-tip.js +63 -0
- package/dist/input.js +1 -1
- package/dist/internal/form-control.d.ts +1 -1
- package/dist/internal/form-control.js +1 -1
- package/dist/internal/tabs-styles.js +3 -3
- package/dist/markdown.d.ts +18 -0
- package/dist/markdown.js +62 -0
- package/dist/meter.d.ts +22 -0
- package/dist/meter.js +17 -0
- package/dist/pagination.js +1 -1
- package/dist/radio-group.js +1 -1
- package/dist/search-picker.d.ts +30 -0
- package/dist/search-picker.js +100 -0
- package/dist/select.js +2 -2
- package/dist/slider.d.ts +20 -0
- package/dist/slider.js +20 -0
- package/dist/stat-tile.d.ts +21 -0
- package/dist/stat-tile.js +17 -0
- package/dist/switch.js +1 -1
- package/dist/table.js +2 -2
- package/dist/textarea.js +1 -1
- package/dist/toggle-group.js +2 -2
- package/dist/toggle.d.ts +11 -1
- package/dist/toggle.js +13 -3
- package/package.json +35 -1
- package/skills/atlas-react-kit/SKILL.md +41 -32
- package/theme.css +23 -9
package/dist/info-tip.js
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import { CircleHelpIcon } from "lucide-react";
|
|
5
|
+
import { Popover, PopoverContent, PopoverDescription, PopoverHeader, PopoverTitle, PopoverTrigger, } from "./popover.js";
|
|
6
|
+
import { cn } from "./utils.js";
|
|
7
|
+
/**
|
|
8
|
+
* True when a mouse can hover. Touch and coarse pointers must use click;
|
|
9
|
+
* a hover-open popover would trap them after the first tap.
|
|
10
|
+
*/
|
|
11
|
+
function canHoverOpen() {
|
|
12
|
+
return window.matchMedia("(hover: hover) and (pointer: fine)").matches;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Longer help than a Tooltip: a Popover with a 20px glyph and a 44px hit
|
|
16
|
+
* area. Hover opens only on a fine pointer; click, tap, and keyboard always
|
|
17
|
+
* toggle it. Not a glossary or "coming soon" chip — pass that copy in.
|
|
18
|
+
*/
|
|
19
|
+
function InfoTip({ label, title, description, children, layout = "box", side = "top", align = "center", badge, icon, className, }) {
|
|
20
|
+
const [open, setOpen] = React.useState(false);
|
|
21
|
+
const closeTimer = React.useRef(null);
|
|
22
|
+
const hoverOpened = React.useRef(false);
|
|
23
|
+
const inline = layout === "inline";
|
|
24
|
+
const cancelClose = () => {
|
|
25
|
+
if (closeTimer.current !== null) {
|
|
26
|
+
window.clearTimeout(closeTimer.current);
|
|
27
|
+
closeTimer.current = null;
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
const openFromHover = () => {
|
|
31
|
+
if (!canHoverOpen())
|
|
32
|
+
return;
|
|
33
|
+
cancelClose();
|
|
34
|
+
hoverOpened.current = true;
|
|
35
|
+
setOpen(true);
|
|
36
|
+
};
|
|
37
|
+
const closeFromHover = () => {
|
|
38
|
+
if (!canHoverOpen() || !hoverOpened.current)
|
|
39
|
+
return;
|
|
40
|
+
cancelClose();
|
|
41
|
+
// Leave a beat so the pointer can travel from the glyph into the panel.
|
|
42
|
+
closeTimer.current = window.setTimeout(() => setOpen(false), 120);
|
|
43
|
+
};
|
|
44
|
+
React.useEffect(() => () => cancelClose(), []);
|
|
45
|
+
return (_jsxs(Popover, { open: open, onOpenChange: (next) => {
|
|
46
|
+
// Hover already opened the panel; the following click should pin
|
|
47
|
+
// it, not toggle it closed (Playwright and real mice both hover
|
|
48
|
+
// before they click).
|
|
49
|
+
if (hoverOpened.current && open && !next) {
|
|
50
|
+
hoverOpened.current = false;
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
hoverOpened.current = false;
|
|
54
|
+
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 outline-none hover:text-selected-foreground focus-visible:border-ring focus-visible:text-selected-foreground focus-visible:ring-2 focus-visible:ring-ring", inline
|
|
56
|
+
? "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
|
+
: "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
|
+
/** InfoTip that sits in a line of text without stretching the line box. */
|
|
60
|
+
function InlineTip(props) {
|
|
61
|
+
return _jsx(InfoTip, { ...props, layout: "inline" });
|
|
62
|
+
}
|
|
63
|
+
export { InfoTip, InlineTip };
|
package/dist/input.js
CHANGED
|
@@ -2,6 +2,6 @@ import { jsx as _jsx } from "react/jsx-runtime";
|
|
|
2
2
|
import * as React from "react";
|
|
3
3
|
import { cn } from "./utils.js";
|
|
4
4
|
function Input({ className, type, ...props }) {
|
|
5
|
-
return (_jsx("input", { type: type, "data-slot": "input", className: cn("h-10 w-full min-w-0 rounded-none border border-input bg-background px-3 py-2 text-base shadow-xs transition-[color,box-shadow] outline-none file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-2 focus-visible:ring-ring
|
|
5
|
+
return (_jsx("input", { type: type, "data-slot": "input", className: cn("h-10 w-full min-w-0 rounded-none border border-input bg-background px-3 py-2 text-base shadow-xs transition-[color,box-shadow] outline-none file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-2 focus-visible:ring-ring disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-2 aria-invalid:ring-destructive/20 md:text-sm dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40", className), ...props }));
|
|
6
6
|
}
|
|
7
7
|
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:border-ring focus-visible:text-foreground focus-visible:ring-ring
|
|
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:border-ring focus-visible:text-foreground focus-visible:ring-ring 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:border-ring focus-visible:text-foreground focus-visible:ring-ring
|
|
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:border-ring focus-visible:text-foreground focus-visible:ring-ring 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";
|
|
@@ -11,9 +11,9 @@ const tabsListVariants = cva('group/tabs-list inline-flex w-fit items-stretch ju
|
|
|
11
11
|
},
|
|
12
12
|
});
|
|
13
13
|
const tabsTriggerVariants = cva([
|
|
14
|
-
'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 focus-visible:
|
|
15
|
-
'group-data-[variant=line]/tabs-list:bg-transparent group-data-[variant=line]/tabs-list:data-[state="active"]:
|
|
16
|
-
'data-[state="active"]:
|
|
14
|
+
'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 focus-visible:ring-2 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 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
|
+
'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
|
+
'data-[state="active"]:border-selected-foreground data-[state="active"]:bg-selected data-[state="active"]:text-selected-foreground',
|
|
17
17
|
'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
18
|
]);
|
|
19
19
|
export { tabsListVariants, tabsTriggerVariants };
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Drop javascript:/data:/unknown schemes. react-markdown already omits raw
|
|
3
|
+
* HTML (no rehype-raw), so this is the remaining URL hole.
|
|
4
|
+
*/
|
|
5
|
+
export declare function safeMarkdownUrl(url: string): string;
|
|
6
|
+
export type MarkdownProps = {
|
|
7
|
+
content: string;
|
|
8
|
+
className?: string;
|
|
9
|
+
};
|
|
10
|
+
/**
|
|
11
|
+
* GitHub-flavoured Markdown, token-styled and sanitized. No Next, no raw
|
|
12
|
+
* HTML, no typography plugin. `react-markdown` and `remark-gfm` stay in this
|
|
13
|
+
* module so other subpaths do not pay for them.
|
|
14
|
+
*/
|
|
15
|
+
declare function Markdown({ content, className }: MarkdownProps): import("react").JSX.Element;
|
|
16
|
+
/** Alias used by apps that imported MarkdownContent. */
|
|
17
|
+
declare const MarkdownContent: typeof Markdown;
|
|
18
|
+
export { Markdown, MarkdownContent };
|
package/dist/markdown.js
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import ReactMarkdown from "react-markdown";
|
|
3
|
+
import remarkGfm from "remark-gfm";
|
|
4
|
+
import { cn } from "./utils.js";
|
|
5
|
+
/** Stable identity so parent re-renders do not re-parse unchanged content. */
|
|
6
|
+
const REMARK_PLUGINS = [remarkGfm];
|
|
7
|
+
const SAFE_PROTOCOLS = new Set(["http:", "https:", "mailto:"]);
|
|
8
|
+
/**
|
|
9
|
+
* Drop javascript:/data:/unknown schemes. react-markdown already omits raw
|
|
10
|
+
* HTML (no rehype-raw), so this is the remaining URL hole.
|
|
11
|
+
*/
|
|
12
|
+
export function safeMarkdownUrl(url) {
|
|
13
|
+
const value = url.trim();
|
|
14
|
+
if (!value)
|
|
15
|
+
return "";
|
|
16
|
+
if (value.startsWith("#") || value.startsWith("/"))
|
|
17
|
+
return value;
|
|
18
|
+
try {
|
|
19
|
+
const parsed = new URL(value);
|
|
20
|
+
return SAFE_PROTOCOLS.has(parsed.protocol) ? value : "";
|
|
21
|
+
}
|
|
22
|
+
catch {
|
|
23
|
+
return "";
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
function headingClass(size) {
|
|
27
|
+
return cn("font-heading font-semibold tracking-tight text-foreground", size);
|
|
28
|
+
}
|
|
29
|
+
const markdownComponents = {
|
|
30
|
+
h1: ({ children }) => _jsx("h1", { className: headingClass("text-xl"), children: children }),
|
|
31
|
+
h2: ({ children }) => _jsx("h2", { className: headingClass("text-lg"), children: children }),
|
|
32
|
+
h3: ({ children }) => (_jsx("h3", { className: headingClass("text-base"), children: children })),
|
|
33
|
+
p: ({ children }) => _jsx("p", { className: "text-sm leading-relaxed", children: children }),
|
|
34
|
+
ul: ({ children }) => (_jsx("ul", { className: "list-disc space-y-1 pl-5 text-sm", children: children })),
|
|
35
|
+
ol: ({ children }) => (_jsx("ol", { className: "list-decimal space-y-1 pl-5 text-sm", children: children })),
|
|
36
|
+
li: ({ children }) => _jsx("li", { className: "leading-relaxed", children: children }),
|
|
37
|
+
blockquote: ({ children }) => (_jsx("blockquote", { className: "border-l-2 border-input pl-3 text-sm text-muted-foreground", children: children })),
|
|
38
|
+
a: ({ href, children }) => href ? (_jsx("a", { href: href, className: "text-selected-foreground underline underline-offset-2", ...(href.startsWith("http")
|
|
39
|
+
? { target: "_blank", rel: "noreferrer noopener" }
|
|
40
|
+
: {}), children: children })) : (_jsx("span", { children: children })),
|
|
41
|
+
code: ({ className, children }) => {
|
|
42
|
+
const fenced = /language-/.test(className ?? "");
|
|
43
|
+
return (_jsx("code", { className: cn("font-mono text-xs", fenced ? "bg-transparent" : "bg-muted px-1 py-0.5"), children: children }));
|
|
44
|
+
},
|
|
45
|
+
pre: ({ children }) => (_jsx("pre", { className: "overflow-x-auto bg-muted p-3 text-xs", children: children })),
|
|
46
|
+
hr: () => _jsx("hr", { className: "border-border" }),
|
|
47
|
+
table: ({ children }) => (_jsx("div", { className: "overflow-x-auto", children: _jsx("table", { className: "w-full text-left text-sm", children: children }) })),
|
|
48
|
+
th: ({ children }) => (_jsx("th", { className: "border-b border-input py-1.5 pr-3 font-medium", children: children })),
|
|
49
|
+
td: ({ children }) => (_jsx("td", { className: "border-b border-border py-1.5 pr-3", children: children })),
|
|
50
|
+
img: ({ src, alt }) => (_jsx("img", { src: src, alt: alt ?? "", className: "max-w-full" })),
|
|
51
|
+
};
|
|
52
|
+
/**
|
|
53
|
+
* GitHub-flavoured Markdown, token-styled and sanitized. No Next, no raw
|
|
54
|
+
* HTML, no typography plugin. `react-markdown` and `remark-gfm` stay in this
|
|
55
|
+
* module so other subpaths do not pay for them.
|
|
56
|
+
*/
|
|
57
|
+
function Markdown({ content, className }) {
|
|
58
|
+
return (_jsx("article", { "data-slot": "markdown", className: cn("flex flex-col gap-3 text-foreground", className), children: _jsx(ReactMarkdown, { remarkPlugins: REMARK_PLUGINS, urlTransform: safeMarkdownUrl, components: markdownComponents, children: content }) }));
|
|
59
|
+
}
|
|
60
|
+
/** Alias used by apps that imported MarkdownContent. */
|
|
61
|
+
const MarkdownContent = Markdown;
|
|
62
|
+
export { Markdown, MarkdownContent };
|
package/dist/meter.d.ts
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export type MeterMarker = {
|
|
2
|
+
/** Same scale as `value` / `min` / `max` (not a 0–1 fraction). */
|
|
3
|
+
position: number;
|
|
4
|
+
className?: string;
|
|
5
|
+
};
|
|
6
|
+
export type MeterProps = {
|
|
7
|
+
value: number;
|
|
8
|
+
min?: number;
|
|
9
|
+
max: number;
|
|
10
|
+
/** App-owned fill (status, pace). Defaults to primary. */
|
|
11
|
+
fillClassName?: string;
|
|
12
|
+
markers?: MeterMarker[];
|
|
13
|
+
"aria-label": string;
|
|
14
|
+
className?: string;
|
|
15
|
+
};
|
|
16
|
+
/**
|
|
17
|
+
* Track, fill, and optional ticks for a current value against a scale.
|
|
18
|
+
* Use `Progress` for a plain 0–100 job bar. The app chooses fill color and
|
|
19
|
+
* marker positions (80% of a cap, 1.0× typical, and so on).
|
|
20
|
+
*/
|
|
21
|
+
declare function Meter({ value, min, max, fillClassName, markers, "aria-label": ariaLabel, className, }: MeterProps): import("react").JSX.Element;
|
|
22
|
+
export { Meter };
|
package/dist/meter.js
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { cn } from "./utils.js";
|
|
3
|
+
/**
|
|
4
|
+
* Track, fill, and optional ticks for a current value against a scale.
|
|
5
|
+
* Use `Progress` for a plain 0–100 job bar. The app chooses fill color and
|
|
6
|
+
* marker positions (80% of a cap, 1.0× typical, and so on).
|
|
7
|
+
*/
|
|
8
|
+
function Meter({ value, min = 0, max, fillClassName = "bg-primary", markers, "aria-label": ariaLabel, className, }) {
|
|
9
|
+
const span = max - min;
|
|
10
|
+
const toPercent = (amount) => {
|
|
11
|
+
if (span === 0)
|
|
12
|
+
return 0;
|
|
13
|
+
return Math.min(100, Math.max(0, ((amount - min) / span) * 100));
|
|
14
|
+
};
|
|
15
|
+
return (_jsxs("div", { role: "meter", "data-slot": "meter", "aria-valuemin": min, "aria-valuemax": max, "aria-valuenow": Math.min(max, Math.max(min, value)), "aria-label": ariaLabel, className: cn("relative h-1.5 w-full overflow-hidden bg-muted", className), children: [_jsx("div", { "data-slot": "meter-fill", className: cn("h-full", fillClassName), style: { width: `${toPercent(value)}%` } }), markers?.map((marker) => (_jsx("div", { "aria-hidden": true, "data-slot": "meter-marker", className: cn("absolute inset-y-0 w-px bg-foreground/40", marker.className), style: { left: `${toPercent(marker.position)}%` } }, marker.position)))] }));
|
|
16
|
+
}
|
|
17
|
+
export { Meter };
|
package/dist/pagination.js
CHANGED
|
@@ -21,7 +21,7 @@ function PaginationLink({ className, isActive, size = "icon", ...props }) {
|
|
|
21
21
|
return (_jsx("a", { "aria-current": isActive ? "page" : undefined, "data-slot": "pagination-link", "data-active": isActive, className: cn(buttonVariants({
|
|
22
22
|
variant: isActive ? "outline" : "ghost",
|
|
23
23
|
size: size === "icon" ? "icon-sm" : "sm",
|
|
24
|
-
}), "font-medium tracking-normal normal-case tabular-nums data-[active=true]:border-
|
|
24
|
+
}), "font-medium tracking-normal normal-case tabular-nums data-[active=true]:border-selected-foreground data-[active=true]:bg-selected data-[active=true]:text-selected-foreground", className), ...props }));
|
|
25
25
|
}
|
|
26
26
|
function PaginationPrevious({ className, children = "Previous", ...props }) {
|
|
27
27
|
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
|
@@ -15,6 +15,6 @@ function RadioGroup({ className, ...props }) {
|
|
|
15
15
|
* differently from square checkboxes.
|
|
16
16
|
*/
|
|
17
17
|
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 outline-none after:absolute after:-inset-x-3 after:-inset-y-2 focus-visible:border-ring focus-visible:ring-2 focus-visible:ring-ring
|
|
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 outline-none after:absolute after:-inset-x-3 after:-inset-y-2 focus-visible:border-ring focus-visible:ring-2 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-2 aria-invalid:ring-destructive/20 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
19
|
}
|
|
20
20
|
export { RadioGroup, RadioGroupItem };
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
/**
|
|
3
|
+
* Case-fold and strip combining marks so "nino" matches "Niño".
|
|
4
|
+
*/
|
|
5
|
+
export declare function foldSearchText(value: string): string;
|
|
6
|
+
/**
|
|
7
|
+
* Default SearchPicker filter: the folded label includes the folded query.
|
|
8
|
+
*/
|
|
9
|
+
export declare function defaultSearchFilter<T>(item: T, query: string, getLabel: (item: T) => string): boolean;
|
|
10
|
+
export type SearchPickerProps<T> = {
|
|
11
|
+
items: T[];
|
|
12
|
+
getValue: (item: T) => string;
|
|
13
|
+
getLabel: (item: T) => string;
|
|
14
|
+
filter?: (item: T, query: string, getLabel: (item: T) => string) => boolean;
|
|
15
|
+
value?: string;
|
|
16
|
+
onSelect: (item: T) => void;
|
|
17
|
+
label: string;
|
|
18
|
+
placeholder?: string;
|
|
19
|
+
emptyText?: string;
|
|
20
|
+
listLabel?: string;
|
|
21
|
+
disabled?: boolean;
|
|
22
|
+
className?: string;
|
|
23
|
+
};
|
|
24
|
+
/**
|
|
25
|
+
* Always-visible combobox: a labelled input and a listbox. Unlike Combobox,
|
|
26
|
+
* the list is not in a popover. Keyboard: arrows, Home/End, PageUp/PageDown,
|
|
27
|
+
* Enter, Escape.
|
|
28
|
+
*/
|
|
29
|
+
declare function SearchPicker<T>({ items, getValue, getLabel, filter, value, onSelect, label, placeholder, emptyText, listLabel, disabled, className, }: SearchPickerProps<T>): React.JSX.Element;
|
|
30
|
+
export { SearchPicker };
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import { Input } from "./input.js";
|
|
5
|
+
import { Label } from "./label.js";
|
|
6
|
+
import { cn } from "./utils.js";
|
|
7
|
+
const LISTBOX_PAGE_SIZE = 10;
|
|
8
|
+
/**
|
|
9
|
+
* Case-fold and strip combining marks so "nino" matches "Niño".
|
|
10
|
+
*/
|
|
11
|
+
export function foldSearchText(value) {
|
|
12
|
+
return value.normalize("NFD").replace(/\p{M}/gu, "").toLowerCase();
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Default SearchPicker filter: the folded label includes the folded query.
|
|
16
|
+
*/
|
|
17
|
+
export function defaultSearchFilter(item, query, getLabel) {
|
|
18
|
+
const needle = foldSearchText(query.trim());
|
|
19
|
+
if (!needle)
|
|
20
|
+
return true;
|
|
21
|
+
return foldSearchText(getLabel(item)).includes(needle);
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Always-visible combobox: a labelled input and a listbox. Unlike Combobox,
|
|
25
|
+
* the list is not in a popover. Keyboard: arrows, Home/End, PageUp/PageDown,
|
|
26
|
+
* Enter, Escape.
|
|
27
|
+
*/
|
|
28
|
+
function SearchPicker({ items, getValue, getLabel, filter = defaultSearchFilter, value, onSelect, label, placeholder = "Search", emptyText = "No results", listLabel, disabled, className, }) {
|
|
29
|
+
const [query, setQuery] = React.useState("");
|
|
30
|
+
const listId = React.useId();
|
|
31
|
+
const inputId = React.useId();
|
|
32
|
+
const filtered = React.useMemo(() => items.filter((item) => filter(item, query, getLabel)), [items, filter, query, getLabel]);
|
|
33
|
+
const selectedIndex = React.useMemo(() => {
|
|
34
|
+
const index = filtered.findIndex((item) => getValue(item) === value);
|
|
35
|
+
return index >= 0 ? index : 0;
|
|
36
|
+
}, [filtered, getValue, value]);
|
|
37
|
+
const [highlight, setHighlight] = React.useState(selectedIndex);
|
|
38
|
+
React.useEffect(() => {
|
|
39
|
+
setHighlight(query ? 0 : selectedIndex);
|
|
40
|
+
}, [query, selectedIndex]);
|
|
41
|
+
const optionId = (index) => `${listId}-option-${index}`;
|
|
42
|
+
const active = filtered[highlight];
|
|
43
|
+
const scrollHighlightedOption = React.useCallback((node) => {
|
|
44
|
+
node?.scrollIntoView({ block: "nearest" });
|
|
45
|
+
}, []);
|
|
46
|
+
const selectItem = (item) => {
|
|
47
|
+
onSelect(item);
|
|
48
|
+
setQuery("");
|
|
49
|
+
};
|
|
50
|
+
const onKeyDown = (event) => {
|
|
51
|
+
if (event.key === "ArrowDown") {
|
|
52
|
+
event.preventDefault();
|
|
53
|
+
setHighlight((index) => filtered.length === 0 ? 0 : Math.min(index + 1, filtered.length - 1));
|
|
54
|
+
}
|
|
55
|
+
if (event.key === "ArrowUp") {
|
|
56
|
+
event.preventDefault();
|
|
57
|
+
setHighlight((index) => Math.max(index - 1, 0));
|
|
58
|
+
}
|
|
59
|
+
if (event.key === "Home") {
|
|
60
|
+
event.preventDefault();
|
|
61
|
+
setHighlight(0);
|
|
62
|
+
}
|
|
63
|
+
if (event.key === "End") {
|
|
64
|
+
event.preventDefault();
|
|
65
|
+
setHighlight(filtered.length === 0 ? 0 : filtered.length - 1);
|
|
66
|
+
}
|
|
67
|
+
if (event.key === "PageDown") {
|
|
68
|
+
event.preventDefault();
|
|
69
|
+
setHighlight((index) => filtered.length === 0
|
|
70
|
+
? 0
|
|
71
|
+
: Math.min(index + LISTBOX_PAGE_SIZE, filtered.length - 1));
|
|
72
|
+
}
|
|
73
|
+
if (event.key === "PageUp") {
|
|
74
|
+
event.preventDefault();
|
|
75
|
+
setHighlight((index) => Math.max(index - LISTBOX_PAGE_SIZE, 0));
|
|
76
|
+
}
|
|
77
|
+
if (event.key === "Enter") {
|
|
78
|
+
event.preventDefault();
|
|
79
|
+
if (active)
|
|
80
|
+
selectItem(active);
|
|
81
|
+
}
|
|
82
|
+
if (event.key === "Escape") {
|
|
83
|
+
event.preventDefault();
|
|
84
|
+
if (query) {
|
|
85
|
+
setQuery("");
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
event.currentTarget.blur();
|
|
89
|
+
}
|
|
90
|
+
};
|
|
91
|
+
return (_jsxs("div", { "data-slot": "search-picker", className: cn("flex flex-col gap-1.5", className), children: [_jsx(Label, { htmlFor: inputId, children: label }), _jsx(Input, { id: inputId, role: "combobox", "aria-expanded": true, "aria-controls": listId, "aria-autocomplete": "list", "aria-activedescendant": active ? optionId(highlight) : undefined, value: query, disabled: disabled, placeholder: placeholder, onChange: (event) => setQuery(event.target.value), onKeyDown: onKeyDown }), _jsx("ul", { id: listId, role: "listbox", "aria-label": listLabel ?? label, className: "max-h-60 overflow-y-auto border border-input bg-background", children: filtered.length === 0 ? (_jsx("li", { className: "px-3 py-4 text-center text-sm text-muted-foreground", children: emptyText })) : (filtered.map((item, index) => {
|
|
92
|
+
const itemValue = getValue(item);
|
|
93
|
+
const isSelected = itemValue === value;
|
|
94
|
+
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 && "bg-selected text-selected-foreground", isActive && "shadow-[inset_2px_0_0_var(--color-ring)]", isActive &&
|
|
96
|
+
!isSelected &&
|
|
97
|
+
"bg-accent text-accent-foreground"), onMouseEnter: () => setHighlight(index), onClick: () => selectItem(item), children: _jsx("span", { className: "truncate", children: getLabel(item) }) }) }, itemValue));
|
|
98
|
+
})) })] }));
|
|
99
|
+
}
|
|
100
|
+
export { SearchPicker };
|
package/dist/select.js
CHANGED
|
@@ -14,7 +14,7 @@ function SelectValue({ ...props }) {
|
|
|
14
14
|
return _jsx(SelectPrimitive.Value, { "data-slot": "select-value", ...props });
|
|
15
15
|
}
|
|
16
16
|
function SelectTrigger({ className, size = "default", children, ...props }) {
|
|
17
|
-
return (_jsxs(SelectPrimitive.Trigger, { "data-slot": "select-trigger", "data-size": size, className: cn("flex w-full min-w-0 items-center justify-between gap-1.5 rounded-none border border-input bg-background px-3 py-2 text-base whitespace-nowrap shadow-xs transition-[color,box-shadow] outline-none focus-visible:border-ring focus-visible:ring-2 focus-visible:ring-ring
|
|
17
|
+
return (_jsxs(SelectPrimitive.Trigger, { "data-slot": "select-trigger", "data-size": size, className: cn("flex w-full min-w-0 items-center justify-between gap-1.5 rounded-none border border-input bg-background px-3 py-2 text-base whitespace-nowrap shadow-xs transition-[color,box-shadow] outline-none focus-visible:border-ring focus-visible:ring-2 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-2 aria-invalid:ring-destructive/20 data-placeholder:text-muted-foreground data-[size=default]:h-10 data-[size=sm]:h-9 md:text-sm *: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 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 [&_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
18
|
}
|
|
19
19
|
function SelectContent({ className, children, position = "popper", align = "center", sideOffset = 4, collisionPadding = 8, ...props }) {
|
|
20
20
|
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 +24,7 @@ function SelectLabel({ className, ...props }) {
|
|
|
24
24
|
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
25
|
}
|
|
26
26
|
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 focus:bg-accent focus:text-accent-foreground not-data-[variant=destructive]:focus:**:text-accent-foreground data-[state=checked]:bg-selected data-[state=checked]:text-selected-foreground data-disabled:pointer-events-none data-disabled:opacity-50 [&_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 })] }));
|
|
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 focus:bg-accent focus:text-accent-foreground focus:shadow-[inset_2px_0_0_var(--color-ring)] not-data-[variant=destructive]:focus:**:text-accent-foreground data-[state=checked]:bg-selected data-[state=checked]:text-selected-foreground data-disabled:pointer-events-none data-disabled:opacity-50 [&_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
28
|
}
|
|
29
29
|
function SelectSeparator({ className, ...props }) {
|
|
30
30
|
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.d.ts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export type SliderProps = {
|
|
2
|
+
value: number;
|
|
3
|
+
onValueChange: (value: number) => void;
|
|
4
|
+
onValueCommit?: (value: number) => void;
|
|
5
|
+
min?: number;
|
|
6
|
+
max?: number;
|
|
7
|
+
step?: number;
|
|
8
|
+
disabled?: boolean;
|
|
9
|
+
id?: string;
|
|
10
|
+
name?: string;
|
|
11
|
+
className?: string;
|
|
12
|
+
"aria-label"?: string;
|
|
13
|
+
"aria-labelledby"?: string;
|
|
14
|
+
};
|
|
15
|
+
/**
|
|
16
|
+
* Single-thumb range on Radix Slider. Square track and thumb, primary fill,
|
|
17
|
+
* solid focus ring, 44px thumb hit area. Pair with a visible `Label`.
|
|
18
|
+
*/
|
|
19
|
+
declare function Slider({ value, onValueChange, onValueCommit, min, max, step, disabled, id, name, className, "aria-label": ariaLabel, "aria-labelledby": ariaLabelledBy, }: SliderProps): import("react").JSX.Element;
|
|
20
|
+
export { Slider };
|
package/dist/slider.js
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { Slider as SliderPrimitive } from "radix-ui";
|
|
4
|
+
import { cn } from "./utils.js";
|
|
5
|
+
/**
|
|
6
|
+
* Single-thumb range on Radix Slider. Square track and thumb, primary fill,
|
|
7
|
+
* solid focus ring, 44px thumb hit area. Pair with a visible `Label`.
|
|
8
|
+
*/
|
|
9
|
+
function Slider({ value, onValueChange, onValueCommit, min = 0, max = 100, step = 1, disabled, id, name, className, "aria-label": ariaLabel, "aria-labelledby": ariaLabelledBy, }) {
|
|
10
|
+
return (_jsxs(SliderPrimitive.Root, { "data-slot": "slider", name: name, min: min, max: max, step: step, disabled: disabled, value: [value], onValueChange: (next) => {
|
|
11
|
+
const amount = next[0];
|
|
12
|
+
if (amount !== undefined)
|
|
13
|
+
onValueChange(amount);
|
|
14
|
+
}, onValueCommit: (next) => {
|
|
15
|
+
const amount = next[0];
|
|
16
|
+
if (amount !== undefined)
|
|
17
|
+
onValueCommit?.(amount);
|
|
18
|
+
}, className: cn("relative flex w-full touch-none items-center select-none data-disabled:opacity-50", 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: "relative block size-4 shrink-0 rounded-none border border-primary bg-background shadow-xs outline-none after:absolute after:top-1/2 after:left-1/2 after:size-11 after:-translate-x-1/2 after:-translate-y-1/2 focus-visible:border-ring focus-visible:ring-2 focus-visible:ring-ring" })] }));
|
|
19
|
+
}
|
|
20
|
+
export { Slider };
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type * as React from "react";
|
|
2
|
+
/**
|
|
3
|
+
* Uppercase muted label used on tiles and thin section chrome. Apps that
|
|
4
|
+
* only needed a heading class can import this instead of a wrapper.
|
|
5
|
+
*/
|
|
6
|
+
export declare const sectionLabel = "text-xs font-semibold tracking-widest text-muted-foreground uppercase";
|
|
7
|
+
/** Row of tiles: stacked on phones, divided columns from `sm`. */
|
|
8
|
+
export declare const statTileGrid = "grid ring-1 ring-foreground/5 sm:divide-x";
|
|
9
|
+
export type StatTileProps = {
|
|
10
|
+
label: string;
|
|
11
|
+
value: string;
|
|
12
|
+
sub?: React.ReactNode;
|
|
13
|
+
children?: React.ReactNode;
|
|
14
|
+
className?: string;
|
|
15
|
+
};
|
|
16
|
+
/**
|
|
17
|
+
* Label, big tabular figure, optional extra (a meter), muted sub line.
|
|
18
|
+
* Domain copy and status colors stay in the app.
|
|
19
|
+
*/
|
|
20
|
+
declare function StatTile({ label, value, sub, children, className }: StatTileProps): React.JSX.Element;
|
|
21
|
+
export { StatTile };
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { cn } from "./utils.js";
|
|
3
|
+
/**
|
|
4
|
+
* Uppercase muted label used on tiles and thin section chrome. Apps that
|
|
5
|
+
* only needed a heading class can import this instead of a wrapper.
|
|
6
|
+
*/
|
|
7
|
+
export const sectionLabel = "text-xs font-semibold tracking-widest text-muted-foreground uppercase";
|
|
8
|
+
/** Row of tiles: stacked on phones, divided columns from `sm`. */
|
|
9
|
+
export const statTileGrid = "grid ring-1 ring-foreground/5 sm:divide-x";
|
|
10
|
+
/**
|
|
11
|
+
* Label, big tabular figure, optional extra (a meter), muted sub line.
|
|
12
|
+
* Domain copy and status colors stay in the app.
|
|
13
|
+
*/
|
|
14
|
+
function StatTile({ label, value, sub, children, className }) {
|
|
15
|
+
return (_jsxs("div", { "data-slot": "stat-tile", className: cn("flex flex-col gap-1 px-3 py-2", className), children: [_jsx("span", { className: sectionLabel, children: label }), _jsx("span", { className: "text-lg font-semibold tracking-tight tabular-nums", children: value }), children, sub ? (_jsx("span", { className: "text-xs leading-snug text-muted-foreground", children: sub })) : null] }));
|
|
16
|
+
}
|
|
17
|
+
export { StatTile };
|
package/dist/switch.js
CHANGED
|
@@ -9,6 +9,6 @@ import { cn } from "./utils.js";
|
|
|
9
9
|
* primitives; checked uses the primary fill, as `Checkbox` does.
|
|
10
10
|
*/
|
|
11
11
|
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 outline-none focus-visible:border-ring focus-visible:ring-2 focus-visible:ring-ring
|
|
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 outline-none focus-visible:border-ring focus-visible:ring-2 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-2 aria-invalid:ring-destructive/20 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
13
|
}
|
|
14
14
|
export { Switch };
|
package/dist/table.js
CHANGED
|
@@ -32,7 +32,7 @@ function useHorizontalOverflow(ref) {
|
|
|
32
32
|
function Table({ className, ...props }) {
|
|
33
33
|
const containerRef = React.useRef(null);
|
|
34
34
|
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 outline-none focus-visible:ring-2 focus-visible:ring-ring
|
|
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 outline-none focus-visible:ring-2 focus-visible:ring-ring", children: _jsx("table", { "data-slot": "table", className: cn("w-full min-w-full caption-bottom text-sm", className), ...props }) }));
|
|
36
36
|
}
|
|
37
37
|
function TableHeader({ className, ...props }) {
|
|
38
38
|
return (_jsx("thead", { "data-slot": "table-header", className: cn("[&_tr]:border-b", className), ...props }));
|
|
@@ -44,7 +44,7 @@ function TableFooter({ className, ...props }) {
|
|
|
44
44
|
return (_jsx("tfoot", { "data-slot": "table-footer", className: cn("border-t bg-muted/50 font-medium [&>tr]:last:border-b-0", className), ...props }));
|
|
45
45
|
}
|
|
46
46
|
function TableRow({ className, ...props }) {
|
|
47
|
-
return (_jsx("tr", { "data-slot": "table-row", className: cn("border-b transition-colors hover:bg-muted/50 has-aria-expanded:bg-muted/50 data-[state=selected]:bg-selected", className), ...props }));
|
|
47
|
+
return (_jsx("tr", { "data-slot": "table-row", className: cn("border-b transition-colors hover:bg-muted/50 has-aria-expanded:bg-muted/50 data-[state=selected]:bg-selected data-[state=selected]:shadow-[inset_2px_0_0_var(--color-selected-foreground)]", className), ...props }));
|
|
48
48
|
}
|
|
49
49
|
function TableHead({ className, ...props }) {
|
|
50
50
|
return (_jsx("th", { "data-slot": "table-head", className: cn("h-10 px-2 text-left align-middle text-xs font-medium tracking-wider whitespace-nowrap text-muted-foreground uppercase sm:h-12 sm:px-3 [&:has([role=checkbox])]:pr-0", className), ...props }));
|
package/dist/textarea.js
CHANGED
|
@@ -2,6 +2,6 @@ import { jsx as _jsx } from "react/jsx-runtime";
|
|
|
2
2
|
import * as React from "react";
|
|
3
3
|
import { cn } from "./utils.js";
|
|
4
4
|
function Textarea({ className, ...props }) {
|
|
5
|
-
return (_jsx("textarea", { "data-slot": "textarea", className: cn("flex field-sizing-content min-h-16 w-full resize-none rounded-none border border-input bg-background px-3 py-2 text-base shadow-xs transition-[color,box-shadow] outline-none placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-2 focus-visible:ring-ring
|
|
5
|
+
return (_jsx("textarea", { "data-slot": "textarea", className: cn("flex field-sizing-content min-h-16 w-full resize-none rounded-none border border-input bg-background px-3 py-2 text-base shadow-xs transition-[color,box-shadow] outline-none placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-2 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-2 aria-invalid:ring-destructive/20 md:text-sm dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40", className), ...props }));
|
|
6
6
|
}
|
|
7
7
|
export { Textarea };
|
package/dist/toggle-group.js
CHANGED
|
@@ -16,9 +16,9 @@ function ToggleGroup({ className, variant, size, spacing = 2, orientation = "hor
|
|
|
16
16
|
}
|
|
17
17
|
function ToggleGroupItem({ className, children, variant = "default", size = "default", ...props }) {
|
|
18
18
|
const context = React.useContext(ToggleGroupContext);
|
|
19
|
-
return (_jsx(ToggleGroupPrimitive.Item, { "data-slot": "toggle-group-item", "data-variant": context.variant || variant, "data-size": context.size || size, "data-spacing": context.spacing, className: cn(
|
|
19
|
+
return (_jsx(ToggleGroupPrimitive.Item, { "data-slot": "toggle-group-item", "data-variant": context.variant || variant, "data-size": context.size || size, "data-spacing": context.spacing, className: cn(toggleVariants({
|
|
20
20
|
variant: context.variant || variant,
|
|
21
21
|
size: context.size || size,
|
|
22
|
-
}), className), ...props, children: children }));
|
|
22
|
+
}), "shrink-0 group-data-[spacing=0]/toggle-group:rounded-none group-data-[spacing=0]/toggle-group:shadow-none focus:z-10 focus-visible:z-10 group-data-horizontal/toggle-group:data-[spacing=0]:first:rounded-none group-data-vertical/toggle-group:data-[spacing=0]:first:rounded-none group-data-horizontal/toggle-group:data-[spacing=0]:last:rounded-none group-data-vertical/toggle-group:data-[spacing=0]:last:rounded-none group-data-horizontal/toggle-group:data-[spacing=0]:data-[variant=outline]:border-l-0 group-data-vertical/toggle-group:data-[spacing=0]:data-[variant=outline]:border-t-0 group-data-horizontal/toggle-group:data-[spacing=0]:data-[variant=outline]:first:border-l group-data-vertical/toggle-group:data-[spacing=0]:data-[variant=outline]:first:border-t", className), ...props, children: children }));
|
|
23
23
|
}
|
|
24
24
|
export { ToggleGroup, ToggleGroupItem };
|
package/dist/toggle.d.ts
CHANGED
|
@@ -1,9 +1,19 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
2
|
import { type VariantProps } from "class-variance-authority";
|
|
3
3
|
import { Toggle as TogglePrimitive } from "radix-ui";
|
|
4
|
+
/**
|
|
5
|
+
* Selected chrome shared by standalone Toggle and ToggleGroupItem.
|
|
6
|
+
*
|
|
7
|
+
* Radix Toggle sets `data-state=on` and `aria-pressed`. ToggleGroup items
|
|
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.
|
|
12
|
+
*/
|
|
13
|
+
declare const selectedToggleChrome = "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";
|
|
4
14
|
declare const toggleVariants: (props?: ({
|
|
5
15
|
variant?: "default" | "outline" | null | undefined;
|
|
6
16
|
size?: "default" | "sm" | "lg" | null | undefined;
|
|
7
17
|
} & import("class-variance-authority/types").ClassProp) | undefined) => string;
|
|
8
18
|
declare function Toggle({ className, variant, size, ...props }: React.ComponentProps<typeof TogglePrimitive.Root> & VariantProps<typeof toggleVariants>): React.JSX.Element;
|
|
9
|
-
export { Toggle, toggleVariants };
|
|
19
|
+
export { Toggle, toggleVariants, selectedToggleChrome };
|
package/dist/toggle.js
CHANGED
|
@@ -4,10 +4,20 @@ import * as React from "react";
|
|
|
4
4
|
import { cva } from "class-variance-authority";
|
|
5
5
|
import { Toggle as TogglePrimitive } from "radix-ui";
|
|
6
6
|
import { cn } from "./utils.js";
|
|
7
|
-
|
|
7
|
+
/**
|
|
8
|
+
* Selected chrome shared by standalone Toggle and ToggleGroupItem.
|
|
9
|
+
*
|
|
10
|
+
* Radix Toggle sets `data-state=on` and `aria-pressed`. ToggleGroup items
|
|
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.
|
|
15
|
+
*/
|
|
16
|
+
const selectedToggleChrome = "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";
|
|
17
|
+
const toggleVariants = cva(`group/toggle inline-flex items-center justify-center gap-1.5 rounded-none text-xs font-semibold tracking-widest whitespace-nowrap uppercase transition-colors outline-none hover:border-selected-foreground hover:bg-selected hover:text-selected-foreground focus-visible:border-ring focus-visible:text-selected-foreground focus-visible:ring-2 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 ${selectedToggleChrome} [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-3.5`, {
|
|
8
18
|
variants: {
|
|
9
19
|
variant: {
|
|
10
|
-
default: "bg-transparent",
|
|
20
|
+
default: "border border-transparent bg-transparent",
|
|
11
21
|
outline: "border border-input bg-transparent",
|
|
12
22
|
},
|
|
13
23
|
size: {
|
|
@@ -24,4 +34,4 @@ const toggleVariants = cva("group/toggle inline-flex items-center justify-center
|
|
|
24
34
|
function Toggle({ className, variant = "default", size = "default", ...props }) {
|
|
25
35
|
return (_jsx(TogglePrimitive.Root, { "data-slot": "toggle", className: cn(toggleVariants({ variant, size, className })), ...props }));
|
|
26
36
|
}
|
|
27
|
-
export { Toggle, toggleVariants };
|
|
37
|
+
export { Toggle, toggleVariants, selectedToggleChrome };
|