@devalok/shilp-sutra 0.37.0 → 0.37.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 (127) hide show
  1. package/dist/composed/inline-edit.d.ts.map +1 -1
  2. package/dist/composed/inline-edit.js +42 -40
  3. package/dist/composed/inline-edit.js.map +1 -1
  4. package/dist/test-utils/conformance.d.ts +37 -0
  5. package/dist/test-utils/conformance.d.ts.map +1 -0
  6. package/docs/components/composed/activity-feed.md +6 -0
  7. package/docs/components/composed/avatar-group.md +8 -0
  8. package/docs/components/composed/bulk-action-bar.md +6 -0
  9. package/docs/components/composed/command-palette.md +7 -0
  10. package/docs/components/composed/confirm-dialog.md +6 -0
  11. package/docs/components/composed/content-card.md +6 -0
  12. package/docs/components/composed/date-picker.md +10 -0
  13. package/docs/components/composed/deadline-indicator.md +7 -0
  14. package/docs/components/composed/emoji-picker.md +8 -0
  15. package/docs/components/composed/empty-state.md +8 -0
  16. package/docs/components/composed/error-boundary.md +7 -0
  17. package/docs/components/composed/file-preview.md +8 -0
  18. package/docs/components/composed/filter-bar.md +8 -0
  19. package/docs/components/composed/form-section.md +6 -0
  20. package/docs/components/composed/global-loading.md +6 -0
  21. package/docs/components/composed/inline-edit.md +8 -0
  22. package/docs/components/composed/loading-skeleton.md +7 -0
  23. package/docs/components/composed/markdown-viewer.md +9 -0
  24. package/docs/components/composed/master-detail.md +7 -0
  25. package/docs/components/composed/member-picker.md +7 -0
  26. package/docs/components/composed/multi-select-popover.md +11 -0
  27. package/docs/components/composed/page-header.md +7 -0
  28. package/docs/components/composed/page-skeletons.md +10 -0
  29. package/docs/components/composed/priority-indicator.md +7 -0
  30. package/docs/components/composed/responsive-overlay.md +7 -0
  31. package/docs/components/composed/rich-chat-input.md +12 -0
  32. package/docs/components/composed/rich-text-editor.md +9 -0
  33. package/docs/components/composed/schedule-view.md +8 -0
  34. package/docs/components/composed/simple-tooltip.md +7 -0
  35. package/docs/components/composed/status-badge.md +10 -0
  36. package/docs/components/shell/app-command-palette.md +8 -0
  37. package/docs/components/shell/bottom-navbar.md +12 -0
  38. package/docs/components/shell/command-registry.md +7 -0
  39. package/docs/components/shell/link-context.md +15 -0
  40. package/docs/components/shell/notification-center.md +10 -0
  41. package/docs/components/shell/notification-preferences.md +7 -0
  42. package/docs/components/shell/sidebar.md +11 -0
  43. package/docs/components/shell/top-bar.md +11 -0
  44. package/docs/components/ui/accordion.md +10 -0
  45. package/docs/components/ui/alert-dialog.md +10 -1
  46. package/docs/components/ui/alert.md +10 -2
  47. package/docs/components/ui/aspect-ratio.md +7 -0
  48. package/docs/components/ui/autocomplete.md +7 -0
  49. package/docs/components/ui/avatar.md +10 -0
  50. package/docs/components/ui/badge-group.md +7 -0
  51. package/docs/components/ui/badge-indicator.md +7 -0
  52. package/docs/components/ui/badge.md +12 -0
  53. package/docs/components/ui/banner.md +7 -0
  54. package/docs/components/ui/breadcrumb.md +8 -0
  55. package/docs/components/ui/button-group.md +24 -4
  56. package/docs/components/ui/button-processing.md +5 -0
  57. package/docs/components/ui/button.md +9 -0
  58. package/docs/components/ui/card.md +16 -4
  59. package/docs/components/ui/charts.md +9 -0
  60. package/docs/components/ui/chat.md +9 -0
  61. package/docs/components/ui/checkbox.md +15 -1
  62. package/docs/components/ui/chip.md +9 -0
  63. package/docs/components/ui/code.md +7 -0
  64. package/docs/components/ui/collapsible.md +9 -0
  65. package/docs/components/ui/color-input.md +8 -0
  66. package/docs/components/ui/color-swatch.md +7 -0
  67. package/docs/components/ui/combobox.md +16 -3
  68. package/docs/components/ui/container.md +11 -0
  69. package/docs/components/ui/context-menu.md +8 -0
  70. package/docs/components/ui/data-table-body.md +4 -0
  71. package/docs/components/ui/data-table-bulk-actions.md +4 -0
  72. package/docs/components/ui/data-table-card.md +4 -0
  73. package/docs/components/ui/data-table-context.md +4 -0
  74. package/docs/components/ui/data-table-header.md +4 -0
  75. package/docs/components/ui/data-table-pagination.md +4 -0
  76. package/docs/components/ui/data-table-toolbar.md +7 -0
  77. package/docs/components/ui/data-table.md +25 -0
  78. package/docs/components/ui/devalok-grain.md +8 -0
  79. package/docs/components/ui/dialog.md +10 -0
  80. package/docs/components/ui/dropdown-menu.md +10 -0
  81. package/docs/components/ui/file-upload.md +8 -0
  82. package/docs/components/ui/form.md +21 -0
  83. package/docs/components/ui/hover-card.md +9 -0
  84. package/docs/components/ui/icon-button.md +7 -0
  85. package/docs/components/ui/icon-context.md +7 -0
  86. package/docs/components/ui/icon-group.md +9 -0
  87. package/docs/components/ui/icon.md +7 -0
  88. package/docs/components/ui/input-otp.md +29 -7
  89. package/docs/components/ui/input.md +7 -0
  90. package/docs/components/ui/label.md +7 -1
  91. package/docs/components/ui/link.md +7 -0
  92. package/docs/components/ui/menubar.md +9 -0
  93. package/docs/components/ui/navigation-menu.md +9 -0
  94. package/docs/components/ui/number-input.md +10 -0
  95. package/docs/components/ui/pagination.md +10 -0
  96. package/docs/components/ui/popover.md +9 -0
  97. package/docs/components/ui/progress-ring.md +7 -0
  98. package/docs/components/ui/progress.md +7 -0
  99. package/docs/components/ui/radio.md +27 -2
  100. package/docs/components/ui/search-input.md +8 -0
  101. package/docs/components/ui/segmented-control.md +8 -0
  102. package/docs/components/ui/select.md +14 -3
  103. package/docs/components/ui/separator.md +7 -0
  104. package/docs/components/ui/sheet.md +10 -0
  105. package/docs/components/ui/sidebar.md +30 -0
  106. package/docs/components/ui/skeleton.md +7 -0
  107. package/docs/components/ui/slider.md +15 -1
  108. package/docs/components/ui/spinner.md +7 -0
  109. package/docs/components/ui/split-button.md +11 -0
  110. package/docs/components/ui/stack.md +8 -0
  111. package/docs/components/ui/stat-card.md +9 -0
  112. package/docs/components/ui/status-dot.md +9 -0
  113. package/docs/components/ui/stepper.md +8 -0
  114. package/docs/components/ui/switch.md +8 -0
  115. package/docs/components/ui/table.md +8 -0
  116. package/docs/components/ui/tabs.md +10 -2
  117. package/docs/components/ui/text.md +9 -4
  118. package/docs/components/ui/textarea.md +7 -1
  119. package/docs/components/ui/toast.md +9 -0
  120. package/docs/components/ui/toaster.md +8 -0
  121. package/docs/components/ui/toggle-group.md +13 -4
  122. package/docs/components/ui/toggle.md +10 -2
  123. package/docs/components/ui/tooltip.md +11 -1
  124. package/docs/components/ui/tree-view.md +12 -0
  125. package/docs/components/ui/visually-hidden.md +7 -0
  126. package/llms-full.txt +1116 -44
  127. package/package.json +1 -1
@@ -199,6 +199,15 @@ Animated bouncing dots with a text description of who is typing.
199
199
 
200
200
  ---
201
201
 
202
+ ## Composability
203
+ - **Chat is a kit of 7 primitives, not a monolithic ChatWindow.** Compose MessageList (scroll container) + Message/SystemMessage (row variants) + DateSeparator/UnreadSeparator (visual dividers) + MessageInput (composer) + TypingIndicator (presence).
204
+ - **Message is a compound component** — `Message.Avatar`, `Message.Content`, `Message.Author`, `Message.Body`, `Message.Reactions`, `Message.Actions`, `Message.Action`. Arrange them to match your design; most layouts only need Avatar + Content (with Author + Body inside).
205
+ - **Message.Actions reveal on hover** via `group-hover/message` — ONLY works when Actions is nested inside a Message root. If you want a custom action bar, do it differently.
206
+ - **MessageList ARIA:** `role="log" + aria-live="polite"` — screen readers announce new messages automatically. Don't manually add aria-live to children.
207
+ - **grouped mode:** For consecutive messages from the same user, set `grouped={true}` to hide the avatar and author (typical chat UI pattern). Logic for "same user as previous" is consumer-side.
208
+ - **MessageInput is specifically for chat** — auto-resizing textarea (up to 160px), Enter-to-send, Shift+Enter-for-newline, streaming support. For richer composition (formatting, attachments), use RichChatInput from composed (built on top of this + TipTap).
209
+ - **TypingIndicator** accepts multiple users — handles pluralization of the "is/are typing" label automatically.
210
+
202
211
  ## Gotchas
203
212
  - MessageList uses `role="log"` with `aria-live="polite"` — screen readers announce new messages
204
213
  - Message entrance animations use Framer Motion springs — AnimatePresence wraps children in MessageList
@@ -7,20 +7,34 @@
7
7
  ## Props
8
8
  checked: boolean | "indeterminate"
9
9
  onCheckedChange: (checked: boolean | "indeterminate") => void
10
+ size: "sm" | "md" | "lg"
10
11
  error: boolean (shows red border)
11
12
  indeterminate: boolean (overrides checked, shows dash icon)
12
13
  disabled: boolean
13
14
 
14
15
  ## Defaults
15
- none
16
+ size="md"
16
17
 
17
18
  ## Example
18
19
  ```jsx
19
20
  <Checkbox checked={agreed} onCheckedChange={(v) => setAgreed(v === true)} />
21
+
22
+ {/* With label */}
23
+ <label htmlFor="terms" className="flex items-center gap-ds-02">
24
+ <Checkbox id="terms" checked={agreed} onCheckedChange={(v) => setAgreed(v === true)} />
25
+ I agree to the terms
26
+ </label>
20
27
  ```
21
28
 
29
+ ## Composability
30
+ - **FormField integration:** Unlike Input/Textarea, Checkbox does NOT auto-consume FormField state — the `error` prop must be set explicitly. (Form-library convention differs for checkboxes since they often don't share the same visual grouping.)
31
+ - **Labels:** Checkbox doesn't carry its own label — pair it with `<Label htmlFor="x" />` + `<Checkbox id="x" />`, or wrap both in a `<label>` element. Screen readers rely on that association.
32
+ - **Controlled vs uncontrolled:** Pass `checked` + `onCheckedChange` for controlled, `defaultChecked` for uncontrolled. Don't mix.
33
+
22
34
  ## Gotchas
23
35
  - indeterminate overrides checked visually
36
+ - Checkbox does NOT auto-consume FormField error state — pass `error` explicitly if using inside a FormField
37
+ - WCAG 2.5.8 minimum target size met at md (24px); sm (20px) may fail if not paired with enough label hit area
24
38
 
25
39
  ## Changes
26
40
  ### v0.22.0
@@ -26,11 +26,20 @@
26
26
  ## Additional Exports
27
27
  ChipGroup — re-exported AnimatePresence from framer-motion; wrap a list of Chips for coordinated exit animations
28
28
 
29
+ ## Composability
30
+ - **DEPRECATED — use Badge instead.** Chip v0.29.0+ superset capability merged into Badge. Chip remains in the library for backward compatibility; new code should use:
31
+ - `<Badge>...</Badge>` for static tags (replaces `<Chip label="..." />`)
32
+ - `<Badge onClick={...} selected={isSelected}>...</Badge>` for interactive filter chips
33
+ - `<Badge onDismiss={...}>...</Badge>` for dismissible chips
34
+ - **Why deprecated:** Chip used `label` prop (not children) and had a separate CVA; Badge now supports everything Chip did via children + onClick/onDismiss/selected, with more colors and variants.
35
+ - **Migration:** `<Chip label="React" color="info" />` → `<Badge variant="subtle" color="info">React</Badge>`. ChipGroup → BadgeGroup or just AnimatePresence wrapper.
36
+
29
37
  ## Gotchas
30
38
  - MUST use label prop — children are NOT rendered
31
39
  - `<Chip>text</Chip>` is WRONG — use `<Chip label="text" />`
32
40
  - Wrap dynamic chip lists in `<ChipGroup>` for exit animations
33
41
  - `color="primary"` will be renamed to `color="brand"` in v1.0 — use `color="primary"` for now
42
+ - **Deprecated — migrate to Badge for all new code**
34
43
 
35
44
  ## Changes
36
45
  ### v0.19.1
@@ -17,8 +17,15 @@
17
17
  <Code variant="block">{`const x = 1;\nconsole.log(x);`}</Code>
18
18
  ```
19
19
 
20
+ ## Composability
21
+ - **Server-safe.** No context, no cascade.
22
+ - Inline variant composes inside Text, paragraphs, list items, table cells — wherever body text goes.
23
+ - Block variant is a `<pre><code>` pair with overflow-x scroll; pair with a dark background Card or MarkdownViewer for long snippets.
24
+ - Not a syntax-highlighted code viewer — for that, use RichTextEditor's code block or a dedicated library. Code here is typographic emphasis, not lexer-aware rendering.
25
+
20
26
  ## Gotchas
21
27
  - "block" renders as `<pre><code>`, "inline" renders as `<code>`
28
+ - Block variant doesn't syntax-highlight — it's a styled pre tag, nothing more
22
29
 
23
30
  ## Changes
24
31
  ### v0.1.1
@@ -25,8 +25,17 @@
25
25
  </Collapsible>
26
26
  ```
27
27
 
28
+ ## Composability
29
+ - Radix Collapsible primitive — smaller than Accordion (no grouping/value matching, just open/close).
30
+ - **When to use:** A single show/hide section where the state is local (e.g. "Advanced options" inside a form, expandable row detail, show-more). For multi-section with mutual exclusion, use Accordion.
31
+ - **Inline rendering:** CollapsibleContent is NOT portalled — it expands inline and pushes sibling content down. Respects `overflow` rules of the parent.
32
+ - **No built-in chevron** — unlike AccordionTrigger, CollapsibleTrigger is bare. Add your own icon + rotate via `data-state` attribute Radix sets (`[data-state=open]:rotate-180`).
33
+ - **Animation:** Height transition via `animate-collapsible-down/up` keyframes + Radix `--radix-collapsible-content-height` CSS custom property.
34
+
28
35
  ## Gotchas
29
36
  - Standard Radix Collapsible API
37
+ - No auto-chevron — add one manually if you want the affordance
38
+ - Not portalled — clipped by parent `overflow: hidden`
30
39
 
31
40
  ## Changes
32
41
  ### v0.13.0
@@ -61,6 +61,14 @@
61
61
  <ColorInput value={color} onChange={setColor} showPicker={false} />
62
62
  ```
63
63
 
64
+ ## Composability
65
+ - **Built on Popover internally** — trigger opens a portal-rendered picker panel. z-popover (1400) stacking.
66
+ - **variant="default" vs "inline":** Default variant is an input-style trigger with gradient swatch + hex label (fits in forms). Inline variant IS the selected color — the entire trigger takes the color as its background with contrast-aware text. Use inline for color-tags in chat, lists, or tight toolbar UIs.
67
+ - **Controlled or uncontrolled:** Works both ways — pass `value` + `onChange` for controlled, or omit and let internal state track.
68
+ - **Presets are compositional:** Pass `{ hex, label }[]` for named brand colors (accessible, keyboard-navigable). Pass `false` to hide the preset strip entirely (picker-only mode).
69
+ - **react-colorful** is bundled (2.8KB gzipped) — zero additional setup. Picker itself is pointer-based; keyboard users edit via the HEX/RGB/HSL format inputs below.
70
+ - **FormField:** Not auto-consumed (no `state` prop). Wrap in FormField for label + helper text; style error visuals via className.
71
+
64
72
  ## Gotchas
65
73
  - Value must be a 6-character hex string (e.g. "#d33163")
66
74
  - Presets accept both `string[]` (backward-compatible) and `{ hex, label }[]` (recommended for accessibility)
@@ -20,6 +20,13 @@
20
20
  <ColorSwatch color="oklch(0.7 0.15 200)" shape="rounded" />
21
21
  ```
22
22
 
23
+ ## Composability
24
+ - **Server-safe, decorative-only primitive.** Accepts any valid CSS color string — hex, rgb, oklch, hsl, named. Pure presentation, no context.
25
+ - **Usage patterns:** Inline color marker next to a label, preview dot in lists, color-indicator in a category chip, legend swatch for charts.
26
+ - **`ring={true}`** adds a subtle outline — essential for white/very-light colors that would otherwise disappear on surface-raised backgrounds.
27
+ - **Interactive color picking:** Use ColorInput (which opens a full picker). ColorSwatch is display-only.
28
+ - No IconProvider cascade, no FormField consumption — composes freely with anything.
29
+
23
30
  ## Gotchas
24
31
  - Color is applied via inline `backgroundColor` style, not a token class — accepts any runtime CSS color string
25
32
  - Renders `role="presentation"` — purely decorative, not interactive
@@ -6,6 +6,7 @@
6
6
 
7
7
  ## Props
8
8
  options: ComboboxOption[] (REQUIRED) — { value: string, label: string, description?: string, icon?: ReactNode, disabled?: boolean }
9
+ size: "xs" | "sm" | "md" | "lg" (trigger height)
9
10
  DISCRIMINATED UNION — type depends on `multiple` flag:
10
11
  Single (default): multiple?: false, value?: string, onValueChange: (value: string) => void
11
12
  Multiple: multiple: true, value?: string[], onValueChange: (value: string[]) => void
@@ -13,13 +14,14 @@
13
14
  searchPlaceholder: string (default: "Search...")
14
15
  emptyMessage: string (default: "No results found")
15
16
  disabled: boolean
16
- triggerClassName: string
17
+ className: string (wrapper div — the positioning container, NOT the trigger)
18
+ triggerClassName: string (the actual Popover trigger button)
17
19
  accessibleLabel: string (custom aria-label for trigger, falls back to placeholder)
18
20
  maxVisible: number (default: 6, max dropdown items before scroll)
19
21
  renderOption: (option, selected) => ReactNode
20
22
 
21
23
  ## Defaults
22
- placeholder="Select...", searchPlaceholder="Search...", emptyMessage="No results found", maxVisible=6
24
+ size="md", placeholder="Select...", searchPlaceholder="Search...", emptyMessage="No results found", maxVisible=6
23
25
 
24
26
  ## Example
25
27
  ```jsx
@@ -32,9 +34,20 @@
32
34
  />
33
35
  ```
34
36
 
37
+ ## Composability
38
+ - **Combobox vs Autocomplete vs Select:** Combobox = typeahead search + forced selection (the user picks from filtered options). Autocomplete = typeahead + free text allowed. Select = no typeahead, click-to-open with fixed options. Pick by user behavior, not visual style.
39
+ - **Single vs multi mode** is a discriminated union — `multiple: true` changes the shape of value (`string[]`) and onValueChange. TypeScript enforces the pairing.
40
+ - **Multi-select pills:** Capped at 2 visible + "+N more" overflow regardless of `maxVisible`. Clicking the +N opens a popover list of all selected items (handled by the component).
41
+ - **className vs triggerClassName:** className lands on the wrapper (positioning); triggerClassName lands on the trigger button (styling the control). Know which you need.
42
+ - **Portal + z-popover (1400):** content stacks above Dialog/Sheet; works inside scrolling containers without clipping.
43
+ - **renderOption:** For complex option rendering (avatar + label + description), pass `renderOption: (option, selected) => <YourCustom />`. The selected state is a boolean flag.
44
+ - **FormField:** Does NOT auto-consume FormField state. Wrap in FormField for label + helper text, but style error manually.
45
+
35
46
  ## Gotchas
36
47
  - Enforces selection from list (unlike Autocomplete which allows free text)
37
- - In multi mode, selected items appear as pills with "+N more" overflow
48
+ - In multi mode, selected items appear as pills with "+N more" overflow (capped at 2 visible pills regardless of `maxVisible`)
49
+ - `className` vs `triggerClassName`: `className` lands on the wrapper div (useful for width/positioning); `triggerClassName` lands on the actual Popover trigger button (useful for styling the control itself)
50
+ - Dropdown content is portalled to document.body — parent styles like `overflow: hidden` don't clip it, and container-scoped test queries won't find it
38
51
 
39
52
  ## Changes
40
53
  ### v0.18.0
@@ -18,8 +18,19 @@
18
18
  </Container>
19
19
  ```
20
20
 
21
+ ## Composability
22
+ - **Server-safe layout primitive.** Safe in RSC trees — no hooks, no context.
23
+ - **maxWidth choices:**
24
+ - `"default"` (standard page container — matches the design system's layout grid)
25
+ - `"body"` (narrower reading width — use for article/blog content)
26
+ - `"full"` (no max — edge-to-edge, useful for full-bleed marketing sections)
27
+ - **Polymorphic via `as`:** Change the rendered element for semantics (`as="main"` for page content, `as="section"` for major subdivisions, `as="article"` for standalone content).
28
+ - **mx-auto centering is automatic** — Container handles horizontal centering; it does NOT add vertical spacing. Pair with `py-*` utility classes on the Container itself or inside it.
29
+ - Nothing cascades — nesting Containers is fine but rarely useful (max-width constraints compound).
30
+
21
31
  ## Gotchas
22
32
  - Server-safe component — can be imported directly in Next.js Server Components
33
+ - Container does NOT add vertical padding — add it explicitly via className if needed
23
34
 
24
35
  ## Changes
25
36
  ### v0.1.0
@@ -29,8 +29,16 @@
29
29
  </ContextMenu>
30
30
  ```
31
31
 
32
+ ## Composability
33
+ - Radix ContextMenu — same item-variant surface as DropdownMenu (checkbox, radio group, sub-menus, label, separator) with the same keyboard model. Swap `Dropdown` → `Context` in imports and everything behaves the same.
34
+ - **Trigger is not a button** — ContextMenuTrigger is a wrapper element (defaults to `<div>`) that listens for `contextmenu` events on itself and children. Use `asChild` to pass styling to your own element.
35
+ - **Touch support:** Radix maps long-press to right-click. On mobile, hold-to-open just works without extra code.
36
+ - **No visible trigger affordance** — unlike DropdownMenu/Popover, the user has to *know* the element is right-clickable. Pair with a visible hint (keyboard shortcut label, menu icon elsewhere) for discoverability.
37
+ - **Portal + z-index:** z-popover (1400).
38
+
32
39
  ## Gotchas
33
40
  - Triggered by right-click (or long-press on touch devices)
41
+ - ContextMenuTrigger doesn't auto-indicate it's interactive — add visual affordance elsewhere on the page
34
42
 
35
43
  ## Changes
36
44
  ### v0.18.0
@@ -7,6 +7,10 @@
7
7
  This component is used internally by `<DataTable>` and should not be imported directly.
8
8
  See [DataTable](./data-table.md) for the public API.
9
9
 
10
+ ## Composability
11
+ - **Internal only.** Renders the tbody + rows for DataTable. Consumes DataTableContext for row data, selection state, expandable state, loading skeletons.
12
+ - Customization routes through DataTable props: `renderExpanded`, `onRowClick`, `getRowId`, `loading`, `emptyState`.
13
+
10
14
  ## Changes
11
15
 
12
16
  ### v0.32.0
@@ -7,6 +7,10 @@
7
7
  This component is used internally by `<DataTable>` and should not be imported directly.
8
8
  See [DataTable](./data-table.md) for the public API.
9
9
 
10
+ ## Composability
11
+ - **Internal only.** Renders the floating bulk-action bar that appears when rows are selected. Reads selection state + bulkActions config from DataTableContext.
12
+ - Customization goes through DataTable's `bulkActions` prop — `{ label, onClick, color?, disabled? }[]`.
13
+
10
14
  ## Changes
11
15
 
12
16
  ### v0.32.0
@@ -7,6 +7,10 @@
7
7
  This component is used internally by `<DataTable>` and should not be imported directly.
8
8
  See [DataTable](./data-table.md) for the public API.
9
9
 
10
+ ## Composability
11
+ - **Internal only.** Renders the card wrapper around DataTable (border, shadow, rounded corners matching the Card primitive's surface treatment).
12
+ - DataTable uses this automatically — no consumer-facing prop toggles it.
13
+
10
14
  ## Changes
11
15
 
12
16
  ### v0.32.0
@@ -7,6 +7,10 @@
7
7
  This component is used internally by `<DataTable>` and should not be imported directly.
8
8
  See [DataTable](./data-table.md) for the public API.
9
9
 
10
+ ## Composability
11
+ - **Internal React context** — binds DataTable's sub-components (header, body, pagination, toolbar, bulk-actions) to shared state: TanStack table instance, density, selection, loading.
12
+ - Consumer code never reads this context — all interaction goes through DataTable's props.
13
+
10
14
  ## Changes
11
15
 
12
16
  ### v0.32.0
@@ -7,6 +7,10 @@
7
7
  This component is used internally by `<DataTable>` and should not be imported directly.
8
8
  See [DataTable](./data-table.md) for the public API.
9
9
 
10
+ ## Composability
11
+ - **Internal only.** Renders the thead + column headers for DataTable. Reads sort state + column definitions from DataTableContext.
12
+ - Customization goes through DataTable's `columns` prop (column definitions are TanStack ColumnDef<TData> — header, sortable, filter, etc. all declared there).
13
+
10
14
  ## Changes
11
15
 
12
16
  ### v0.32.0
@@ -7,6 +7,10 @@
7
7
  This component is used internally by `<DataTable>` and should not be imported directly.
8
8
  See [DataTable](./data-table.md) for the public API.
9
9
 
10
+ ## Composability
11
+ - **Internal only.** Renders the pagination footer for DataTable. Reads pagination state from DataTableContext.
12
+ - Customization goes through DataTable's `pagination` prop (server-side: pass `{ page, pageSize, total, onPageChange }`) or `pageSize` + `paginated` (client-side).
13
+
10
14
  ## Changes
11
15
 
12
16
  ### v0.32.0
@@ -31,10 +31,17 @@ import { DataTableToolbar } from '@devalok/shilp-sutra/ui/data-table-toolbar'
31
31
  />
32
32
  ```
33
33
 
34
+ ## Composability
35
+ - **Companion to DataTable** — typically enabled via DataTable's `toolbar={true}` prop (which auto-renders this internally). Use DataTableToolbar directly only if you're building a custom DataTable integration that needs the toolbar positioned/styled differently.
36
+ - **Reads table state via the `table` instance** (TanStack react-table) — you must pass it. For the auto-rendered version inside DataTable, this wiring is automatic.
37
+ - **Feature toggles:** `globalFilter` (search across all columns), density switcher (compact/standard/comfortable row heights), CSV export. Turn each on/off independently.
38
+ - **Density changes are runtime** — the switcher writes to the same `defaultDensity` that DataTable's prop seeds. State lives in DataTable context.
39
+
34
40
  ## Gotchas
35
41
  - Barrel-isolated since v0.5.0 — must use `@devalok/shilp-sutra/ui/data-table-toolbar`, NOT the `ui` barrel
36
42
  - Companion to DataTable — provides column visibility, density toggle, and CSV export controls
37
43
  - Requires @tanstack/react-table as a peer dependency
44
+ - Prefer DataTable's `toolbar={true}` prop over rendering this directly
38
45
 
39
46
  ## Changes
40
47
  ### v0.5.0
@@ -55,6 +55,30 @@ import { DataTable } from '@devalok/shilp-sutra/ui/data-table'
55
55
  />
56
56
  ```
57
57
 
58
+ ## Composability
59
+ **Server vs client mode is prop-driven, not explicit.**
60
+ - Pass `onSort` → server-side sort (manual, rows stay in data order — you're responsible for re-fetching).
61
+ - Pass `pagination` object → server-side pagination (manual, pass total count).
62
+ - Omit both → client-side sort/pagination via TanStack react-table.
63
+ - Mix-and-match: `onSort` + no pagination = server sort + client pagination.
64
+
65
+ **Companion components:**
66
+ - `DataTableToolbar` — enabled via `toolbar={true}`. Provides column visibility, density switcher, CSV export. Reads table state via `DataTableContext` (internal). Rendered ABOVE the table automatically.
67
+ - `BulkActionBar` (floating) — appears when rows are selected AND `bulkActions` array is non-empty. Synced with `selectedIds`; shows count + action buttons.
68
+ - `EmptyState` from `@devalok/shilp-sutra/composed` — pass to `emptyState` prop. Takes precedence over `noResultsText` string.
69
+
70
+ **Controlled selection:**
71
+ - Pass `selectedIds` (Set<string>) + `onSelectionChange` for controlled row selection.
72
+ - Provide `getRowId: (row) => row.id` so selection survives data refetches (otherwise TanStack uses array index, which breaks on sort/filter).
73
+ - `selectableFilter: (row) => boolean` disables selection on specific rows (e.g. archived items).
74
+
75
+ **Row click model:**
76
+ - `onRowClick` fires on row-level click BUT excludes clicks on checkboxes, buttons, links, and inputs automatically. No manual `stopPropagation` needed for standard interactive elements.
77
+
78
+ **Virtualization:** `virtualRows={true}` enables row virtualization via `@tanstack/react-virtual`. Turn it on for 1000+ row datasets; the scroll container must have a bounded height.
79
+
80
+ **Density integration:** `defaultDensity="compact"` is the Karm-style dense mode (h-9 rows). DataTableToolbar's density switcher updates this at runtime; the prop sets the initial state only.
81
+
58
82
  ## Gotchas
59
83
  - Barrel-isolated since v0.5.0 — must use `@devalok/shilp-sutra/ui/data-table`, NOT the `ui` barrel
60
84
  - Requires @tanstack/react-table and @tanstack/react-virtual as peer dependencies
@@ -63,6 +87,7 @@ import { DataTable } from '@devalok/shilp-sutra/ui/data-table'
63
87
  - selectedIds syncs via useEffect — provide getRowId for custom row IDs
64
88
  - onRowClick does NOT fire when clicking checkboxes, buttons, links, or inputs
65
89
  - Use defaultDensity="compact" for Karm-style h-9 rows
90
+ - `virtualRows={true}` requires a bounded scroll container — unbounded height silently disables virtualization
66
91
 
67
92
  ## Changes
68
93
  ### v0.29.0
@@ -41,6 +41,14 @@
41
41
  </div>
42
42
  ```
43
43
 
44
+ ## Composability
45
+ - **Brand texture overlay** — drops into any parent with `relative overflow-hidden isolate`. Auto-inherits the parent's border radius.
46
+ - **Button auto-extracts Grain children** — if you nest `<DevalokGrain>` inside a `<Button>`, Button auto-separates it from the label slot and positions it correctly. No extra wrapping needed.
47
+ - **Card, custom hero sections, landing-page tiles** — wrap in a positioned container (or add the `relative overflow-hidden isolate` classes to Card explicitly) and drop Grain in as a sibling to your content.
48
+ - **z-layer contract:** Grain renders at `z-[1]`. Content on top needs `z-[2]+`. The component doesn't boost child z-index — that's consumer responsibility.
49
+ - **`hoverIntensify` depends on parent** — the parent must have the Tailwind `group` class so `group-hover:` selectors apply. Forgetting this silently disables the effect.
50
+ - **Tint composes with surface:** Pass an OKLCH color (or CSS variable reference like `var(--color-accent-9)`) to `tint` for a directional gradient on top of the noise. Without tint, just noise.
51
+
44
52
  ## Gotchas
45
53
  - Parent element MUST have `relative overflow-hidden isolate` for the grain to render correctly
46
54
  - The grain layers are absolute-positioned at `z-[1]` — content that should appear above must use `z-[2]` or higher
@@ -35,8 +35,18 @@
35
35
  </Dialog>
36
36
  ```
37
37
 
38
+ ## Composability
39
+ - Built on Radix Dialog primitives — every standard Radix prop passes through (`open`, `onOpenChange`, `defaultOpen`, `modal`).
40
+ - **Trigger composition:** `<DialogTrigger asChild>` renders the wrapped element as the trigger — typical pattern is `<DialogTrigger asChild><Button>...</Button></DialogTrigger>`. Without `asChild`, a default button is injected.
41
+ - **Portal rendering:** DialogContent portals to `document.body`. CSS containers (`overflow: hidden`, `transform`, stacking contexts) on ancestors of the trigger don't clip it. Container-scoped test queries miss portalled content — use `screen` or the portal root.
42
+ - **Focus management:** Radix traps focus inside Content while open and restores it to the trigger on close. First focusable element inside receives focus automatically.
43
+ - **Imperative close** from deep children: use `<DialogClose asChild>` around your own button, or `useDialogContext` — no prop drilling.
44
+ - **z-index:** DialogContent uses `z-dialog` (configured in Tailwind theme). Nested overlays (Popover inside Dialog) stack on top because Popover uses `z-popover` (1400) higher than `z-dialog`.
45
+
38
46
  ## Gotchas
39
47
  - DialogTitle is required for accessibility — screen readers announce it when the dialog opens
48
+ - If your layout suppresses the title visually, use `<VisuallyHidden>` around DialogTitle — don't omit it
49
+ - Focus returns to the trigger on close — if the trigger is conditionally unmounted, focus lands on body; handle manually if that matters
40
50
 
41
51
  ## Changes
42
52
  ### v0.19.1
@@ -31,8 +31,18 @@
31
31
  </DropdownMenu>
32
32
  ```
33
33
 
34
+ ## Composability
35
+ - Radix DropdownMenu underneath — same prop surface (`open`, `onOpenChange`, `defaultOpen`, `modal`).
36
+ - **Item variants** stack cleanly: `DropdownMenuCheckboxItem` for multi-select toggles, `DropdownMenuRadioGroup` + `DropdownMenuRadioItem` for single-select, `DropdownMenuSub` + `DropdownMenuSubTrigger/SubContent` for nested submenus. Each has its own keyboard model pre-wired.
37
+ - **Keyboard:** Arrow keys navigate items, Enter/Space activates, Esc closes, typeahead jumps to first letter. All handled by Radix — don't re-implement.
38
+ - **Trigger:** `<DropdownMenuTrigger asChild>` around any button. IconButton is the common pairing.
39
+ - **Shortcut hints:** `<DropdownMenuShortcut>` inside an item renders a right-aligned `⌘K`-style kbd. Visual only — does NOT bind the shortcut globally.
40
+ - **Closing from a handler:** Item onSelect auto-closes the menu by default. Pass `event.preventDefault()` inside the handler to keep it open (e.g. for checkbox items that shouldn't close on toggle).
41
+
34
42
  ## Gotchas
35
43
  - Use `asChild` on DropdownMenuTrigger to render your own button element
44
+ - DropdownMenuShortcut is decorative — bind keyboard shortcuts separately (e.g. with `useHotkeys`)
45
+ - Sub-menus need BOTH DropdownMenuSubTrigger (visible item) and DropdownMenuSubContent (the submenu panel) — missing either silently breaks the hover-open behavior
36
46
 
37
47
  ## Changes
38
48
  ### v0.22.0
@@ -32,9 +32,17 @@
32
32
  />
33
33
  ```
34
34
 
35
+ ## Composability
36
+ - **Two modes, one component:** `compact={false}` (default) = drag-and-drop zone with visible affordance; `compact={true}` = inline "Upload" button. Swap by prop, same callback.
37
+ - **Progress integration:** Drive `uploading` + `progress` from your upload logic. The component renders an embedded Progress bar when uploading=true.
38
+ - **Validation is built-in:** `accept` (MIME types / extensions) + `maxSize` are enforced before `onFiles` fires. Invalid files produce `error` state instead of calling through. Your onFiles never has to re-validate.
39
+ - **Multi-file:** `multiple={true}` accepts File[] (array always, even for single-file mode — use `files[0]`).
40
+ - **Composes with Toast for feedback:** Pair with toast.success on upload complete, toast.error on failure.
41
+
35
42
  ## Gotchas
36
43
  - compact=true renders a small inline button; false (default) renders a large drag-and-drop zone
37
44
  - Client-side validation: invalid files are rejected before onFiles is called
45
+ - `onFiles` always receives an array — use `files[0]` even when `multiple={false}`
38
46
 
39
47
  ## Changes
40
48
  ### v0.1.0
@@ -31,10 +31,31 @@
31
31
  </FormField>
32
32
  ```
33
33
 
34
+ ## Composability
35
+ The FormField/useFormField pair is the central a11y wiring pattern. Components that consume it automatically become accessible inside a FormField without any explicit ARIA work on your end.
36
+
37
+ **FormField cascades through context to:**
38
+ - **Input, Textarea, NumberInput, InputOTP** — auto-receive `aria-describedby` (wired to FormHelperText's id), `aria-invalid` (when state="error"), and `aria-required` (when required=true).
39
+ - **FormHelperText** — auto-reads `state` and `helperTextId` from context; renders `role="alert"` on error so screen readers interrupt.
40
+ - **Label** — pair it with an Input via `htmlFor` / `id` in the normal way; FormField doesn't auto-wire labels (labels need the explicit association to satisfy screen readers reliably).
41
+
42
+ **Explicit props always override context.** Setting `state="error"` on an Input inside a FormField with `state="helper"` makes only that Input look errored.
43
+
44
+ **Nesting is NOT supported.** Don't nest FormField inside FormField — only the outermost context wins, and some a11y wiring silently breaks.
45
+
46
+ **Consuming context in your own components:**
47
+ ```tsx
48
+ const field = useFormField() // returns { state, helperTextId, required } or undefined
49
+ // Thread field?.state onto your control's state prop,
50
+ // and field?.helperTextId onto aria-describedby.
51
+ ```
52
+
34
53
  ## Gotchas
35
54
  - getFormFieldA11y() was REMOVED — use useFormField() hook instead
36
55
  - FormHelperText auto-reads state and id from FormField context
37
56
  - FormHelperText renders role="alert" when state="error"
57
+ - Don't nest FormField components — one FormField per field
58
+ - FormField does NOT auto-wire Label→Input association; use `<Label htmlFor="x" />` + `<Input id="x" />` explicitly
38
59
 
39
60
  ## Changes
40
61
  ### v0.18.0
@@ -17,8 +17,17 @@
17
17
  </HoverCard>
18
18
  ```
19
19
 
20
+ ## Composability
21
+ - Radix HoverCard — accepts `open`, `onOpenChange`, `defaultOpen`, `openDelay` (ms before show), `closeDelay` (ms before hide).
22
+ - **Distinction from Tooltip:** HoverCardContent can contain interactive elements (links, buttons, form fields). Tooltip is for inert text labels. If you need a rich hover preview — user card, link preview, product card — use HoverCard.
23
+ - **Distinction from Popover:** Popover opens on click/focus; HoverCard opens on hover/focus. Use HoverCard when the intent is a passive preview, Popover when the user must explicitly invoke the panel.
24
+ - **Trigger:** `<HoverCardTrigger asChild>` wraps any element. Works on `<span>`, `<a>`, `<button>` — the trigger doesn't need to be interactive itself (unlike Popover where click matters).
25
+ - **Accessibility:** Hover-only interactions are invisible on touch. Pair with a focus-visible state or use Popover instead for critical content.
26
+ - **Portal + z-index:** z-popover (1400), same as DropdownMenu/Popover.
27
+
20
28
  ## Gotchas
21
29
  - Overlay component — uses Framer Motion for enter/exit animations (v0.18.0)
30
+ - Don't nest another HoverCard inside one — both open on the same hover and conflict
22
31
 
23
32
  ## Changes
24
33
  ### v0.18.0
@@ -25,6 +25,13 @@
25
25
  <IconButton icon={<Icon icon={IconTrash} />} variant="solid" color="error" aria-label="Delete" />
26
26
  ```
27
27
 
28
+ ## Composability
29
+ - **Built on Button internally** — inherits ALL Button features: variant, color, loading, onClickAsync, processing, ButtonGroup context consumption, asChild. Use IconButton when your button is icon-only; use Button for label-plus-icon.
30
+ - **aria-label is TypeScript-enforced** — the library makes it impossible to forget. This is the key accessibility constraint for icon-only buttons.
31
+ - **IconProvider cascade:** The `icon` prop expects `<Icon icon={...} />` — Icon auto-sizes based on IconButton's `size` via IconProvider context (no need to pass size to the nested Icon).
32
+ - **Inside ButtonGroup:** Inherits variant/color/size/weight from ButtonGroup context just like Button does. Useful for icon-only toolbars.
33
+ - **IconGroup vs ButtonGroup of IconButtons:** IconGroup is for static icon clusters (not interactive, or for decorative displays). ButtonGroup of IconButtons is for interactive toolbars where each icon is a button.
34
+
28
35
  ## Gotchas
29
36
  - aria-label is enforced by TypeScript — you MUST provide it
30
37
  - Prefer IconButton over Button with size="icon-*" for icon-only buttons
@@ -28,6 +28,13 @@ import { IconProvider, useIconContext } from '@devalok/shilp-sutra/ui'
28
28
  const { size, stroke } = useIconContext()
29
29
  ```
30
30
 
31
+ ## Composability
32
+ - **Low-level context primitive** — rarely used directly. Most consumers want IconGroup (which wraps IconProvider + layout) or just nest Icons inside Button/Input/Badge (which provide their own IconProvider).
33
+ - **When to reach for IconProvider manually:** Building a custom container that hosts many Icons (a dashboard tile row, a custom toolbar, a nav rail) and you want consistent icon sizing without repeating props. IconProvider is the escape hatch for that.
34
+ - **Consumer contract:** `useIconContext()` returns `{}` (empty) when no provider is present — your consumer code MUST handle that (fall back to a default size/stroke). Don't assume a provider exists.
35
+ - **Memoization:** The provider memoizes its value, so consumers don't re-render unless size or stroke actually change. Safe to use in tight loops / frequently-re-rendering trees.
36
+ - **Composes down:** IconProvider nests — inner providers override outer ones. A `<Button size="lg"><IconProvider size="xs">...</IconProvider></Button>` overrides the button's icon sizing.
37
+
31
38
  ## Gotchas
32
39
  - Used internally by IconGroup and Button to propagate icon sizing to children
33
40
  - If no provider is present, `useIconContext()` returns `{}` (empty object) — consumers should fall back to defaults
@@ -26,6 +26,15 @@
26
26
  </IconGroup>
27
27
  ```
28
28
 
29
+ ## Composability
30
+ - **IconProvider wrapper + layout** — IconGroup is IconProvider plus a flex container. All child `<Icon>` elements auto-size via the provider. Don't pass size to nested Icons.
31
+ - **IconGroup vs ButtonGroup-of-IconButtons:**
32
+ - IconGroup = static icon displays (legend rows, feature lists, decorative clusters). Not interactive.
33
+ - ButtonGroup of IconButtons = interactive toolbar where each icon is a button.
34
+ - If you want a formatting toolbar (interactive), use ButtonGroup or ToggleGroup, not IconGroup.
35
+ - **Toolbar mode:** Set `role="toolbar"` + provide `label` to give the group a semantic role. Only then does `label` become `aria-label` on the container.
36
+ - **Gap semantics:** tight=2px, default=4px, loose=8px — smaller values than ButtonGroup or Stack because icons are visually dense.
37
+
29
38
  ## Gotchas
30
39
  - Wraps children in an IconProvider — all child Icons inherit size/stroke from the group
31
40
  - `label` is only applied as `aria-label` when `role="toolbar"` is set
@@ -30,6 +30,13 @@
30
30
  <Icon icon={IconPlus} state="success" />
31
31
  ```
32
32
 
33
+ ## Composability
34
+ - **IconContext consumption** — Icon auto-reads `size` and `stroke` from the nearest IconProvider. Button, IconButton, IconGroup, Input (startSection/endSection), Badge, NumberInput, and more all wrap their children in IconProvider so nested Icons auto-size. Explicit props always override context.
35
+ - **Use Tabler icon components** — the `icon` prop expects a ForwardRef SVG component (the shape Tabler React exports). Any icon lib following that shape works, but Tabler is the standard.
36
+ - **Label for a11y:** Without `label`, Icon is `aria-hidden="true"` (decorative — appropriate inside labeled buttons). With `label`, renders `role="img"` + aria-label + `<title>` for standalone icons.
37
+ - **State overrides animate:** When both are set, state wins. `state="loading"` renders a bare Spinner; success/error render animated glyphs via Framer Motion.
38
+ - **Reduced motion:** All animations respect `prefers-reduced-motion` — animate props fall back to static render.
39
+
33
40
  ## Gotchas
34
41
  - Without `label`, the icon renders `aria-hidden="true"` (decorative)
35
42
  - With `label`, the icon renders `role="img"` with `aria-label` and a `<title>` element
@@ -5,17 +5,32 @@
5
5
  - Category: ui
6
6
 
7
7
  ## Props
8
- Standard input-otp props (maxLength, pattern, etc.)
8
+ ### InputOTP
9
+ maxLength: number (REQUIRED) — total number of slots
10
+ value: string (controlled value; defaults to empty string)
11
+ onChange: (value: string) => void
12
+ onComplete: (value: string) => void — fires when all slots filled
13
+ pattern: string | RegExp — restrict input (e.g. `REGEXP_ONLY_DIGITS`)
14
+ state: "default" | "error" (error adds red border; auto-inherits from FormField)
15
+ size: "sm" | "md" | "lg" (slot dimensions — propagates to InputOTPSlot via context)
16
+ disabled: boolean
17
+ containerClassName: string (on the outer group container — separate from inner input's className)
18
+
19
+ ### InputOTPSlot
20
+ index: number (REQUIRED, 0-based) — which position this slot renders
9
21
 
10
22
  ## Compound Components
11
- InputOTP (root)
12
- InputOTPGroup
13
- InputOTPSlot (index: number, REQUIRED)
14
- InputOTPSeparator
23
+ InputOTP (root — maxLength, value, onChange, size propagated via context)
24
+ InputOTPGroup (visual group of slots)
25
+ InputOTPSlot (index: number, REQUIRED — reads size from context)
26
+ InputOTPSeparator (visual separator, e.g. between two groups of 3)
27
+
28
+ ## Defaults
29
+ size="md", state="default"
15
30
 
16
31
  ## Example
17
32
  ```jsx
18
- <InputOTP maxLength={6}>
33
+ <InputOTP maxLength={6} onComplete={verifyCode}>
19
34
  <InputOTPGroup>
20
35
  <InputOTPSlot index={0} />
21
36
  <InputOTPSlot index={1} />
@@ -30,8 +45,15 @@
30
45
  </InputOTP>
31
46
  ```
32
47
 
48
+ ## Composability
49
+ - Size propagates from InputOTP → InputOTPSlot via `InputOTPSizeContext` — don't set size on individual slots.
50
+ - Inside `<FormField>`: auto-inherits state, aria-describedby, aria-required from context. Explicit `state="error"` overrides.
51
+ - Underlying library is `input-otp` (OTPInput) — all standard library props (pattern, inputMode, autoFocus, etc.) pass through.
52
+
33
53
  ## Gotchas
34
- - Each InputOTPSlot requires an `index` prop (0-based)
54
+ - Each InputOTPSlot requires an `index` prop (0-based) matching its position
55
+ - `onComplete` only fires when ALL slots are filled — use `onChange` for per-character reactivity
56
+ - `containerClassName` (outer visual group) is distinct from `className` (the hidden input element itself)
35
57
 
36
58
  ## Changes
37
59
  ### v0.18.0
@@ -32,6 +32,13 @@
32
32
  <Input endSection=".00" endSectionType="label" startSection={<Icon icon={IconCurrencyDollar} />} placeholder="0" />
33
33
  ```
34
34
 
35
+ ## Composability
36
+ - **FormField auto-consumption:** Inside a `<FormField>`, Input auto-inherits `state`, `aria-describedby` (linked to FormHelperText), `aria-invalid` (when state="error"), `aria-required`. Explicit props on Input override context.
37
+ - **IconProvider cascade:** Icons in `startSection` / `endSection` are auto-sized via IconProvider per the input's `size` (xs → sm icon, sm → sm icon, md → md icon, lg → md icon). Don't pass explicit size to nested `<Icon>`.
38
+ - **Container-first architecture (v0.29.0+):** Border, background, focus ring all live on the wrapper div (accessed via `wrapperClassName`). The raw `<input>` inside is transparent. `className` goes to the input element; `wrapperClassName` goes to the wrapper.
39
+ - **Section types:** Strings in startSection/endSection auto-render as `label` (tinted bg + border separator); React elements auto-render as `icon` (fixed-width centered cell). Override via `startSectionType` / `endSectionType`.
40
+ - **Label pairing:** Always pair with `<Label htmlFor="x" />` + `<Input id="x" />` or wrap both in a `<label>` — FormField doesn't auto-wire the visual label to the input.
41
+
35
42
  ## Gotchas
36
43
  - HTML native "size" attribute is excluded — use CSS width instead
37
44
  - state="error" sets aria-invalid automatically