atlas-react-kit 0.3.0 → 0.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/COMPONENTS.md +44 -36
- package/DESIGN_RULES.md +17 -4
- package/README.md +3 -3
- package/dist/button.d.ts +7 -1
- package/dist/button.js +9 -3
- package/dist/card.d.ts +6 -3
- package/dist/card.js +10 -7
- package/dist/combobox.d.ts +1 -0
- package/dist/combobox.js +3 -1
- package/dist/date-picker.d.ts +12 -5
- package/dist/date-picker.js +18 -9
- package/dist/form/date-picker-field.d.ts +3 -1
- package/dist/form/date-picker-field.js +2 -2
- package/dist/internal/form-control.d.ts +9 -0
- package/dist/internal/form-control.js +9 -0
- package/dist/internal/use-media-query.d.ts +7 -0
- package/dist/internal/use-media-query.js +27 -0
- package/dist/select.js +1 -1
- package/dist/table.d.ts +5 -0
- package/dist/table.js +8 -3
- package/dist/toggle.js +2 -2
- package/package.json +1 -1
- package/theme.css +60 -0
package/COMPONENTS.md
CHANGED
|
@@ -5,39 +5,39 @@ underlying element or Radix props, including `className`, alongside their option
|
|
|
5
5
|
Form helpers and `TableBodySkeleton` accept only their documented props. There is
|
|
6
6
|
no root component barrel.
|
|
7
7
|
|
|
8
|
-
| Subpath | Main exports and options
|
|
9
|
-
| ---------------------------- |
|
|
10
|
-
| `button` | `Button`, `buttonVariants`; `variant`, `size`, `asChild
|
|
11
|
-
| `badge` | `Badge`, `badgeVariants`; `variant`, `tone`, `asChild`
|
|
12
|
-
| `calendar` | `Calendar`, `CalendarDayButton`; React Day Picker props and `buttonVariant`
|
|
13
|
-
| `card` | `Card`, `CardHeader`, `CardTitle`, `CardDescription`, `CardAction`, `CardContent`, `CardFooter`; `Card` supports `flush`
|
|
14
|
-
| `checkbox` | `Checkbox`
|
|
15
|
-
| `combobox` | `Combobox`; `options` as `{ value, label }[]`, `value`, `onValueChange`; optional placeholder, search, empty text; keyboard highlight scrolls into view
|
|
16
|
-
| `date-picker` | `DatePickerButton`, `toDate`, `formatLocalDate`, `DateRange`; `mode` `"single"` (default) or `"range"`; single `value` is `Date` or `yyyy-MM-dd`; range `value` is `{ from, to }`; optional `numberOfMonths` (range default 2) |
|
|
17
|
-
| `dialog` | `Dialog`, trigger, portal, overlay, content, close, header, footer, title, description; `DialogContent` supports `showCloseButton`
|
|
18
|
-
| `empty-panel` | `EmptyPanel`; required `children`; optional `size`, `muted`, `className`
|
|
19
|
-
| `error-boundary` | `ErrorBoundary`, `ErrorFallback`; optional `message`; retry resets, reload refreshes the page
|
|
20
|
-
| `field` | Field, label, legend, description, group, set, title, content, separator, error; `Field` supports `orientation`
|
|
21
|
-
| `input`, `textarea`, `label` | `Input`, `Textarea`, `Label`
|
|
22
|
-
| `popover` | `Popover`, trigger, anchor, content, header, title, description
|
|
23
|
-
| `select` | Select, trigger, value, content, group, label, item, separator, scroll buttons; trigger `size`
|
|
24
|
-
| `separator`, `skeleton` | `Separator`, `Skeleton`
|
|
25
|
-
| `sortable-table-head` | `SortableTableHead`; `sorted`, `direction` (`asc` \| `desc`), `onClick`; sets `aria-sort`
|
|
26
|
-
| `sonner` | `Toaster`; Sonner props, with `next-themes` theme defaults
|
|
27
|
-
| `table` | Table, header, body, footer, row, head, cell, caption
|
|
28
|
-
| `table-body-skeleton` | `TableBodySkeleton`; `columns`, optional `rows` (default 5)
|
|
29
|
-
| `tabs` | `Tabs`, `TabsList`, `TabsTrigger`, `TabsContent`, `TabsNav`, `tabsListVariants`; list `variant` is `default` or `line`
|
|
30
|
-
| `toggle` | `Toggle`, `toggleVariants`; `variant`, `size`
|
|
31
|
-
| `toggle-group` | `ToggleGroup`, `ToggleGroupItem`; `variant`, `size`
|
|
32
|
-
| `theme-provider` | `ThemeProvider`, `useTheme`; optional `storageKey` (default `atlas-theme`), `enableShortcut` for the `d` toggle
|
|
33
|
-
| `form/text-field` | `FormTextField`, type `TextFieldApi`; controlled field, label, multiline, description, input constraints
|
|
34
|
-
| `form/select-field` | `FormSelectField`; `value`, `onValueChange`, `label`, optional `meta` and placeholder; select item children
|
|
35
|
-
| `form/date-picker-field` | `FormDatePickerField`; `value`, `onValueChange`, `label`, optional `meta
|
|
36
|
-
| `form/field-error` | `FieldError`, `getFieldErrorMessage`; types `FieldErrorMessage`, `FieldMetaState`
|
|
37
|
-
| `form/feedback-field` | `FormFeedbackField`; nullable `message`
|
|
38
|
-
| `next/tabs-nav-link` | `TabsNavLink`; Next Link props and required `active` boolean; requires Next.js 16
|
|
39
|
-
| `utils` | `cn`, `onInputChange`
|
|
40
|
-
| `theme.css` | Tailwind 4 theme; installation in [README](./README.md)
|
|
8
|
+
| Subpath | Main exports and options |
|
|
9
|
+
| ---------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
10
|
+
| `button` | `Button`, `buttonVariants`, `quietButtonInteraction`; `variant`, `size`, `asChild`; outline/ghost hover is brand purple + selected wash, not muted grey |
|
|
11
|
+
| `badge` | `Badge`, `badgeVariants`; `variant`, `tone`, `asChild` |
|
|
12
|
+
| `calendar` | `Calendar`, `CalendarDayButton`; React Day Picker props and `buttonVariant` |
|
|
13
|
+
| `card` | `Card`, `CardHeader`, `CardTitle`, `CardDescription`, `CardAction`, `CardContent`, `CardFooter`; `Card` supports `flush`; root uses `min-h-min shrink-0` so flex columns do not collapse it, and `min-w-0` so nested tables scroll; `CardContent className="px-0"` overrides padding without `!` |
|
|
14
|
+
| `checkbox` | `Checkbox` |
|
|
15
|
+
| `combobox` | `Combobox`; `options` as `{ value, label }[]`, `value`, `onValueChange`; optional placeholder, search, empty text; trigger matches Input/SelectTrigger chrome; keyboard highlight scrolls into view |
|
|
16
|
+
| `date-picker` | `DatePickerButton`, `toDate`, `formatLocalDate`, `DateRange`; `mode` `"single"` (default) or `"range"`; single `value` is `Date` or `yyyy-MM-dd`; range `value` is `{ from, to }`; trigger matches Input chrome; optional `numberOfMonths` (range default 1 below `md`, 2 at `md+`) and `side` |
|
|
17
|
+
| `dialog` | `Dialog`, trigger, portal, overlay, content, close, header, footer, title, description; `DialogContent` supports `showCloseButton` |
|
|
18
|
+
| `empty-panel` | `EmptyPanel`; required `children`; optional `size`, `muted`, `className` |
|
|
19
|
+
| `error-boundary` | `ErrorBoundary`, `ErrorFallback`; optional `message`; retry resets, reload refreshes the page |
|
|
20
|
+
| `field` | Field, label, legend, description, group, set, title, content, separator, error; `Field` supports `orientation` |
|
|
21
|
+
| `input`, `textarea`, `label` | `Input`, `Textarea`, `Label` |
|
|
22
|
+
| `popover` | `Popover`, trigger, anchor, content, header, title, description |
|
|
23
|
+
| `select` | Select, trigger, value, content, group, label, item, separator, scroll buttons; trigger `size`; trigger text is `text-base md:text-sm` like Input |
|
|
24
|
+
| `separator`, `skeleton` | `Separator`, `Skeleton` |
|
|
25
|
+
| `sortable-table-head` | `SortableTableHead`; `sorted`, `direction` (`asc` \| `desc`), `onClick`; sets `aria-sort` |
|
|
26
|
+
| `sonner` | `Toaster`; Sonner props, with `next-themes` theme defaults |
|
|
27
|
+
| `table` | Table, header, body, footer, row, head, cell, caption; root scroller is `min-w-0 overflow-x-auto` with a fade cue; cells are `px-2` below `sm` |
|
|
28
|
+
| `table-body-skeleton` | `TableBodySkeleton`; `columns`, optional `rows` (default 5) |
|
|
29
|
+
| `tabs` | `Tabs`, `TabsList`, `TabsTrigger`, `TabsContent`, `TabsNav`, `tabsListVariants`; list `variant` is `default` or `line` |
|
|
30
|
+
| `toggle` | `Toggle`, `toggleVariants`; `variant`, `size` |
|
|
31
|
+
| `toggle-group` | `ToggleGroup`, `ToggleGroupItem`; `variant`, `size` |
|
|
32
|
+
| `theme-provider` | `ThemeProvider`, `useTheme`; optional `storageKey` (default `atlas-theme`), `enableShortcut` for the `d` toggle |
|
|
33
|
+
| `form/text-field` | `FormTextField`, type `TextFieldApi`; controlled field, label, multiline, description, input constraints |
|
|
34
|
+
| `form/select-field` | `FormSelectField`; `value`, `onValueChange`, `label`, optional `meta` and placeholder; select item children |
|
|
35
|
+
| `form/date-picker-field` | `FormDatePickerField`; `value`, `onValueChange`, `label`, optional `meta`, placeholder, and `className` (forwarded to the trigger) |
|
|
36
|
+
| `form/field-error` | `FieldError`, `getFieldErrorMessage`; types `FieldErrorMessage`, `FieldMetaState` |
|
|
37
|
+
| `form/feedback-field` | `FormFeedbackField`; nullable `message` |
|
|
38
|
+
| `next/tabs-nav-link` | `TabsNavLink`; Next Link props and required `active` boolean; requires Next.js 16 |
|
|
39
|
+
| `utils` | `cn`, `onInputChange` |
|
|
40
|
+
| `theme.css` | Tailwind 4 theme; installation in [README](./README.md) |
|
|
41
41
|
|
|
42
42
|
## Forms
|
|
43
43
|
|
|
@@ -64,9 +64,17 @@ accept the same error meta shape. `FormDatePickerField` takes a `Date` or
|
|
|
64
64
|
|
|
65
65
|
`DatePickerButton` is single-day by default. For a from/to range, set
|
|
66
66
|
`mode="range"`; `value` is `{ from, to }` (each side `Date` or `yyyy-MM-dd`)
|
|
67
|
-
and `onChange` receives `DateRange | undefined`. The
|
|
68
|
-
|
|
69
|
-
|
|
67
|
+
and `onChange` receives `DateRange | undefined`. The trigger uses the same
|
|
68
|
+
form-control chrome as `Input` and `SelectTrigger`. The range popover shows
|
|
69
|
+
one month below `md` and two months at `md+` unless `numberOfMonths` is set,
|
|
70
|
+
clamps to the viewport with collision padding and scroll, stays `w-auto` so
|
|
71
|
+
it does not collapse to the default popover width, and includes a Clear
|
|
72
|
+
action. Pass `side` to prefer a placement; Radix still flips on collision.
|
|
73
|
+
`FormDatePickerField` forwards `className` to that trigger.
|
|
74
|
+
|
|
75
|
+
Outline and ghost `Button` hover and focus use `border-primary`,
|
|
76
|
+
`text-primary`, and `bg-selected`. They do not use a muted grey fill, including
|
|
77
|
+
when the control sits on a selected row (for example "Clear selection").
|
|
70
78
|
|
|
71
79
|
```tsx
|
|
72
80
|
import { DatePickerButton, type DateRange } from "atlas-react-kit/date-picker"
|
package/DESIGN_RULES.md
CHANGED
|
@@ -11,8 +11,10 @@ Use semantic colors, radii, and fonts: `bg-primary`, `text-muted-foreground`,
|
|
|
11
11
|
light/dark color pairs; tokens already switch with the theme.
|
|
12
12
|
|
|
13
13
|
Selected or active navigation, tabs, toggles, chips, rows, and labels use
|
|
14
|
-
`bg-selected text-selected-foreground`.
|
|
15
|
-
|
|
14
|
+
`bg-selected text-selected-foreground`. Outline and ghost buttons hover and
|
|
15
|
+
focus with `border-primary text-primary` and the selected wash, not a muted
|
|
16
|
+
grey fill. Table row hover can stay `hover:bg-muted`. The shipped controls
|
|
17
|
+
apply this distinction.
|
|
16
18
|
|
|
17
19
|
Use `success`, `warning`, and `destructive` for good, caution, and bad states.
|
|
18
20
|
For tinted status chips, use `Badge variant="soft" tone="success"` or
|
|
@@ -28,8 +30,19 @@ other circles. Card and dialog titles already use uppercase, tracked
|
|
|
28
30
|
`font-heading` styling.
|
|
29
31
|
|
|
30
32
|
Use `EmptyPanel` for empty states, `TableBodySkeleton` for loading table bodies,
|
|
31
|
-
and `<Card flush>` for an edge-to-edge list or table. `
|
|
32
|
-
inside
|
|
33
|
+
and `<Card flush>` for an edge-to-edge list or table. `Table` scrolls wide
|
|
34
|
+
columns inside itself (`min-w-0 overflow-x-auto`) and tightens cell padding
|
|
35
|
+
below `sm`. `CardContent className="px-0"`
|
|
36
|
+
overrides body padding without `!px-0` when only the body should go flush. Cards
|
|
37
|
+
keep `min-h-min shrink-0` so they do not collapse inside a bounded flex
|
|
38
|
+
column, and `min-w-0` so nested tables scroll instead of widening the card.
|
|
39
|
+
`Dialog` already scrolls inside the viewport on small screens; avoid
|
|
40
|
+
additional height limits.
|
|
41
|
+
|
|
42
|
+
`Input`, `SelectTrigger`, `DatePickerButton`, and `Combobox` share form-control
|
|
43
|
+
chrome: 40px height, `px-3`, `text-base md:text-sm`, `border-input`, and
|
|
44
|
+
`shadow-xs`. Do not restyle those triggers as outline buttons in application
|
|
45
|
+
code.
|
|
33
46
|
|
|
34
47
|
Use `Tabs` with actual `TabsContent` panels. For URL view switches, use a labelled
|
|
35
48
|
`TabsNav` with links and `aria-current="page"` on the active link. The optional
|
package/README.md
CHANGED
|
@@ -4,7 +4,7 @@ Shared React components and a Tailwind theme with square corners, semantic
|
|
|
4
4
|
colors, and distinct selected states. React 19 and Tailwind 4 are required.
|
|
5
5
|
|
|
6
6
|
```sh
|
|
7
|
-
npm install --save-exact atlas-react-kit@0.
|
|
7
|
+
npm install --save-exact atlas-react-kit@0.4.1
|
|
8
8
|
```
|
|
9
9
|
|
|
10
10
|
Import components by subpath:
|
|
@@ -59,7 +59,7 @@ without compilation and processed by the consuming application's Tailwind build.
|
|
|
59
59
|
The optional Next.js 16 adapter requires Next only when imported. The package
|
|
60
60
|
does not bundle React.
|
|
61
61
|
|
|
62
|
-
0.
|
|
62
|
+
0.4.0 is already on npm. Bumping the version in `package.json` and
|
|
63
63
|
merging to `main` (path-filtered to package files) or running
|
|
64
64
|
`workflow_dispatch` publishes through npm OIDC trusted publishing after
|
|
65
65
|
check, test, and pack gates. Versions already on npm are skipped.
|
|
@@ -70,7 +70,7 @@ artifact with the package owner's npm login:
|
|
|
70
70
|
```sh
|
|
71
71
|
mkdir -p release-artifacts
|
|
72
72
|
npm pack --pack-destination release-artifacts
|
|
73
|
-
npm publish ./release-artifacts/atlas-react-kit-0.
|
|
73
|
+
npm publish ./release-artifacts/atlas-react-kit-0.4.1.tgz --access public
|
|
74
74
|
```
|
|
75
75
|
|
|
76
76
|
Pin an exact package version in consuming applications. Roll back an upgrade by
|
package/dist/button.d.ts
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
2
|
import { type VariantProps } from "class-variance-authority";
|
|
3
|
+
/**
|
|
4
|
+
* Outline and ghost hover/focus: brand purple border and text, optional
|
|
5
|
+
* selected wash, no muted grey fill. Used by quiet actions such as
|
|
6
|
+
* "Clear selection" sitting on a selected row.
|
|
7
|
+
*/
|
|
8
|
+
declare const quietButtonInteraction = "hover:border-primary hover:bg-selected hover:text-primary focus-visible:border-primary focus-visible:text-primary focus-visible:ring-primary/30 aria-expanded:border-primary aria-expanded:bg-selected aria-expanded:text-primary";
|
|
3
9
|
declare const buttonVariants: (props?: ({
|
|
4
10
|
variant?: "default" | "secondary" | "destructive" | "outline" | "ghost" | "link" | null | undefined;
|
|
5
11
|
size?: "default" | "xs" | "sm" | "lg" | "icon" | "icon-xs" | "icon-sm" | "icon-lg" | null | undefined;
|
|
@@ -7,4 +13,4 @@ declare const buttonVariants: (props?: ({
|
|
|
7
13
|
declare function Button({ className, variant, size, asChild, ...props }: React.ComponentProps<"button"> & VariantProps<typeof buttonVariants> & {
|
|
8
14
|
asChild?: boolean;
|
|
9
15
|
}): React.JSX.Element;
|
|
10
|
-
export { Button, buttonVariants };
|
|
16
|
+
export { Button, buttonVariants, quietButtonInteraction };
|
package/dist/button.js
CHANGED
|
@@ -3,13 +3,19 @@ import * as React from "react";
|
|
|
3
3
|
import { cva } from "class-variance-authority";
|
|
4
4
|
import { Slot } from "radix-ui";
|
|
5
5
|
import { cn } from "./utils.js";
|
|
6
|
+
/**
|
|
7
|
+
* Outline and ghost hover/focus: brand purple border and text, optional
|
|
8
|
+
* selected wash, no muted grey fill. Used by quiet actions such as
|
|
9
|
+
* "Clear selection" sitting on a selected row.
|
|
10
|
+
*/
|
|
11
|
+
const quietButtonInteraction = "hover:border-primary hover:bg-selected hover:text-primary focus-visible:border-primary focus-visible:text-primary focus-visible:ring-primary/30 aria-expanded:border-primary aria-expanded:bg-selected aria-expanded:text-primary";
|
|
6
12
|
const buttonVariants = cva("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 outline-none select-none focus-visible:border-ring focus-visible:ring-2 focus-visible:ring-ring/30 active:not-aria-[haspopup]:translate-y-px disabled:pointer-events-none disabled:opacity-50 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 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-3.5", {
|
|
7
13
|
variants: {
|
|
8
14
|
variant: {
|
|
9
15
|
default: "bg-primary text-primary-foreground hover:bg-primary/80",
|
|
10
|
-
outline:
|
|
16
|
+
outline: `border-border bg-transparent ${quietButtonInteraction}`,
|
|
11
17
|
secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80 aria-expanded:bg-secondary aria-expanded:text-secondary-foreground",
|
|
12
|
-
ghost:
|
|
18
|
+
ghost: quietButtonInteraction,
|
|
13
19
|
destructive: "bg-destructive/10 text-destructive hover:bg-destructive/20 focus-visible:border-destructive/40 focus-visible:ring-destructive/20 dark:bg-destructive/20 dark:hover:bg-destructive/30 dark:focus-visible:ring-destructive/40",
|
|
14
20
|
link: "text-primary underline underline-offset-4 hover:underline",
|
|
15
21
|
},
|
|
@@ -33,4 +39,4 @@ function Button({ className, variant = "default", size = "default", asChild = fa
|
|
|
33
39
|
const Comp = asChild ? Slot.Root : "button";
|
|
34
40
|
return (_jsx(Comp, { "data-slot": "button", "data-variant": variant, "data-size": size, className: cn(buttonVariants({ variant, size, className })), ...props }));
|
|
35
41
|
}
|
|
36
|
-
export { Button, buttonVariants };
|
|
42
|
+
export { Button, buttonVariants, quietButtonInteraction };
|
package/dist/card.d.ts
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
4
|
-
* `
|
|
5
|
-
*
|
|
3
|
+
* Size and flush padding live in `--card-p` (set here, read from `theme.css`
|
|
4
|
+
* `@layer components`) so a utility `px-0` / `py-0` on Card or its sections
|
|
5
|
+
* overrides it without `!`. `flush` sets the variable to 0 so lists and
|
|
6
|
+
* tables can run edge to edge. `min-h-min shrink-0` keeps the card from
|
|
7
|
+
* collapsing inside a bounded flex column. `min-w-0` lets nested tables
|
|
8
|
+
* scroll instead of widening the card. `overflow-hidden` still clips media.
|
|
6
9
|
*/
|
|
7
10
|
declare function Card({ className, size, flush, ...props }: React.ComponentProps<"div"> & {
|
|
8
11
|
size?: "default" | "sm";
|
package/dist/card.js
CHANGED
|
@@ -2,17 +2,20 @@ import { jsx as _jsx } from "react/jsx-runtime";
|
|
|
2
2
|
import * as React from "react";
|
|
3
3
|
import { cn } from "./utils.js";
|
|
4
4
|
/**
|
|
5
|
-
*
|
|
6
|
-
* `
|
|
7
|
-
*
|
|
5
|
+
* Size and flush padding live in `--card-p` (set here, read from `theme.css`
|
|
6
|
+
* `@layer components`) so a utility `px-0` / `py-0` on Card or its sections
|
|
7
|
+
* overrides it without `!`. `flush` sets the variable to 0 so lists and
|
|
8
|
+
* tables can run edge to edge. `min-h-min shrink-0` keeps the card from
|
|
9
|
+
* collapsing inside a bounded flex column. `min-w-0` lets nested tables
|
|
10
|
+
* scroll instead of widening the card. `overflow-hidden` still clips media.
|
|
8
11
|
*/
|
|
9
12
|
function Card({ className, size = "default", flush = false, ...props }) {
|
|
10
|
-
return (_jsx("div", { "data-slot": "card", "data-size": size, className: cn("group/card flex flex-col overflow-hidden bg-card
|
|
13
|
+
return (_jsx("div", { "data-slot": "card", "data-size": size, className: cn("group/card flex min-h-min min-w-0 shrink-0 flex-col overflow-hidden bg-card text-sm text-card-foreground shadow-sm ring-1 ring-foreground/5 has-[>img:first-child]:pt-0 *:[img:first-child]:rounded-none *:[img:last-child]:rounded-none", size === "sm"
|
|
11
14
|
? "gap-5 [--card-p:--spacing(5)]"
|
|
12
15
|
: "gap-8 [--card-p:--spacing(8)]", flush && "[--card-p:0px]", className), ...props }));
|
|
13
16
|
}
|
|
14
17
|
function CardHeader({ className, ...props }) {
|
|
15
|
-
return (_jsx("div", { "data-slot": "card-header", className: cn("group/card-header @container/card-header grid auto-rows-min items-start gap-1.5 rounded-none
|
|
18
|
+
return (_jsx("div", { "data-slot": "card-header", className: cn("group/card-header @container/card-header grid min-w-0 auto-rows-min items-start gap-1.5 rounded-none has-data-[slot=card-action]:grid-cols-[1fr_auto] has-data-[slot=card-description]:grid-rows-[auto_auto]", className), ...props }));
|
|
16
19
|
}
|
|
17
20
|
function CardTitle({ as: Component = "div", className, ...props }) {
|
|
18
21
|
return (_jsx(Component, { "data-slot": "card-title", className: cn("font-heading text-base font-semibold tracking-wider uppercase", className), ...props }));
|
|
@@ -24,9 +27,9 @@ function CardAction({ className, ...props }) {
|
|
|
24
27
|
return (_jsx("div", { "data-slot": "card-action", className: cn("col-start-2 row-span-2 row-start-1 self-start justify-self-end", className), ...props }));
|
|
25
28
|
}
|
|
26
29
|
function CardContent({ className, ...props }) {
|
|
27
|
-
return (_jsx("div", { "data-slot": "card-content", className: cn("
|
|
30
|
+
return (_jsx("div", { "data-slot": "card-content", className: cn("min-w-0", className), ...props }));
|
|
28
31
|
}
|
|
29
32
|
function CardFooter({ className, ...props }) {
|
|
30
|
-
return (_jsx("div", { "data-slot": "card-footer", className: cn("flex
|
|
33
|
+
return (_jsx("div", { "data-slot": "card-footer", className: cn("flex min-w-0 items-center", className), ...props }));
|
|
31
34
|
}
|
|
32
35
|
export { Card, CardHeader, CardFooter, CardTitle, CardAction, CardDescription, CardContent, };
|
package/dist/combobox.d.ts
CHANGED
|
@@ -17,6 +17,7 @@ type ComboboxProps = {
|
|
|
17
17
|
};
|
|
18
18
|
/**
|
|
19
19
|
* Searchable single-select built from Button, Popover, and Input.
|
|
20
|
+
* The trigger uses the same form-control chrome as Input and SelectTrigger.
|
|
20
21
|
* Options are a plain `{ value, label }` list; domain data stays in the app.
|
|
21
22
|
* Keyboard highlight (arrows, Home/End, PageUp/PageDown) scrolls the active
|
|
22
23
|
* option into view inside the overflow list.
|
package/dist/combobox.js
CHANGED
|
@@ -4,6 +4,7 @@ import * as React from "react";
|
|
|
4
4
|
import { CheckIcon, ChevronDownIcon } from "lucide-react";
|
|
5
5
|
import { Button } from "./button.js";
|
|
6
6
|
import { Input } from "./input.js";
|
|
7
|
+
import { formControlTriggerClassName } from "./internal/form-control.js";
|
|
7
8
|
import { Popover, PopoverContent, PopoverTrigger } from "./popover.js";
|
|
8
9
|
import { cn, onInputChange } from "./utils.js";
|
|
9
10
|
/**
|
|
@@ -14,6 +15,7 @@ import { cn, onInputChange } from "./utils.js";
|
|
|
14
15
|
const LISTBOX_PAGE_SIZE = 10;
|
|
15
16
|
/**
|
|
16
17
|
* Searchable single-select built from Button, Popover, and Input.
|
|
18
|
+
* The trigger uses the same form-control chrome as Input and SelectTrigger.
|
|
17
19
|
* Options are a plain `{ value, label }` list; domain data stays in the app.
|
|
18
20
|
* Keyboard highlight (arrows, Home/End, PageUp/PageDown) scrolls the active
|
|
19
21
|
* option into view inside the overflow list.
|
|
@@ -100,7 +102,7 @@ export function Combobox({ options, value, onValueChange, placeholder = "Select"
|
|
|
100
102
|
setHighlight((index) => Math.max(index - LISTBOX_PAGE_SIZE, 0));
|
|
101
103
|
}
|
|
102
104
|
};
|
|
103
|
-
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, disabled: disabled, className: cn("
|
|
105
|
+
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, 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
|
|
104
106
|
? `${listId}-option-${activeOption.value}`
|
|
105
107
|
: 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) => {
|
|
106
108
|
const isSelected = option.value === value;
|
package/dist/date-picker.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
2
|
import type { DateRange } from "react-day-picker";
|
|
3
|
+
import { PopoverContent } from "./popover.js";
|
|
3
4
|
export type { DateRange };
|
|
4
5
|
export type DateLike = Date | string | number | null | undefined;
|
|
5
6
|
/** From/to value for `DatePickerButton` in range mode; each side is `DateLike`. */
|
|
@@ -22,10 +23,13 @@ type DatePickerSharedProps = {
|
|
|
22
23
|
className?: string;
|
|
23
24
|
"aria-invalid"?: boolean;
|
|
24
25
|
/**
|
|
25
|
-
* Visible months in the popover calendar. Defaults to 1 in single mode
|
|
26
|
-
* and 2
|
|
26
|
+
* Visible months in the popover calendar. Defaults to 1 in single mode.
|
|
27
|
+
* In range mode, defaults to 1 below the `md` breakpoint and 2 at `md`
|
|
28
|
+
* and above so two stacked months do not open off-screen on mobile.
|
|
27
29
|
*/
|
|
28
30
|
numberOfMonths?: number;
|
|
31
|
+
/** Popover side. Defaults to `bottom`; Radix still flips on collision. */
|
|
32
|
+
side?: React.ComponentProps<typeof PopoverContent>["side"];
|
|
29
33
|
/** Month shown when the popover opens; falls back to the selected date. */
|
|
30
34
|
defaultMonth?: Date;
|
|
31
35
|
};
|
|
@@ -41,9 +45,12 @@ export type DatePickerRangeProps = DatePickerSharedProps & {
|
|
|
41
45
|
};
|
|
42
46
|
export type DatePickerButtonProps = DatePickerSingleProps | DatePickerRangeProps;
|
|
43
47
|
/**
|
|
44
|
-
* Calendar popover trigger
|
|
48
|
+
* Calendar popover trigger styled as a form control (same chrome as Input
|
|
49
|
+
* and SelectTrigger). `mode="single"` (default) accepts `Date` or
|
|
45
50
|
* `yyyy-MM-dd` and emits `Date | undefined`. `mode="range"` accepts
|
|
46
|
-
* `{ from, to }` and emits `DateRange | undefined`.
|
|
47
|
-
*
|
|
51
|
+
* `{ from, to }` and emits `DateRange | undefined`. Range mode shows one
|
|
52
|
+
* month below `md` and two at `md+` unless `numberOfMonths` is set. The
|
|
53
|
+
* popover clamps to the viewport (`side` is overridable). Square corners
|
|
54
|
+
* follow the shared shape rules.
|
|
48
55
|
*/
|
|
49
56
|
export declare function DatePickerButton(props: DatePickerButtonProps): React.JSX.Element;
|
package/dist/date-picker.js
CHANGED
|
@@ -5,6 +5,8 @@ import { format, isSameDay, parseISO } from "date-fns";
|
|
|
5
5
|
import { CalendarIcon } from "lucide-react";
|
|
6
6
|
import { Button } from "./button.js";
|
|
7
7
|
import { Calendar } from "./calendar.js";
|
|
8
|
+
import { formControlTriggerClassName } from "./internal/form-control.js";
|
|
9
|
+
import { MD_MEDIA_QUERY, useMediaQuery } from "./internal/use-media-query.js";
|
|
8
10
|
import { Popover, PopoverContent, PopoverTrigger } from "./popover.js";
|
|
9
11
|
import { cn } from "./utils.js";
|
|
10
12
|
/**
|
|
@@ -54,24 +56,28 @@ function formatRangeTriggerLabel(from, to, placeholder) {
|
|
|
54
56
|
return format(from, "PPP");
|
|
55
57
|
return `${format(from, "PPP")} – ${format(to, "PPP")}`;
|
|
56
58
|
}
|
|
59
|
+
/** Gutter used when the calendar popover collides with the viewport. */
|
|
60
|
+
const DATE_PICKER_COLLISION_PADDING = 16;
|
|
57
61
|
/** Shared trigger + auto-sized popover chrome for single and range modes. */
|
|
58
|
-
function DatePickerShell({ id, disabled, ariaInvalid, className, empty, label, open, onOpenChange, children, }) {
|
|
59
|
-
return (_jsxs(Popover, { open: open, onOpenChange: onOpenChange, children: [_jsx(PopoverTrigger, { asChild: true, children: _jsxs(Button, { id: id, type: "button", variant: "outline", disabled: disabled, "aria-invalid": ariaInvalid, "data-empty": empty, className: cn("
|
|
62
|
+
function DatePickerShell({ id, disabled, ariaInvalid, className, empty, label, open, onOpenChange, side, children, }) {
|
|
63
|
+
return (_jsxs(Popover, { open: open, onOpenChange: onOpenChange, children: [_jsx(PopoverTrigger, { asChild: true, children: _jsxs(Button, { id: id, type: "button", variant: "outline", disabled: disabled, "aria-invalid": ariaInvalid, "data-empty": empty, className: cn(formControlTriggerClassName, "justify-start data-[empty=true]:text-muted-foreground", className), children: [_jsx(CalendarIcon, { "data-icon": "inline-start" }), label] }) }), _jsx(PopoverContent, { align: "start", side: side, collisionPadding: DATE_PICKER_COLLISION_PADDING, className: "w-auto max-h-(--radix-popover-content-available-height) overflow-y-auto rounded-none p-0", children: children })] }));
|
|
60
64
|
}
|
|
61
|
-
function DatePickerSingleButton({ id, value, onChange, placeholder = "Pick a date", disabled, className, "aria-invalid": ariaInvalid, numberOfMonths, defaultMonth, }) {
|
|
65
|
+
function DatePickerSingleButton({ id, value, onChange, placeholder = "Pick a date", disabled, className, "aria-invalid": ariaInvalid, numberOfMonths, side, defaultMonth, }) {
|
|
62
66
|
const [open, setOpen] = React.useState(false);
|
|
63
67
|
const date = toDate(value);
|
|
64
|
-
return (_jsx(DatePickerShell, { id: id, disabled: disabled, ariaInvalid: ariaInvalid, className: className, empty: !date, label: date ? format(date, "PPP") : placeholder, open: open, onOpenChange: setOpen, children: _jsx(Calendar, { mode: "single", selected: date, defaultMonth: defaultMonth ?? date, numberOfMonths: numberOfMonths, onSelect: (next) => {
|
|
68
|
+
return (_jsx(DatePickerShell, { id: id, disabled: disabled, ariaInvalid: ariaInvalid, className: className, empty: !date, label: date ? format(date, "PPP") : placeholder, open: open, onOpenChange: setOpen, side: side, children: _jsx(Calendar, { mode: "single", selected: date, defaultMonth: defaultMonth ?? date, numberOfMonths: numberOfMonths, onSelect: (next) => {
|
|
65
69
|
onChange(next);
|
|
66
70
|
setOpen(false);
|
|
67
71
|
} }) }));
|
|
68
72
|
}
|
|
69
|
-
function DatePickerRangeButton({ id, value, onChange, placeholder = "Pick a date range", disabled, className, "aria-invalid": ariaInvalid, numberOfMonths
|
|
73
|
+
function DatePickerRangeButton({ id, value, onChange, placeholder = "Pick a date range", disabled, className, "aria-invalid": ariaInvalid, numberOfMonths, side, defaultMonth, }) {
|
|
70
74
|
const [open, setOpen] = React.useState(false);
|
|
75
|
+
const mdUp = useMediaQuery(MD_MEDIA_QUERY);
|
|
76
|
+
const months = numberOfMonths ?? (mdUp ? 2 : 1);
|
|
71
77
|
const from = toDate(value?.from);
|
|
72
78
|
const to = toDate(value?.to);
|
|
73
79
|
const selected = from || to ? { from, to } : undefined;
|
|
74
|
-
return (_jsxs(DatePickerShell, { id: id, disabled: disabled, ariaInvalid: ariaInvalid, className: className, empty: !from, label: formatRangeTriggerLabel(from, to, placeholder), open: open, onOpenChange: setOpen, children: [_jsx(Calendar, { mode: "range", selected: selected, defaultMonth: defaultMonth ?? from ?? to, numberOfMonths:
|
|
80
|
+
return (_jsxs(DatePickerShell, { id: id, disabled: disabled, ariaInvalid: ariaInvalid, className: className, empty: !from, label: formatRangeTriggerLabel(from, to, placeholder), open: open, onOpenChange: setOpen, side: side, children: [_jsx(Calendar, { mode: "range", selected: selected, defaultMonth: defaultMonth ?? from ?? to, numberOfMonths: months, onSelect: (next) => {
|
|
75
81
|
onChange(next);
|
|
76
82
|
// First click is a same-day range; keep the popover open so the
|
|
77
83
|
// user can set `to`. Close once the ends differ.
|
|
@@ -84,10 +90,13 @@ function DatePickerRangeButton({ id, value, onChange, placeholder = "Pick a date
|
|
|
84
90
|
}, children: "Clear" }) })] }));
|
|
85
91
|
}
|
|
86
92
|
/**
|
|
87
|
-
* Calendar popover trigger
|
|
93
|
+
* Calendar popover trigger styled as a form control (same chrome as Input
|
|
94
|
+
* and SelectTrigger). `mode="single"` (default) accepts `Date` or
|
|
88
95
|
* `yyyy-MM-dd` and emits `Date | undefined`. `mode="range"` accepts
|
|
89
|
-
* `{ from, to }` and emits `DateRange | undefined`.
|
|
90
|
-
*
|
|
96
|
+
* `{ from, to }` and emits `DateRange | undefined`. Range mode shows one
|
|
97
|
+
* month below `md` and two at `md+` unless `numberOfMonths` is set. The
|
|
98
|
+
* popover clamps to the viewport (`side` is overridable). Square corners
|
|
99
|
+
* follow the shared shape rules.
|
|
91
100
|
*/
|
|
92
101
|
export function DatePickerButton(props) {
|
|
93
102
|
if (props.mode === "range") {
|
|
@@ -7,7 +7,9 @@ type Props = {
|
|
|
7
7
|
meta?: FieldMetaState;
|
|
8
8
|
placeholder?: string;
|
|
9
9
|
disabled?: boolean;
|
|
10
|
+
/** Forwarded to the date picker trigger. */
|
|
11
|
+
className?: string;
|
|
10
12
|
};
|
|
11
13
|
/** Date picker with built-in field error text and invalid styling. */
|
|
12
|
-
export declare function FormDatePickerField({ value, onValueChange, label, meta, placeholder, disabled, }: Props): import("react").JSX.Element;
|
|
14
|
+
export declare function FormDatePickerField({ value, onValueChange, label, meta, placeholder, disabled, className, }: Props): import("react").JSX.Element;
|
|
13
15
|
export {};
|
|
@@ -5,8 +5,8 @@ import { DatePickerButton } from "../date-picker.js";
|
|
|
5
5
|
import { Label } from "../label.js";
|
|
6
6
|
import { FieldError, getFieldErrorMessage, } from "./field-error.js";
|
|
7
7
|
/** Date picker with built-in field error text and invalid styling. */
|
|
8
|
-
export function FormDatePickerField({ value, onValueChange, label, meta, placeholder, disabled, }) {
|
|
8
|
+
export function FormDatePickerField({ value, onValueChange, label, meta, placeholder, disabled, className, }) {
|
|
9
9
|
const errorText = meta ? getFieldErrorMessage(meta.errors[0]) : undefined;
|
|
10
10
|
const id = useId();
|
|
11
|
-
return (_jsxs("div", { className: "flex flex-col gap-1.5", children: [_jsx(Label, { htmlFor: id, children: label }), _jsx(DatePickerButton, { id: id, value: value, onChange: onValueChange, placeholder: placeholder, disabled: disabled, "aria-invalid": errorText ? true : undefined }), _jsx(FieldError, { text: errorText })] }));
|
|
11
|
+
return (_jsxs("div", { className: "flex flex-col gap-1.5", children: [_jsx(Label, { htmlFor: id, children: label }), _jsx(DatePickerButton, { id: id, value: value, onChange: onValueChange, placeholder: placeholder, disabled: disabled, className: className, "aria-invalid": errorText ? true : undefined }), _jsx(FieldError, { text: errorText })] }));
|
|
12
12
|
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Chrome that makes a Button-based picker trigger match Input / SelectTrigger:
|
|
3
|
+
* 40px height, px-3, body text (16px below md so iOS does not zoom), border-input,
|
|
4
|
+
* background, and shadow. Button outline defaults are text-xs semibold uppercase
|
|
5
|
+
* with px-6 and icon-indented pl-4/pr-4, which reads as a smaller, more indented
|
|
6
|
+
* control next to real form fields. Hover stays Input-like and cancels the
|
|
7
|
+
* outline Button's purple selected wash.
|
|
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/30 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,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Chrome that makes a Button-based picker trigger match Input / SelectTrigger:
|
|
3
|
+
* 40px height, px-3, body text (16px below md so iOS does not zoom), border-input,
|
|
4
|
+
* background, and shadow. Button outline defaults are text-xs semibold uppercase
|
|
5
|
+
* with px-6 and icon-indented pl-4/pr-4, which reads as a smaller, more indented
|
|
6
|
+
* control next to real form fields. Hover stays Input-like and cancels the
|
|
7
|
+
* outline Button's purple selected wash.
|
|
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/30 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,7 @@
|
|
|
1
|
+
/** Tailwind `md` breakpoint. Used for range calendars that stack on small screens. */
|
|
2
|
+
export declare const MD_MEDIA_QUERY = "(min-width: 768px)";
|
|
3
|
+
/**
|
|
4
|
+
* Subscribe to a CSS media query. `defaultMatches` covers SSR and environments
|
|
5
|
+
* without `matchMedia` (jsdom unless the test stubs it).
|
|
6
|
+
*/
|
|
7
|
+
export declare function useMediaQuery(query: string, defaultMatches?: boolean): boolean;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import * as React from "react";
|
|
3
|
+
/** Tailwind `md` breakpoint. Used for range calendars that stack on small screens. */
|
|
4
|
+
export const MD_MEDIA_QUERY = "(min-width: 768px)";
|
|
5
|
+
/**
|
|
6
|
+
* Subscribe to a CSS media query. `defaultMatches` covers SSR and environments
|
|
7
|
+
* without `matchMedia` (jsdom unless the test stubs it).
|
|
8
|
+
*/
|
|
9
|
+
export function useMediaQuery(query, defaultMatches = false) {
|
|
10
|
+
const [matches, setMatches] = React.useState(() => {
|
|
11
|
+
if (typeof window === "undefined" ||
|
|
12
|
+
typeof window.matchMedia !== "function") {
|
|
13
|
+
return defaultMatches;
|
|
14
|
+
}
|
|
15
|
+
return window.matchMedia(query).matches;
|
|
16
|
+
});
|
|
17
|
+
React.useEffect(() => {
|
|
18
|
+
if (typeof window.matchMedia !== "function")
|
|
19
|
+
return;
|
|
20
|
+
const media = window.matchMedia(query);
|
|
21
|
+
const onChange = () => setMatches(media.matches);
|
|
22
|
+
onChange();
|
|
23
|
+
media.addEventListener("change", onChange);
|
|
24
|
+
return () => media.removeEventListener("change", onChange);
|
|
25
|
+
}, [query]);
|
|
26
|
+
return matches;
|
|
27
|
+
}
|
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-
|
|
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/30 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" &&
|
package/dist/table.d.ts
CHANGED
|
@@ -1,4 +1,9 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
|
+
/**
|
|
3
|
+
* Data table. The root is a `min-w-0 overflow-x-auto` scroller so nowrap
|
|
4
|
+
* columns scroll inside the table instead of widening a card or panel.
|
|
5
|
+
* Cell padding is `px-2` below `sm` and the original `p-3` from `sm` up.
|
|
6
|
+
*/
|
|
2
7
|
declare function Table({ className, ...props }: React.ComponentProps<"table">): React.JSX.Element;
|
|
3
8
|
declare function TableHeader({ className, ...props }: React.ComponentProps<"thead">): React.JSX.Element;
|
|
4
9
|
declare function TableBody({ className, ...props }: React.ComponentProps<"tbody">): React.JSX.Element;
|
package/dist/table.js
CHANGED
|
@@ -2,8 +2,13 @@
|
|
|
2
2
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
3
|
import * as React from "react";
|
|
4
4
|
import { cn } from "./utils.js";
|
|
5
|
+
/**
|
|
6
|
+
* Data table. The root is a `min-w-0 overflow-x-auto` scroller so nowrap
|
|
7
|
+
* columns scroll inside the table instead of widening a card or panel.
|
|
8
|
+
* Cell padding is `px-2` below `sm` and the original `p-3` from `sm` up.
|
|
9
|
+
*/
|
|
5
10
|
function Table({ className, ...props }) {
|
|
6
|
-
return (_jsx("div", { "data-slot": "table-container", className: "relative w-full overflow-x-auto", children: _jsx("table", { "data-slot": "table", className: cn("w-full caption-bottom text-sm", className), ...props }) }));
|
|
11
|
+
return (_jsx("div", { "data-slot": "table-container", className: "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 }) }));
|
|
7
12
|
}
|
|
8
13
|
function TableHeader({ className, ...props }) {
|
|
9
14
|
return (_jsx("thead", { "data-slot": "table-header", className: cn("[&_tr]:border-b", className), ...props }));
|
|
@@ -18,10 +23,10 @@ function TableRow({ className, ...props }) {
|
|
|
18
23
|
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 }));
|
|
19
24
|
}
|
|
20
25
|
function TableHead({ className, ...props }) {
|
|
21
|
-
return (_jsx("th", { "data-slot": "table-head", className: cn("h-
|
|
26
|
+
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 }));
|
|
22
27
|
}
|
|
23
28
|
function TableCell({ className, ...props }) {
|
|
24
|
-
return (_jsx("td", { "data-slot": "table-cell", className: cn("
|
|
29
|
+
return (_jsx("td", { "data-slot": "table-cell", className: cn("px-2 py-2 align-middle whitespace-nowrap sm:p-3 [&:has([role=checkbox])]:pr-0", className), ...props }));
|
|
25
30
|
}
|
|
26
31
|
function TableCaption({ className, ...props }) {
|
|
27
32
|
return (_jsx("caption", { "data-slot": "table-caption", className: cn("mt-4 text-sm text-muted-foreground", className), ...props }));
|
package/dist/toggle.js
CHANGED
|
@@ -4,11 +4,11 @@ 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
|
-
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:bg-
|
|
7
|
+
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-primary hover:bg-selected hover:text-primary focus-visible:border-primary focus-visible:text-primary focus-visible:ring-[3px] focus-visible:ring-primary/30 disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 aria-pressed:bg-selected aria-pressed:text-selected-foreground dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-3.5", {
|
|
8
8
|
variants: {
|
|
9
9
|
variant: {
|
|
10
10
|
default: "bg-transparent",
|
|
11
|
-
outline: "border border-input bg-transparent
|
|
11
|
+
outline: "border border-input bg-transparent",
|
|
12
12
|
},
|
|
13
13
|
size: {
|
|
14
14
|
default: "h-10 min-w-10 px-6 has-data-[icon=inline-end]:pr-4 has-data-[icon=inline-start]:pl-4",
|
package/package.json
CHANGED
package/theme.css
CHANGED
|
@@ -182,3 +182,63 @@
|
|
|
182
182
|
@apply font-sans;
|
|
183
183
|
}
|
|
184
184
|
}
|
|
185
|
+
|
|
186
|
+
/*
|
|
187
|
+
* Card padding in this layer so utility `px-0` / `py-0` override `--card-p`
|
|
188
|
+
* without `!`. Size and flush still set the variable on `[data-slot=card]`.
|
|
189
|
+
*/
|
|
190
|
+
@layer components {
|
|
191
|
+
[data-slot="card"] {
|
|
192
|
+
padding-block: var(--card-p);
|
|
193
|
+
}
|
|
194
|
+
[data-slot="card-header"],
|
|
195
|
+
[data-slot="card-content"],
|
|
196
|
+
[data-slot="card-footer"] {
|
|
197
|
+
padding-inline: var(--card-p);
|
|
198
|
+
}
|
|
199
|
+
[data-slot="card-header"].border-b {
|
|
200
|
+
padding-bottom: var(--card-p);
|
|
201
|
+
}
|
|
202
|
+
[data-slot="card-footer"].border-t {
|
|
203
|
+
padding-top: var(--card-p);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/*
|
|
207
|
+
* Horizontal scroll shadows: edge fades stay pinned (`scroll`) while
|
|
208
|
+
* matching covers ride with the content (`local`), so a clipped column
|
|
209
|
+
* like LICENSE is visible as overflow instead of looking truncated.
|
|
210
|
+
*/
|
|
211
|
+
[data-slot="table-container"] {
|
|
212
|
+
--table-scroll-fade: var(--background);
|
|
213
|
+
background:
|
|
214
|
+
linear-gradient(to right, var(--table-scroll-fade) 40%, transparent),
|
|
215
|
+
linear-gradient(to left, var(--table-scroll-fade) 40%, transparent),
|
|
216
|
+
linear-gradient(
|
|
217
|
+
to right,
|
|
218
|
+
color-mix(in oklch, var(--foreground) 16%, transparent),
|
|
219
|
+
transparent 80%
|
|
220
|
+
),
|
|
221
|
+
linear-gradient(
|
|
222
|
+
to left,
|
|
223
|
+
color-mix(in oklch, var(--foreground) 16%, transparent),
|
|
224
|
+
transparent 80%
|
|
225
|
+
);
|
|
226
|
+
background-repeat: no-repeat;
|
|
227
|
+
background-size:
|
|
228
|
+
1.5rem 100%,
|
|
229
|
+
1.5rem 100%,
|
|
230
|
+
0.75rem 100%,
|
|
231
|
+
0.75rem 100%;
|
|
232
|
+
background-position:
|
|
233
|
+
left center,
|
|
234
|
+
right center,
|
|
235
|
+
left center,
|
|
236
|
+
right center;
|
|
237
|
+
background-attachment: local, local, scroll, scroll;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/* Covers match the card face so the fade does not flash the page background. */
|
|
241
|
+
[data-slot="card"] [data-slot="table-container"] {
|
|
242
|
+
--table-scroll-fade: var(--card);
|
|
243
|
+
}
|
|
244
|
+
}
|