atlas-react-kit 0.13.0 → 1.1.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.
Files changed (103) hide show
  1. package/COMPONENTS.md +144 -120
  2. package/DESIGN_RULES.md +53 -32
  3. package/README.md +12 -53
  4. package/dist/accordion.d.ts +1 -0
  5. package/dist/avatar.d.ts +1 -0
  6. package/dist/badge.d.ts +2 -3
  7. package/dist/breadcrumb.d.ts +2 -3
  8. package/dist/button.d.ts +2 -3
  9. package/dist/calendar.d.ts +2 -0
  10. package/dist/card.d.ts +3 -0
  11. package/dist/chart.d.ts +152 -0
  12. package/dist/chart.js +177 -0
  13. package/dist/color-legend.d.ts +8 -0
  14. package/dist/combobox.d.ts +13 -4
  15. package/dist/combobox.js +1 -8
  16. package/dist/date-picker.d.ts +26 -3
  17. package/dist/date-picker.js +37 -31
  18. package/dist/date-time-picker.d.ts +5 -2
  19. package/dist/date-time-picker.js +25 -38
  20. package/dist/dialog.d.ts +1 -0
  21. package/dist/dialog.js +4 -2
  22. package/dist/dropdown-menu.d.ts +9 -8
  23. package/dist/empty-panel.d.ts +8 -2
  24. package/dist/empty-panel.js +2 -2
  25. package/dist/error-boundary.d.ts +6 -1
  26. package/dist/field.d.ts +3 -0
  27. package/dist/field.js +1 -1
  28. package/dist/form/checkbox-field.d.ts +45 -0
  29. package/dist/form/checkbox-field.js +39 -0
  30. package/dist/form/combobox-field.d.ts +13 -0
  31. package/dist/form/combobox-field.js +14 -0
  32. package/dist/form/control-field.d.ts +6 -7
  33. package/dist/form/date-picker-field.d.ts +12 -21
  34. package/dist/form/date-picker-field.js +5 -4
  35. package/dist/form/date-time-picker-field.d.ts +7 -22
  36. package/dist/form/date-time-picker-field.js +4 -3
  37. package/dist/form/feedback-field.d.ts +2 -0
  38. package/dist/form/field-error.d.ts +35 -3
  39. package/dist/form/field-shell.d.ts +35 -6
  40. package/dist/form/field-shell.js +1 -1
  41. package/dist/form/focus-first-invalid.d.ts +28 -0
  42. package/dist/form/focus-first-invalid.js +29 -0
  43. package/dist/form/markdown-field.d.ts +4 -16
  44. package/dist/form/select-field.d.ts +13 -7
  45. package/dist/form/select-field.js +3 -2
  46. package/dist/form/text-field.d.ts +29 -10
  47. package/dist/form/text-field.js +4 -0
  48. package/dist/form/toggle-group-field.d.ts +30 -0
  49. package/dist/form/toggle-group-field.js +19 -0
  50. package/dist/info-tip.d.ts +10 -1
  51. package/dist/internal/as-child.d.ts +8 -0
  52. package/dist/internal/as-child.js +1 -0
  53. package/dist/internal/form-control.d.ts +3 -2
  54. package/dist/internal/form-control.js +3 -2
  55. package/dist/internal/kanban-layout.d.ts +55 -0
  56. package/dist/internal/kanban-layout.js +80 -0
  57. package/dist/internal/kanban-plugins.d.ts +17 -0
  58. package/dist/internal/kanban-plugins.js +17 -0
  59. package/dist/internal/listbox-navigation.d.ts +0 -11
  60. package/dist/internal/listbox-navigation.js +2 -2
  61. package/dist/internal/listbox-option.d.ts +0 -15
  62. package/dist/internal/listbox-option.js +1 -1
  63. package/dist/internal/use-drag-scroll.d.ts +17 -0
  64. package/dist/internal/use-drag-scroll.js +98 -0
  65. package/dist/internal/use-media-query.d.ts +4 -2
  66. package/dist/internal/use-media-query.js +11 -15
  67. package/dist/kanban.d.ts +84 -0
  68. package/dist/kanban.js +197 -0
  69. package/dist/markdown.d.ts +2 -0
  70. package/dist/meter.d.ts +7 -0
  71. package/dist/nav-link.d.ts +15 -6
  72. package/dist/nav-link.js +14 -4
  73. package/dist/next/nav-link.d.ts +6 -3
  74. package/dist/next/nav-link.js +8 -6
  75. package/dist/next/period-stepper.d.ts +11 -0
  76. package/dist/next/period-stepper.js +16 -0
  77. package/dist/next/tabs-nav-link.d.ts +6 -0
  78. package/dist/next/tabs-nav-link.js +8 -3
  79. package/dist/page-header.d.ts +19 -0
  80. package/dist/page-header.js +11 -0
  81. package/dist/pagination.d.ts +2 -0
  82. package/dist/period-stepper.d.ts +32 -0
  83. package/dist/period-stepper.js +27 -0
  84. package/dist/picker-shell.d.ts +33 -18
  85. package/dist/picker-shell.js +12 -10
  86. package/dist/progress.d.ts +1 -0
  87. package/dist/search-picker.d.ts +12 -0
  88. package/dist/select.d.ts +1 -0
  89. package/dist/skip-link.d.ts +7 -0
  90. package/dist/skip-link.js +10 -0
  91. package/dist/slider.d.ts +11 -0
  92. package/dist/sortable-table-head.d.ts +2 -0
  93. package/dist/stat-tile.d.ts +5 -0
  94. package/dist/table-body-skeleton.d.ts +2 -0
  95. package/dist/table.d.ts +1 -0
  96. package/dist/tabs.d.ts +27 -12
  97. package/dist/tabs.js +23 -10
  98. package/dist/toast.d.ts +7 -1
  99. package/dist/toggle-group.d.ts +9 -2
  100. package/dist/toggle-group.js +2 -1
  101. package/package.json +72 -6
  102. package/skills/atlas-react-kit/SKILL.md +46 -40
  103. package/theme.css +13 -0
@@ -5,7 +5,7 @@ import { CalendarIcon } from "lucide-react";
5
5
  import { Button } from "./button.js";
6
6
  import { toDate, } from "./internal/date-like.js";
7
7
  import { PICKER_COLLISION_PADDING, pickerContentClassName, pickerTriggerClassName, pickerValueId, } from "./internal/form-control.js";
8
- import { preloadCalendar } from "./lazy-calendar.js";
8
+ import { LazyCalendar, preloadCalendar } from "./lazy-calendar.js";
9
9
  import { Popover, PopoverContent, PopoverTrigger } from "./popover.js";
10
10
  import { cn } from "./utils.js";
11
11
  /**
@@ -13,7 +13,7 @@ import { cn } from "./utils.js";
13
13
  * any `disabled` matchers, and they bound paging unless `startMonth` /
14
14
  * `endMonth` are set.
15
15
  */
16
- export function limitCalendarProps(calendarProps, minDate, maxDate) {
16
+ function limitCalendarProps(calendarProps, minDate, maxDate) {
17
17
  const min = toDate(minDate);
18
18
  const max = toDate(maxDate);
19
19
  if (!min && !max)
@@ -35,15 +35,17 @@ export function limitCalendarProps(calendarProps, minDate, maxDate) {
35
35
  endMonth: calendarProps?.endMonth ?? max,
36
36
  };
37
37
  }
38
- /** Form-control trigger plus the viewport-clamped calendar popover. */
39
- export function PickerShell({ id, disabled, className, contentClassName, "aria-invalid": ariaInvalid, "aria-labelledby": ariaLabelledBy, "aria-describedby": ariaDescribedBy, side, empty, label, dialogLabel, open, onOpenChange, onInteractOutside, children, }) {
40
- 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, "aria-labelledby": ariaLabelledBy, "aria-describedby": ariaDescribedBy, "data-empty": empty, className: cn(pickerTriggerClassName, className), onPointerEnter: preloadCalendar, onFocus: preloadCalendar, children: [_jsx(CalendarIcon, { "data-icon": "inline-start" }), _jsx("span", { "data-slot": "date-picker-value", id: id ? pickerValueId(id) : undefined, children: label })] }) }), _jsx(PopoverContent, { align: "start", side: side, collisionPadding: PICKER_COLLISION_PADDING, "aria-label": dialogLabel,
38
+ /**
39
+ * Form-control trigger plus the viewport-clamped popover holding the
40
+ * calendar, any extra rows, and an optional Clear row.
41
+ */
42
+ export function PickerShell({ id, disabled, className, contentClassName, "aria-invalid": ariaInvalid, "aria-labelledby": ariaLabelledBy, "aria-describedby": ariaDescribedBy, side, defaultMonth, minDate, maxDate, calendarProps, empty, label, dialogLabel, open, onOpenChange, calendar, onClear, clearDisabled = false, children, }) {
43
+ 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, "aria-labelledby": ariaLabelledBy, "aria-describedby": ariaDescribedBy, "data-empty": empty, className: cn(pickerTriggerClassName, className), onPointerEnter: preloadCalendar, onFocus: preloadCalendar, children: [_jsx(CalendarIcon, { "data-icon": "inline-start" }), _jsx("span", { "data-slot": "date-picker-value", className: "min-w-0 text-left whitespace-normal", id: id ? pickerValueId(id) : undefined, children: label })] }) }), _jsxs(PopoverContent, { align: "start", side: side, collisionPadding: PICKER_COLLISION_PADDING, "aria-label": dialogLabel,
41
44
  // The calendar's `autoFocus` puts focus on the selected day (or
42
45
  // today) once it loads, as the APG date picker dialog does; Radix
43
46
  // focusing the popover first would leave it on the container.
44
- onOpenAutoFocus: (event) => event.preventDefault(), onInteractOutside: onInteractOutside, className: cn("w-auto min-w-0", pickerContentClassName, contentClassName), children: children })] }));
45
- }
46
- /** Clear button row under the calendar. */
47
- export function PickerClearFooter({ disabled, onClear, }) {
48
- return (_jsx("div", { "data-slot": "date-picker-footer", className: "flex justify-end border-t p-2", children: _jsx(Button, { type: "button", variant: "ghost", size: "sm", disabled: disabled, onClick: onClear, children: "Clear" }) }));
47
+ onOpenAutoFocus: (event) => event.preventDefault(), className: cn("w-auto min-w-0", pickerContentClassName, contentClassName), children: [_jsx(LazyCalendar, { ...limitCalendarProps(calendarProps, minDate, maxDate), ...calendar, autoFocus: true, defaultMonth: defaultMonth }), children, onClear ? (_jsx("div", { "data-slot": "date-picker-footer", className: "flex justify-end border-t p-2", children: _jsx(Button, { type: "button", variant: "ghost", size: "sm", disabled: clearDisabled, onClick: () => {
48
+ onClear();
49
+ onOpenChange(false);
50
+ }, children: "Clear" }) })) : null] })] }));
49
51
  }
@@ -7,6 +7,7 @@ import { Progress as ProgressPrimitive } from "radix-ui";
7
7
  * label. `indicatorClassName` styles the fill (default `bg-primary`).
8
8
  */
9
9
  declare function Progress({ className, indicatorClassName, value, max, ...props }: React.ComponentProps<typeof ProgressPrimitive.Root> & {
10
+ /** Classes for the fill, e.g. a status color. Defaults to `bg-primary`. */
10
11
  indicatorClassName?: string;
11
12
  }): React.JSX.Element;
12
13
  export { Progress };
@@ -6,17 +6,29 @@ export { foldSearchText };
6
6
  */
7
7
  export declare function defaultSearchFilter<T>(item: T, query: string, getLabel: (item: T) => string): boolean;
8
8
  export type SearchPickerProps<T> = {
9
+ /** Every choice, in display order. */
9
10
  items: T[];
11
+ /** A unique, stable value for an item; matched against `value`. */
10
12
  getValue: (item: T) => string;
13
+ /** The item's visible text, also what the default filter searches. */
11
14
  getLabel: (item: T) => string;
15
+ /** Whether an item matches the query. Default `defaultSearchFilter` (label contains query, ignoring case and accents). */
12
16
  filter?: (item: T, query: string, getLabel: (item: T) => string) => boolean;
17
+ /** The selected item's value, marked in the list. */
13
18
  value?: string;
19
+ /** Called with the item picked by click or Enter; the query then clears. */
14
20
  onSelect: (item: T) => void;
21
+ /** Visible label for the search input. */
15
22
  label: string;
23
+ /** Placeholder for the search input. Default `"Search"`. */
16
24
  placeholder?: string;
25
+ /** Text shown when nothing matches the query. Default `"No results"`. */
17
26
  emptyText?: string;
27
+ /** Accessible name for the listbox. Defaults to `label`. */
18
28
  listLabel?: string;
29
+ /** Disables the input and every option. */
19
30
  disabled?: boolean;
31
+ /** Classes for the wrapper, e.g. its width. */
20
32
  className?: string;
21
33
  /** Classes for the listbox, e.g. its height. Defaults to `max-h-60`. */
22
34
  listClassName?: string;
package/dist/select.d.ts CHANGED
@@ -4,6 +4,7 @@ declare function Select({ ...props }: React.ComponentProps<typeof SelectPrimitiv
4
4
  declare function SelectGroup({ className, ...props }: React.ComponentProps<typeof SelectPrimitive.Group>): React.JSX.Element;
5
5
  declare function SelectValue({ ...props }: React.ComponentProps<typeof SelectPrimitive.Value>): React.JSX.Element;
6
6
  declare function SelectTrigger({ className, size, children, ...props }: React.ComponentProps<typeof SelectPrimitive.Trigger> & {
7
+ /** Trigger height: `default` (`h-10`) or `sm` (`h-9`). Default `default`. */
7
8
  size?: "sm" | "default";
8
9
  }): React.JSX.Element;
9
10
  declare function SelectContent({ className, children, position, align, sideOffset, collisionPadding, ...props }: React.ComponentProps<typeof SelectPrimitive.Content>): React.JSX.Element;
@@ -0,0 +1,7 @@
1
+ import type * as React from "react";
2
+ /**
3
+ * First focusable link on the page: hidden until a keyboard user tabs to
4
+ * it, then it jumps past the navigation to `href` (the main landmark's id).
5
+ */
6
+ declare function SkipLink({ href, className, children, ...props }: React.ComponentProps<"a">): React.JSX.Element;
7
+ export { SkipLink };
@@ -0,0 +1,10 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { cn } from "./utils.js";
3
+ /**
4
+ * First focusable link on the page: hidden until a keyboard user tabs to
5
+ * it, then it jumps past the navigation to `href` (the main landmark's id).
6
+ */
7
+ function SkipLink({ href = "#main-content", className, children = "Skip to content", ...props }) {
8
+ return (_jsx("a", { "data-slot": "skip-link", href: href, className: cn("sr-only focus:not-sr-only focus:absolute focus:top-4 focus:left-4 focus:z-50 focus:bg-background focus:px-3 focus:py-2 focus:text-sm focus:ring-2 focus:ring-ring", className), ...props, children: children }));
9
+ }
10
+ export { SkipLink };
package/dist/slider.d.ts CHANGED
@@ -1,18 +1,29 @@
1
1
  import type * as React from "react";
2
2
  import { Slider as SliderPrimitive } from "radix-ui";
3
3
  export type SliderProps = Omit<React.ComponentProps<typeof SliderPrimitive.Root>, "value" | "defaultValue" | "onValueChange" | "onValueCommit" | "children"> & {
4
+ /** The current value; the slider is always controlled. */
4
5
  value: number;
6
+ /** Called with the new value on every move while dragging or on a key press. */
5
7
  onValueChange: (value: number) => void;
8
+ /** Called with the final value when a drag ends or a key press settles. */
6
9
  onValueCommit?: (value: number) => void;
10
+ /** The lowest value. Default 0. */
7
11
  min?: number;
12
+ /** The highest value. Default 100. */
8
13
  max?: number;
14
+ /** The amount each move snaps to. Default 1. */
9
15
  step?: number;
16
+ /** Dims the slider and ignores pointer and keyboard input. */
10
17
  disabled?: boolean;
11
18
  /** Goes on the thumb, so a `<Label htmlFor>` names the slider. */
12
19
  id?: string;
20
+ /** Form field name, so the value submits with a surrounding `<form>`. */
13
21
  name?: string;
22
+ /** Classes for the root, e.g. its width. */
14
23
  className?: string;
24
+ /** Names the slider when there is no `<Label htmlFor={id}>`. Goes on the thumb. */
15
25
  "aria-label"?: string;
26
+ /** Id of the element that names the slider. Goes on the thumb. */
16
27
  "aria-labelledby"?: string;
17
28
  };
18
29
  /**
@@ -4,7 +4,9 @@ export type SortDirection = "asc" | "desc";
4
4
  type SortableTableHeadProps = ComponentProps<typeof TableHead> & {
5
5
  /** Whether this column is the active sort key. */
6
6
  sorted?: boolean;
7
+ /** Sort order shown while `sorted`. Default `asc`. */
7
8
  direction?: SortDirection;
9
+ /** Called when the header button is pressed, e.g. to toggle the sort. */
8
10
  onClick?: () => void;
9
11
  /** Classes for the inner sort button (`data-slot="sortable-table-head-button"`). */
10
12
  buttonClassName?: string;
@@ -8,10 +8,15 @@ export { sectionLabel };
8
8
  /** Row of tiles: stacked on phones, divided columns from `sm`. */
9
9
  export declare const statTileGrid = "grid ring-1 ring-foreground/5 sm:divide-x";
10
10
  export type StatTileProps = Omit<React.ComponentProps<"div">, "children" | "title"> & {
11
+ /** Small uppercase label above the figure. */
11
12
  label: string;
13
+ /** The big figure, already formatted. */
12
14
  value: string;
15
+ /** Muted line under the figure; hidden when empty. */
13
16
  sub?: React.ReactNode;
17
+ /** Extra content between the figure and `sub`, e.g. a `Meter`. */
14
18
  children?: React.ReactNode;
19
+ /** Classes for the root `div`. */
15
20
  className?: string;
16
21
  };
17
22
  /**
@@ -1,5 +1,7 @@
1
1
  type Props = {
2
+ /** Placeholder cells per row; match the table's column count. */
2
3
  columns: number;
4
+ /** Placeholder rows. Default `5`. */
3
5
  rows?: number;
4
6
  /** Classes for the `tbody`. */
5
7
  className?: string;
package/dist/table.d.ts CHANGED
@@ -11,6 +11,7 @@ import * as React from "react";
11
11
  * cap (`max-h-96 overflow-y-auto`) or a border.
12
12
  */
13
13
  declare function Table({ className, containerClassName, ...props }: React.ComponentProps<"table"> & {
14
+ /** Classes for the scroller around the table, e.g. a height cap or border. */
14
15
  containerClassName?: string;
15
16
  }): React.JSX.Element;
16
17
  declare function TableHeader({ className, ...props }: React.ComponentProps<"thead">): React.JSX.Element;
package/dist/tabs.d.ts CHANGED
@@ -1,36 +1,51 @@
1
1
  import * as React from "react";
2
2
  import { type VariantProps } from "class-variance-authority";
3
3
  import { Tabs as TabsPrimitive } from "radix-ui";
4
+ import type { AsChildProps } from "./internal/as-child.js";
4
5
  import { tabsListVariants, tabsTriggerVariants } from "./internal/tabs-styles.js";
6
+ /** Props `TabsList` and `TabsNav` share for the scroller around their row. */
7
+ type TabsScrollerClassNameProps = {
8
+ /** Classes for the scroller around the tab row (`h-13` with `p-1` by default). */
9
+ scrollerClassName?: string;
10
+ };
5
11
  declare function Tabs({ className, orientation, ...props }: React.ComponentProps<typeof TabsPrimitive.Root>): React.JSX.Element;
6
12
  /**
7
13
  * The tab row inside its scroller. `className` styles the list (its `h-11`
8
14
  * height, fill, gap); `scrollerClassName` styles the scroller around it
9
15
  * (`h-13` with `p-1` for the active border and focus ring).
10
16
  */
11
- declare function TabsList({ className, scrollerClassName, variant, ...props }: React.ComponentProps<typeof TabsPrimitive.List> & Omit<VariantProps<typeof tabsListVariants>, "orientation"> & {
12
- scrollerClassName?: string;
13
- }): React.JSX.Element;
17
+ declare function TabsList({ className, scrollerClassName, variant, ...props }: React.ComponentProps<typeof TabsPrimitive.List> & Omit<VariantProps<typeof tabsListVariants>, "orientation"> & TabsScrollerClassNameProps): React.JSX.Element;
14
18
  declare function TabsTrigger({ className, ...props }: React.ComponentProps<typeof TabsPrimitive.Trigger>): React.JSX.Element;
15
19
  declare function TabsContent({ className, ...props }: React.ComponentProps<typeof TabsPrimitive.Content>): React.JSX.Element;
20
+ /**
21
+ * Tab-styled link for `TabsNav`. Pass `active` from the route: the active
22
+ * link gets the trigger's selected chrome (`data-state="active"`) and
23
+ * `aria-current="page"`. `asChild` wraps a router `Link` with these styles,
24
+ * like `NavLink`; Next apps can import `next/tabs-nav-link` instead.
25
+ */
26
+ declare function TabsNavLink({ asChild, active, className, ...props }: React.ComponentProps<"a"> & AsChildProps & {
27
+ /** This link is the current view: `aria-current="page"` and the active chrome. */
28
+ active: boolean;
29
+ }): React.JSX.Element;
16
30
  /**
17
31
  * Tab-styled links for a view switcher that changes the URL. Use this, not
18
32
  * `Tabs` + `TabsTrigger asChild`, when there are no `TabsContent` panels:
19
- * tabs would point assistive tech at panels that don't exist. Style each link
20
- * with `tabsTriggerVariants()` and mark the active one with
21
- * `data-state="active"` and `aria-current="page"` (the Next adapter
22
- * `TabsNavLink` does this for you).
33
+ * tabs would point assistive tech at panels that don't exist. Each link is
34
+ * a `TabsNavLink` with `active` from the app (`asChild` for a router link,
35
+ * or the Next adapter `next/tabs-nav-link`).
23
36
  *
24
37
  * Overflow is handled here: the row scrolls horizontally only, with padding
25
38
  * for active borders and focus rings counted in the scroller height (so the
26
- * list cannot overflow on y), an edge fade while it overflows, and the
27
- * active link scrolled into view on mount. Do not wrap it in
39
+ * list cannot overflow on y), an edge fade while it overflows, the
40
+ * active link scrolled into view on mount, and a mouse drag that scrolls
41
+ * the row (a plain click still follows the link). Do not wrap it in
28
42
  * `overflow-x-auto`. `scrollerClassName` styles that scroller and
29
43
  * `listClassName` the row of links inside it.
30
44
  */
31
- declare function TabsNav({ className, scrollerClassName, listClassName, children, ...props }: React.ComponentProps<"nav"> & {
45
+ declare function TabsNav({ className, scrollerClassName, listClassName, children, ...props }: React.ComponentProps<"nav"> & TabsScrollerClassNameProps & {
46
+ /** Required accessible name for the nav landmark. */
32
47
  "aria-label": string;
33
- scrollerClassName?: string;
48
+ /** Classes for the row of links inside the scroller. */
34
49
  listClassName?: string;
35
50
  }): React.JSX.Element;
36
- export { Tabs, TabsList, TabsTrigger, TabsContent, TabsNav, tabsListVariants, tabsTriggerVariants, };
51
+ export { Tabs, TabsList, TabsTrigger, TabsContent, TabsNav, TabsNavLink, tabsListVariants, tabsTriggerVariants, };
package/dist/tabs.js CHANGED
@@ -2,9 +2,10 @@
2
2
  import { jsx as _jsx } from "react/jsx-runtime";
3
3
  import * as React from "react";
4
4
  import {} from "class-variance-authority";
5
- import { Tabs as TabsPrimitive } from "radix-ui";
5
+ import { Slot, Tabs as TabsPrimitive } from "radix-ui";
6
6
  import { focusRing } from "./internal/styles.js";
7
7
  import { tabsListVariants, tabsScrollerVariants, tabsTriggerVariants, } from "./internal/tabs-styles.js";
8
+ import { useDragScroll } from "./internal/use-drag-scroll.js";
8
9
  import { useHorizontalOverflow } from "./internal/use-horizontal-overflow.js";
9
10
  import { cn } from "./utils.js";
10
11
  /**
@@ -22,12 +23,14 @@ function Tabs({ className, orientation = "horizontal", ...props }) {
22
23
  * active tab's border and focus ring grow a vertical scrollbar. Pad for
23
24
  * those strokes *inside* the box (`h-13` = `h-11` list + `p-1`), clip the
24
25
  * y axis as a backstop, and fade the edges while the row overflows —
25
- * same cue as `Table`.
26
+ * same cue as `Table`. While it overflows, a mouse can also drag the row
27
+ * sideways (`useDragScroll`); touch, wheel and keyboard scroll as before.
26
28
  */
27
29
  function TabsScroller({ variant = "default", orientation = "horizontal", className, children, }) {
28
30
  const ref = React.useRef(null);
29
31
  const revealed = React.useRef(null);
30
32
  const overflowing = useHorizontalOverflow(ref);
33
+ useDragScroll(ref, overflowing);
31
34
  // Runs after every render, not just mount: TabsNav usually sits in a
32
35
  // persistent layout, so a route change updates the active link in place.
33
36
  React.useLayoutEffect(() => {
@@ -61,24 +64,34 @@ function TabsTrigger({ className, ...props }) {
61
64
  return (_jsx(TabsPrimitive.Trigger, { "data-slot": "tabs-trigger", className: cn(tabsTriggerVariants(), className), ...props }));
62
65
  }
63
66
  function TabsContent({ className, ...props }) {
64
- return (_jsx(TabsPrimitive.Content, { "data-slot": "tabs-content", className: cn("flex-1 text-sm outline-none", className), ...props }));
67
+ return (_jsx(TabsPrimitive.Content, { "data-slot": "tabs-content", className: cn("flex-1 text-sm", focusRing, className), ...props }));
68
+ }
69
+ /**
70
+ * Tab-styled link for `TabsNav`. Pass `active` from the route: the active
71
+ * link gets the trigger's selected chrome (`data-state="active"`) and
72
+ * `aria-current="page"`. `asChild` wraps a router `Link` with these styles,
73
+ * like `NavLink`; Next apps can import `next/tabs-nav-link` instead.
74
+ */
75
+ function TabsNavLink({ asChild, active, className, ...props }) {
76
+ const Comp = asChild ? Slot.Root : "a";
77
+ return (_jsx(Comp, { "data-slot": "tabs-nav-link", "data-state": active ? "active" : "inactive", "aria-current": active ? "page" : undefined, className: cn(tabsTriggerVariants(), className), ...props }));
65
78
  }
66
79
  /**
67
80
  * Tab-styled links for a view switcher that changes the URL. Use this, not
68
81
  * `Tabs` + `TabsTrigger asChild`, when there are no `TabsContent` panels:
69
- * tabs would point assistive tech at panels that don't exist. Style each link
70
- * with `tabsTriggerVariants()` and mark the active one with
71
- * `data-state="active"` and `aria-current="page"` (the Next adapter
72
- * `TabsNavLink` does this for you).
82
+ * tabs would point assistive tech at panels that don't exist. Each link is
83
+ * a `TabsNavLink` with `active` from the app (`asChild` for a router link,
84
+ * or the Next adapter `next/tabs-nav-link`).
73
85
  *
74
86
  * Overflow is handled here: the row scrolls horizontally only, with padding
75
87
  * for active borders and focus rings counted in the scroller height (so the
76
- * list cannot overflow on y), an edge fade while it overflows, and the
77
- * active link scrolled into view on mount. Do not wrap it in
88
+ * list cannot overflow on y), an edge fade while it overflows, the
89
+ * active link scrolled into view on mount, and a mouse drag that scrolls
90
+ * the row (a plain click still follows the link). Do not wrap it in
78
91
  * `overflow-x-auto`. `scrollerClassName` styles that scroller and
79
92
  * `listClassName` the row of links inside it.
80
93
  */
81
94
  function TabsNav({ className, scrollerClassName, listClassName, children, ...props }) {
82
95
  return (_jsx("nav", { "data-slot": "tabs-nav", "data-orientation": "horizontal", className: cn("group/tabs min-w-0 max-w-full", className), ...props, children: _jsx(TabsScroller, { className: scrollerClassName, children: _jsx("div", { "data-slot": "tabs-list", "data-variant": "default", className: cn(tabsListVariants({ orientation: "horizontal" }), "bg-transparent", listClassName), children: children }) }) }));
83
96
  }
84
- export { Tabs, TabsList, TabsTrigger, TabsContent, TabsNav, tabsListVariants, tabsTriggerVariants, };
97
+ export { Tabs, TabsList, TabsTrigger, TabsContent, TabsNav, TabsNavLink, tabsListVariants, tabsTriggerVariants, };
package/dist/toast.d.ts CHANGED
@@ -3,7 +3,9 @@ import { type ReactNode } from "react";
3
3
  type ToastId = string | number;
4
4
  /** A button in a toast. Clicking it runs `onClick`, then closes the toast. */
5
5
  interface ToastAction {
6
+ /** The button's text. */
6
7
  label: string;
8
+ /** Runs when the button is clicked, before the toast closes. */
7
9
  onClick: () => void;
8
10
  }
9
11
  interface ToastOptions {
@@ -21,9 +23,13 @@ interface ToastOptions {
21
23
  /** Shows a toast and returns its id. */
22
24
  type ShowToast = (message: ReactNode, options?: ToastOptions) => ToastId;
23
25
  type Toast = ShowToast & {
26
+ /** A toast with a check icon and a success-toned title and border. */
24
27
  success: ShowToast;
28
+ /** A toast with an info icon. */
25
29
  info: ShowToast;
30
+ /** A toast with an alert icon and a warning-toned title and border. */
26
31
  warning: ShowToast;
32
+ /** A toast with an alert icon and a destructive-toned title and border. */
27
33
  error: ShowToast;
28
34
  /** A spinner toast that stays until it is replaced (same `id`) or dismissed. */
29
35
  loading: ShowToast;
@@ -36,7 +42,7 @@ interface ToasterProps {
36
42
  position?: ToastPosition;
37
43
  /** A labelled close button on every toast. Default `true`. */
38
44
  closeButton?: boolean;
39
- /** Keep the stack expanded instead of collapsed behind the newest toast. */
45
+ /** Keep the stack expanded instead of collapsed behind the newest toast. Default `false`. */
40
46
  expand?: boolean;
41
47
  /** How many toasts show at once. Default 3. */
42
48
  visibleToasts?: number;
@@ -2,9 +2,16 @@ import * as React from "react";
2
2
  import { type VariantProps } from "class-variance-authority";
3
3
  import { ToggleGroup as ToggleGroupPrimitive } from "radix-ui";
4
4
  import { toggleVariants } from "./toggle.js";
5
- declare function ToggleGroup({ className, variant, size, spacing, orientation, children, ...props }: React.ComponentProps<typeof ToggleGroupPrimitive.Root> & VariantProps<typeof toggleVariants> & {
5
+ /** Layout props a `ToggleGroup` shares with its items. */
6
+ type ToggleGroupLayoutProps = {
7
+ /**
8
+ * Gap between items, in spacing units. `0` joins them into one segmented
9
+ * control. Default `2`.
10
+ */
6
11
  spacing?: number;
12
+ /** Lay items out in a row or a column. Default `horizontal`. */
7
13
  orientation?: "horizontal" | "vertical";
8
- }): React.JSX.Element;
14
+ };
15
+ declare function ToggleGroup({ className, variant, size, spacing, orientation, children, ...props }: React.ComponentProps<typeof ToggleGroupPrimitive.Root> & VariantProps<typeof toggleVariants> & ToggleGroupLayoutProps): React.JSX.Element;
9
16
  declare function ToggleGroupItem({ className, children, variant, size, ...props }: React.ComponentProps<typeof ToggleGroupPrimitive.Item> & VariantProps<typeof toggleVariants>): React.JSX.Element;
10
17
  export { ToggleGroup, ToggleGroupItem };
@@ -12,7 +12,8 @@ const ToggleGroupContext = React.createContext({
12
12
  orientation: "horizontal",
13
13
  });
14
14
  function ToggleGroup({ className, variant, size, spacing = 2, orientation = "horizontal", children, ...props }) {
15
- return (_jsx(ToggleGroupPrimitive.Root, { "data-slot": "toggle-group", "data-variant": variant, "data-size": size, "data-spacing": spacing, "data-orientation": orientation, style: { "--gap": spacing }, className: cn("group/toggle-group flex w-fit flex-row items-center gap-[--spacing(var(--gap))] data-[spacing=0]:data-[variant=outline]:rounded-none data-vertical:flex-col data-vertical:items-stretch", className), ...props, children: _jsx(ToggleGroupContext.Provider, { value: { variant, size, spacing, orientation }, children: children }) }));
15
+ const context = React.useMemo(() => ({ variant, size, spacing, orientation }), [variant, size, spacing, orientation]);
16
+ return (_jsx(ToggleGroupPrimitive.Root, { "data-slot": "toggle-group", "data-variant": variant, "data-size": size, "data-spacing": spacing, "data-orientation": orientation, style: { "--gap": spacing }, className: cn("group/toggle-group flex w-fit max-w-full flex-row flex-wrap items-center gap-[--spacing(var(--gap))] data-[spacing=0]:data-[variant=outline]:rounded-none data-vertical:flex-col data-vertical:flex-nowrap data-vertical:items-stretch", className), ...props, children: _jsx(ToggleGroupContext.Provider, { value: context, children: children }) }));
16
17
  }
17
18
  function ToggleGroupItem({ className, children, variant = "default", size = "default", ...props }) {
18
19
  const context = React.useContext(ToggleGroupContext);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "atlas-react-kit",
3
- "version": "0.13.0",
3
+ "version": "1.1.0",
4
4
  "description": "Shared React components and Tailwind theme for Atlas",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -52,6 +52,10 @@
52
52
  "types": "./dist/card.d.ts",
53
53
  "default": "./dist/card.js"
54
54
  },
55
+ "./chart": {
56
+ "types": "./dist/chart.d.ts",
57
+ "default": "./dist/chart.js"
58
+ },
55
59
  "./checkbox": {
56
60
  "types": "./dist/checkbox.d.ts",
57
61
  "default": "./dist/checkbox.js"
@@ -100,6 +104,10 @@
100
104
  "types": "./dist/input.d.ts",
101
105
  "default": "./dist/input.js"
102
106
  },
107
+ "./kanban": {
108
+ "types": "./dist/kanban.d.ts",
109
+ "default": "./dist/kanban.js"
110
+ },
103
111
  "./label": {
104
112
  "types": "./dist/label.d.ts",
105
113
  "default": "./dist/label.js"
@@ -116,10 +124,18 @@
116
124
  "types": "./dist/nav-link.d.ts",
117
125
  "default": "./dist/nav-link.js"
118
126
  },
127
+ "./page-header": {
128
+ "types": "./dist/page-header.d.ts",
129
+ "default": "./dist/page-header.js"
130
+ },
119
131
  "./pagination": {
120
132
  "types": "./dist/pagination.d.ts",
121
133
  "default": "./dist/pagination.js"
122
134
  },
135
+ "./period-stepper": {
136
+ "types": "./dist/period-stepper.d.ts",
137
+ "default": "./dist/period-stepper.js"
138
+ },
123
139
  "./popover": {
124
140
  "types": "./dist/popover.d.ts",
125
141
  "default": "./dist/popover.js"
@@ -148,6 +164,10 @@
148
164
  "types": "./dist/skeleton.d.ts",
149
165
  "default": "./dist/skeleton.js"
150
166
  },
167
+ "./skip-link": {
168
+ "types": "./dist/skip-link.d.ts",
169
+ "default": "./dist/skip-link.js"
170
+ },
151
171
  "./slider": {
152
172
  "types": "./dist/slider.d.ts",
153
173
  "default": "./dist/slider.js"
@@ -204,6 +224,14 @@
204
224
  "types": "./dist/tooltip.d.ts",
205
225
  "default": "./dist/tooltip.js"
206
226
  },
227
+ "./form/checkbox-field": {
228
+ "types": "./dist/form/checkbox-field.d.ts",
229
+ "default": "./dist/form/checkbox-field.js"
230
+ },
231
+ "./form/combobox-field": {
232
+ "types": "./dist/form/combobox-field.d.ts",
233
+ "default": "./dist/form/combobox-field.js"
234
+ },
207
235
  "./form/control-field": {
208
236
  "types": "./dist/form/control-field.d.ts",
209
237
  "default": "./dist/form/control-field.js"
@@ -216,13 +244,17 @@
216
244
  "types": "./dist/form/date-time-picker-field.d.ts",
217
245
  "default": "./dist/form/date-time-picker-field.js"
218
246
  },
247
+ "./form/feedback-field": {
248
+ "types": "./dist/form/feedback-field.d.ts",
249
+ "default": "./dist/form/feedback-field.js"
250
+ },
219
251
  "./form/field-error": {
220
252
  "types": "./dist/form/field-error.d.ts",
221
253
  "default": "./dist/form/field-error.js"
222
254
  },
223
- "./form/feedback-field": {
224
- "types": "./dist/form/feedback-field.d.ts",
225
- "default": "./dist/form/feedback-field.js"
255
+ "./form/focus-first-invalid": {
256
+ "types": "./dist/form/focus-first-invalid.d.ts",
257
+ "default": "./dist/form/focus-first-invalid.js"
226
258
  },
227
259
  "./form/markdown-field": {
228
260
  "types": "./dist/form/markdown-field.d.ts",
@@ -236,10 +268,18 @@
236
268
  "types": "./dist/form/text-field.d.ts",
237
269
  "default": "./dist/form/text-field.js"
238
270
  },
271
+ "./form/toggle-group-field": {
272
+ "types": "./dist/form/toggle-group-field.d.ts",
273
+ "default": "./dist/form/toggle-group-field.js"
274
+ },
239
275
  "./next/nav-link": {
240
276
  "types": "./dist/next/nav-link.d.ts",
241
277
  "default": "./dist/next/nav-link.js"
242
278
  },
279
+ "./next/period-stepper": {
280
+ "types": "./dist/next/period-stepper.d.ts",
281
+ "default": "./dist/next/period-stepper.js"
282
+ },
243
283
  "./next/tabs-nav-link": {
244
284
  "types": "./dist/next/tabs-nav-link.d.ts",
245
285
  "default": "./dist/next/tabs-nav-link.js"
@@ -257,9 +297,17 @@
257
297
  "build": "tsc",
258
298
  "check": "npm run build && npm run docs:check && prettier --check .",
259
299
  "docs:check": "node scripts/check-doc-links.mjs",
300
+ "lint": "oxlint --type-aware --deny-warnings",
301
+ "knip": "knip",
302
+ "sast": "semgrep scan --config .semgrep.yml --metrics=off --disable-version-check --error --quiet src",
303
+ "secrets": "secretlint \"**/*\"",
304
+ "size": "size-limit",
305
+ "gate:static": "npm run lint && npm run knip && npm run sast && npm run secrets",
306
+ "gate": "npm run check && npm run gate:static && npm run size && vitest run --coverage",
260
307
  "test": "npm run build && vitest run",
261
308
  "test:coverage": "npm run build && vitest run --coverage",
262
- "prepack": "npm run build"
309
+ "prepack": "npm run build",
310
+ "prepare": "lefthook install"
263
311
  },
264
312
  "engines": {
265
313
  "node": ">=20.19.0"
@@ -268,6 +316,8 @@
268
316
  "access": "public"
269
317
  },
270
318
  "peerDependencies": {
319
+ "@dnd-kit/dom": "^0.4.0",
320
+ "@dnd-kit/react": "^0.4.0",
271
321
  "date-fns": "^4.4.0",
272
322
  "next": "^16.0.0",
273
323
  "next-themes": "^0.4.6",
@@ -276,11 +326,16 @@
276
326
  "react-dom": "^19.0.0",
277
327
  "react-markdown": "^10.1.0",
278
328
  "remark-gfm": "^4.0.1",
279
- "remark-parse": "^11.0.0",
280
329
  "sonner": "^2.0.7",
281
330
  "tailwindcss": "^4.0.0"
282
331
  },
283
332
  "peerDependenciesMeta": {
333
+ "@dnd-kit/dom": {
334
+ "optional": true
335
+ },
336
+ "@dnd-kit/react": {
337
+ "optional": true
338
+ },
284
339
  "date-fns": {
285
340
  "optional": true
286
341
  },
@@ -312,6 +367,10 @@
312
367
  "tw-animate-css": "^1.4.0"
313
368
  },
314
369
  "devDependencies": {
370
+ "@dnd-kit/dom": "^0.4.0",
371
+ "@dnd-kit/react": "^0.4.0",
372
+ "@secretlint/secretlint-rule-preset-recommend": "^13.0.7",
373
+ "@size-limit/file": "^14.2.0",
315
374
  "@testing-library/dom": "^10.4.2",
316
375
  "@testing-library/jest-dom": "^7.0.1",
317
376
  "@testing-library/react": "^16.3.3",
@@ -322,14 +381,21 @@
322
381
  "@vitest/coverage-v8": "5.0.5",
323
382
  "date-fns": "^4.4.0",
324
383
  "jsdom": "^29.1.1",
384
+ "knip": "^6.41.0",
385
+ "lefthook": "^2.2.2",
325
386
  "next": "16.4.2",
326
387
  "next-themes": "^0.4.6",
388
+ "oxlint": "^1.88.0",
389
+ "oxlint-tsgolint": "^7.0.2004",
327
390
  "prettier": "^3.8.1",
328
391
  "react": "^19.2.4",
329
392
  "react-day-picker": "^10.0.1",
330
393
  "react-dom": "^19.2.4",
331
394
  "react-markdown": "^10.1.0",
332
395
  "remark-gfm": "^4.0.1",
396
+ "remark-parse": "^11.0.0",
397
+ "secretlint": "^13.0.7",
398
+ "size-limit": "^14.2.0",
333
399
  "sonner": "^2.0.7",
334
400
  "tailwindcss": "^4.2.1",
335
401
  "typescript": "^7.0.2",