atlas-react-kit 0.12.0 → 0.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/COMPONENTS.md +112 -31
- package/DESIGN_RULES.md +14 -1
- package/README.md +1 -1
- package/dist/alert.d.ts +1 -1
- package/dist/badge.d.ts +2 -2
- package/dist/button.d.ts +2 -2
- package/dist/date-picker.d.ts +2 -4
- package/dist/date-picker.js +1 -1
- package/dist/error-boundary.d.ts +1 -1
- package/dist/field.d.ts +1 -1
- package/dist/form/control-field.d.ts +31 -0
- package/dist/form/control-field.js +22 -0
- package/dist/form/date-picker-field.d.ts +3 -3
- package/dist/form/date-picker-field.js +2 -2
- package/dist/form/date-time-picker-field.d.ts +3 -3
- package/dist/form/date-time-picker-field.js +2 -2
- package/dist/form/field-error.d.ts +18 -0
- package/dist/form/field-shell.d.ts +19 -8
- package/dist/form/field-shell.js +24 -7
- package/dist/form/markdown-field.d.ts +2 -2
- package/dist/form/markdown-field.js +2 -2
- package/dist/form/select-field.d.ts +3 -3
- package/dist/form/select-field.js +2 -2
- package/dist/form/text-field.d.ts +2 -4
- package/dist/form/text-field.js +6 -6
- package/dist/internal/date-like.d.ts +23 -0
- package/dist/nav-link.d.ts +22 -0
- package/dist/nav-link.js +31 -0
- package/dist/next/nav-link.d.ts +11 -0
- package/dist/next/nav-link.js +14 -0
- package/dist/picker-shell.d.ts +31 -6
- package/dist/picker-shell.js +1 -1
- package/dist/toggle.d.ts +1 -1
- package/package.json +17 -5
- package/skills/atlas-react-kit/SKILL.md +2 -0
package/COMPONENTS.md
CHANGED
|
@@ -26,7 +26,7 @@ theme from `next-themes`, so toast consumers install that too.
|
|
|
26
26
|
| `markdown`, `form/markdown-field` | `npm i react-markdown@^10.1.0 remark-gfm@^4.0.1` |
|
|
27
27
|
| `theme-provider` | `npm i next-themes@^0.4.6` |
|
|
28
28
|
| `toast` | `npm i sonner@^2.0.7 next-themes@^0.4.6` |
|
|
29
|
-
| `next/tabs-nav-link`
|
|
29
|
+
| `next/nav-link`, `next/tabs-nav-link` | `npm i next@^16.0.0` |
|
|
30
30
|
|
|
31
31
|
| Subpath | Main exports and options |
|
|
32
32
|
| ----------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
@@ -41,7 +41,7 @@ theme from `next-themes`, so toast consumers install that too.
|
|
|
41
41
|
| `checkbox` | `Checkbox` |
|
|
42
42
|
| `color-legend` | `ColorLegend`; `title`, `items` as `{ color, range?, label }[]`, optional `caption`, `children`, `aria-label`, `className` (overlay chrome). No attribution. |
|
|
43
43
|
| `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 |
|
|
44
|
-
| `date-picker` | `DatePickerButton`, `toDate`, `formatLocalDate`, `DateRange
|
|
44
|
+
| `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` |
|
|
45
45
|
| `date-time-picker` | `DateTimePickerButton`, `resolveTimeStep`, `DEFAULT_TIME_STEP`; controlled `value` is `Date` or `null`; `onChange` emits `Date \ |
|
|
46
46
|
| `dialog` | `Dialog`, trigger, portal, overlay, content, close, header, footer, title, description; `DialogContent` supports `showCloseButton` |
|
|
47
47
|
| `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,6 +52,7 @@ theme from `next-themes`, so toast consumers install that too.
|
|
|
52
52
|
| `input`, `textarea`, `label` | `Input`, `Textarea`, `Label`; `Label` `required` shows a visible marker (set `required` on the control too) |
|
|
53
53
|
| `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. |
|
|
54
54
|
| `meter` | `Meter`; `value`, `max`, optional `min` (default 0), `fillClassName`, `markers` as `{ position, className? }[]`, required `aria-label`, `className`. `role="meter"`. Not `Progress`. |
|
|
55
|
+
| `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. |
|
|
55
56
|
| `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` |
|
|
56
57
|
| `popover` | `Popover`, trigger, anchor, content, header, title, description |
|
|
57
58
|
| `progress` | `Progress`; Radix props, `value` 0–100; label it with `aria-label` or `aria-labelledby`. For a value against a scale with ticks, use `Meter`. |
|
|
@@ -72,13 +73,15 @@ theme from `next-themes`, so toast consumers install that too.
|
|
|
72
73
|
| `toggle-group` | `ToggleGroup`, `ToggleGroupItem`; `variant`, `size`. Items share `toggleVariants`, so single (radio) and multiple selections match a pressed `Toggle` |
|
|
73
74
|
| `theme-provider` | `ThemeProvider`, `useTheme`; optional `storageKey` (default `atlas-theme`), `enableShortcut` for the `d` toggle |
|
|
74
75
|
| `tooltip` | `Tooltip`, `TooltipTrigger`, `TooltipContent`, `TooltipProvider`; opens on hover and focus; short labels only |
|
|
76
|
+
| `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 }` |
|
|
75
77
|
| `form/markdown-field` | `FormMarkdownField`; `field` is `TextFieldApi`; `label`, optional `id`, `placeholder`, `rows`, `showError`, `required`. Write / preview; preview uses `Markdown`. |
|
|
76
78
|
| `form/text-field` | `FormTextField`, type `TextFieldApi`; controlled field, label, multiline, description, input constraints; `required` also marks the label |
|
|
77
79
|
| `form/select-field` | `FormSelectField`; `value`, `onValueChange`, `label`, optional `meta`, placeholder, and `required`; select item children |
|
|
78
80
|
| `form/date-picker-field` | `FormDatePickerField`; `value`, `onValueChange`, `label`, optional `meta`, placeholder, `required`, `minDate` / `maxDate`, and `className` (forwarded to the trigger) |
|
|
79
81
|
| `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) |
|
|
80
|
-
| `form/field-error` | `FieldError` (optional `id`), `getFieldErrorMessage`; types `FieldErrorMessage`, `FieldMetaState`
|
|
82
|
+
| `form/field-error` | `FieldError` (optional `id`), `getFieldErrorMessage`; types `FieldErrorMessage`, `FieldMetaState`, `ErrorTimingProps` |
|
|
81
83
|
| `form/feedback-field` | `FormFeedbackField`; nullable `message` |
|
|
84
|
+
| `next/nav-link` | `NavLink`; Next Link props and required `active` boolean; same pill as `nav-link`; requires Next.js 16 |
|
|
82
85
|
| `next/tabs-nav-link` | `TabsNavLink`; Next Link props and required `active` boolean; requires Next.js 16 |
|
|
83
86
|
| `utils` | `cn`, `onInputChange` |
|
|
84
87
|
| `theme.css` | Tailwind 4 theme; installation in [README](./README.md) |
|
|
@@ -90,11 +93,10 @@ through the deprecation policy in
|
|
|
90
93
|
[CONTRIBUTING.md](https://github.com/Kerubi-5/atlas-design-system/blob/main/CONTRIBUTING.md):
|
|
91
94
|
a deprecation notice first, removal in a later minor release.
|
|
92
95
|
|
|
93
|
-
**Beta** components may still change shape in a minor release
|
|
94
|
-
changelog calls out every
|
|
95
|
-
`
|
|
96
|
-
`
|
|
97
|
-
`slider`, `spinner`, `stat-tile`, `switch`, `tooltip`.
|
|
96
|
+
**Beta** components may still change shape in a minor release, before and
|
|
97
|
+
after 1.0; they are exempt from semver, and the changelog calls out every
|
|
98
|
+
change: `accordion`, `avatar`, `breadcrumb`, `dropdown-menu`, `nav-link`,
|
|
99
|
+
`pagination`, `radio-group`, `spinner`, `switch`.
|
|
98
100
|
|
|
99
101
|
## Styling parts
|
|
100
102
|
|
|
@@ -103,27 +105,27 @@ through to it. Where a part sits inside a component, a prop names it, and
|
|
|
103
105
|
each part carries a `data-slot` for parent selectors such as
|
|
104
106
|
`*:data-[slot=stat-tile-value]:text-2xl`.
|
|
105
107
|
|
|
106
|
-
| Component | Part props
|
|
107
|
-
| -------------------------------------------------- |
|
|
108
|
-
| `DialogContent` | `overlayClassName`, `closeButtonClassName`
|
|
109
|
-
| `SelectTrigger` | a `className` height replaces `size`
|
|
110
|
-
| `TooltipContent` | `arrowClassName`
|
|
111
|
-
| `Combobox` | `contentClassName` (panel, default trigger width), `listClassName`; button props go to trigger
|
|
112
|
-
| `SearchPicker` | `listClassName`; div props go to the root
|
|
113
|
-
| `InfoTip` | `contentClassName`; button props go to the trigger
|
|
114
|
-
| `Label`, `FieldLabel` | `requiredClassName`
|
|
115
|
-
| `DatePickerButton`, `DateTimePickerButton` | `contentClassName`, `calendarProps` (disabled days, `startMonth`, `endMonth`, `weekStartsOn`, `locale`); `minDate` / `maxDate` add to its disabled days | `date-picker-value`, `date-picker-footer`, `date-time-picker-time` |
|
|
116
|
-
| `form/*` fields | `fieldClassName` (wrapper); `className` styles the control; `FormSelectField` `contentClassName`; `FormMarkdownField` `previewClassName`
|
|
117
|
-
| `Table` | `containerClassName` (the scroll box, e.g. `max-h-96 overflow-y-auto`); `className` styles the `<table>`
|
|
118
|
-
| `TabsList`, `TabsNav` | `scrollerClassName`; `TabsNav` `listClassName`; a list `className` height replaces `h-11`
|
|
119
|
-
| `SortableTableHead` | `buttonClassName`
|
|
120
|
-
| `AccordionTrigger`, `AccordionContent` | `headerClassName`; `AccordionContent` `className` styles the inner box
|
|
121
|
-
| `Progress` | `indicatorClassName`
|
|
122
|
-
| `Meter` | `fillClassName`
|
|
123
|
-
| `StatTile` |
|
|
124
|
-
| `ColorLegend` |
|
|
125
|
-
| `RadioGroupItem` |
|
|
126
|
-
| `EmptyPanel`, `ErrorFallback`, `TableBodySkeleton` | `className`
|
|
108
|
+
| Component | Part props | Slots |
|
|
109
|
+
| -------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------- |
|
|
110
|
+
| `DialogContent` | `overlayClassName`, `closeButtonClassName` | `dialog-close-button` (the built-in X; your own `DialogClose` keeps `dialog-close`) |
|
|
111
|
+
| `SelectTrigger` | a `className` height replaces `size` | `select-icon`, `select-viewport`, `select-item-indicator` |
|
|
112
|
+
| `TooltipContent` | `arrowClassName` | `tooltip-arrow` |
|
|
113
|
+
| `Combobox` | `contentClassName` (panel, default trigger width), `listClassName`; button props go to trigger | `combobox-search`, `combobox-list`, `combobox-option` |
|
|
114
|
+
| `SearchPicker` | `listClassName`; div props go to the root | `search-picker-option`, `search-picker-empty` |
|
|
115
|
+
| `InfoTip` | `contentClassName`; button props go to the trigger | |
|
|
116
|
+
| `Label`, `FieldLabel` | `requiredClassName` | `label-required` |
|
|
117
|
+
| `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` |
|
|
118
|
+
| `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` |
|
|
119
|
+
| `Table` | `containerClassName` (the scroll box, e.g. `max-h-96 overflow-y-auto`); `className` styles the `<table>` | |
|
|
120
|
+
| `TabsList`, `TabsNav` | `scrollerClassName`; `TabsNav` `listClassName`; a list `className` height replaces `h-11` | `tabs-scroller` |
|
|
121
|
+
| `SortableTableHead` | `buttonClassName` | `sortable-table-head-button` |
|
|
122
|
+
| `AccordionTrigger`, `AccordionContent` | `headerClassName`; `AccordionContent` `className` styles the inner box | `accordion-header`, `accordion-content-inner` |
|
|
123
|
+
| `Progress` | `indicatorClassName` | `progress-indicator` |
|
|
124
|
+
| `Meter` | `fillClassName` | `meter-fill`, `meter-marker` |
|
|
125
|
+
| `StatTile` | | `stat-tile-label`, `stat-tile-value`, `stat-tile-sub` |
|
|
126
|
+
| `ColorLegend` | | `color-legend-title`, `-list`, `-item`, `-swatch`, `-range`, `-label`, `-caption` |
|
|
127
|
+
| `RadioGroupItem` | | `radio-group-dot` |
|
|
128
|
+
| `EmptyPanel`, `ErrorFallback`, `TableBodySkeleton` | `className` | `empty-panel`, `error-boundary` |
|
|
127
129
|
|
|
128
130
|
## Forms
|
|
129
131
|
|
|
@@ -134,7 +136,10 @@ type TextFieldApi = {
|
|
|
134
136
|
name: string
|
|
135
137
|
state: {
|
|
136
138
|
value: string
|
|
137
|
-
meta: {
|
|
139
|
+
meta: {
|
|
140
|
+
errors: (string | { message?: string } | undefined)[]
|
|
141
|
+
isTouched?: boolean
|
|
142
|
+
}
|
|
138
143
|
}
|
|
139
144
|
handleChange: (value: string) => void
|
|
140
145
|
handleBlur: () => void
|
|
@@ -156,6 +161,38 @@ description. The date fields label their trigger with `aria-labelledby` (the
|
|
|
156
161
|
label, then the trigger) rather than `<label for>`, so the picked date is
|
|
157
162
|
part of its name.
|
|
158
163
|
|
|
164
|
+
Errors wait for the person. When `meta.isTouched` is `false`, a `form/*`
|
|
165
|
+
field hides its error text and invalid styling until the field is touched or
|
|
166
|
+
its `submitted` prop is set (TanStack Form:
|
|
167
|
+
`form.state.submissionAttempts > 0`). Pass TanStack's `field.state.meta` as
|
|
168
|
+
is and this works on its own; meta without `isTouched` shows errors as soon
|
|
169
|
+
as they exist, as before. `showError={false}` always hides them. Every
|
|
170
|
+
`form/*` field, `FormSelectField` and both date fields included, takes
|
|
171
|
+
`submitted` and `showError`.
|
|
172
|
+
|
|
173
|
+
`FormControlField` gives a control the kit does not ship the same label,
|
|
174
|
+
required marker, hint, error, ids, and error timing. `children` is a
|
|
175
|
+
function that receives the props to spread on the control:
|
|
176
|
+
|
|
177
|
+
```tsx
|
|
178
|
+
<div className="grid grid-cols-2 items-start gap-3">
|
|
179
|
+
<FormTextField field={amount} label="Amount" required />
|
|
180
|
+
<FormControlField label="Currency" meta={currency.state.meta}>
|
|
181
|
+
{(control) => (
|
|
182
|
+
<Combobox
|
|
183
|
+
{...control}
|
|
184
|
+
options={options}
|
|
185
|
+
value={value}
|
|
186
|
+
onValueChange={set}
|
|
187
|
+
/>
|
|
188
|
+
)}
|
|
189
|
+
</FormControlField>
|
|
190
|
+
</div>
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
Errors sit below the control and take no space until they show, so use
|
|
194
|
+
`items-start` on a row of fields to keep the controls on one top edge.
|
|
195
|
+
|
|
159
196
|
Set `required` on a `form/*` field to mark its label with a visible `*`.
|
|
160
197
|
The marker is hidden from screen readers, which announce the control's own
|
|
161
198
|
`required` (`aria-required` on the select trigger). The date pickers are
|
|
@@ -262,6 +299,50 @@ choice under `atlas-theme` unless `storageKey` is set. The `d` dark/light toggle
|
|
|
262
299
|
is off by default; pass `enableShortcut` to turn it on, and give people a setting
|
|
263
300
|
to turn it off again, since a single-key shortcut can fire by accident (WCAG 2.1.4).
|
|
264
301
|
|
|
302
|
+
## App shell navigation
|
|
303
|
+
|
|
304
|
+
Pill links for top-level destinations (Calendar, Tasks, Settings). Pass
|
|
305
|
+
`active` from the route; the kit does not read the URL. Wrap a router link
|
|
306
|
+
with `asChild`, or use the optional Next adapter.
|
|
307
|
+
|
|
308
|
+
```tsx
|
|
309
|
+
import Link from "next/link"
|
|
310
|
+
import { NavLink } from "atlas-react-kit/nav-link"
|
|
311
|
+
|
|
312
|
+
export function AppNav({ pathname }: { pathname: string }) {
|
|
313
|
+
return (
|
|
314
|
+
<nav aria-label="App" className="flex items-center gap-1">
|
|
315
|
+
<NavLink asChild active={pathname === "/calendar"}>
|
|
316
|
+
<Link href="/calendar">Calendar</Link>
|
|
317
|
+
</NavLink>
|
|
318
|
+
<NavLink asChild active={pathname === "/tasks"}>
|
|
319
|
+
<Link href="/tasks">Tasks</Link>
|
|
320
|
+
</NavLink>
|
|
321
|
+
</nav>
|
|
322
|
+
)
|
|
323
|
+
}
|
|
324
|
+
```
|
|
325
|
+
|
|
326
|
+
```tsx
|
|
327
|
+
import { NavLink } from "atlas-react-kit/next/nav-link"
|
|
328
|
+
|
|
329
|
+
export function AppNav({ pathname }: { pathname: string }) {
|
|
330
|
+
return (
|
|
331
|
+
<nav aria-label="App" className="flex items-center gap-1">
|
|
332
|
+
<NavLink href="/calendar" active={pathname === "/calendar"}>
|
|
333
|
+
Calendar
|
|
334
|
+
</NavLink>
|
|
335
|
+
<NavLink href="/tasks" active={pathname === "/tasks"}>
|
|
336
|
+
Tasks
|
|
337
|
+
</NavLink>
|
|
338
|
+
</nav>
|
|
339
|
+
)
|
|
340
|
+
}
|
|
341
|
+
```
|
|
342
|
+
|
|
343
|
+
In-page view switches that still belong in the URL use `TabsNav`, not
|
|
344
|
+
`NavLink`.
|
|
345
|
+
|
|
265
346
|
## URL navigation in Next.js
|
|
266
347
|
|
|
267
348
|
```tsx
|
|
@@ -309,5 +390,5 @@ export function ViewSwitcher({ view }: { view: string }) {
|
|
|
309
390
|
```
|
|
310
391
|
|
|
311
392
|
Hook-driven controls and form handlers declare their client boundary; presentational
|
|
312
|
-
modules such as `button`, `badge`, `card`, and `skeleton` can be composed from
|
|
393
|
+
modules such as `button`, `badge`, `card`, `nav-link`, and `skeleton` can be composed from
|
|
313
394
|
React Server Components. Pass interactive callbacks from client components.
|
package/DESIGN_RULES.md
CHANGED
|
@@ -138,9 +138,22 @@ or its `data-slot`.
|
|
|
138
138
|
`text-base md:text-sm`, `border-input`, and `shadow-xs`. Do not restyle those
|
|
139
139
|
triggers as outline buttons in application code.
|
|
140
140
|
|
|
141
|
+
`form/*` fields stack label, hint, control, and error with `gap-1.5`, and the
|
|
142
|
+
error sits below the control. It takes no space until it shows, so a row of
|
|
143
|
+
fields keeps its controls on one top edge only with `items-start`
|
|
144
|
+
(`grid grid-cols-2 items-start gap-3`); the default `stretch` is fine for a
|
|
145
|
+
single column. Wrap a control the kit does not ship (a combobox, a segmented
|
|
146
|
+
picker) in `FormControlField` instead of `Field` and `FieldLabel`, whose
|
|
147
|
+
`gap-3` spacing does not match. A field shows its error once
|
|
148
|
+
`meta.isTouched` is true or `submitted` is set, never on a field the person
|
|
149
|
+
has not reached yet.
|
|
150
|
+
|
|
141
151
|
Use `Tabs` with actual `TabsContent` panels. For URL view switches, use a labelled
|
|
142
152
|
`TabsNav` with links and `aria-current="page"` on the active link. The optional
|
|
143
|
-
Next adapter provides `TabsNavLink` with this behavior.
|
|
153
|
+
Next adapter provides `TabsNavLink` with this behavior. App-shell destinations
|
|
154
|
+
(Calendar, Tasks, Settings) use `NavLink` from `nav-link`: a pill with `active`
|
|
155
|
+
from the app, not tabs. Pass `asChild` for a router link, or use the optional
|
|
156
|
+
Next adapter `next/nav-link`. `TabsNav` and `TabsList`
|
|
144
157
|
scroll overflowing labels inside themselves (`overflow-x-auto overflow-y-hidden`,
|
|
145
158
|
with padding for the active border and focus ring counted in the scroller
|
|
146
159
|
height, and an edge fade like `Table`). Do not wrap them in another
|
package/README.md
CHANGED
|
@@ -5,7 +5,7 @@ colors, and distinct selected states. 0.6.0 added generic pieces apps were
|
|
|
5
5
|
keeping locally (`InfoTip`, `ColorLegend`, `SearchPicker`, `Markdown`,
|
|
6
6
|
`StatTile`, `Meter`, `Slider`) plus the ToggleGroup selected-state fix.
|
|
7
7
|
React 19, Tailwind 4, and Node >=20.19 are required. Next 16 is needed only
|
|
8
|
-
when using `next/tabs-nav-link`.
|
|
8
|
+
when using `next/nav-link` or `next/tabs-nav-link`.
|
|
9
9
|
|
|
10
10
|
```sh
|
|
11
11
|
npm install --save-exact atlas-react-kit@0.12.0
|
package/dist/alert.d.ts
CHANGED
|
@@ -6,7 +6,7 @@ import { type VariantProps } from "class-variance-authority";
|
|
|
6
6
|
* (the destructive tint is too faint for text in light mode).
|
|
7
7
|
*/
|
|
8
8
|
declare const alertVariants: (props?: ({
|
|
9
|
-
variant?: "default" | "
|
|
9
|
+
variant?: "default" | "destructive" | "success" | "warning" | null | undefined;
|
|
10
10
|
} & import("class-variance-authority/types").ClassProp) | undefined) => string;
|
|
11
11
|
/**
|
|
12
12
|
* Inline message about the page or a section (save failed, trial ending).
|
package/dist/badge.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
2
|
import { type VariantProps } from "class-variance-authority";
|
|
3
3
|
declare const badgeVariants: (props?: ({
|
|
4
|
-
variant?: "default" | "
|
|
5
|
-
tone?: "
|
|
4
|
+
variant?: "default" | "destructive" | "ghost" | "link" | "outline" | "secondary" | "soft" | null | undefined;
|
|
5
|
+
tone?: "destructive" | "neutral" | "success" | "warning" | null | undefined;
|
|
6
6
|
} & import("class-variance-authority/types").ClassProp) | undefined) => string;
|
|
7
7
|
declare function Badge({ className, variant, tone, asChild, ...props }: React.ComponentProps<"span"> & VariantProps<typeof badgeVariants> & {
|
|
8
8
|
asChild?: boolean;
|
package/dist/button.d.ts
CHANGED
|
@@ -8,8 +8,8 @@ import { type VariantProps } from "class-variance-authority";
|
|
|
8
8
|
*/
|
|
9
9
|
declare const quietButtonInteraction: string;
|
|
10
10
|
declare const buttonVariants: (props?: ({
|
|
11
|
-
variant?: "default" | "
|
|
12
|
-
size?: "default" | "
|
|
11
|
+
variant?: "default" | "destructive" | "ghost" | "link" | "outline" | "secondary" | null | undefined;
|
|
12
|
+
size?: "default" | "icon" | "icon-lg" | "icon-sm" | "icon-xs" | "lg" | "sm" | "xs" | null | undefined;
|
|
13
13
|
} & import("class-variance-authority/types").ClassProp) | undefined) => string;
|
|
14
14
|
declare function Button({ className, variant, size, asChild, ...props }: React.ComponentProps<"button"> & VariantProps<typeof buttonVariants> & {
|
|
15
15
|
asChild?: boolean;
|
package/dist/date-picker.d.ts
CHANGED
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
|
-
import type
|
|
3
|
-
import { toDate, type DateLike } from "./internal/date-like.js";
|
|
2
|
+
import { toDate, type DateLike, type DateMatcher, type DateRange } from "./internal/date-like.js";
|
|
4
3
|
import { type PickerCalendarProps, type PickerTriggerProps } from "./picker-shell.js";
|
|
5
|
-
export type { DateRange };
|
|
6
|
-
export type { DateLike };
|
|
4
|
+
export type { DateLike, DateMatcher, DateRange };
|
|
7
5
|
export { toDate };
|
|
8
6
|
/** From/to value for `DatePickerButton` in range mode; each side is `DateLike`. */
|
|
9
7
|
export type DatePickerRangeValue = {
|
package/dist/date-picker.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
3
|
import * as React from "react";
|
|
4
4
|
import { format, isSameDay } from "date-fns";
|
|
5
|
-
import { toDate } from "./internal/date-like.js";
|
|
5
|
+
import { toDate, } from "./internal/date-like.js";
|
|
6
6
|
import { MD_MEDIA_QUERY, useMediaQuery } from "./internal/use-media-query.js";
|
|
7
7
|
import { LazyCalendar } from "./lazy-calendar.js";
|
|
8
8
|
import { PickerClearFooter, PickerShell, limitCalendarProps, } from "./picker-shell.js";
|
package/dist/error-boundary.d.ts
CHANGED
|
@@ -31,6 +31,6 @@ export declare class ErrorBoundary extends Component<ErrorBoundaryProps, ErrorBo
|
|
|
31
31
|
componentDidCatch(error: Error, info: ErrorInfo): void;
|
|
32
32
|
retry: () => void;
|
|
33
33
|
reload: () => void;
|
|
34
|
-
render(): string | number | bigint | boolean | Iterable<ReactNode> | Promise<string | number | bigint | boolean |
|
|
34
|
+
render(): string | number | bigint | boolean | import("react").JSX.Element | Iterable<ReactNode> | Promise<string | number | bigint | boolean | Iterable<ReactNode> | import("react").ReactElement<unknown, string | import("react").JSXElementConstructor<any>> | import("react").ReactPortal | null | undefined> | null | undefined;
|
|
35
35
|
}
|
|
36
36
|
export {};
|
package/dist/field.d.ts
CHANGED
|
@@ -19,7 +19,7 @@ declare function FieldGroup({ className, ...props }: React.ComponentProps<"div">
|
|
|
19
19
|
* content in a row.
|
|
20
20
|
*/
|
|
21
21
|
declare const fieldVariants: (props?: ({
|
|
22
|
-
orientation?: "horizontal" | "
|
|
22
|
+
orientation?: "horizontal" | "responsive" | "vertical" | null | undefined;
|
|
23
23
|
} & import("class-variance-authority/types").ClassProp) | undefined) => string;
|
|
24
24
|
declare function Field({ className, orientation, ...props }: React.ComponentProps<"div"> & VariantProps<typeof fieldVariants>): import("react").JSX.Element;
|
|
25
25
|
declare function FieldContent({ className, ...props }: React.ComponentProps<"div">): import("react").JSX.Element;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { type ComponentProps, type ReactNode } from "react";
|
|
2
|
+
import type { ErrorTimingProps, FieldMetaState } from "./field-error.js";
|
|
3
|
+
/** Props `FormControlField` hands its control: spread them onto it. */
|
|
4
|
+
export type FormControlProps = {
|
|
5
|
+
id: string;
|
|
6
|
+
"aria-invalid": true | undefined;
|
|
7
|
+
"aria-describedby": string | undefined;
|
|
8
|
+
"aria-required": true | undefined;
|
|
9
|
+
};
|
|
10
|
+
type Props = ErrorTimingProps & Omit<ComponentProps<"div">, "id" | "children"> & {
|
|
11
|
+
/** Control id the label points at. Defaults to a generated id. */
|
|
12
|
+
id?: string;
|
|
13
|
+
label: string;
|
|
14
|
+
/** Hint between the label and the control, read with the control. */
|
|
15
|
+
description?: ReactNode;
|
|
16
|
+
/** Marks the label and passes `aria-required` to the control. */
|
|
17
|
+
required?: boolean;
|
|
18
|
+
/** Error meta, the same shape the other `form/*` fields take. */
|
|
19
|
+
meta?: FieldMetaState;
|
|
20
|
+
/** Renders the control from the id and ARIA props it should carry. */
|
|
21
|
+
children: (control: FormControlProps) => ReactNode;
|
|
22
|
+
};
|
|
23
|
+
/**
|
|
24
|
+
* The `form/*` field layout for a control the kit does not ship (a
|
|
25
|
+
* combobox, a segmented picker): label with the required marker, optional
|
|
26
|
+
* hint, the control, and its error, with the same spacing, ids, and error
|
|
27
|
+
* timing as `FormTextField`. `className` and other div props go to the
|
|
28
|
+
* wrapper (`data-slot="form-field"`).
|
|
29
|
+
*/
|
|
30
|
+
export declare function FormControlField({ id: idProp, label, description, required, meta, submitted, showError, children, ...props }: Props): import("react").JSX.Element;
|
|
31
|
+
export {};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
|
+
import { useId } from "react";
|
|
4
|
+
import { describedBy, descriptionIdFor, FormFieldShell, invalidWhen, metaErrorText, } from "./field-shell.js";
|
|
5
|
+
/**
|
|
6
|
+
* The `form/*` field layout for a control the kit does not ship (a
|
|
7
|
+
* combobox, a segmented picker): label with the required marker, optional
|
|
8
|
+
* hint, the control, and its error, with the same spacing, ids, and error
|
|
9
|
+
* timing as `FormTextField`. `className` and other div props go to the
|
|
10
|
+
* wrapper (`data-slot="form-field"`).
|
|
11
|
+
*/
|
|
12
|
+
export function FormControlField({ id: idProp, label, description, required, meta, submitted, showError, children, ...props }) {
|
|
13
|
+
const generatedId = useId();
|
|
14
|
+
const id = idProp ?? generatedId;
|
|
15
|
+
const errorText = metaErrorText(meta, { submitted, showError });
|
|
16
|
+
return (_jsx(FormFieldShell, { ...props, id: id, label: label, description: description, errorText: errorText, required: required, children: children({
|
|
17
|
+
id,
|
|
18
|
+
"aria-invalid": invalidWhen(errorText),
|
|
19
|
+
"aria-describedby": describedBy(id, errorText, description ? descriptionIdFor(id) : undefined),
|
|
20
|
+
"aria-required": required || undefined,
|
|
21
|
+
}) }));
|
|
22
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { type DateLike, type DatePickerCalendarProps } from "../date-picker.js";
|
|
2
|
-
import type { FieldMetaState } from "./field-error.js";
|
|
3
|
-
type Props = {
|
|
2
|
+
import type { ErrorTimingProps, FieldMetaState } from "./field-error.js";
|
|
3
|
+
type Props = ErrorTimingProps & {
|
|
4
4
|
value: DateLike;
|
|
5
5
|
onValueChange: (date: Date | undefined) => void;
|
|
6
6
|
label: string;
|
|
@@ -23,5 +23,5 @@ type Props = {
|
|
|
23
23
|
calendarProps?: DatePickerCalendarProps;
|
|
24
24
|
};
|
|
25
25
|
/** Date picker with built-in field error text and invalid styling. */
|
|
26
|
-
export declare function FormDatePickerField({ onValueChange, label, meta, required, fieldClassName, ...picker }: Props): import("react").JSX.Element;
|
|
26
|
+
export declare function FormDatePickerField({ onValueChange, label, meta, submitted, showError, required, fieldClassName, ...picker }: Props): import("react").JSX.Element;
|
|
27
27
|
export {};
|
|
@@ -4,8 +4,8 @@ import { useId } from "react";
|
|
|
4
4
|
import { DatePickerButton, } from "../date-picker.js";
|
|
5
5
|
import { FormFieldShell, metaErrorText, pickerFieldAria, } from "./field-shell.js";
|
|
6
6
|
/** Date picker with built-in field error text and invalid styling. */
|
|
7
|
-
export function FormDatePickerField({ onValueChange, label, meta, required, fieldClassName, ...picker }) {
|
|
8
|
-
const errorText = metaErrorText(meta);
|
|
7
|
+
export function FormDatePickerField({ onValueChange, label, meta, submitted, showError, required, fieldClassName, ...picker }) {
|
|
8
|
+
const errorText = metaErrorText(meta, { submitted, showError });
|
|
9
9
|
const id = useId();
|
|
10
10
|
return (_jsx(FormFieldShell, { id: id, label: label, errorText: errorText, required: required, className: fieldClassName, children: _jsx(DatePickerButton, { ...picker, id: id, onChange: onValueChange, ...pickerFieldAria(id, errorText, required) }) }));
|
|
11
11
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { DateLike, DatePickerCalendarProps } from "../date-picker.js";
|
|
2
|
-
import type { FieldMetaState } from "./field-error.js";
|
|
3
|
-
type Props = {
|
|
2
|
+
import type { ErrorTimingProps, FieldMetaState } from "./field-error.js";
|
|
3
|
+
type Props = ErrorTimingProps & {
|
|
4
4
|
value: Date | null;
|
|
5
5
|
onValueChange: (date: Date | null) => void;
|
|
6
6
|
label: string;
|
|
@@ -25,5 +25,5 @@ type Props = {
|
|
|
25
25
|
calendarProps?: DatePickerCalendarProps;
|
|
26
26
|
};
|
|
27
27
|
/** Date-time picker with built-in field error text and invalid styling. */
|
|
28
|
-
export declare function FormDateTimePickerField({ onValueChange, label, meta, required, fieldClassName, ...picker }: Props): import("react").JSX.Element;
|
|
28
|
+
export declare function FormDateTimePickerField({ onValueChange, label, meta, submitted, showError, required, fieldClassName, ...picker }: Props): import("react").JSX.Element;
|
|
29
29
|
export {};
|
|
@@ -4,8 +4,8 @@ import { useId } from "react";
|
|
|
4
4
|
import { DateTimePickerButton } from "../date-time-picker.js";
|
|
5
5
|
import { FormFieldShell, metaErrorText, pickerFieldAria, } from "./field-shell.js";
|
|
6
6
|
/** Date-time picker with built-in field error text and invalid styling. */
|
|
7
|
-
export function FormDateTimePickerField({ onValueChange, label, meta, required, fieldClassName, ...picker }) {
|
|
8
|
-
const errorText = metaErrorText(meta);
|
|
7
|
+
export function FormDateTimePickerField({ onValueChange, label, meta, submitted, showError, required, fieldClassName, ...picker }) {
|
|
8
|
+
const errorText = metaErrorText(meta, { submitted, showError });
|
|
9
9
|
const id = useId();
|
|
10
10
|
return (_jsx(FormFieldShell, { id: id, label: label, errorText: errorText, required: required, className: fieldClassName, children: _jsx(DateTimePickerButton, { ...picker, id: id, onChange: onValueChange, ...pickerFieldAria(id, errorText, required) }) }));
|
|
11
11
|
}
|
|
@@ -3,6 +3,24 @@ export type FieldErrorMessage = string | {
|
|
|
3
3
|
} | undefined;
|
|
4
4
|
export type FieldMetaState = {
|
|
5
5
|
errors: FieldErrorMessage[];
|
|
6
|
+
/**
|
|
7
|
+
* Whether the person has changed or left the field (TanStack Form's
|
|
8
|
+
* `meta.isTouched`). When it is `false`, the `form/*` fields hold back
|
|
9
|
+
* the error until the field is touched or their `submitted` prop is set.
|
|
10
|
+
* Leave it out to show errors as soon as they exist.
|
|
11
|
+
*/
|
|
12
|
+
isTouched?: boolean;
|
|
13
|
+
};
|
|
14
|
+
/** When a field shows its error: the `form/*` fields' shared timing props. */
|
|
15
|
+
export type ErrorTimingProps = {
|
|
16
|
+
/**
|
|
17
|
+
* Set once the form has been submitted (TanStack Form:
|
|
18
|
+
* `form.state.submissionAttempts > 0`), so an untouched field shows its
|
|
19
|
+
* error too. Only matters when `meta.isTouched` is passed.
|
|
20
|
+
*/
|
|
21
|
+
submitted?: boolean;
|
|
22
|
+
/** When false, never shows error text or invalid styling. Default true. */
|
|
23
|
+
showError?: boolean;
|
|
6
24
|
};
|
|
7
25
|
/** First TanStack Form error message for a field, if any. */
|
|
8
26
|
export declare function getFieldErrorMessage(message: FieldErrorMessage): string | undefined;
|
|
@@ -1,15 +1,22 @@
|
|
|
1
|
-
import type { ReactNode } from "react";
|
|
2
|
-
import { type FieldMetaState } from "./field-error.js";
|
|
1
|
+
import type { ComponentProps, ReactNode } from "react";
|
|
2
|
+
import { type ErrorTimingProps, type FieldMetaState } from "./field-error.js";
|
|
3
3
|
/**
|
|
4
4
|
* Not a public entry. The layout and error wiring every `form/*` field
|
|
5
|
-
* shares, so a field only says which control it renders.
|
|
5
|
+
* shares, so a field only says which control it renders. Custom controls
|
|
6
|
+
* get the same shell through the public `FormControlField`.
|
|
6
7
|
*/
|
|
7
|
-
/**
|
|
8
|
-
|
|
8
|
+
/**
|
|
9
|
+
* First error in `meta`, or `undefined` when there is no meta, no error,
|
|
10
|
+
* `showError` is false, or the field is untouched (`isTouched: false`)
|
|
11
|
+
* and the form has not been submitted.
|
|
12
|
+
*/
|
|
13
|
+
export declare function metaErrorText(meta: FieldMetaState | undefined, { submitted, showError }?: ErrorTimingProps): string | undefined;
|
|
9
14
|
/** `aria-invalid` for the control: `true` while there is error text. */
|
|
10
15
|
export declare function invalidWhen(errorText: string | undefined): true | undefined;
|
|
11
16
|
/** Id of the label `FormFieldShell` renders for control `id`. */
|
|
12
17
|
export declare function labelIdFor(id: string): string;
|
|
18
|
+
/** Id of the hint `FormFieldShell` renders for control `id`. */
|
|
19
|
+
export declare function descriptionIdFor(id: string): string;
|
|
13
20
|
/** Id of the error text `FormFieldShell` renders for control `id`. */
|
|
14
21
|
export declare function errorIdFor(id: string): string;
|
|
15
22
|
/**
|
|
@@ -38,16 +45,20 @@ export declare function pickerFieldAria(id: string, errorText: string | undefine
|
|
|
38
45
|
* `labelAction` sits on the label's row (the Markdown field's mode toggle).
|
|
39
46
|
* Set `labelFor` to false when the control is not rendered (Markdown
|
|
40
47
|
* preview), so the label does not point at a missing element.
|
|
41
|
-
* `
|
|
48
|
+
* `description` is a hint between the label and the control, with id
|
|
49
|
+
* `descriptionIdFor(id)`. `className` and other div props go to the
|
|
50
|
+
* wrapper (`data-slot="form-field"`), e.g. a width.
|
|
42
51
|
* `required` marks the label; the control still sets its own `required`.
|
|
52
|
+
* The error sits below the control and takes no space while there is none,
|
|
53
|
+
* so put fields in a grid row with `items-start` to keep controls aligned.
|
|
43
54
|
*/
|
|
44
|
-
export declare function FormFieldShell({ id, label, labelAction, errorText, labelFor, required, className, children, }: {
|
|
55
|
+
export declare function FormFieldShell({ id, label, labelAction, description, errorText, labelFor, required, className, children, ...props }: Omit<ComponentProps<"div">, "id" | "children"> & {
|
|
45
56
|
id: string;
|
|
46
57
|
label: string;
|
|
47
58
|
labelAction?: ReactNode;
|
|
59
|
+
description?: ReactNode;
|
|
48
60
|
errorText: string | undefined;
|
|
49
61
|
labelFor?: boolean;
|
|
50
62
|
required?: boolean;
|
|
51
|
-
className?: string;
|
|
52
63
|
children: ReactNode;
|
|
53
64
|
}): import("react").JSX.Element;
|
package/dist/form/field-shell.js
CHANGED
|
@@ -5,11 +5,20 @@ import { cn } from "../utils.js";
|
|
|
5
5
|
import { FieldError, getFieldErrorMessage, } from "./field-error.js";
|
|
6
6
|
/**
|
|
7
7
|
* Not a public entry. The layout and error wiring every `form/*` field
|
|
8
|
-
* shares, so a field only says which control it renders.
|
|
8
|
+
* shares, so a field only says which control it renders. Custom controls
|
|
9
|
+
* get the same shell through the public `FormControlField`.
|
|
9
10
|
*/
|
|
10
|
-
/**
|
|
11
|
-
|
|
12
|
-
|
|
11
|
+
/**
|
|
12
|
+
* First error in `meta`, or `undefined` when there is no meta, no error,
|
|
13
|
+
* `showError` is false, or the field is untouched (`isTouched: false`)
|
|
14
|
+
* and the form has not been submitted.
|
|
15
|
+
*/
|
|
16
|
+
export function metaErrorText(meta, { submitted, showError = true } = {}) {
|
|
17
|
+
if (!meta || !showError)
|
|
18
|
+
return undefined;
|
|
19
|
+
if (meta.isTouched === false && !submitted)
|
|
20
|
+
return undefined;
|
|
21
|
+
return getFieldErrorMessage(meta.errors[0]);
|
|
13
22
|
}
|
|
14
23
|
/** `aria-invalid` for the control: `true` while there is error text. */
|
|
15
24
|
export function invalidWhen(errorText) {
|
|
@@ -19,6 +28,10 @@ export function invalidWhen(errorText) {
|
|
|
19
28
|
export function labelIdFor(id) {
|
|
20
29
|
return `${id}-label`;
|
|
21
30
|
}
|
|
31
|
+
/** Id of the hint `FormFieldShell` renders for control `id`. */
|
|
32
|
+
export function descriptionIdFor(id) {
|
|
33
|
+
return `${id}-description`;
|
|
34
|
+
}
|
|
22
35
|
/** Id of the error text `FormFieldShell` renders for control `id`. */
|
|
23
36
|
export function errorIdFor(id) {
|
|
24
37
|
return `${id}-error`;
|
|
@@ -56,10 +69,14 @@ export function pickerFieldAria(id, errorText, required) {
|
|
|
56
69
|
* `labelAction` sits on the label's row (the Markdown field's mode toggle).
|
|
57
70
|
* Set `labelFor` to false when the control is not rendered (Markdown
|
|
58
71
|
* preview), so the label does not point at a missing element.
|
|
59
|
-
* `
|
|
72
|
+
* `description` is a hint between the label and the control, with id
|
|
73
|
+
* `descriptionIdFor(id)`. `className` and other div props go to the
|
|
74
|
+
* wrapper (`data-slot="form-field"`), e.g. a width.
|
|
60
75
|
* `required` marks the label; the control still sets its own `required`.
|
|
76
|
+
* The error sits below the control and takes no space while there is none,
|
|
77
|
+
* so put fields in a grid row with `items-start` to keep controls aligned.
|
|
61
78
|
*/
|
|
62
|
-
export function FormFieldShell({ id, label, labelAction, errorText, labelFor = true, required, className, children, }) {
|
|
79
|
+
export function FormFieldShell({ id, label, labelAction, description, errorText, labelFor = true, required, className, children, ...props }) {
|
|
63
80
|
const labelNode = (_jsx(Label, { id: labelIdFor(id), htmlFor: labelFor ? id : undefined, required: required, children: label }));
|
|
64
|
-
return (_jsxs("div", { "data-slot": "form-field", className: cn("flex flex-col gap-1.5", className), children: [labelAction ? (_jsxs("div", { "data-slot": "form-field-header", className: "flex items-center justify-between gap-2", children: [labelNode, labelAction] })) : (labelNode), children, required ? (_jsx("span", { id: requiredIdFor(id), hidden: true, children: "Required" })) : null, _jsx(FieldError, { id: errorIdFor(id), text: errorText })] }));
|
|
81
|
+
return (_jsxs("div", { "data-slot": "form-field", className: cn("flex flex-col gap-1.5", className), ...props, children: [labelAction ? (_jsxs("div", { "data-slot": "form-field-header", className: "flex items-center justify-between gap-2", children: [labelNode, labelAction] })) : (labelNode), description ? (_jsx("p", { id: descriptionIdFor(id), "data-slot": "form-field-description", className: "text-xs text-muted-foreground", children: description })) : null, children, required ? (_jsx("span", { id: requiredIdFor(id), hidden: true, children: "Required" })) : null, _jsx(FieldError, { id: errorIdFor(id), text: errorText })] }));
|
|
65
82
|
}
|
|
@@ -1,9 +1,10 @@
|
|
|
1
|
+
import type { ErrorTimingProps } from "./field-error.js";
|
|
1
2
|
import type { TextFieldApi } from "./text-field.js";
|
|
2
3
|
/**
|
|
3
4
|
* GFM body field: Write is a textarea, Preview is the sanitized Markdown
|
|
4
5
|
* renderer. String-in/string-out — not a WYSIWYG document model.
|
|
5
6
|
*/
|
|
6
|
-
export declare function FormMarkdownField({ field, id, label, placeholder, rows, showError, required, className, fieldClassName, previewClassName, }: {
|
|
7
|
+
export declare function FormMarkdownField({ field, id, label, placeholder, rows, showError, submitted, required, className, fieldClassName, previewClassName, }: ErrorTimingProps & {
|
|
7
8
|
field: TextFieldApi;
|
|
8
9
|
/**
|
|
9
10
|
* Control id the label points at. Defaults to `field.name`; pass a unique
|
|
@@ -13,7 +14,6 @@ export declare function FormMarkdownField({ field, id, label, placeholder, rows,
|
|
|
13
14
|
label: string;
|
|
14
15
|
placeholder?: string;
|
|
15
16
|
rows?: number;
|
|
16
|
-
showError?: boolean;
|
|
17
17
|
/** Sets `required` on the Write textarea and marks the label. */
|
|
18
18
|
required?: boolean;
|
|
19
19
|
/** Forwarded to the Write textarea. */
|
|
@@ -16,10 +16,10 @@ const Markdown = lazy(() => loadMarkdown().then((module) => ({ default: module.M
|
|
|
16
16
|
* GFM body field: Write is a textarea, Preview is the sanitized Markdown
|
|
17
17
|
* renderer. String-in/string-out — not a WYSIWYG document model.
|
|
18
18
|
*/
|
|
19
|
-
export function FormMarkdownField({ field, id = field.name, label, placeholder = "Markdown supported", rows = 5, showError
|
|
19
|
+
export function FormMarkdownField({ field, id = field.name, label, placeholder = "Markdown supported", rows = 5, showError, submitted, required, className, fieldClassName, previewClassName, }) {
|
|
20
20
|
const [mode, setMode] = useState("write");
|
|
21
21
|
const onChange = onInputChange(field.handleChange);
|
|
22
|
-
const errorText =
|
|
22
|
+
const errorText = metaErrorText(field.state.meta, { submitted, showError });
|
|
23
23
|
const trimmed = field.state.value.trim();
|
|
24
24
|
function handleModeChange(value) {
|
|
25
25
|
if (value === "write" || value === "preview")
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { type ReactNode } from "react";
|
|
2
|
-
import type { FieldMetaState } from "./field-error.js";
|
|
3
|
-
type Props = {
|
|
2
|
+
import type { ErrorTimingProps, FieldMetaState } from "./field-error.js";
|
|
3
|
+
type Props = ErrorTimingProps & {
|
|
4
4
|
value: string | undefined;
|
|
5
5
|
meta?: FieldMetaState;
|
|
6
6
|
label: string;
|
|
@@ -17,5 +17,5 @@ type Props = {
|
|
|
17
17
|
children: ReactNode;
|
|
18
18
|
};
|
|
19
19
|
/** Select control with built-in field error text and invalid styling. */
|
|
20
|
-
export declare function FormSelectField({ value, meta, label, onValueChange, placeholder, required, className, fieldClassName, contentClassName, children, }: Props): import("react").JSX.Element;
|
|
20
|
+
export declare function FormSelectField({ value, meta, submitted, showError, label, onValueChange, placeholder, required, className, fieldClassName, contentClassName, children, }: Props): import("react").JSX.Element;
|
|
21
21
|
export {};
|
|
@@ -4,8 +4,8 @@ import { useId } from "react";
|
|
|
4
4
|
import { Select, SelectContent, SelectTrigger, SelectValue } from "../select.js";
|
|
5
5
|
import { describedBy, FormFieldShell, invalidWhen, metaErrorText, } from "./field-shell.js";
|
|
6
6
|
/** Select control with built-in field error text and invalid styling. */
|
|
7
|
-
export function FormSelectField({ value, meta, label, onValueChange, placeholder, required, className, fieldClassName, contentClassName, children, }) {
|
|
8
|
-
const errorText = metaErrorText(meta);
|
|
7
|
+
export function FormSelectField({ value, meta, submitted, showError, label, onValueChange, placeholder, required, className, fieldClassName, contentClassName, children, }) {
|
|
8
|
+
const errorText = metaErrorText(meta, { submitted, showError });
|
|
9
9
|
const id = useId();
|
|
10
10
|
return (_jsx(FormFieldShell, { id: id, label: label, errorText: errorText, required: required, className: fieldClassName, children: _jsxs(Select, { value: value, onValueChange: onValueChange, required: required, children: [_jsx(SelectTrigger, { id: id, className: className, "aria-required": required || undefined, "aria-invalid": invalidWhen(errorText), "aria-describedby": describedBy(id, errorText), children: _jsx(SelectValue, { placeholder: placeholder }) }), _jsx(SelectContent, { className: contentClassName, children: children })] }) }));
|
|
11
11
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { FieldMetaState } from "./field-error.js";
|
|
1
|
+
import type { ErrorTimingProps, FieldMetaState } from "./field-error.js";
|
|
2
2
|
export type TextFieldApi = {
|
|
3
3
|
name: string;
|
|
4
4
|
state: {
|
|
@@ -8,7 +8,7 @@ export type TextFieldApi = {
|
|
|
8
8
|
handleChange: (value: string) => void;
|
|
9
9
|
handleBlur: () => void;
|
|
10
10
|
};
|
|
11
|
-
export declare function FormTextField({ field, id, label, placeholder, description, autoFocus, multiline, rows, inputType, showError, autoComplete, required, minLength, className, fieldClassName, }: {
|
|
11
|
+
export declare function FormTextField({ field, id, label, placeholder, description, autoFocus, multiline, rows, inputType, showError, submitted, autoComplete, required, minLength, className, fieldClassName, }: ErrorTimingProps & {
|
|
12
12
|
field: TextFieldApi;
|
|
13
13
|
/**
|
|
14
14
|
* Control id the label points at. Defaults to `field.name`; pass a unique
|
|
@@ -25,8 +25,6 @@ export declare function FormTextField({ field, id, label, placeholder, descripti
|
|
|
25
25
|
/** Textarea rows when `multiline` is set. */
|
|
26
26
|
rows?: number;
|
|
27
27
|
inputType?: "text" | "email" | "password" | "date" | "datetime-local" | "time" | "number";
|
|
28
|
-
/** When true (default), shows meta.errors and aria-invalid styling. */
|
|
29
|
-
showError?: boolean;
|
|
30
28
|
autoComplete?: string;
|
|
31
29
|
/** Sets `required` on the control and marks the label. */
|
|
32
30
|
required?: boolean;
|
package/dist/form/text-field.js
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { jsx as _jsx
|
|
2
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
3
|
import { Input } from "../input.js";
|
|
4
4
|
import { Textarea } from "../textarea.js";
|
|
5
5
|
import { onInputChange } from "../utils.js";
|
|
6
|
-
import { describedBy, FormFieldShell, invalidWhen, metaErrorText, } from "./field-shell.js";
|
|
7
|
-
export function FormTextField({ field, id = field.name, label, placeholder, description, autoFocus, multiline, rows = 3, inputType = "text", showError
|
|
6
|
+
import { describedBy, descriptionIdFor, FormFieldShell, invalidWhen, metaErrorText, } from "./field-shell.js";
|
|
7
|
+
export function FormTextField({ field, id = field.name, label, placeholder, description, autoFocus, multiline, rows = 3, inputType = "text", showError, submitted, autoComplete, required, minLength, className, fieldClassName, }) {
|
|
8
8
|
const onChange = onInputChange(field.handleChange);
|
|
9
|
-
const errorText =
|
|
10
|
-
const descriptionId = description ?
|
|
9
|
+
const errorText = metaErrorText(field.state.meta, { submitted, showError });
|
|
10
|
+
const descriptionId = description ? descriptionIdFor(id) : undefined;
|
|
11
11
|
const controlProps = {
|
|
12
12
|
id,
|
|
13
13
|
value: field.state.value,
|
|
@@ -21,5 +21,5 @@ export function FormTextField({ field, id = field.name, label, placeholder, desc
|
|
|
21
21
|
"aria-invalid": invalidWhen(errorText),
|
|
22
22
|
"aria-describedby": describedBy(id, errorText, descriptionId),
|
|
23
23
|
};
|
|
24
|
-
return (
|
|
24
|
+
return (_jsx(FormFieldShell, { id: id, label: label, description: description, errorText: errorText, required: required, className: fieldClassName, children: multiline ? (_jsx(Textarea, { ...controlProps, rows: rows })) : (_jsx(Input, { ...controlProps, type: inputType, autoFocus: autoFocus })) }));
|
|
25
25
|
}
|
|
@@ -6,3 +6,26 @@ export type DateLike = Date | string | number | null | undefined;
|
|
|
6
6
|
* shift when the runtime timezone is behind UTC.
|
|
7
7
|
*/
|
|
8
8
|
export declare function toDate(value: DateLike): Date | undefined;
|
|
9
|
+
/**
|
|
10
|
+
* A from/to pair of days. `to` is unset while a range is half picked. Same
|
|
11
|
+
* shape as Day Picker's, so either can be passed where the other is expected.
|
|
12
|
+
*/
|
|
13
|
+
export type DateRange = {
|
|
14
|
+
from: Date | undefined;
|
|
15
|
+
to?: Date | undefined;
|
|
16
|
+
};
|
|
17
|
+
/**
|
|
18
|
+
* Matches calendar days, for `disabled` and `hidden`: `true` for every day,
|
|
19
|
+
* one date or a list, a range, days `before` / `after` a date (both: the days
|
|
20
|
+
* outside them), weekdays (`0` is Sunday), or a predicate.
|
|
21
|
+
*/
|
|
22
|
+
export type DateMatcher = boolean | Date | Date[] | DateRange | {
|
|
23
|
+
before: Date;
|
|
24
|
+
} | {
|
|
25
|
+
after: Date;
|
|
26
|
+
} | {
|
|
27
|
+
before: Date;
|
|
28
|
+
after: Date;
|
|
29
|
+
} | {
|
|
30
|
+
dayOfWeek: number | number[];
|
|
31
|
+
} | ((date: Date) => boolean);
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
/**
|
|
3
|
+
* Pill chrome for app-shell navigation. Inactive is muted text with an
|
|
4
|
+
* accent hover; active uses the shared selected wash, text, and border
|
|
5
|
+
* (`selectedState.active`) so the current page meets non-text contrast.
|
|
6
|
+
* Apps pass `active` — the kit has no router.
|
|
7
|
+
*/
|
|
8
|
+
declare const navLinkVariants: (props?: import("class-variance-authority/types").ClassProp | undefined) => string;
|
|
9
|
+
/**
|
|
10
|
+
* Pill link for app-shell destinations (Calendar, Tasks, Settings). Pass
|
|
11
|
+
* `active` from the route; the kit does not read the URL. `asChild` wraps a
|
|
12
|
+
* router `Link` with these styles, the same Slot pattern as `BreadcrumbLink`.
|
|
13
|
+
*
|
|
14
|
+
* Not a tab: in-page view switches that belong in the URL use `TabsNav`
|
|
15
|
+
* and `tabsTriggerVariants()`. Next apps can import the optional adapter
|
|
16
|
+
* from `atlas-react-kit/next/nav-link` instead of composing `asChild`.
|
|
17
|
+
*/
|
|
18
|
+
declare function NavLink({ asChild, active, className, ...props }: React.ComponentProps<"a"> & {
|
|
19
|
+
asChild?: boolean;
|
|
20
|
+
active: boolean;
|
|
21
|
+
}): React.JSX.Element;
|
|
22
|
+
export { NavLink, navLinkVariants };
|
package/dist/nav-link.js
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import * as React from "react";
|
|
3
|
+
import { cva } from "class-variance-authority";
|
|
4
|
+
import { Slot } from "radix-ui";
|
|
5
|
+
import { focusRing, selectedState } from "./internal/styles.js";
|
|
6
|
+
import { cn } from "./utils.js";
|
|
7
|
+
/**
|
|
8
|
+
* Pill chrome for app-shell navigation. Inactive is muted text with an
|
|
9
|
+
* accent hover; active uses the shared selected wash, text, and border
|
|
10
|
+
* (`selectedState.active`) so the current page meets non-text contrast.
|
|
11
|
+
* Apps pass `active` — the kit has no router.
|
|
12
|
+
*/
|
|
13
|
+
const navLinkVariants = cva([
|
|
14
|
+
focusRing,
|
|
15
|
+
selectedState.active,
|
|
16
|
+
"inline-flex items-center gap-1.5 rounded-none border border-transparent px-3 py-1.5 text-sm text-muted-foreground transition-colors hover:bg-accent data-[state=active]:font-medium",
|
|
17
|
+
]);
|
|
18
|
+
/**
|
|
19
|
+
* Pill link for app-shell destinations (Calendar, Tasks, Settings). Pass
|
|
20
|
+
* `active` from the route; the kit does not read the URL. `asChild` wraps a
|
|
21
|
+
* router `Link` with these styles, the same Slot pattern as `BreadcrumbLink`.
|
|
22
|
+
*
|
|
23
|
+
* Not a tab: in-page view switches that belong in the URL use `TabsNav`
|
|
24
|
+
* and `tabsTriggerVariants()`. Next apps can import the optional adapter
|
|
25
|
+
* from `atlas-react-kit/next/nav-link` instead of composing `asChild`.
|
|
26
|
+
*/
|
|
27
|
+
function NavLink({ asChild, active, className, ...props }) {
|
|
28
|
+
const Comp = asChild ? Slot.Root : "a";
|
|
29
|
+
return (_jsx(Comp, { "data-slot": "nav-link", "data-state": active ? "active" : "inactive", "aria-current": active ? "page" : undefined, className: cn(navLinkVariants(), className), ...props }));
|
|
30
|
+
}
|
|
31
|
+
export { NavLink, navLinkVariants };
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type * as React from "react";
|
|
2
|
+
import Link from "next/link.js";
|
|
3
|
+
/**
|
|
4
|
+
* Next.js adapter for the shell `NavLink` pill. Same contract as
|
|
5
|
+
* `TabsNavLink`: Next `Link` props plus a required `active` boolean. The
|
|
6
|
+
* kit still does not read the router; the app passes `active`.
|
|
7
|
+
*/
|
|
8
|
+
declare function NavLink({ active, className, ...props }: React.ComponentProps<typeof Link> & {
|
|
9
|
+
active: boolean;
|
|
10
|
+
}): React.JSX.Element;
|
|
11
|
+
export { NavLink };
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
|
+
import Link from "next/link.js";
|
|
4
|
+
import { navLinkVariants } from "../nav-link.js";
|
|
5
|
+
import { cn } from "../utils.js";
|
|
6
|
+
/**
|
|
7
|
+
* Next.js adapter for the shell `NavLink` pill. Same contract as
|
|
8
|
+
* `TabsNavLink`: Next `Link` props plus a required `active` boolean. The
|
|
9
|
+
* kit still does not read the router; the app passes `active`.
|
|
10
|
+
*/
|
|
11
|
+
function NavLink({ active, className, ...props }) {
|
|
12
|
+
return (_jsx(Link, { "data-slot": "nav-link", "data-state": active ? "active" : "inactive", "aria-current": active ? "page" : undefined, className: cn(navLinkVariants(), className), ...props }));
|
|
13
|
+
}
|
|
14
|
+
export { NavLink };
|
package/dist/picker-shell.d.ts
CHANGED
|
@@ -1,17 +1,42 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
|
-
import type {
|
|
3
|
-
import { type DateLike } from "./internal/date-like.js";
|
|
2
|
+
import type { Locale } from "date-fns";
|
|
3
|
+
import { type DateLike, type DateMatcher } from "./internal/date-like.js";
|
|
4
4
|
import { PopoverContent } from "./popover.js";
|
|
5
5
|
/**
|
|
6
6
|
* Not a public entry. The trigger, popover, and Clear footer that
|
|
7
7
|
* `DatePickerButton` and `DateTimePickerButton` share.
|
|
8
8
|
*/
|
|
9
9
|
/**
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
10
|
+
* Options for the picker's calendar. The picker owns the selection, focus,
|
|
11
|
+
* and month props; these are the kit's own types, so a Day Picker upgrade
|
|
12
|
+
* does not change them.
|
|
13
13
|
*/
|
|
14
|
-
export type PickerCalendarProps =
|
|
14
|
+
export type PickerCalendarProps = {
|
|
15
|
+
/** Days that cannot be picked. */
|
|
16
|
+
disabled?: DateMatcher | DateMatcher[];
|
|
17
|
+
/** Days left out of the grid. */
|
|
18
|
+
hidden?: DateMatcher | DateMatcher[];
|
|
19
|
+
/** Earliest month the calendar pages to. */
|
|
20
|
+
startMonth?: Date;
|
|
21
|
+
/** Latest month the calendar pages to. */
|
|
22
|
+
endMonth?: Date;
|
|
23
|
+
/** First day of the week, `0` (Sunday) to `6`. Defaults to the locale's. */
|
|
24
|
+
weekStartsOn?: 0 | 1 | 2 | 3 | 4 | 5 | 6;
|
|
25
|
+
/** A date-fns locale for month and weekday names. */
|
|
26
|
+
locale?: Locale;
|
|
27
|
+
/** Show the leading and trailing days of the neighbouring months. Default `true`. */
|
|
28
|
+
showOutsideDays?: boolean;
|
|
29
|
+
/** Always show six weeks, so the popover height does not change. */
|
|
30
|
+
fixedWeeks?: boolean;
|
|
31
|
+
/** Show ISO or locale week numbers in a first column. */
|
|
32
|
+
showWeekNumber?: boolean;
|
|
33
|
+
/** Month and year as text (`label`, default) or as dropdowns. */
|
|
34
|
+
captionLayout?: "label" | "dropdown" | "dropdown-months" | "dropdown-years";
|
|
35
|
+
/** The day marked as today. Defaults to the current date. */
|
|
36
|
+
today?: Date;
|
|
37
|
+
/** Classes for the calendar root. */
|
|
38
|
+
className?: string;
|
|
39
|
+
};
|
|
15
40
|
/** Props both pickers take for their trigger and popover. */
|
|
16
41
|
export type PickerTriggerProps = {
|
|
17
42
|
id?: string;
|
package/dist/picker-shell.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 { CalendarIcon } from "lucide-react";
|
|
5
5
|
import { Button } from "./button.js";
|
|
6
|
-
import { toDate } from "./internal/date-like.js";
|
|
6
|
+
import { toDate, } from "./internal/date-like.js";
|
|
7
7
|
import { PICKER_COLLISION_PADDING, pickerContentClassName, pickerTriggerClassName, pickerValueId, } from "./internal/form-control.js";
|
|
8
8
|
import { preloadCalendar } from "./lazy-calendar.js";
|
|
9
9
|
import { Popover, PopoverContent, PopoverTrigger } from "./popover.js";
|
package/dist/toggle.d.ts
CHANGED
|
@@ -9,7 +9,7 @@ import { Toggle as TogglePrimitive } from "radix-ui";
|
|
|
9
9
|
declare const selectedToggleChrome: string;
|
|
10
10
|
declare const toggleVariants: (props?: ({
|
|
11
11
|
variant?: "default" | "outline" | null | undefined;
|
|
12
|
-
size?: "default" | "
|
|
12
|
+
size?: "default" | "lg" | "sm" | null | undefined;
|
|
13
13
|
} & import("class-variance-authority/types").ClassProp) | undefined) => string;
|
|
14
14
|
declare function Toggle({ className, variant, size, ...props }: React.ComponentProps<typeof TogglePrimitive.Root> & VariantProps<typeof toggleVariants>): React.JSX.Element;
|
|
15
15
|
export { Toggle, toggleVariants, selectedToggleChrome };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "atlas-react-kit",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.13.0",
|
|
4
4
|
"description": "Shared React components and Tailwind theme for Atlas",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -112,6 +112,10 @@
|
|
|
112
112
|
"types": "./dist/meter.d.ts",
|
|
113
113
|
"default": "./dist/meter.js"
|
|
114
114
|
},
|
|
115
|
+
"./nav-link": {
|
|
116
|
+
"types": "./dist/nav-link.d.ts",
|
|
117
|
+
"default": "./dist/nav-link.js"
|
|
118
|
+
},
|
|
115
119
|
"./pagination": {
|
|
116
120
|
"types": "./dist/pagination.d.ts",
|
|
117
121
|
"default": "./dist/pagination.js"
|
|
@@ -200,6 +204,10 @@
|
|
|
200
204
|
"types": "./dist/tooltip.d.ts",
|
|
201
205
|
"default": "./dist/tooltip.js"
|
|
202
206
|
},
|
|
207
|
+
"./form/control-field": {
|
|
208
|
+
"types": "./dist/form/control-field.d.ts",
|
|
209
|
+
"default": "./dist/form/control-field.js"
|
|
210
|
+
},
|
|
203
211
|
"./form/date-picker-field": {
|
|
204
212
|
"types": "./dist/form/date-picker-field.d.ts",
|
|
205
213
|
"default": "./dist/form/date-picker-field.js"
|
|
@@ -228,6 +236,10 @@
|
|
|
228
236
|
"types": "./dist/form/text-field.d.ts",
|
|
229
237
|
"default": "./dist/form/text-field.js"
|
|
230
238
|
},
|
|
239
|
+
"./next/nav-link": {
|
|
240
|
+
"types": "./dist/next/nav-link.d.ts",
|
|
241
|
+
"default": "./dist/next/nav-link.js"
|
|
242
|
+
},
|
|
231
243
|
"./next/tabs-nav-link": {
|
|
232
244
|
"types": "./dist/next/tabs-nav-link.d.ts",
|
|
233
245
|
"default": "./dist/next/tabs-nav-link.js"
|
|
@@ -307,10 +319,10 @@
|
|
|
307
319
|
"@types/node": "^25.5.0",
|
|
308
320
|
"@types/react": "^19.2.14",
|
|
309
321
|
"@types/react-dom": "^19.2.3",
|
|
310
|
-
"@vitest/coverage-v8": "
|
|
322
|
+
"@vitest/coverage-v8": "5.0.5",
|
|
311
323
|
"date-fns": "^4.4.0",
|
|
312
324
|
"jsdom": "^29.1.1",
|
|
313
|
-
"next": "16.2
|
|
325
|
+
"next": "16.4.2",
|
|
314
326
|
"next-themes": "^0.4.6",
|
|
315
327
|
"prettier": "^3.8.1",
|
|
316
328
|
"react": "^19.2.4",
|
|
@@ -320,8 +332,8 @@
|
|
|
320
332
|
"remark-gfm": "^4.0.1",
|
|
321
333
|
"sonner": "^2.0.7",
|
|
322
334
|
"tailwindcss": "^4.2.1",
|
|
323
|
-
"typescript": "^
|
|
335
|
+
"typescript": "^7.0.2",
|
|
324
336
|
"unified": "^11.0.5",
|
|
325
|
-
"vitest": "^
|
|
337
|
+
"vitest": "^5.0.5"
|
|
326
338
|
}
|
|
327
339
|
}
|
|
@@ -61,9 +61,11 @@ Import each component from its subpath, e.g.
|
|
|
61
61
|
| One date, or a from/to range | `DatePickerButton` from `date-picker` (`mode="range"` for ranges) |
|
|
62
62
|
| Date and time (not `datetime-local`) | `DateTimePickerButton` from `date-time-picker` |
|
|
63
63
|
| Labelled field with error text | `FormTextField`, `FormSelectField`, `FormDatePickerField`, `FormDateTimePickerField`, `FormMarkdownField` from `form/*` |
|
|
64
|
+
| Labelled custom control (combobox) in a form | `FormControlField` from `form/control-field` |
|
|
64
65
|
| Form-level error (save failed) | `FormFeedbackField` from `form/feedback-field` |
|
|
65
66
|
| Content panels on one page | `Tabs`, `TabsList`, `TabsTrigger`, `TabsContent` from `tabs` |
|
|
66
67
|
| View switch that changes the URL | `TabsNav` with links: `TabsNavLink` from `next/tabs-nav-link` in Next, else `tabsTriggerVariants()` |
|
|
68
|
+
| App shell nav (Calendar, Tasks, Settings) | `NavLink` from `nav-link` (`asChild` for a router link); Next: `next/nav-link` |
|
|
67
69
|
| Status chip (paid, pending, failed) | `Badge variant="soft"` with `tone="success"`, `"warning"`, `"destructive"`, or `"neutral"` |
|
|
68
70
|
| Data table | `Table` family from `table`; `SortableTableHead`; `TableBodySkeleton` while loading |
|
|
69
71
|
| Nothing to show yet | `EmptyPanel` from `empty-panel` |
|