@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.
- package/dist/composed/inline-edit.d.ts.map +1 -1
- package/dist/composed/inline-edit.js +42 -40
- package/dist/composed/inline-edit.js.map +1 -1
- package/dist/test-utils/conformance.d.ts +37 -0
- package/dist/test-utils/conformance.d.ts.map +1 -0
- package/docs/components/composed/activity-feed.md +6 -0
- package/docs/components/composed/avatar-group.md +8 -0
- package/docs/components/composed/bulk-action-bar.md +6 -0
- package/docs/components/composed/command-palette.md +7 -0
- package/docs/components/composed/confirm-dialog.md +6 -0
- package/docs/components/composed/content-card.md +6 -0
- package/docs/components/composed/date-picker.md +10 -0
- package/docs/components/composed/deadline-indicator.md +7 -0
- package/docs/components/composed/emoji-picker.md +8 -0
- package/docs/components/composed/empty-state.md +8 -0
- package/docs/components/composed/error-boundary.md +7 -0
- package/docs/components/composed/file-preview.md +8 -0
- package/docs/components/composed/filter-bar.md +8 -0
- package/docs/components/composed/form-section.md +6 -0
- package/docs/components/composed/global-loading.md +6 -0
- package/docs/components/composed/inline-edit.md +8 -0
- package/docs/components/composed/loading-skeleton.md +7 -0
- package/docs/components/composed/markdown-viewer.md +9 -0
- package/docs/components/composed/master-detail.md +7 -0
- package/docs/components/composed/member-picker.md +7 -0
- package/docs/components/composed/multi-select-popover.md +11 -0
- package/docs/components/composed/page-header.md +7 -0
- package/docs/components/composed/page-skeletons.md +10 -0
- package/docs/components/composed/priority-indicator.md +7 -0
- package/docs/components/composed/responsive-overlay.md +7 -0
- package/docs/components/composed/rich-chat-input.md +12 -0
- package/docs/components/composed/rich-text-editor.md +9 -0
- package/docs/components/composed/schedule-view.md +8 -0
- package/docs/components/composed/simple-tooltip.md +7 -0
- package/docs/components/composed/status-badge.md +10 -0
- package/docs/components/shell/app-command-palette.md +8 -0
- package/docs/components/shell/bottom-navbar.md +12 -0
- package/docs/components/shell/command-registry.md +7 -0
- package/docs/components/shell/link-context.md +15 -0
- package/docs/components/shell/notification-center.md +10 -0
- package/docs/components/shell/notification-preferences.md +7 -0
- package/docs/components/shell/sidebar.md +11 -0
- package/docs/components/shell/top-bar.md +11 -0
- package/docs/components/ui/accordion.md +10 -0
- package/docs/components/ui/alert-dialog.md +10 -1
- package/docs/components/ui/alert.md +10 -2
- package/docs/components/ui/aspect-ratio.md +7 -0
- package/docs/components/ui/autocomplete.md +7 -0
- package/docs/components/ui/avatar.md +10 -0
- package/docs/components/ui/badge-group.md +7 -0
- package/docs/components/ui/badge-indicator.md +7 -0
- package/docs/components/ui/badge.md +12 -0
- package/docs/components/ui/banner.md +7 -0
- package/docs/components/ui/breadcrumb.md +8 -0
- package/docs/components/ui/button-group.md +24 -4
- package/docs/components/ui/button-processing.md +5 -0
- package/docs/components/ui/button.md +9 -0
- package/docs/components/ui/card.md +16 -4
- package/docs/components/ui/charts.md +9 -0
- package/docs/components/ui/chat.md +9 -0
- package/docs/components/ui/checkbox.md +15 -1
- package/docs/components/ui/chip.md +9 -0
- package/docs/components/ui/code.md +7 -0
- package/docs/components/ui/collapsible.md +9 -0
- package/docs/components/ui/color-input.md +8 -0
- package/docs/components/ui/color-swatch.md +7 -0
- package/docs/components/ui/combobox.md +16 -3
- package/docs/components/ui/container.md +11 -0
- package/docs/components/ui/context-menu.md +8 -0
- package/docs/components/ui/data-table-body.md +4 -0
- package/docs/components/ui/data-table-bulk-actions.md +4 -0
- package/docs/components/ui/data-table-card.md +4 -0
- package/docs/components/ui/data-table-context.md +4 -0
- package/docs/components/ui/data-table-header.md +4 -0
- package/docs/components/ui/data-table-pagination.md +4 -0
- package/docs/components/ui/data-table-toolbar.md +7 -0
- package/docs/components/ui/data-table.md +25 -0
- package/docs/components/ui/devalok-grain.md +8 -0
- package/docs/components/ui/dialog.md +10 -0
- package/docs/components/ui/dropdown-menu.md +10 -0
- package/docs/components/ui/file-upload.md +8 -0
- package/docs/components/ui/form.md +21 -0
- package/docs/components/ui/hover-card.md +9 -0
- package/docs/components/ui/icon-button.md +7 -0
- package/docs/components/ui/icon-context.md +7 -0
- package/docs/components/ui/icon-group.md +9 -0
- package/docs/components/ui/icon.md +7 -0
- package/docs/components/ui/input-otp.md +29 -7
- package/docs/components/ui/input.md +7 -0
- package/docs/components/ui/label.md +7 -1
- package/docs/components/ui/link.md +7 -0
- package/docs/components/ui/menubar.md +9 -0
- package/docs/components/ui/navigation-menu.md +9 -0
- package/docs/components/ui/number-input.md +10 -0
- package/docs/components/ui/pagination.md +10 -0
- package/docs/components/ui/popover.md +9 -0
- package/docs/components/ui/progress-ring.md +7 -0
- package/docs/components/ui/progress.md +7 -0
- package/docs/components/ui/radio.md +27 -2
- package/docs/components/ui/search-input.md +8 -0
- package/docs/components/ui/segmented-control.md +8 -0
- package/docs/components/ui/select.md +14 -3
- package/docs/components/ui/separator.md +7 -0
- package/docs/components/ui/sheet.md +10 -0
- package/docs/components/ui/sidebar.md +30 -0
- package/docs/components/ui/skeleton.md +7 -0
- package/docs/components/ui/slider.md +15 -1
- package/docs/components/ui/spinner.md +7 -0
- package/docs/components/ui/split-button.md +11 -0
- package/docs/components/ui/stack.md +8 -0
- package/docs/components/ui/stat-card.md +9 -0
- package/docs/components/ui/status-dot.md +9 -0
- package/docs/components/ui/stepper.md +8 -0
- package/docs/components/ui/switch.md +8 -0
- package/docs/components/ui/table.md +8 -0
- package/docs/components/ui/tabs.md +10 -2
- package/docs/components/ui/text.md +9 -4
- package/docs/components/ui/textarea.md +7 -1
- package/docs/components/ui/toast.md +9 -0
- package/docs/components/ui/toaster.md +8 -0
- package/docs/components/ui/toggle-group.md +13 -4
- package/docs/components/ui/toggle.md +10 -2
- package/docs/components/ui/tooltip.md +11 -1
- package/docs/components/ui/tree-view.md +12 -0
- package/docs/components/ui/visually-hidden.md +7 -0
- package/llms-full.txt +1116 -44
- 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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|