atlas-react-kit 1.0.0 → 1.2.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 (61) hide show
  1. package/COMPONENTS.md +43 -39
  2. package/DESIGN_RULES.md +23 -32
  3. package/README.md +5 -47
  4. package/dist/accordion.js +2 -2
  5. package/dist/badge.js +5 -2
  6. package/dist/breadcrumb.js +2 -2
  7. package/dist/button.js +7 -4
  8. package/dist/combobox.js +4 -5
  9. package/dist/date-picker.d.ts +13 -2
  10. package/dist/date-picker.js +35 -22
  11. package/dist/date-time-picker.d.ts +1 -1
  12. package/dist/date-time-picker.js +24 -38
  13. package/dist/dropdown-menu.js +3 -3
  14. package/dist/form/date-picker-field.d.ts +6 -1
  15. package/dist/form/date-picker-field.js +3 -2
  16. package/dist/form/date-time-picker-field.d.ts +1 -1
  17. package/dist/form/date-time-picker-field.js +3 -2
  18. package/dist/form/field-error.d.ts +4 -3
  19. package/dist/form/field-shell.d.ts +6 -5
  20. package/dist/form/field-shell.js +1 -1
  21. package/dist/form/focus-first-invalid.d.ts +28 -0
  22. package/dist/form/focus-first-invalid.js +29 -0
  23. package/dist/form/select-field.d.ts +7 -1
  24. package/dist/form/select-field.js +3 -2
  25. package/dist/info-tip.js +7 -11
  26. package/dist/internal/listbox-navigation.d.ts +0 -16
  27. package/dist/internal/listbox-navigation.js +2 -9
  28. package/dist/internal/listbox-option.d.ts +0 -15
  29. package/dist/internal/listbox-option.js +1 -1
  30. package/dist/internal/overlay-styles.d.ts +0 -2
  31. package/dist/internal/overlay-styles.js +0 -2
  32. package/dist/internal/search.d.ts +5 -1
  33. package/dist/internal/search.js +8 -5
  34. package/dist/internal/styles.d.ts +27 -0
  35. package/dist/internal/styles.js +27 -0
  36. package/dist/internal/tabs-styles.d.ts +8 -7
  37. package/dist/internal/tabs-styles.js +15 -13
  38. package/dist/internal/use-drag-scroll.d.ts +17 -0
  39. package/dist/internal/use-drag-scroll.js +98 -0
  40. package/dist/internal/use-media-query.d.ts +5 -0
  41. package/dist/internal/use-media-query.js +5 -0
  42. package/dist/kanban.d.ts +1 -1
  43. package/dist/kanban.js +4 -3
  44. package/dist/nav-link.d.ts +12 -4
  45. package/dist/nav-link.js +14 -4
  46. package/dist/next/nav-link.d.ts +6 -3
  47. package/dist/next/nav-link.js +8 -6
  48. package/dist/next/tabs-nav-link.d.ts +6 -0
  49. package/dist/next/tabs-nav-link.js +8 -3
  50. package/dist/picker-shell.d.ts +24 -21
  51. package/dist/picker-shell.js +12 -10
  52. package/dist/search-picker.d.ts +1 -1
  53. package/dist/search-picker.js +5 -6
  54. package/dist/select.js +5 -5
  55. package/dist/tabs.d.ts +21 -10
  56. package/dist/tabs.js +26 -13
  57. package/dist/toggle.d.ts +2 -0
  58. package/dist/toggle.js +8 -3
  59. package/package.json +7 -3
  60. package/skills/atlas-react-kit/SKILL.md +46 -40
  61. package/theme.css +2 -0
package/COMPONENTS.md CHANGED
@@ -43,7 +43,7 @@ theme from `next-themes`, so toast consumers install that too.
43
43
  | `checkbox` | `Checkbox` |
44
44
  | `color-legend` | `ColorLegend`; `title`, `items` as `{ color, range?, label }[]`, optional `caption`, `children`, `aria-label`, `className` (overlay chrome). No attribution. |
45
45
  | `combobox` | `Combobox`; `options` as `{ value, label }[]`, `value`, `onValueChange`; optional placeholder, search, empty text, `creatable`, `normalizeCreateValue` (query → value or `null`), `createLabel`; trigger matches Input/SelectTrigger chrome; keyboard highlight scrolls into view; create is a listbox option selected with Enter |
46
- | `date-picker` | `DatePickerButton`, `toDate`, `formatLocalDate`, and the kit's own `DateRange`, `DateMatcher`, `DateLike`, and `DatePickerCalendarProps` types (no Day Picker types); `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+`), `minDate` / `maxDate` (days outside are disabled), `side`, `aria-labelledby`, and `aria-describedby` |
46
+ | `date-picker` | `DatePickerButton`, `toDate`, `formatLocalDate`, and the kit's own `DateRange`, `DateMatcher`, `DateLike`, and `DatePickerCalendarProps` types (no Day Picker types); `mode` `"single"` (default) or `"range"`; single `value` is `Date` or `yyyy-MM-dd` (`clearable` adds a Clear row that emits `undefined`); range `value` is `{ from, to }`; trigger matches Input chrome; optional `numberOfMonths` (range default 1 below `md`, 2 at `md+`), `minDate` / `maxDate` (days outside are disabled), `side`, `aria-labelledby`, and `aria-describedby` |
47
47
  | `date-time-picker` | `DateTimePickerButton`, `resolveTimeStep`, `DEFAULT_TIME_STEP`; controlled `value` is `Date` or `null`; `onValueChange` emits `Date \ |
48
48
  | `dialog` | `Dialog`, trigger, portal, overlay, content, close, header, footer, title, description; `DialogContent` supports `showCloseButton` |
49
49
  | `dropdown-menu` | `DropdownMenu` family (trigger, content, item, checkbox/radio items, label, separator, shortcut, sub menus); item `variant="destructive"` and `inset`; for commands, not values |
@@ -52,10 +52,10 @@ theme from `next-themes`, so toast consumers install that too.
52
52
  | `field` | Field, label, legend, description, group, set, title, content, separator, error; `Field` supports `orientation` |
53
53
  | `info-tip` | `InfoTip`, `InlineTip`; required `label` (aria-label); `title`, `description`, `children`, `layout` `box` \| `inline`, `side`, `align`, `badge`, `icon`, `className`. Popover, 20px glyph, 44px hit. Hover opens only on a fine pointer. Not a Tooltip. |
54
54
  | `kanban` | `KanbanBoard<T>`, `KanbanSkeleton`, `KanbanColumn` and `KanbanDropTarget` types; `columns` (`{ id, label, color?, badge?, emptyLabel? }`), `items` (`{ id, position }`), `getColumnId`, `onMove(item, toColumnId, { beforePosition, afterPosition })` for every column change or reorder, `renderCard`; optional `onCardClick` (body becomes a button), `onComplete` / `onDelete` (Finish / Remove drop zones while dragging), `isLoading` (skeleton cards on a first load, dimmed board on a refetch), `emptyLabel`, `completeLabel`, `deleteLabel`. Layout comes from `items`, so an optimistic cache update is the only state. Keep `renderCard` and `onCardClick` stable (`useCallback`) and only the changed card re-renders. Cards drag by the grip (`Drag to move`); dnd-kit edge-scrolls while dragging, and off-screen columns get fades and scroll buttons. `KanbanSkeleton` takes `columnCount` (default 4). |
55
- | `input`, `textarea`, `label` | `Input`, `Textarea`, `Label`; `Label` `required` shows a visible marker (set `required` on the control too) |
55
+ | `input`, `textarea`, `label` | `Input`, `Textarea`, `Label`; `Textarea` starts at `min-h-16` (override with `className`); `Label` `required` shows a visible marker (set `required` on the control too) |
56
56
  | `markdown` | `Markdown`, `MarkdownContent`; `content`, optional `className`. GFM via `react-markdown` + `remark-gfm` in this subpath only. Sanitized (no raw HTML; http/https/mailto/#/`/` URLs). Token styles, no typography plugin. |
57
57
  | `meter` | `Meter`; `value`, `max`, optional `min` (default 0), `fillClassName`, `markers` as `{ position, className? }[]`, required `aria-label`, `className`. `role="meter"`. Not `Progress`. |
58
- | `nav-link` | `NavLink`, `navLinkVariants`; required `active` boolean, `asChild` for router links. Pill chrome: inactive `text-muted-foreground` + `hover:bg-accent`, active selected wash. No router. |
58
+ | `nav-link` | `NavLink`, `navLinkVariants`, `isNavActive(pathname, href)` (true on the path or a page under it); required `active` boolean, `asChild` for router links. Pill chrome: inactive `text-muted-foreground` + `hover:bg-accent`, active selected wash. No router. |
59
59
  | `page-header` | `PageHeader`; `title`, optional `description`, `actions`, `children` (under the description), `titleAs` (`h1` default), `titleClassName`. Stacks below `sm`. |
60
60
  | `pagination` | `Pagination`, `PaginationContent`, `PaginationItem`, `PaginationLink` (`isActive` sets `aria-current`, the selected wash, and a selected border; `disabled` drops the `href` for a page that does not exist, such as Previous on page 1), `PaginationPrevious`, `PaginationNext` (custom text becomes the link name), `PaginationEllipsis` |
61
61
  | `period-stepper` | `PeriodStepper`; `label` (announced with `aria-live`), `stepLabel` ("month" names "Previous month"), `onPrevious`, `onNext`, optional `onToday` (no Today button without it), `labelClassName`. `PeriodStepperFrame` takes a `control(step, content)` render for another router's links. `next/period-stepper` takes `previousHref`, `todayHref`, `nextHref` instead. |
@@ -73,26 +73,27 @@ theme from `next-themes`, so toast consumers install that too.
73
73
  | `switch` | `Switch`; settings that apply immediately; square track and thumb |
74
74
  | `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` |
75
75
  | `table-body-skeleton` | `TableBodySkeleton`; `columns`, optional `rows` (default 5) |
76
- | `tabs` | `Tabs`, `TabsList`, `TabsTrigger`, `TabsContent`, `TabsNav`, `tabsListVariants`, `tabsTriggerVariants`; list `variant` is `default` or `line`; style `TabsNav` links with `tabsTriggerVariants()`; `TabsNav` / `TabsList` scroll horizontally only (`overflow-y` hidden, `p-1` counted in the scroller height, edge fade, active item into view) — do not wrap them in `overflow-x-auto` |
76
+ | `tabs` | `Tabs`, `TabsList`, `TabsTrigger`, `TabsContent`, `TabsNav`, `TabsNavLink`, `tabsListVariants`, `tabsTriggerVariants`; list `variant` is `default` or `line`; `TabsNavLink` is the link inside `TabsNav` (required `active`, `asChild` for a router link); `TabsNav` / `TabsList` scroll horizontally only (`overflow-y` hidden, `p-1` counted in the scroller height, edge fade, active item into view, mouse drag scrolls the row) — do not wrap them in `overflow-x-auto` |
77
77
  | `toast` | `Toaster`, `toast`; mount one `Toaster` (`position`, `closeButton` (default on), `expand`, `visibleToasts`, `duration`), then `toast(message, options)`. The stack expands on hover and while keyboard focus is inside it; collapsed toasts behind the front one are hidden, `.success`, `.info`, `.warning`, `.error`, `.loading`, `.dismiss(id?)`. Options: `id` (replaces that toast), `description`, `duration`, `action` / `cancel` as `{ label, onClick }`. Kit-owned API: no toast library is exposed |
78
- | `toggle` | `Toggle`, `toggleVariants`, `selectedToggleChrome`; `variant`, `size`. Pressed state is the selected wash, text, and border via `data-state=on`, `aria-pressed`, and `aria-checked` |
78
+ | `toggle` | `Toggle`, `toggleVariants`; `variant`, `size`. `selectedToggleChrome` is deprecated: `toggleVariants()` already includes it. Pressed state is the selected wash, text, and border via `data-state=on`, `aria-pressed`, and `aria-checked` |
79
79
  | `toggle-group` | `ToggleGroup`, `ToggleGroupItem`; `variant`, `size`. Items share `toggleVariants`, so single (radio) and multiple selections match a pressed `Toggle` |
80
80
  | `theme-provider` | `ThemeProvider`, `useTheme`; optional `storageKey` (default `atlas-theme`), `enableShortcut` for the `d` toggle |
81
- | `tooltip` | `Tooltip`, `TooltipTrigger`, `TooltipContent`, `TooltipProvider`; opens on hover and focus; short labels only |
81
+ | `tooltip` | `Tooltip`, `TooltipTrigger`, `TooltipContent`, `TooltipProvider`; opens on hover and focus; short labels only; content keeps a `max-w-xs` reading width (override with `className`) |
82
82
  | `form/checkbox-field` | `FormCheckboxField` (`checked`, `onCheckedChange(boolean)`, `label`, optional `description`, `controlPosition` `start` \| `end`, `meta`, `required`, `disabled`, `onBlur`) and `FormCheckboxGroupField<T>` (`label` as legend, `options` `{ value, label }[]`, `value: T[]`, `onValueChange` in option order, `className` lays out the options). |
83
83
  | `form/combobox-field` | `FormComboboxField`; every `Combobox` prop plus `label`, `description`, `meta`, `required`; pass the field's `onBlur`. |
84
84
  | `form/control-field` | `FormControlField`, type `FormControlProps`; `label`, optional `id`, `description`, `required`, `meta`, `submitted`, `showError`; `children` renders the control from `{ id, aria-invalid, aria-describedby, aria-required }` |
85
85
  | `form/markdown-field` | `FormMarkdownField`; `field` is `TextFieldApi`; `label`, optional `id`, `placeholder`, `rows`, `showError`, `required`. Write / preview; preview uses `Markdown`. |
86
86
  | `form/text-field` | `FormTextField`, type `TextFieldApi`; controlled field, label, multiline, description, input constraints; `required` also marks the label |
87
- | `form/select-field` | `FormSelectField`; `value`, `onValueChange`, `label`, optional `meta`, placeholder, and `required`; select item children |
88
- | `form/date-picker-field` | `FormDatePickerField`; `value`, `onValueChange`, `label`, optional `meta`, placeholder, `required`, `minDate` / `maxDate`, and `className` (forwarded to the trigger) |
89
- | `form/date-time-picker-field` | `FormDateTimePickerField`; `value` is `Date` or `null`; `onValueChange` emits `Date` or `null`; optional `meta`, placeholder, `timeStep`, `required`, `minDate` / `maxDate`, and `className` (forwarded to the trigger) |
87
+ | `form/select-field` | `FormSelectField`; `value`, `onValueChange`, `label`, optional `id` (default generated; pass the field name for `focusFirstInvalid`), `meta`, placeholder, and `required`; select item children |
88
+ | `form/date-picker-field` | `FormDatePickerField`; `value`, `onValueChange`, `label`, optional `id` (default generated), `meta`, placeholder, `required`, `clearable` (a Clear row that emits `undefined`), `minDate` / `maxDate`, and `className` (forwarded to the trigger) |
89
+ | `form/date-time-picker-field` | `FormDateTimePickerField`; `value` is `Date` or `null`; `onValueChange` emits `Date` or `null`; optional `id` (default generated), `meta`, placeholder, `timeStep`, `required`, `minDate` / `maxDate`, and `className` (forwarded to the trigger) |
90
90
  | `form/field-error` | `FieldError` (optional `id`), `getFieldErrorMessage`; types `FieldErrorMessage`, `FieldMetaState`, `ErrorTimingProps` |
91
+ | `form/focus-first-invalid` | `focusFirstInvalid(formId)`: an `onSubmitInvalid` handler that focuses the first control in the form whose field (matched by control id, so pass `id={field.name}`) has errors; type `InvalidSubmitEvent` |
91
92
  | `form/feedback-field` | `FormFeedbackField`; nullable `message` |
92
93
  | `form/toggle-group-field` | `FormToggleGroupField<T>`; one choice from `options` `{ value, label }[]` as a joined outline toggle group named by its label; `value` (`""` for none), `onValueChange(T)` (pressing the active item keeps it), optional `description`, `meta`, `size` (`sm` default). |
93
- | `next/nav-link` | `NavLink`; Next Link props and required `active` boolean; same pill as `nav-link`; requires Next.js 16 |
94
+ | `next/nav-link` | `NavLink`; Next Link props and required `active` boolean; the `nav-link` pill with `asChild` around a Next `Link`; requires Next.js 16 |
94
95
  | `next/period-stepper` | `PeriodStepper` with prefetched Next links: `previousHref`, `nextHref`, optional `todayHref`. |
95
- | `next/tabs-nav-link` | `TabsNavLink`; Next Link props and required `active` boolean; requires Next.js 16 |
96
+ | `next/tabs-nav-link` | `TabsNavLink`; Next Link props and required `active` boolean; the `tabs` link with `asChild` around a Next `Link`; requires Next.js 16 |
96
97
  | `utils` | `cn`, `onInputChange` |
97
98
  | `theme.css` | Tailwind 4 theme; installation in [README](./README.md) |
98
99
 
@@ -100,8 +101,7 @@ theme from `next-themes`, so toast consumers install that too.
100
101
 
101
102
  Components are **stable** unless listed here. Stable components change
102
103
  through the deprecation policy in
103
- [CONTRIBUTING.md](https://github.com/Kerubi-5/atlas-design-system/blob/main/CONTRIBUTING.md):
104
- a deprecation notice first, removal in a later minor release.
104
+ [CONTRIBUTING.md](https://github.com/Kerubi-5/atlas-design-system/blob/main/CONTRIBUTING.md#deprecations).
105
105
 
106
106
  **Beta** components may still change shape in a minor release, before and
107
107
  after 1.0; they are exempt from semver, and the changelog calls out every
@@ -121,10 +121,10 @@ each part carries a `data-slot` for parent selectors such as
121
121
  | `DialogContent` | `overlayClassName`, `closeButtonClassName` | `dialog-close-button` (the built-in X; your own `DialogClose` keeps `dialog-close`) |
122
122
  | `SelectTrigger` | a `className` height replaces `size` | `select-icon`, `select-viewport`, `select-item-indicator` |
123
123
  | `TooltipContent` | `arrowClassName` | `tooltip-arrow` |
124
- | `Combobox` | `contentClassName` (panel, default trigger width), `listClassName`; button props go to trigger | `combobox-search`, `combobox-list`, `combobox-option` |
125
- | `SearchPicker` | `listClassName`; div props go to the root | `search-picker-option`, `search-picker-empty` |
126
- | `InfoTip` | `contentClassName`; button props go to the trigger | |
127
- | `KanbanBoard` | `scrollerClassName`, `columnClassName` (default `w-72 min-w-72` and a drop-area min height), `cardClassName` (cards and the drag overlay); div props go to the root | `kanban-board`, `kanban-scroller`, `kanban-column`, `kanban-column-header`, `kanban-column-empty`, `kanban-card`, `kanban-card-handle`, `kanban-card-body`, `kanban-drag-overlay`, `kanban-action-rail`, `kanban-action-zone`, `kanban-scroll-button`, `kanban-overflow-fade`, `kanban-skeleton` |
124
+ | `Combobox` | `contentClassName` (panel, default trigger width), `listClassName` (default `max-h-60`); button props go to trigger | `combobox-search`, `combobox-list`, `combobox-option` |
125
+ | `SearchPicker` | `listClassName` (default `max-h-60`); div props go to the root | `search-picker-option`, `search-picker-empty` |
126
+ | `InfoTip` | `contentClassName` (default `w-72`); button props go to the trigger | |
127
+ | `KanbanBoard` | `scrollerClassName`, `columnClassName` (default `flex-1 basis-72 min-w-72` and a drop-area min height; columns grow to fill the board and keep an 18rem minimum so a long row still scrolls), `cardClassName` (cards and the drag overlay); div props go to the root | `kanban-board`, `kanban-scroller`, `kanban-column`, `kanban-column-header`, `kanban-column-empty`, `kanban-card`, `kanban-card-handle`, `kanban-card-body`, `kanban-drag-overlay`, `kanban-action-rail`, `kanban-action-zone`, `kanban-scroll-button`, `kanban-overflow-fade`, `kanban-skeleton` |
128
128
  | `chart` pieces | `ChartTooltip` and `ChartLegend` take div props; `ChartLegendKey` `className` | `chart-y-axis`, `chart-tooltip`, `chart-tooltip-row`, `chart-legend`, `chart-legend-key` |
129
129
  | `PageHeader`, `PeriodStepper`, `SkipLink` | `PageHeader` `titleClassName`; `PeriodStepper` `labelClassName` | `page-header`, `page-header-heading`, `page-header-title`, `page-header-description`, `page-header-actions`, `period-stepper`, `period-stepper-label`, `skip-link` |
130
130
  | `FormCheckboxGroupField` | `className` (the options box), `fieldClassName` (the fieldset) | `form-checkbox-group-options` |
@@ -132,7 +132,7 @@ each part carries a `data-slot` for parent selectors such as
132
132
  | `DatePickerButton`, `DateTimePickerButton` | `contentClassName`, `calendarProps` (`disabled` / `hidden` days, `startMonth`, `endMonth`, `weekStartsOn`, date-fns `locale`, `showOutsideDays`, `fixedWeeks`, `showWeekNumber`, `captionLayout`, `today`, `className`); `minDate` / `maxDate` add to its disabled days | `date-picker-value`, `date-picker-footer`, `date-time-picker-time` |
133
133
  | `form/*` fields | `fieldClassName` (wrapper); `className` styles the control (on `FormControlField`, the wrapper); `FormSelectField` `contentClassName`; `FormMarkdownField` `previewClassName` | `form-field`, `form-field-header`, `form-field-description`, `form-field-error`, `form-feedback`, `form-markdown-preview` |
134
134
  | `Table` | `containerClassName` (the scroll box, e.g. `max-h-96 overflow-y-auto`); `className` styles the `<table>` | |
135
- | `TabsList`, `TabsNav` | `scrollerClassName`; `TabsNav` `listClassName`; a list `className` height replaces `h-11` | `tabs-scroller` |
135
+ | `TabsList`, `TabsNav` | `scrollerClassName`; `TabsNav` `listClassName`; a list `className` height replaces `h-7`; default scroller is `h-9` (`p-1` + list `h-7`) to match `Button` `size="sm"`; `TabsNav` sizes to its links in a flex row, so add `w-full` to stretch it | `tabs-scroller` |
136
136
  | `SortableTableHead` | `buttonClassName` | `sortable-table-head-button` |
137
137
  | `AccordionTrigger`, `AccordionContent` | `headerClassName`; `AccordionContent` `className` styles the inner box | `accordion-header`, `accordion-content-inner` |
138
138
  | `Progress` | `indicatorClassName` | `progress-indicator` |
@@ -168,7 +168,11 @@ for a Write / Preview GFM body. `FormSelectField`, `FormDatePickerField`, and
168
168
  `FormDateTimePickerField` accept the same error meta shape.
169
169
  `FormDatePickerField` takes a `Date` or `yyyy-MM-dd` string and calls
170
170
  `onValueChange` with `Date | undefined`. `FormDateTimePickerField` takes a
171
- `Date` or `null` and calls `onValueChange` with `Date | null`. `FieldError`
171
+ `Date` or `null` and calls `onValueChange` with `Date | null`. Pass
172
+ `id={field.name}` to the select and date fields (the text field defaults to
173
+ it) so a field is addressable by name, for example by `focusFirstInvalid`
174
+ from `form/focus-first-invalid`, which focuses the first field with errors
175
+ when a submit fails. `FieldError`
172
176
  accepts either `message` or pre-resolved `text`, and an `id` to point the
173
177
  control's `aria-describedby` at. Every `form/*` field does that for you: the
174
178
  error text (and `FormTextField`'s `description`) is the control's accessible
@@ -178,12 +182,14 @@ part of its name.
178
182
 
179
183
  Errors wait for the person. When `meta.isTouched` is `false`, a `form/*`
180
184
  field hides its error text and invalid styling until the field is touched or
181
- its `submitted` prop is set (TanStack Form:
182
- `form.state.submissionAttempts > 0`). Pass TanStack's `field.state.meta` as
183
- is and this works on its own; meta without `isTouched` shows errors as soon
184
- as they exist, as before. `showError={false}` always hides them. Every
185
- `form/*` field, `FormSelectField` and both date fields included, takes
186
- `submitted` and `showError`.
185
+ its `submitted` prop is set. Pass TanStack Form's `field.state.meta` as is
186
+ and this works on its own: TanStack marks every mounted field touched when
187
+ the form is submitted, so untouched fields show their errors then without
188
+ `submitted`. `submitted` is for form libraries that do not touch fields on
189
+ submit. Meta without `isTouched` shows errors as soon as they exist.
190
+ `showError={false}` always hides them. Every `form/*` field,
191
+ `FormSelectField` and both date fields included, takes `submitted` and
192
+ `showError`.
187
193
 
188
194
  `FormControlField` gives a control the kit does not ship the same label,
189
195
  required marker, hint, error, ids, and error timing. `children` is a
@@ -224,7 +230,10 @@ height classes still override that default. The range popover shows one
224
230
  month below `md` and two months at `md+` unless `numberOfMonths` is set,
225
231
  clamps to the viewport with collision padding and scroll, stays `w-auto` so
226
232
  it does not collapse to the default popover width, and includes a Clear
227
- action. Pass `side` to prefer a placement; Radix still flips on collision.
233
+ action. In single mode, `clearable` adds the same Clear row (emits
234
+ `undefined`, closes the popover) and keeps the selected day on a second
235
+ click; without it, a second click on the selected day clears it. Pass
236
+ `side` to prefer a placement; Radix still flips on collision.
228
237
  `minDate` and `maxDate` (each `Date` or `yyyy-MM-dd`) disable the days
229
238
  outside them, on top of any `calendarProps.disabled`, and stop month paging
230
239
  there unless `calendarProps` sets `startMonth` / `endMonth`. They limit the
@@ -237,7 +246,7 @@ the trigger text's id (a `<label for>` alone names the button by the label
237
246
  only).
238
247
  `FormDatePickerField` forwards `className` to that trigger.
239
248
 
240
- Both pickers took `onChange` before 0.14; it still works as a deprecated
249
+ Both pickers took `onChange` before 1.0; it still works as a deprecated
241
250
  alias until 2.0.
242
251
 
243
252
  `DateTimePickerButton` is the date-and-time control. `value` is a `Date` or
@@ -383,26 +392,21 @@ export function ViewSwitcher({ view }: { view: string }) {
383
392
  }
384
393
  ```
385
394
 
386
- Outside Next, compose `TabsNav` with your own links: style each one with
387
- `tabsTriggerVariants()` and set `data-state="active"` plus `aria-current="page"`
388
- on the active link.
395
+ Outside Next, use `TabsNavLink` from `tabs`: a plain anchor with the same
396
+ chrome, `data-state`, and `aria-current="page"`, or `asChild` around your
397
+ router's link. `isNavActive` from `nav-link` works for the `active` check
398
+ here too.
389
399
 
390
400
  ```tsx
391
- import { TabsNav, tabsTriggerVariants } from "atlas-react-kit/tabs"
401
+ import { TabsNav, TabsNavLink } from "atlas-react-kit/tabs"
392
402
 
393
403
  export function ViewSwitcher({ view }: { view: string }) {
394
404
  return (
395
405
  <TabsNav aria-label="Items view">
396
406
  {["list", "board"].map((item) => (
397
- <a
398
- key={item}
399
- href={`?view=${item}`}
400
- data-state={view === item ? "active" : "inactive"}
401
- aria-current={view === item ? "page" : undefined}
402
- className={tabsTriggerVariants()}
403
- >
407
+ <TabsNavLink key={item} href={`?view=${item}`} active={view === item}>
404
408
  {item}
405
- </a>
409
+ </TabsNavLink>
406
410
  ))}
407
411
  </TabsNav>
408
412
  )
package/DESIGN_RULES.md CHANGED
@@ -129,13 +129,10 @@ or the viewport minus gutters on small screens.
129
129
 
130
130
  Use `EmptyPanel` for empty states, `TableBodySkeleton` for loading table bodies,
131
131
  and `<Card flush>` for an edge-to-edge list or table. `Table` scrolls wide
132
- columns inside itself (`min-w-0 overflow-x-auto`) and tightens cell padding
133
- below `sm`. `CardContent className="px-0"`
134
- overrides body padding without `!px-0` when only the body should go flush. Cards
135
- keep `min-h-min shrink-0` so they do not collapse inside a bounded flex
136
- column, and `min-w-0` so nested tables scroll instead of widening the card.
137
- `Dialog` already scrolls inside the viewport on small screens; avoid
138
- additional height limits.
132
+ columns inside itself, `Card` neither collapses in a bounded flex column nor
133
+ widens around a nested table, and `Dialog` already scrolls inside the
134
+ viewport on small screens; avoid additional height limits. The classes each
135
+ one uses are in its COMPONENTS.md row.
139
136
 
140
137
  Apps decide how wide and tall containers and overlays are. The kit sets
141
138
  intrinsic control sizes (the button, input, tab and row heights, icons,
@@ -144,12 +141,10 @@ clamps overlays to the viewport, but no design widths or heights on
144
141
  dialogs, popovers, menus, or lists. Set them with `className`: give each
145
142
  `DialogContent` a width such as `sm:max-w-md`, and each `PopoverContent`
146
143
  one such as `w-72`. Where the sized part is not the element `className`
147
- reaches, a prop names it: `InfoTip` takes `contentClassName` (default
148
- `w-72`), and `Combobox` and `SearchPicker` take `listClassName` (default
149
- `max-h-60`). `TabsNav` fills a block container but sizes to its links in a
150
- flex row; add `w-full` to stretch it there. `Tooltip` keeps a `max-w-xs` reading width, `Textarea` a
151
- `min-h-16` starting height, and toasts their 356px width; override the first
152
- two with `className`.
144
+ reaches, a `*ClassName` prop names it, with its default where there is one
145
+ (COMPONENTS.md, "Styling parts"). The few parts that keep a reading width or
146
+ a starting height (`Tooltip`, `Textarea`) take `className` to override it;
147
+ toasts keep their width.
153
148
 
154
149
  Parents do not force sizes on their children. A vertical `Field` stretches
155
150
  its controls through the flex column, so a control's own width class (such
@@ -172,32 +167,28 @@ triggers as outline buttons in application code.
172
167
  error sits below the control. It takes no space until it shows, so a row of
173
168
  fields keeps its controls on one top edge only with `items-start`
174
169
  (`grid grid-cols-2 items-start gap-3`); the default `stretch` is fine for a
175
- single column. Wrap a control the kit does not ship (a combobox, a segmented
170
+ single column. Wrap a control the kit has no form field for (a colour
176
171
  picker) in `FormControlField` instead of `Field` and `FieldLabel`, whose
177
- `gap-3` spacing does not match. A field shows its error once
178
- `meta.isTouched` is true or `submitted` is set, never on a field the person
179
- has not reached yet.
172
+ `gap-3` spacing does not match. A field shows its error once the person has
173
+ touched it (`meta.isTouched`), never on a field they have not reached yet.
174
+ `submitted` is for form libraries that do not mark every field touched on
175
+ submit; TanStack Form does, so its users pass `meta` only.
180
176
 
181
177
  Use `Tabs` with actual `TabsContent` panels. For URL view switches, use a labelled
182
- `TabsNav` with links and `aria-current="page"` on the active link. The optional
183
- Next adapter provides `TabsNavLink` with this behavior. App-shell destinations
178
+ `TabsNav` of `TabsNavLink`s with `active` from the app. App-shell destinations
184
179
  (Calendar, Tasks, Settings) use `NavLink` from `nav-link`: a pill with `active`
185
- from the app, not tabs. Pass `asChild` for a router link, or use the optional
186
- Next adapter `next/nav-link`. `TabsNav` and `TabsList`
187
- scroll overflowing labels inside themselves (`overflow-x-auto overflow-y-hidden`,
188
- with padding for the active border and focus ring counted in the scroller
189
- height, and an edge fade like `Table`). Do not wrap them in another
190
- `overflow-x-auto` — that pairing forces `overflow-y: auto` and a stray
191
- vertical scrollbar.
192
-
193
- Use `FormTextField`, `FormSelectField`, `FormDatePickerField`,
194
- `FormDateTimePickerField`, `FormMarkdownField`, and `FormFeedbackField` for
195
- portable form controls, and `FieldError` for field messages. `Combobox` (popover)
180
+ from the app, not tabs. Both links take `asChild` for a router link, or come
181
+ as Next adapters (`next/tabs-nav-link`, `next/nav-link`); `isNavActive`
182
+ compares the pathname with a link's `href`. `TabsNav` and `TabsList` scroll overflowing
183
+ labels inside themselves, like `Table`. Do not wrap them in another
184
+ horizontal scroller: that pairing adds a stray vertical scrollbar.
185
+
186
+ Use the `form/*` fields (one row each in COMPONENTS.md) for portable form
187
+ controls, and `FieldError` for field messages. `Combobox` (popover)
196
188
  and `SearchPicker` (always-visible list), `DatePickerButton` (including
197
189
  `mode="range"`), `DateTimePickerButton`, `Slider`, `Meter` (scale with ticks;
198
190
  `Progress` is a plain 0–100 job bar), `InfoTip` (longer than `Tooltip`),
199
191
  `Markdown`, `ColorLegend`, `StatTile`, `ThemeProvider`, `ErrorBoundary`, and
200
192
  `SortableTableHead` are first-class kit pieces. Domain data and application
201
193
  shells stay with the application: currency or timezone option lists,
202
- query-library error views, WYSIWYG editors, kanban boards, and dashboard
203
- widgets.
194
+ query-library error views, and WYSIWYG editors.
package/README.md CHANGED
@@ -8,7 +8,7 @@ React 19, Tailwind 4, and Node >=20.19 are required. Next 16 is needed only
8
8
  when using `next/nav-link`, `next/period-stepper`, or `next/tabs-nav-link`.
9
9
 
10
10
  ```sh
11
- npm install --save-exact atlas-react-kit@1.0.0
11
+ npm install --save-exact atlas-react-kit@1.2.0
12
12
  ```
13
13
 
14
14
  Import components by subpath:
@@ -55,7 +55,8 @@ with the application.
55
55
  - [Design rules](./DESIGN_RULES.md): tokens, accessibility, writing, and
56
56
  composition. This guide ships in the npm package.
57
57
  - [Contributing](./CONTRIBUTING.md): component changes, checks, and releases.
58
- - [Roadmap to 1.0](./ROADMAP-1.0.md): decisions needed before the 1.0 release.
58
+ - [Decisions behind 1.0 (record)](./ROADMAP-1.0.md): the closed roadmap that
59
+ led to 1.0.0.
59
60
  - [Changelog](./CHANGELOG.md): published and upcoming changes.
60
61
 
61
62
  ## AI coding assistants
@@ -86,51 +87,8 @@ and package entry points for search and indexing tools.
86
87
 
87
88
  ## Development and releases
88
89
 
89
- ```sh
90
- npm ci
91
- npm run check
92
- npm test
93
- npm pack
94
- npm ci --prefix site
95
- npm run build --prefix site
96
- npm run preview --prefix site
97
- ```
98
-
99
- `tsc` emits individual ESM files and TypeScript declarations. CSS is shipped
100
- without compilation and processed by the consuming application's Tailwind build.
101
- The public site is Storybook in `site/`. `npm run build --prefix site`
102
- typechecks the stories, builds Storybook into `site/dist`, then checks the
103
- static output. Vercel serves that directory; `/playground` and `/storybook`
104
- redirect to `/`. The site and Storybook dependencies are not part of the npm
105
- package. Optional feature libraries (markdown, dates, toasts, theme, Next, dnd-kit) are
106
- peer dependencies and are required only when those entry points are imported.
107
- The package does not bundle React.
108
-
109
- Run `npm run dev --prefix site` (or `npm run storybook --prefix site`) for the
110
- playground on port 6006. Existing examples live in `stories/` and are imported
111
- by CSF story files in `site/stories/`, so component tests and previews share the
112
- same examples. Add Storybook entries there when adding an example; the
113
- static-output check reports any existing demo that has no Storybook entry.
114
- Interactive stories use Storybook args so changing a control or interacting with
115
- the component updates the same value. Configuration is in `site/.storybook/`.
116
-
117
- The Storybook preview disables the kit's theme shortcut and uses the theme
118
- toolbar instead. The accessibility panel checks the rendered story; it does not
119
- replace keyboard or assistive-technology testing.
120
-
121
- Bumping the version in `package.json` and merging to `main` (path-filtered
122
- to package files) or running `workflow_dispatch` publishes through npm OIDC
123
- trusted publishing after check, test, and pack gates. Versions already on npm
124
- are skipped.
125
-
126
- If trusted publishing is not configured, pack and publish a validated
127
- artifact with the package owner's npm login:
128
-
129
- ```sh
130
- mkdir -p release-artifacts
131
- npm pack --pack-destination release-artifacts
132
- npm publish ./release-artifacts/atlas-react-kit-1.0.0.tgz --access public
133
- ```
90
+ Setup, checks, the component checklist, and releases are in
91
+ [CONTRIBUTING.md](./CONTRIBUTING.md).
134
92
 
135
93
  Pin an exact package version in consuming applications. Roll back an upgrade by
136
94
  restoring the previous version and lockfile. License: MIT; upstream notices are
package/dist/accordion.js CHANGED
@@ -3,7 +3,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import * as React from "react";
4
4
  import { Accordion as AccordionPrimitive } from "radix-ui";
5
5
  import { ChevronDownIcon } from "lucide-react";
6
- import { disabledState, focusRing } from "./internal/styles.js";
6
+ import { disabledState, focusRing, quietTextHover } from "./internal/styles.js";
7
7
  import { cn } from "./utils.js";
8
8
  /** Stacked sections that expand in place (FAQs, settings groups). */
9
9
  function Accordion({ ...props }) {
@@ -14,7 +14,7 @@ function AccordionItem({ className, ...props }) {
14
14
  }
15
15
  /** `headerClassName` styles the heading wrapper (`data-slot="accordion-header"`). */
16
16
  function AccordionTrigger({ className, headerClassName, children, ...props }) {
17
- return (_jsx(AccordionPrimitive.Header, { "data-slot": "accordion-header", className: cn("flex", headerClassName), children: _jsxs(AccordionPrimitive.Trigger, { "data-slot": "accordion-trigger", className: cn(focusRing, disabledState, "flex flex-1 items-start justify-between gap-4 rounded-none py-4 text-left text-sm font-medium transition-colors hover:text-selected-foreground focus-visible:text-selected-foreground [&[data-state=open]>svg]:rotate-180", className), ...props, children: [children, _jsx(ChevronDownIcon, { className: "pointer-events-none size-4 shrink-0 translate-y-0.5 text-muted-foreground transition-transform duration-200" })] }) }));
17
+ return (_jsx(AccordionPrimitive.Header, { "data-slot": "accordion-header", className: cn("flex", headerClassName), children: _jsxs(AccordionPrimitive.Trigger, { "data-slot": "accordion-trigger", className: cn(focusRing, disabledState, "flex flex-1 items-start justify-between gap-4 rounded-none py-4 text-left text-sm font-medium transition-colors", quietTextHover, "[&[data-state=open]>svg]:rotate-180", className), ...props, children: [children, _jsx(ChevronDownIcon, { className: "pointer-events-none size-4 shrink-0 translate-y-0.5 text-muted-foreground transition-transform duration-200" })] }) }));
18
18
  }
19
19
  /**
20
20
  * `className` styles the padded inner box (`data-slot="accordion-content-inner"`);
package/dist/badge.js CHANGED
@@ -2,12 +2,15 @@ import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import * as React from "react";
3
3
  import { cva } from "class-variance-authority";
4
4
  import { Slot } from "radix-ui";
5
- import { focusRing, invalidState } from "./internal/styles.js";
5
+ import { controlText, focusRing, invalidState } from "./internal/styles.js";
6
6
  import { cn } from "./utils.js";
7
7
  const badgeVariants = cva([
8
8
  focusRing,
9
9
  invalidState,
10
- "group/badge inline-flex w-fit shrink-0 items-center justify-center gap-1.5 overflow-hidden rounded-none border-0 bg-transparent px-0 py-0 text-[0.625rem] font-semibold tracking-widest whitespace-nowrap uppercase transition-colors has-data-[icon=inline-end]:pr-0 has-data-[icon=inline-start]:pl-0 [&>svg]:pointer-events-none [&>svg:not([class*='size-'])]:size-3",
10
+ "group/badge inline-flex w-fit shrink-0 items-center justify-center gap-1.5 overflow-hidden rounded-none border-0 bg-transparent px-0 py-0",
11
+ controlText,
12
+ // Smaller than `controlText`'s `text-xs`; tailwind-merge keeps this one.
13
+ "text-[0.625rem] transition-colors has-data-[icon=inline-end]:pr-0 has-data-[icon=inline-start]:pl-0 [&>svg]:pointer-events-none [&>svg:not([class*='size-'])]:size-3",
11
14
  ], {
12
15
  variants: {
13
16
  variant: {
@@ -2,7 +2,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import * as React from "react";
3
3
  import { Slot } from "radix-ui";
4
4
  import { ChevronRightIcon, MoreHorizontalIcon } from "lucide-react";
5
- import { focusRing } from "./internal/styles.js";
5
+ import { focusRing, quietTextHover } from "./internal/styles.js";
6
6
  import { cn } from "./utils.js";
7
7
  /**
8
8
  * Where the current page sits in a hierarchy. The last item is
@@ -20,7 +20,7 @@ function BreadcrumbItem({ className, ...props }) {
20
20
  /** Pass `asChild` to render a router link (e.g. Next `Link`) with these styles. */
21
21
  function BreadcrumbLink({ asChild, className, ...props }) {
22
22
  const Comp = asChild ? Slot.Root : "a";
23
- return (_jsx(Comp, { "data-slot": "breadcrumb-link", className: cn(focusRing, "transition-colors hover:text-selected-foreground focus-visible:text-selected-foreground", className), ...props }));
23
+ return (_jsx(Comp, { "data-slot": "breadcrumb-link", className: cn(focusRing, "transition-colors", quietTextHover, className), ...props }));
24
24
  }
25
25
  function BreadcrumbPage({ className, ...props }) {
26
26
  return (_jsx("span", { "data-slot": "breadcrumb-page", "aria-current": "page", className: cn("font-medium text-foreground", className), ...props }));
package/dist/button.js CHANGED
@@ -2,7 +2,7 @@ import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import * as React from "react";
3
3
  import { cva } from "class-variance-authority";
4
4
  import { Slot } from "radix-ui";
5
- import { controlSize, disabledState, focusRing, invalidState, selectedState, } from "./internal/styles.js";
5
+ import { controlSize, controlText, disabledState, focusRing, inlineIcon, invalidState, selectedState, } from "./internal/styles.js";
6
6
  import { cn } from "./utils.js";
7
7
  /**
8
8
  * Outline and ghost hover/focus: selected-foreground border, selected wash, and
@@ -19,7 +19,10 @@ const buttonVariants = cva([
19
19
  focusRing,
20
20
  invalidState,
21
21
  disabledState,
22
- "group/button inline-flex shrink-0 items-center justify-center rounded-none border border-transparent bg-clip-padding text-xs font-semibold tracking-widest whitespace-nowrap uppercase transition-all select-none active:not-aria-[haspopup]:translate-y-px [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-3.5",
22
+ "group/button inline-flex shrink-0 items-center justify-center rounded-none border border-transparent bg-clip-padding",
23
+ controlText,
24
+ "transition-all select-none active:not-aria-[haspopup]:translate-y-px",
25
+ inlineIcon.default,
23
26
  ], {
24
27
  variants: {
25
28
  variant: {
@@ -32,11 +35,11 @@ const buttonVariants = cva([
32
35
  },
33
36
  size: {
34
37
  default: `${controlSize.default} gap-1.5`,
35
- xs: "h-7 gap-1 px-3 has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2 [&_svg:not([class*='size-'])]:size-3",
38
+ xs: `h-7 gap-1 px-3 has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2 ${inlineIcon.sm}`,
36
39
  sm: `${controlSize.sm} gap-1`,
37
40
  lg: `${controlSize.lg} gap-1.5`,
38
41
  icon: "size-10",
39
- "icon-xs": "size-7 [&_svg:not([class*='size-'])]:size-3",
42
+ "icon-xs": `size-7 ${inlineIcon.sm}`,
40
43
  "icon-sm": "size-9",
41
44
  "icon-lg": "size-11",
42
45
  },
package/dist/combobox.js CHANGED
@@ -5,9 +5,10 @@ import { ChevronDownIcon } from "lucide-react";
5
5
  import { Button } from "./button.js";
6
6
  import { Input } from "./input.js";
7
7
  import { formControlTriggerClassName } from "./internal/form-control.js";
8
- import { foldSearchText, handleListboxKey, listboxOptionId, } from "./internal/listbox-navigation.js";
8
+ import { handleListboxKey, listboxOptionId, } from "./internal/listbox-navigation.js";
9
9
  import { ListboxEmptyStatus, PickerOption } from "./internal/listbox-option.js";
10
- import { createSearchIndex, filterSearchIndex, normalizeSearchQuery, } from "./internal/search.js";
10
+ import { createSearchIndex, filterSearchIndex, foldSearchText, normalizeSearchQuery, } from "./internal/search.js";
11
+ import { listboxScroll } from "./internal/styles.js";
11
12
  import { Popover, PopoverContent, PopoverTrigger } from "./popover.js";
12
13
  import { cn, onInputChange } from "./utils.js";
13
14
  const CREATE_OPTION_KEY = "__atlas-combobox-create";
@@ -134,9 +135,7 @@ export function Combobox({ options, value, onValueChange, placeholder = "Select"
134
135
  },
135
136
  });
136
137
  };
137
- return (_jsxs(Popover, { open: open, onOpenChange: onOpenChange, children: [_jsx(PopoverTrigger, { asChild: true, children: _jsxs(Button, { ...props, id: triggerId, type: "button", variant: "outline", role: "combobox", "aria-expanded": open, "aria-controls": listId, "aria-invalid": ariaInvalid, "aria-label": ariaLabel, "aria-labelledby": ariaLabelledBy, disabled: disabled, onKeyDown: onTriggerKeyDown, 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: cn("w-[var(--radix-popover-trigger-width)] gap-0 rounded-none p-0", contentClassName), children: [_jsx("div", { "data-slot": "combobox-search", className: "border-b p-2", children: _jsx(Input, { value: query, onChange: onQueryChange, onKeyDown: onSearchKeyDown, placeholder: searchPlaceholder, "aria-label": searchPlaceholder, "aria-autocomplete": "list", "aria-controls": listId, "aria-activedescendant": activeOption ? listboxOptionId(listId, highlight) : undefined }) }), _jsx("ul", { id: listId, role: "listbox", "data-slot": "combobox-list", "aria-label": ariaLabelledBy ? undefined : ariaLabel, "aria-labelledby": ariaLabelledBy ?? (ariaLabel ? undefined : triggerId), className: empty
138
- ? "sr-only"
139
- : cn("max-h-60 overflow-y-auto p-1", listClassName), children: list.map((option, index) => {
138
+ return (_jsxs(Popover, { open: open, onOpenChange: onOpenChange, children: [_jsx(PopoverTrigger, { asChild: true, children: _jsxs(Button, { ...props, id: triggerId, type: "button", variant: "outline", role: "combobox", "aria-expanded": open, "aria-controls": listId, "aria-invalid": ariaInvalid, "aria-label": ariaLabel, "aria-labelledby": ariaLabelledBy, disabled: disabled, onKeyDown: onTriggerKeyDown, 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: cn("w-[var(--radix-popover-trigger-width)] gap-0 rounded-none p-0", contentClassName), children: [_jsx("div", { "data-slot": "combobox-search", className: "border-b p-2", children: _jsx(Input, { value: query, onChange: onQueryChange, onKeyDown: onSearchKeyDown, placeholder: searchPlaceholder, "aria-label": searchPlaceholder, "aria-autocomplete": "list", "aria-controls": listId, "aria-activedescendant": activeOption ? listboxOptionId(listId, highlight) : undefined }) }), _jsx("ul", { id: listId, role: "listbox", "data-slot": "combobox-list", "aria-label": ariaLabelledBy ? undefined : ariaLabel, "aria-labelledby": ariaLabelledBy ?? (ariaLabel ? undefined : triggerId), className: empty ? "sr-only" : cn(listboxScroll, "p-1", listClassName), children: list.map((option, index) => {
140
139
  const isCreate = createOption != null && option === createOption;
141
140
  const isSelected = option.value === value && !isCreate;
142
141
  return (_jsx(PickerOption, { "data-slot": "combobox-option", listId: listId, index: index, active: index === highlight, selected: isSelected, showCheck: true, item: option, getLabel: getOptionLabel, onHighlight: setHighlight, onSelect: selectOption }, isCreate ? CREATE_OPTION_KEY : option.value));