division-ds 0.2.1 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (82) hide show
  1. package/AGENTS.md +4 -4
  2. package/package.json +1 -1
  3. package/public/docs/components/accordion.md +1 -1
  4. package/public/docs/components/ai-label.md +1 -1
  5. package/public/docs/components/alert-dialog.md +1 -1
  6. package/public/docs/components/alert.md +1 -1
  7. package/public/docs/components/approval-request.md +1 -1
  8. package/public/docs/components/auth-form.md +1 -1
  9. package/public/docs/components/avatar-group.md +1 -1
  10. package/public/docs/components/avatar.md +1 -1
  11. package/public/docs/components/badge.md +1 -1
  12. package/public/docs/components/breadcrumbs.md +1 -1
  13. package/public/docs/components/button.md +1 -1
  14. package/public/docs/components/card.md +1 -1
  15. package/public/docs/components/carousel.md +1 -1
  16. package/public/docs/components/cart.md +1 -1
  17. package/public/docs/components/chart-panel.md +1 -1
  18. package/public/docs/components/chat-message.md +1 -1
  19. package/public/docs/components/chat-thread.md +1 -1
  20. package/public/docs/components/checkbox.md +1 -1
  21. package/public/docs/components/checkout-form.md +1 -1
  22. package/public/docs/components/command-palette.md +5 -2
  23. package/public/docs/components/data-table.md +1 -1
  24. package/public/docs/components/date-picker.md +166 -0
  25. package/public/docs/components/date-presets.md +111 -0
  26. package/public/docs/components/date-range-picker.md +135 -0
  27. package/public/docs/components/description-list.md +1 -1
  28. package/public/docs/components/dialog.md +1 -1
  29. package/public/docs/components/divider.md +1 -1
  30. package/public/docs/components/dropdown-menu.md +1 -1
  31. package/public/docs/components/empty-state.md +1 -1
  32. package/public/docs/components/footer.md +1 -1
  33. package/public/docs/components/form-field.md +2 -2
  34. package/public/docs/components/icon-button.md +1 -1
  35. package/public/docs/components/input.md +1 -1
  36. package/public/docs/components/kanban-board.md +1 -1
  37. package/public/docs/components/kbd.md +1 -1
  38. package/public/docs/components/link.md +1 -1
  39. package/public/docs/components/list-item.md +1 -1
  40. package/public/docs/components/loading-animation.md +1 -1
  41. package/public/docs/components/multi-select.md +5 -5
  42. package/public/docs/components/navbar.md +1 -1
  43. package/public/docs/components/newsletter-form.md +1 -1
  44. package/public/docs/components/page-header.md +1 -1
  45. package/public/docs/components/pagination.md +1 -1
  46. package/public/docs/components/product-card.md +1 -1
  47. package/public/docs/components/progress.md +1 -1
  48. package/public/docs/components/prompt-input.md +1 -1
  49. package/public/docs/components/quantity-stepper.md +2 -2
  50. package/public/docs/components/radio.md +1 -1
  51. package/public/docs/components/rating.md +1 -1
  52. package/public/docs/components/score-ring.md +1 -1
  53. package/public/docs/components/search.md +1 -1
  54. package/public/docs/components/section-heading.md +1 -1
  55. package/public/docs/components/segmented-control.md +1 -1
  56. package/public/docs/components/select.md +2 -2
  57. package/public/docs/components/sheet.md +1 -1
  58. package/public/docs/components/sidebar.md +1 -1
  59. package/public/docs/components/skeleton.md +1 -1
  60. package/public/docs/components/slider.md +1 -1
  61. package/public/docs/components/sparkline.md +1 -1
  62. package/public/docs/components/spinner.md +1 -1
  63. package/public/docs/components/stat.md +1 -1
  64. package/public/docs/components/status-tracker.md +1 -1
  65. package/public/docs/components/stepper.md +1 -1
  66. package/public/docs/components/switch.md +1 -1
  67. package/public/docs/components/tabs.md +1 -1
  68. package/public/docs/components/tag.md +1 -1
  69. package/public/docs/components/textarea.md +1 -1
  70. package/public/docs/components/time-field.md +127 -0
  71. package/public/docs/components/timeline.md +1 -1
  72. package/public/docs/components/toast.md +1 -1
  73. package/public/docs/components/tool-call.md +1 -1
  74. package/public/docs/components/tooltip.md +1 -1
  75. package/public/manifest/components.json +1045 -86
  76. package/scripts/lib/agent-docs.mjs +22 -1
  77. package/scripts/lib/mcp-tools.mjs +2 -14
  78. package/scripts/lib/props-validator.mjs +36 -2
  79. package/scripts/lib/search-score.mjs +24 -0
  80. package/skills/division-ds/SKILL.md +2 -2
  81. package/skills/division-ds/topics/forms.md +1 -1
  82. package/division-ds-0.2.1.tgz +0 -0
package/AGENTS.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Division DS: instructions for coding agents
2
2
 
3
- A React 19 + Tailwind 4 + Radix design system: 68 components, tokens in three layers, light and
3
+ A React 19 + Tailwind 4 + Radix design system: 72 components, tokens in three layers, light and
4
4
  dark themes, WCAG 2.2 AA. This file is the entry point. It is short on purpose; everything it
5
5
  points to is generated from the code, so it is current.
6
6
 
@@ -51,7 +51,7 @@ installs these rules into that project's `AGENTS.md`, `GEMINI.md` and Cursor rul
51
51
  `.claude/skills/` (Claude Code).
52
52
 
53
53
  An agent that supports MCP can have the docs and the same checks as tools instead:
54
- `claude mcp add division-ds -- npx -y division-ds@0.2.1 mcp` (`list_components`, `get_component`,
54
+ `claude mcp add division-ds -- npx -y division-ds@0.3.1 mcp` (`list_components`, `get_component`,
55
55
  `search`, `get_tokens`, `validate_code`, `check_contrast`, `get_add_command`,
56
56
  `get_review_checklist`).
57
57
 
@@ -91,7 +91,7 @@ provider. A project that uses the components mounts both itself (step 4 of `inst
91
91
  `src/demo/states/` files are not part of the system.
92
92
 
93
93
  **What the system does not have.** No Popover, no Combobox (single value: use `Select`; several:
94
- `MultiSelect`), no date picker. Do not reach into `src/demo/` from a component.
94
+ `MultiSelect`). Do not reach into `src/demo/` from a component.
95
95
 
96
96
  **Vocabulary.** Inside this repository, `npm run check:vocabulary` bans a list of words in `src/`,
97
97
  `scripts/` and `e2e/` (the list is `scripts/lib/vocabulary.mjs`; it is not published with the
@@ -154,7 +154,7 @@ renders `ErrorSummary` at the top and focuses it; `useFieldErrors` holds the sta
154
154
  `public/docs/helpers.md`).
155
155
 
156
156
  A `RadioGroup` in a form sits in a `<fieldset>` whose `<legend>` names it (code on the Radio
157
- page). A date is `<Input type="date">` in a `FormField`.
157
+ page). A date is `DatePicker` in a `FormField`; its value is 'YYYY-MM-DD'.
158
158
 
159
159
  **Buttons.** Two or more buttons side by side need a parent with a gap
160
160
  (`flex flex-wrap gap-3`); `DialogFooter` and `CardFooter` already are one. A `Button` is
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "division-ds",
3
- "version": "0.2.1",
3
+ "version": "0.3.1",
4
4
  "description": "Checks a project that uses Division DS components (raw colours, invented tokens, touching buttons, unknown props), and serves the docs and the same checks to agents over MCP.",
5
5
  "license": "MIT",
6
6
  "author": "Victor Ilioiu",
@@ -13,7 +13,7 @@
13
13
  import { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from 'src/components/molecules/accordion/Accordion'
14
14
  ```
15
15
 
16
- Paths are from the Division DS repository root. Installed with shadcn they start `src/division-ds/` instead of `src/` (`src/division-ds/components/...`); import them relative to your file.
16
+ Paths are from the Division DS repository root. Installed with shadcn they start `src/division-ds/` instead of `src/` (`src/division-ds/components/...`); import them relative to your file. An install has no index barrels: import each component from its own file, never from `components/molecules`.
17
17
 
18
18
  ## Props
19
19
 
@@ -13,7 +13,7 @@
13
13
  import { AiLabel } from 'src/components/atoms/ai-label/AiLabel'
14
14
  ```
15
15
 
16
- Paths are from the Division DS repository root. Installed with shadcn they start `src/division-ds/` instead of `src/` (`src/division-ds/components/...`); import them relative to your file.
16
+ Paths are from the Division DS repository root. Installed with shadcn they start `src/division-ds/` instead of `src/` (`src/division-ds/components/...`); import them relative to your file. An install has no index barrels: import each component from its own file, never from `components/molecules`.
17
17
 
18
18
  ## Props
19
19
 
@@ -13,7 +13,7 @@
13
13
  import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogFooter, AlertDialogTrigger } from 'src/components/organisms/alert-dialog/AlertDialog'
14
14
  ```
15
15
 
16
- Paths are from the Division DS repository root. Installed with shadcn they start `src/division-ds/` instead of `src/` (`src/division-ds/components/...`); import them relative to your file.
16
+ Paths are from the Division DS repository root. Installed with shadcn they start `src/division-ds/` instead of `src/` (`src/division-ds/components/...`); import them relative to your file. An install has no index barrels: import each component from its own file, never from `components/molecules`.
17
17
 
18
18
  ## Props
19
19
 
@@ -13,7 +13,7 @@
13
13
  import { Alert } from 'src/components/molecules/alert/Alert'
14
14
  ```
15
15
 
16
- Paths are from the Division DS repository root. Installed with shadcn they start `src/division-ds/` instead of `src/` (`src/division-ds/components/...`); import them relative to your file.
16
+ Paths are from the Division DS repository root. Installed with shadcn they start `src/division-ds/` instead of `src/` (`src/division-ds/components/...`); import them relative to your file. An install has no index barrels: import each component from its own file, never from `components/molecules`.
17
17
 
18
18
  ## Props
19
19
 
@@ -13,7 +13,7 @@
13
13
  import { ApprovalRequest } from 'src/components/molecules/approval-request/ApprovalRequest'
14
14
  ```
15
15
 
16
- Paths are from the Division DS repository root. Installed with shadcn they start `src/division-ds/` instead of `src/` (`src/division-ds/components/...`); import them relative to your file.
16
+ Paths are from the Division DS repository root. Installed with shadcn they start `src/division-ds/` instead of `src/` (`src/division-ds/components/...`); import them relative to your file. An install has no index barrels: import each component from its own file, never from `components/molecules`.
17
17
 
18
18
  ## Requires
19
19
 
@@ -13,7 +13,7 @@
13
13
  import { AuthForm } from 'src/components/organisms/auth-form/AuthForm'
14
14
  ```
15
15
 
16
- Paths are from the Division DS repository root. Installed with shadcn they start `src/division-ds/` instead of `src/` (`src/division-ds/components/...`); import them relative to your file.
16
+ Paths are from the Division DS repository root. Installed with shadcn they start `src/division-ds/` instead of `src/` (`src/division-ds/components/...`); import them relative to your file. An install has no index barrels: import each component from its own file, never from `components/molecules`.
17
17
 
18
18
  ## Props
19
19
 
@@ -13,7 +13,7 @@
13
13
  import { AvatarGroup } from 'src/components/molecules/avatar-group/AvatarGroup'
14
14
  ```
15
15
 
16
- Paths are from the Division DS repository root. Installed with shadcn they start `src/division-ds/` instead of `src/` (`src/division-ds/components/...`); import them relative to your file.
16
+ Paths are from the Division DS repository root. Installed with shadcn they start `src/division-ds/` instead of `src/` (`src/division-ds/components/...`); import them relative to your file. An install has no index barrels: import each component from its own file, never from `components/molecules`.
17
17
 
18
18
  ## Requires
19
19
 
@@ -13,7 +13,7 @@
13
13
  import { Avatar, avatarVariants, initialsFrom } from 'src/components/atoms/avatar/Avatar'
14
14
  ```
15
15
 
16
- Paths are from the Division DS repository root. Installed with shadcn they start `src/division-ds/` instead of `src/` (`src/division-ds/components/...`); import them relative to your file.
16
+ Paths are from the Division DS repository root. Installed with shadcn they start `src/division-ds/` instead of `src/` (`src/division-ds/components/...`); import them relative to your file. An install has no index barrels: import each component from its own file, never from `components/molecules`.
17
17
 
18
18
  ## Props
19
19
 
@@ -13,7 +13,7 @@
13
13
  import { Badge, badgeVariants } from 'src/components/atoms/badge/Badge'
14
14
  ```
15
15
 
16
- Paths are from the Division DS repository root. Installed with shadcn they start `src/division-ds/` instead of `src/` (`src/division-ds/components/...`); import them relative to your file.
16
+ Paths are from the Division DS repository root. Installed with shadcn they start `src/division-ds/` instead of `src/` (`src/division-ds/components/...`); import them relative to your file. An install has no index barrels: import each component from its own file, never from `components/molecules`.
17
17
 
18
18
  ## Props
19
19
 
@@ -13,7 +13,7 @@
13
13
  import { Breadcrumbs, collapseCrumbs } from 'src/components/molecules/breadcrumbs/Breadcrumbs'
14
14
  ```
15
15
 
16
- Paths are from the Division DS repository root. Installed with shadcn they start `src/division-ds/` instead of `src/` (`src/division-ds/components/...`); import them relative to your file.
16
+ Paths are from the Division DS repository root. Installed with shadcn they start `src/division-ds/` instead of `src/` (`src/division-ds/components/...`); import them relative to your file. An install has no index barrels: import each component from its own file, never from `components/molecules`.
17
17
 
18
18
  ## Props
19
19
 
@@ -13,7 +13,7 @@
13
13
  import { Button, buttonVariants } from 'src/components/atoms/button/Button'
14
14
  ```
15
15
 
16
- Paths are from the Division DS repository root. Installed with shadcn they start `src/division-ds/` instead of `src/` (`src/division-ds/components/...`); import them relative to your file.
16
+ Paths are from the Division DS repository root. Installed with shadcn they start `src/division-ds/` instead of `src/` (`src/division-ds/components/...`); import them relative to your file. An install has no index barrels: import each component from its own file, never from `components/molecules`.
17
17
 
18
18
  ## Props
19
19
 
@@ -13,7 +13,7 @@
13
13
  import { Card, CardActions, CardBody, CardDescription, CardFooter, CardHeader, CardMedia, CardTitle, cardLinkClass } from 'src/components/molecules/card/Card'
14
14
  ```
15
15
 
16
- Paths are from the Division DS repository root. Installed with shadcn they start `src/division-ds/` instead of `src/` (`src/division-ds/components/...`); import them relative to your file.
16
+ Paths are from the Division DS repository root. Installed with shadcn they start `src/division-ds/` instead of `src/` (`src/division-ds/components/...`); import them relative to your file. An install has no index barrels: import each component from its own file, never from `components/molecules`.
17
17
 
18
18
  ## Props
19
19
 
@@ -13,7 +13,7 @@
13
13
  import { Carousel } from 'src/components/organisms/carousel/Carousel'
14
14
  ```
15
15
 
16
- Paths are from the Division DS repository root. Installed with shadcn they start `src/division-ds/` instead of `src/` (`src/division-ds/components/...`); import them relative to your file.
16
+ Paths are from the Division DS repository root. Installed with shadcn they start `src/division-ds/` instead of `src/` (`src/division-ds/components/...`); import them relative to your file. An install has no index barrels: import each component from its own file, never from `components/molecules`.
17
17
 
18
18
  ## Props
19
19
 
@@ -13,7 +13,7 @@
13
13
  import { Cart } from 'src/components/organisms/cart/Cart'
14
14
  ```
15
15
 
16
- Paths are from the Division DS repository root. Installed with shadcn they start `src/division-ds/` instead of `src/` (`src/division-ds/components/...`); import them relative to your file.
16
+ Paths are from the Division DS repository root. Installed with shadcn they start `src/division-ds/` instead of `src/` (`src/division-ds/components/...`); import them relative to your file. An install has no index barrels: import each component from its own file, never from `components/molecules`.
17
17
 
18
18
  ## Props
19
19
 
@@ -13,7 +13,7 @@
13
13
  import { ChartPanel } from 'src/components/organisms/chart-panel/ChartPanel'
14
14
  ```
15
15
 
16
- Paths are from the Division DS repository root. Installed with shadcn they start `src/division-ds/` instead of `src/` (`src/division-ds/components/...`); import them relative to your file.
16
+ Paths are from the Division DS repository root. Installed with shadcn they start `src/division-ds/` instead of `src/` (`src/division-ds/components/...`); import them relative to your file. An install has no index barrels: import each component from its own file, never from `components/molecules`.
17
17
 
18
18
  ## Props
19
19
 
@@ -15,7 +15,7 @@ import { ChatMessage } from 'src/components/molecules/chat-message/ChatMessage'
15
15
  import { ResponseFeedback } from 'src/components/molecules/chat-message/ResponseFeedback'
16
16
  ```
17
17
 
18
- Paths are from the Division DS repository root. Installed with shadcn they start `src/division-ds/` instead of `src/` (`src/division-ds/components/...`); import them relative to your file.
18
+ Paths are from the Division DS repository root. Installed with shadcn they start `src/division-ds/` instead of `src/` (`src/division-ds/components/...`); import them relative to your file. An install has no index barrels: import each component from its own file, never from `components/molecules`.
19
19
 
20
20
  ## Requires
21
21
 
@@ -13,7 +13,7 @@
13
13
  import { ChatThread } from 'src/components/organisms/chat-thread/ChatThread'
14
14
  ```
15
15
 
16
- Paths are from the Division DS repository root. Installed with shadcn they start `src/division-ds/` instead of `src/` (`src/division-ds/components/...`); import them relative to your file.
16
+ Paths are from the Division DS repository root. Installed with shadcn they start `src/division-ds/` instead of `src/` (`src/division-ds/components/...`); import them relative to your file. An install has no index barrels: import each component from its own file, never from `components/molecules`.
17
17
 
18
18
  ## Requires
19
19
 
@@ -13,7 +13,7 @@
13
13
  import { Checkbox } from 'src/components/atoms/checkbox/Checkbox'
14
14
  ```
15
15
 
16
- Paths are from the Division DS repository root. Installed with shadcn they start `src/division-ds/` instead of `src/` (`src/division-ds/components/...`); import them relative to your file.
16
+ Paths are from the Division DS repository root. Installed with shadcn they start `src/division-ds/` instead of `src/` (`src/division-ds/components/...`); import them relative to your file. An install has no index barrels: import each component from its own file, never from `components/molecules`.
17
17
 
18
18
  ## Props
19
19
 
@@ -13,7 +13,7 @@
13
13
  import { CheckoutForm, DELIVERY_OPTIONS } from 'src/components/organisms/checkout-form/CheckoutForm'
14
14
  ```
15
15
 
16
- Paths are from the Division DS repository root. Installed with shadcn they start `src/division-ds/` instead of `src/` (`src/division-ds/components/...`); import them relative to your file.
16
+ Paths are from the Division DS repository root. Installed with shadcn they start `src/division-ds/` instead of `src/` (`src/division-ds/components/...`); import them relative to your file. An install has no index barrels: import each component from its own file, never from `components/molecules`.
17
17
 
18
18
  ## Props
19
19
 
@@ -13,13 +13,13 @@
13
13
  import { CommandPalette, filterCommands, groupCommands } from 'src/components/organisms/command-palette/CommandPalette'
14
14
  ```
15
15
 
16
- Paths are from the Division DS repository root. Installed with shadcn they start `src/division-ds/` instead of `src/` (`src/division-ds/components/...`); import them relative to your file.
16
+ Paths are from the Division DS repository root. Installed with shadcn they start `src/division-ds/` instead of `src/` (`src/division-ds/components/...`); import them relative to your file. An install has no index barrels: import each component from its own file, never from `components/molecules`.
17
17
 
18
18
  ## Props
19
19
 
20
20
  | Prop | Type | Default | Description |
21
21
  |---|---|---|---|
22
- | `commands` | `Command[]` | | Required. Each has id, label, group, keywords, shortcut, icon and onSelect. |
22
+ | `commands` | `Command[]` | | Required. Each has id, label, description (a quieter second line), group, keywords, shortcut, icon and onSelect. |
23
23
  | `open` | `boolean` | | Open state when controlled, meaning your code decides whether it is open. |
24
24
  | `defaultOpen` | `boolean` | `false` | Starting open state when uncontrolled, meaning the palette keeps track of it itself. |
25
25
  | `onOpenChange` | `(open: boolean) => void` | | Called on every open and close, including Escape and a click on the scrim (the dimmed backdrop). If you refuse the close, the chosen command stays ready to run until the user does anything else in the palette. Closing it yourself later counts as accepting it. |
@@ -28,6 +28,7 @@ Paths are from the Division DS repository root. Installed with shadcn they start
28
28
  | `searchLabel` | `string` | `'Search commands'` | The search field's accessible name. |
29
29
  | `placeholder` | `string` | `'Type a command or search'` | Shown in the empty search field. |
30
30
  | `emptyMessage` | `string` | `'No matching commands'` | Shown when nothing matches the search. |
31
+ | `filter` | `(commands: readonly Command[], query: string) => Command[]` | `filterCommands` | Your own matching and ranking. Gets every command and the query, returns the ones to show in order; groups follow that order. The docs site search uses it to rank with the same scorer as the MCP server. |
31
32
  | `ref` | `Ref<HTMLInputElement>` | | The search field, while the palette is open. |
32
33
 
33
34
  ## Types and helpers
@@ -38,6 +39,8 @@ Exported beside the component, from the same file. Import a type with `import ty
38
39
  export type Command = {
39
40
  id: string
40
41
  label: string
42
+ /** A second, quieter line under the label, e.g. what a search result is for. */
43
+ description?: string
41
44
  /** Commands with the same group are drawn together under its heading. */
42
45
  group?: string
43
46
  /** Extra words a search matches on. */
@@ -13,7 +13,7 @@
13
13
  import { DataTable, sortRows } from 'src/components/organisms/data-table/DataTable'
14
14
  ```
15
15
 
16
- Paths are from the Division DS repository root. Installed with shadcn they start `src/division-ds/` instead of `src/` (`src/division-ds/components/...`); import them relative to your file.
16
+ Paths are from the Division DS repository root. Installed with shadcn they start `src/division-ds/` instead of `src/` (`src/division-ds/components/...`); import them relative to your file. An install has no index barrels: import each component from its own file, never from `components/molecules`.
17
17
 
18
18
  ## Props
19
19
 
@@ -0,0 +1,166 @@
1
+ # DatePicker
2
+
3
+ > One date, typed or picked. The field has a segment for the day, the month and the year, in the order your language writes them: type the digits and it moves on by itself, or use the arrow keys. The button inside it opens a calendar. The value is a plain 'YYYY-MM-DD' string with no time zone, so it means the same day for everyone. Calendar and DateField are exported too, for building your own.
4
+
5
+ - **Best for:** One date: a delivery day, a deadline, a birthday.
6
+ - **Not for:** A range: DateRangePicker. A time: TimeField.
7
+ - **Also on this page:** `Calendar`, `DateField`
8
+ - Molecule. Source: `src/components/molecules/date-picker/DatePicker.tsx`
9
+ - Rendered docs page: `/components/date-picker`
10
+
11
+ ## Import
12
+
13
+ ```tsx
14
+ import { Calendar } from 'src/components/molecules/date-picker/Calendar'
15
+ import { DateField } from 'src/components/molecules/date-picker/DateField'
16
+ import { DatePicker } from 'src/components/molecules/date-picker/DatePicker'
17
+ ```
18
+
19
+ Paths are from the Division DS repository root. Installed with shadcn they start `src/division-ds/` instead of `src/` (`src/division-ds/components/...`); import them relative to your file. An install has no index barrels: import each component from its own file, never from `components/molecules`.
20
+
21
+ ## Props
22
+
23
+ | Prop | Type | Default | Description |
24
+ |---|---|---|---|
25
+ | `label` | `string` | | Names the field. Leave it out inside a FormField, which sets it from its own label. Outside one it is required, and `division-ds check` flags a missing one. |
26
+ | `value` | `string ('YYYY-MM-DD' \| '')` | | Controlled value (your page holds it). |
27
+ | `defaultValue` | `string` | `''` | Starting value when the picker holds the value itself. |
28
+ | `onValueChange` | `(value: string) => void` | | Fires with a whole, real date, or '' when the field is emptied. A half-typed date does not fire. |
29
+ | `min` | `string` | | Earliest date, 'YYYY-MM-DD'. Earlier days are struck through; typing one marks the field invalid. |
30
+ | `max` | `string` | | Latest date, 'YYYY-MM-DD'. |
31
+ | `isDateDisabled` | `(date: string) => boolean` | | Days that cannot be picked, such as weekends or holidays. |
32
+ | `locale` | `string` | | A language tag such as 'en-GB' or 'de'. Sets the segment order, the separator, the month and day names and the first day of the week. Defaults to the browser's. |
33
+ | `weekStartsOn` | `number` | | 0 Sunday to 6 Saturday. Defaults to the locale's. |
34
+ | `showToday` | `boolean` | `true` | Shows the Today button under the calendar when today can be picked. |
35
+ | `disabled` | `boolean` | `false` | Disables the field and the calendar button. |
36
+ | `name` | `string` | | Submits the 'YYYY-MM-DD' value with a form, through a hidden input. |
37
+ | `invalid` | `boolean` | | Marks the field invalid. FormField sets it when there is an error. |
38
+ | `required` | `boolean` | | Marks the field required. FormField sets it. |
39
+ | `id` | `string` | | The field's id, so a FormField's error text can point at it. FormField sets it. |
40
+ | `aria-describedby` | `string` | | The ids of the help and error text. FormField sets it. |
41
+ | `className` | `string` | | Combined with the field's classes. |
42
+ | `ref` | `Ref<HTMLDivElement>` | | The field's group element. |
43
+ | `trailing (DateField)` | `ReactNode` | | Something inside the field after the segments. DatePicker puts its calendar button here. |
44
+ | `bare (DateField)` | `boolean` | `false` | No border, fill or ring of its own, for two dates inside one border (DateRangePicker draws them). |
45
+ | `onSelect (Calendar)` | `(date: string) => void` | | A day was picked with a click, Enter or Space. |
46
+ | `value (Calendar)` | `string \| null` | | The picked day. Ignored when dayState is given. |
47
+ | `month (Calendar)` | `string` | | Any day in the first month shown. Controlled with onMonthChange. |
48
+ | `defaultMonth (Calendar)` | `string` | | The first month shown when the calendar holds it itself. Defaults to the value's month, or today's. |
49
+ | `onMonthChange (Calendar)` | `(month: string) => void` | | Fires with the new first month's 1st day. |
50
+ | `dayState (Calendar)` | `(date: string) => 'single' \| 'start' \| 'end' \| 'start-end' \| 'middle' \| undefined` | | Draws each day yourself, for a range or any other selection. |
51
+ | `onDayEnter (Calendar)` | `(date: string) => void` | | The keyboard or the pointer moved onto a day. A range uses it to preview the band. |
52
+ | `numberOfMonths (Calendar)` | `1 \| 2` | `1` | Months side by side, stacked on narrow screens. |
53
+ | `autoFocus (Calendar)` | `boolean` | `false` | Moves focus to the picked day, or today, when the calendar appears. |
54
+
55
+ ## Types and helpers
56
+
57
+ Exported beside the component, from the same file. Import a type with `import type { ... }`.
58
+
59
+ ```ts
60
+ /** How a day is drawn. `single` is one picked day; the rest belong to a range. */
61
+ export type DayState = 'single' | 'start' | 'end' | 'start-end' | 'middle'
62
+ ```
63
+
64
+ ## Examples
65
+
66
+ Each is compiled and rendered on the docs page. Imports are omitted; see above.
67
+
68
+ ### Default
69
+
70
+ ```tsx
71
+ <div className="w-full max-w-xs">
72
+ <DatePicker label="Delivery date" />
73
+ </div>
74
+ ```
75
+
76
+ ### In a FormField, with help text
77
+
78
+ ```tsx
79
+ <div className="w-full max-w-xs">
80
+ <FormField label="Trip date" help="Type it, or open the calendar.">
81
+ <DatePicker />
82
+ </FormField>
83
+ </div>
84
+ ```
85
+
86
+ ### Controlled: the page holds the value
87
+
88
+ ```tsx
89
+ function Controlled() {
90
+ const [date, setDate] = useState('2026-11-14')
91
+ return (
92
+ <div className="flex w-full max-w-xs flex-col gap-2">
93
+ <DatePicker label="Dispatch date" value={date} onValueChange={setDate} />
94
+ <span className="font-mono text-xs text-[var(--color-text-secondary)]">value: {date ? `'${date}'` : "''"}</span>
95
+ </div>
96
+ )
97
+ }
98
+
99
+ <Controlled />
100
+ ```
101
+
102
+ ### Limits: weekdays only, within the next two months
103
+
104
+ ```tsx
105
+ const weekend = (date: string) => [0, 6].includes(new Date(`${date}T12:00:00Z`).getUTCDay())
106
+
107
+ <div className="w-full max-w-xs">
108
+ <DatePicker label="Collection day" min="2026-11-02" max="2026-12-31" defaultValue="2026-11-16" isDateDisabled={weekend} />
109
+ </div>
110
+ ```
111
+
112
+ ### US order and a Sunday start, from the locale
113
+
114
+ ```tsx
115
+ <div className="w-full max-w-xs">
116
+ <DatePicker label="Ship date" locale="en-US" defaultValue="2026-11-14" />
117
+ </div>
118
+ ```
119
+
120
+ ### The calendar on its own
121
+
122
+ ```tsx
123
+ function InlineCalendar() {
124
+ const [date, setDate] = useState<string | null>(null)
125
+ return (
126
+ <div className="flex flex-col gap-2">
127
+ <Calendar value={date} onSelect={setDate} />
128
+ <span className="font-mono text-xs text-[var(--color-text-secondary)]">{date ? formatLong(date) : 'No day picked'}</span>
129
+ </div>
130
+ )
131
+ }
132
+
133
+ <InlineCalendar />
134
+ ```
135
+
136
+ ## Keyboard
137
+
138
+ | Keys | Result |
139
+ |---|---|
140
+ | `0–9` | Types into the focused segment and moves on once it is full. |
141
+ | `↑` `↓` | In the field: one more or one less in the segment. In the calendar: a week earlier or later. |
142
+ | `←` `→` | In the field: the previous or next segment. In the calendar: the day before or after. |
143
+ | `Backspace` | Clears the segment, or moves back when it is already empty. |
144
+ | `Page Up` `Page Down` | The same day a month earlier or later. With Shift, a year. |
145
+ | `Home` `End` | The first or last day of the week. |
146
+ | `Enter` `Space` | Picks the focused day and closes the calendar. |
147
+ | `Esc` | Closes the calendar without a change. |
148
+
149
+ ## Do
150
+
151
+ - Store and send the 'YYYY-MM-DD' value as it is. It has no time zone, so it can't shift by a day.
152
+ - Set min and max to the real limits, so days nobody can book are struck through rather than refused after the fact.
153
+ - Wrap it in a FormField for a visible label, help text and errors.
154
+
155
+ ## Don't
156
+
157
+ - Don't convert the value with new Date('2026-11-14'): that reads it as midnight UTC and can show the day before in the Americas.
158
+ - Don't hide unavailable days. Struck through and announced, they explain themselves.
159
+ - Don't use it for a date of birth without setting max to today.
160
+
161
+ ## Anatomy
162
+
163
+ - **Field (DateField)**: A group named by the label, holding three spinbuttons (Day, Month, Year). Each says its value or "Empty". A date that does not exist (31 February) or falls outside min and max marks the field invalid.
164
+ - **Calendar button**: A ghost IconButton inside the field. Its name includes the picked date: "Choose date, Saturday, 14 November 2026".
165
+ - **Calendar**: A grid with one tab stop. Each day is a button named with the full date. The picked day is a solid accent square with a cut corner; today has aria-current and a short bar under its number. Days that can't be picked stay visible, struck through and announced as unavailable.
166
+ - **Today**: A secondary Button under the calendar, shown only when today can be picked.
@@ -0,0 +1,111 @@
1
+ # DatePresets
2
+
3
+ > Named periods such as Last 7 days, one click each. The defaults: Today, Yesterday, Last 7 days, Last 30 days, This month, Last month, This year, or your own. An add-on: put it in a DateRangePicker's aside and it reads and sets the picker's range by itself, then closes it. On its own it is a list you give a value and an onSelect. Installs DateRangePicker and DatePicker with it.
4
+
5
+ - **Best for:** Reports and logs, where people want the usual periods.
6
+ - **Not for:** Exact dates: DateRangePicker alone.
7
+ - Molecule. Source: `src/components/molecules/date-presets/DatePresets.tsx`
8
+ - Rendered docs page: `/components/date-presets`
9
+
10
+ ## Import
11
+
12
+ ```tsx
13
+ import { DatePresets, defaultPresets } from 'src/components/molecules/date-presets/DatePresets'
14
+ ```
15
+
16
+ Paths are from the Division DS repository root. Installed with shadcn they start `src/division-ds/` instead of `src/` (`src/division-ds/components/...`); import them relative to your file. An install has no index barrels: import each component from its own file, never from `components/molecules`.
17
+
18
+ ## Props
19
+
20
+ | Prop | Type | Default | Description |
21
+ |---|---|---|---|
22
+ | `presets` | `DatePreset[]` | `defaultPresets()` | Each { label, range }. Make your own, or start from defaultPresets(today). |
23
+ | `value` | `{ start: string; end: string }` | | The current range. Inside a DateRangePicker it comes from the picker. |
24
+ | `onSelect` | `(range: DateRange) => void` | | A preset was chosen. Inside a DateRangePicker the picker also takes it and closes. |
25
+ | `label` | `string` | `'Presets'` | The list's accessible name. |
26
+ | `className` | `string` | | Combined with the list's classes. |
27
+ | `ref` | `Ref<HTMLDivElement>` | | The list element. |
28
+ | `defaultPresets()` | `(today?: string) => DatePreset[]` | | The seven usual periods counted back from today ('YYYY-MM-DD'; defaults to the reader's today). Every one includes today. |
29
+
30
+ ## Types and helpers
31
+
32
+ Exported beside the component, from the same file. Import a type with `import type { ... }`.
33
+
34
+ ```ts
35
+ export type DatePreset = { label: string; range: DateRange }
36
+ ```
37
+
38
+ ```ts
39
+ /** The usual periods, counted back from `today` (default: the reader's today). Each includes today. */
40
+ export function defaultPresets(today: IsoDate = todayIso()): DatePreset[]
41
+ ```
42
+
43
+ ## Examples
44
+
45
+ Each is compiled and rendered on the docs page. Imports are omitted; see above.
46
+
47
+ ### Inside a DateRangePicker: no wiring
48
+
49
+ ```tsx
50
+ <div className="w-full max-w-sm">
51
+ <DateRangePicker label="Activity" aside={<DatePresets />} />
52
+ </div>
53
+ ```
54
+
55
+ ### On its own
56
+
57
+ ```tsx
58
+ function OnItsOwn() {
59
+ const [range, setRange] = useState<DateRange>(defaultPresets()[2].range)
60
+ return (
61
+ <div className="flex flex-col gap-3">
62
+ <DatePresets value={range} onSelect={setRange} />
63
+ <span className="font-mono text-xs text-[var(--color-text-secondary)]">
64
+ {range.start} to {range.end}
65
+ </span>
66
+ </div>
67
+ )
68
+ }
69
+
70
+ <OnItsOwn />
71
+ ```
72
+
73
+ ### Your own periods
74
+
75
+ ```tsx
76
+ <div className="w-full max-w-sm">
77
+ <DateRangePicker
78
+ label="Season"
79
+ aside={
80
+ <DatePresets
81
+ label="Seasons"
82
+ presets={[
83
+ { label: 'Winter 2026', range: { start: '2026-01-01', end: '2026-03-31' } },
84
+ { label: 'Spring 2026', range: { start: '2026-04-01', end: '2026-06-30' } },
85
+ { label: 'Summer 2026', range: { start: '2026-07-01', end: '2026-09-30' } },
86
+ ]}
87
+ />
88
+ }
89
+ />
90
+ </div>
91
+ ```
92
+
93
+ ## Keyboard
94
+
95
+ | Keys | Result |
96
+ |---|---|
97
+ | `Tab` | Moves through the presets. |
98
+ | `Enter` `Space` | Chooses the focused preset. |
99
+
100
+ ## Do
101
+
102
+ - Keep the list short, seven or fewer.
103
+ - Name periods the way your users say them: "This quarter" if they plan in quarters.
104
+
105
+ ## Don't
106
+
107
+ - Don't offer a preset nobody can pick, such as a future period when max is today.
108
+
109
+ ## Anatomy
110
+
111
+ - **List**: A group named "Presets". Each period is a button; the one matching the current range is pressed (aria-pressed) and filled.