@devalok/shilp-sutra 0.6.1 → 0.6.2
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/llms-full.txt +1753 -0
- package/llms.txt +180 -0
- package/package.json +528 -526
package/llms-full.txt
ADDED
|
@@ -0,0 +1,1753 @@
|
|
|
1
|
+
# @devalok/shilp-sutra — Full Component Reference
|
|
2
|
+
|
|
3
|
+
> Exhaustive API reference for AI coding agents.
|
|
4
|
+
> For a concise cheatsheet, see llms.txt instead.
|
|
5
|
+
> All variant values and props verified from source CVA definitions.
|
|
6
|
+
>
|
|
7
|
+
> Package: @devalok/shilp-sutra
|
|
8
|
+
> Version: 0.6.1
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## Architecture Notes
|
|
13
|
+
|
|
14
|
+
### The Two-Axis Variant System
|
|
15
|
+
|
|
16
|
+
Many components use TWO props where shadcn/ui uses one:
|
|
17
|
+
- `variant` controls SHAPE/SURFACE: solid, outline, ghost, subtle, filled, etc.
|
|
18
|
+
- `color` controls INTENT/SEMANTICS: default, error, success, warning, info, etc.
|
|
19
|
+
|
|
20
|
+
Components with two-axis system: Button, Badge, Alert, Chip, Toast, Banner, Progress, StatusBadge
|
|
21
|
+
|
|
22
|
+
### Server-Safe Components (no "use client")
|
|
23
|
+
|
|
24
|
+
These can be imported directly in Next.js Server Components:
|
|
25
|
+
- UI: Text, Skeleton, Spinner, Stack, Container, Table (and sub-components), Code, VisuallyHidden
|
|
26
|
+
- Composed: ContentCard, EmptyState, PageHeader, LoadingSkeleton, PageSkeletons, PriorityIndicator, StatusBadge
|
|
27
|
+
|
|
28
|
+
Use per-component imports for server components:
|
|
29
|
+
import { Text } from '@devalok/shilp-sutra/ui/text'
|
|
30
|
+
import { PageHeader } from '@devalok/shilp-sutra/composed/page-header'
|
|
31
|
+
|
|
32
|
+
DO NOT use barrel imports in Server Components — they include "use client" components.
|
|
33
|
+
|
|
34
|
+
### Toast Setup Pattern
|
|
35
|
+
|
|
36
|
+
1. Mount <Toaster /> once at your root layout.
|
|
37
|
+
2. Call useToast() or the imperative toast() function from any component.
|
|
38
|
+
3. Valid colors: 'neutral' | 'success' | 'warning' | 'error' | 'info'
|
|
39
|
+
|
|
40
|
+
### Form Accessibility Pattern
|
|
41
|
+
|
|
42
|
+
Use <FormField> + useFormField() hook:
|
|
43
|
+
<FormField state="error">
|
|
44
|
+
<Label htmlFor="email">Email</Label>
|
|
45
|
+
<Input id="email" state="error" />
|
|
46
|
+
<FormHelperText>Error message here.</FormHelperText>
|
|
47
|
+
</FormField>
|
|
48
|
+
|
|
49
|
+
useFormField() returns { state, helperTextId, required } from context.
|
|
50
|
+
Wire manually: <Input aria-describedby={helperTextId} aria-invalid={state === 'error'} />
|
|
51
|
+
|
|
52
|
+
Note: getFormFieldA11y() was removed in favor of useFormField() hook.
|
|
53
|
+
|
|
54
|
+
---
|
|
55
|
+
|
|
56
|
+
# UI COMPONENTS
|
|
57
|
+
# Alphabetical within this section.
|
|
58
|
+
# Import from: @devalok/shilp-sutra/ui/<kebab-name>
|
|
59
|
+
|
|
60
|
+
---
|
|
61
|
+
|
|
62
|
+
## Accordion
|
|
63
|
+
- Import: @devalok/shilp-sutra/ui/accordion
|
|
64
|
+
- Server-safe: No
|
|
65
|
+
- Props (Accordion root):
|
|
66
|
+
type: "single" | "multiple" (REQUIRED)
|
|
67
|
+
defaultValue: string (single) | string[] (multiple)
|
|
68
|
+
value: string | string[] (controlled)
|
|
69
|
+
onValueChange: (value) => void
|
|
70
|
+
collapsible: boolean (only valid when type="single")
|
|
71
|
+
- Compound:
|
|
72
|
+
Accordion (root)
|
|
73
|
+
AccordionItem (value: string, REQUIRED)
|
|
74
|
+
AccordionTrigger (clickable header, chevron auto-renders)
|
|
75
|
+
AccordionContent (collapsible body)
|
|
76
|
+
- Defaults: none (type is required)
|
|
77
|
+
- Example:
|
|
78
|
+
<Accordion type="single" defaultValue="item-1" collapsible>
|
|
79
|
+
<AccordionItem value="item-1">
|
|
80
|
+
<AccordionTrigger>Question?</AccordionTrigger>
|
|
81
|
+
<AccordionContent>Answer.</AccordionContent>
|
|
82
|
+
</AccordionItem>
|
|
83
|
+
</Accordion>
|
|
84
|
+
- Gotchas:
|
|
85
|
+
- type is REQUIRED — omitting it causes runtime error
|
|
86
|
+
- collapsible only works with type="single"
|
|
87
|
+
|
|
88
|
+
## Alert
|
|
89
|
+
- Import: @devalok/shilp-sutra/ui/alert
|
|
90
|
+
- Server-safe: No
|
|
91
|
+
- Props:
|
|
92
|
+
variant: "subtle" | "filled" | "outline"
|
|
93
|
+
color: "info" | "success" | "warning" | "error" | "neutral"
|
|
94
|
+
title: string (optional)
|
|
95
|
+
onDismiss: () => void (optional, shows X button when provided)
|
|
96
|
+
children: ReactNode (body text)
|
|
97
|
+
- Defaults: variant="subtle", color="info"
|
|
98
|
+
- Example:
|
|
99
|
+
<Alert color="error" title="Save failed" onDismiss={() => setError(null)}>
|
|
100
|
+
Your changes could not be saved.
|
|
101
|
+
</Alert>
|
|
102
|
+
- Gotchas:
|
|
103
|
+
- NOT a compound component — use title prop, NOT <AlertTitle>
|
|
104
|
+
- DO NOT use variant="destructive" — use color="error"
|
|
105
|
+
- Renders role="alert" automatically
|
|
106
|
+
- Icon is auto-selected by color (info=circle, success=check, warning=triangle, error=alert)
|
|
107
|
+
|
|
108
|
+
## AlertDialog
|
|
109
|
+
- Import: @devalok/shilp-sutra/ui/alert-dialog
|
|
110
|
+
- Server-safe: No
|
|
111
|
+
- Props: Standard Radix AlertDialog props (open, onOpenChange, defaultOpen)
|
|
112
|
+
- Compound:
|
|
113
|
+
AlertDialog (root)
|
|
114
|
+
AlertDialogTrigger
|
|
115
|
+
AlertDialogContent
|
|
116
|
+
AlertDialogHeader
|
|
117
|
+
AlertDialogTitle
|
|
118
|
+
AlertDialogDescription
|
|
119
|
+
AlertDialogFooter
|
|
120
|
+
AlertDialogCancel
|
|
121
|
+
AlertDialogAction
|
|
122
|
+
- Example:
|
|
123
|
+
<AlertDialog>
|
|
124
|
+
<AlertDialogTrigger asChild>
|
|
125
|
+
<Button color="error">Delete</Button>
|
|
126
|
+
</AlertDialogTrigger>
|
|
127
|
+
<AlertDialogContent>
|
|
128
|
+
<AlertDialogHeader>
|
|
129
|
+
<AlertDialogTitle>Are you sure?</AlertDialogTitle>
|
|
130
|
+
<AlertDialogDescription>This cannot be undone.</AlertDialogDescription>
|
|
131
|
+
</AlertDialogHeader>
|
|
132
|
+
<AlertDialogFooter>
|
|
133
|
+
<AlertDialogCancel>Cancel</AlertDialogCancel>
|
|
134
|
+
<AlertDialogAction>Delete</AlertDialogAction>
|
|
135
|
+
</AlertDialogFooter>
|
|
136
|
+
</AlertDialogContent>
|
|
137
|
+
</AlertDialog>
|
|
138
|
+
- Gotchas: AlertDialogAction does NOT have color="error" styling — add it yourself via className or wrap a Button
|
|
139
|
+
|
|
140
|
+
## AspectRatio
|
|
141
|
+
- Import: @devalok/shilp-sutra/ui/aspect-ratio
|
|
142
|
+
- Server-safe: No
|
|
143
|
+
- Props:
|
|
144
|
+
ratio: number (e.g. 16/9, 4/3, 1)
|
|
145
|
+
- Example:
|
|
146
|
+
<AspectRatio ratio={16 / 9}>
|
|
147
|
+
<img src="/photo.jpg" alt="Photo" className="object-cover w-full h-full" />
|
|
148
|
+
</AspectRatio>
|
|
149
|
+
|
|
150
|
+
## Autocomplete
|
|
151
|
+
- Import: @devalok/shilp-sutra/ui/autocomplete
|
|
152
|
+
- Server-safe: No
|
|
153
|
+
- Props:
|
|
154
|
+
options: AutocompleteOption[] (REQUIRED) — { value: string, label: string }
|
|
155
|
+
value: AutocompleteOption | null
|
|
156
|
+
onValueChange: (option: AutocompleteOption) => void
|
|
157
|
+
placeholder: string
|
|
158
|
+
emptyText: string (default: "No options")
|
|
159
|
+
disabled: boolean
|
|
160
|
+
className: string
|
|
161
|
+
id: string
|
|
162
|
+
- Example:
|
|
163
|
+
<Autocomplete
|
|
164
|
+
options={[{ value: 'mumbai', label: 'Mumbai' }]}
|
|
165
|
+
value={selectedCity}
|
|
166
|
+
onValueChange={setSelectedCity}
|
|
167
|
+
placeholder="Search cities..."
|
|
168
|
+
/>
|
|
169
|
+
- Gotchas:
|
|
170
|
+
- Allows free-text input (no forced selection) — use Combobox for forced selection
|
|
171
|
+
- value is an object { value, label }, NOT just a string
|
|
172
|
+
|
|
173
|
+
## Avatar
|
|
174
|
+
- Import: @devalok/shilp-sutra/ui/avatar
|
|
175
|
+
- Server-safe: No
|
|
176
|
+
- Props (Avatar root):
|
|
177
|
+
size: "xs" | "sm" | "md" | "lg" | "xl"
|
|
178
|
+
shape: "circle" | "square" | "rounded"
|
|
179
|
+
status: "online" | "offline" | "busy" | "away"
|
|
180
|
+
- Defaults: size="md", shape="circle"
|
|
181
|
+
- Compound:
|
|
182
|
+
Avatar (root with size/shape/status)
|
|
183
|
+
AvatarImage (src, alt)
|
|
184
|
+
AvatarFallback (children = initials text)
|
|
185
|
+
- Example:
|
|
186
|
+
<Avatar size="lg" status="online">
|
|
187
|
+
<AvatarImage src={user.photoUrl} alt={user.name} />
|
|
188
|
+
<AvatarFallback>JD</AvatarFallback>
|
|
189
|
+
</Avatar>
|
|
190
|
+
- Gotchas:
|
|
191
|
+
- Status dot renders with role="img" and aria-label (accessible, not decorative)
|
|
192
|
+
- Dot size scales automatically with avatar size
|
|
193
|
+
|
|
194
|
+
## Badge
|
|
195
|
+
- Import: @devalok/shilp-sutra/ui/badge
|
|
196
|
+
- Server-safe: No
|
|
197
|
+
- Props:
|
|
198
|
+
variant: "subtle" | "solid" | "outline" | "secondary" (alias->subtle) | "destructive" (alias->solid+error)
|
|
199
|
+
color: "default" | "info" | "success" | "error" | "warning" | "brand" | "accent" | "teal" | "amber" | "slate" | "indigo" | "cyan" | "orange" | "emerald"
|
|
200
|
+
size: "sm" | "md" | "lg"
|
|
201
|
+
dot: boolean (shows leading dot indicator)
|
|
202
|
+
onDismiss: () => void (shows X button when provided)
|
|
203
|
+
children: ReactNode
|
|
204
|
+
- Defaults: variant="subtle", color="default", size="md"
|
|
205
|
+
- Example:
|
|
206
|
+
<Badge variant="solid" color="success">Active</Badge>
|
|
207
|
+
<Badge color="teal" onDismiss={() => removeFilter('teal')}>Teal team</Badge>
|
|
208
|
+
- Gotchas:
|
|
209
|
+
- DO NOT use variant="destructive" — use variant="solid" color="error"
|
|
210
|
+
- Badge is display-only; for interactive tags use Chip
|
|
211
|
+
|
|
212
|
+
## Banner
|
|
213
|
+
- Import: @devalok/shilp-sutra/ui/banner
|
|
214
|
+
- Server-safe: No
|
|
215
|
+
- Props:
|
|
216
|
+
color: "info" | "success" | "warning" | "error" | "neutral"
|
|
217
|
+
action: ReactNode (optional action slot, typically a ghost Button)
|
|
218
|
+
onDismiss: () => void (optional, shows X button)
|
|
219
|
+
children: ReactNode (message text)
|
|
220
|
+
- Defaults: color="info"
|
|
221
|
+
- Example:
|
|
222
|
+
<Banner color="warning" onDismiss={() => dismiss()}>
|
|
223
|
+
Scheduled maintenance on Sunday.
|
|
224
|
+
</Banner>
|
|
225
|
+
- Gotchas:
|
|
226
|
+
- Banner is full-width (spans container). Alert is inline.
|
|
227
|
+
- Renders role="alert" automatically
|
|
228
|
+
|
|
229
|
+
## Breadcrumb
|
|
230
|
+
- Import: @devalok/shilp-sutra/ui/breadcrumb
|
|
231
|
+
- Server-safe: No
|
|
232
|
+
- Compound:
|
|
233
|
+
Breadcrumb (root nav)
|
|
234
|
+
BreadcrumbList (ol)
|
|
235
|
+
BreadcrumbItem (li)
|
|
236
|
+
BreadcrumbLink (for clickable items) | BreadcrumbPage (for current page)
|
|
237
|
+
BreadcrumbSeparator (auto-rendered or custom)
|
|
238
|
+
BreadcrumbEllipsis (for collapsed items)
|
|
239
|
+
- Example:
|
|
240
|
+
<Breadcrumb>
|
|
241
|
+
<BreadcrumbList>
|
|
242
|
+
<BreadcrumbItem><BreadcrumbLink href="/">Home</BreadcrumbLink></BreadcrumbItem>
|
|
243
|
+
<BreadcrumbSeparator />
|
|
244
|
+
<BreadcrumbItem><BreadcrumbPage>Settings</BreadcrumbPage></BreadcrumbItem>
|
|
245
|
+
</BreadcrumbList>
|
|
246
|
+
</Breadcrumb>
|
|
247
|
+
|
|
248
|
+
## Button
|
|
249
|
+
- Import: @devalok/shilp-sutra/ui/button
|
|
250
|
+
- Server-safe: No
|
|
251
|
+
- Props:
|
|
252
|
+
variant: "solid" | "default" (alias->solid) | "outline" | "ghost" | "link" | "destructive" (alias->solid+error)
|
|
253
|
+
color: "default" | "error"
|
|
254
|
+
size: "sm" | "md" | "lg" | "icon" | "icon-sm" | "icon-md" | "icon-lg"
|
|
255
|
+
startIcon: ReactNode
|
|
256
|
+
endIcon: ReactNode
|
|
257
|
+
loading: boolean (disables button, shows spinner)
|
|
258
|
+
loadingPosition: "start" | "end" | "center" (default: "start")
|
|
259
|
+
fullWidth: boolean
|
|
260
|
+
asChild: boolean
|
|
261
|
+
- Defaults: variant="solid", color="default", size="md"
|
|
262
|
+
- Example:
|
|
263
|
+
<Button variant="solid" color="error" startIcon={<IconTrash />} loading={isDeleting}>
|
|
264
|
+
Delete project
|
|
265
|
+
</Button>
|
|
266
|
+
- Gotchas:
|
|
267
|
+
- DO NOT use variant="destructive" — use variant="solid" color="error"
|
|
268
|
+
- DO NOT use variant="secondary" — use variant="outline" or variant="ghost"
|
|
269
|
+
- DO NOT use size="default" — use size="md"
|
|
270
|
+
- DO NOT use color="danger" — use color="error"
|
|
271
|
+
- Inherits variant/color/size from ButtonGroup context if present
|
|
272
|
+
|
|
273
|
+
## ButtonGroup
|
|
274
|
+
- Import: @devalok/shilp-sutra/ui/button-group
|
|
275
|
+
- Server-safe: No
|
|
276
|
+
- Props:
|
|
277
|
+
variant: ButtonProps['variant'] (propagated to children)
|
|
278
|
+
color: ButtonProps['color'] (propagated to children)
|
|
279
|
+
size: ButtonProps['size'] (propagated to children)
|
|
280
|
+
orientation: "horizontal" | "vertical" (default: "horizontal")
|
|
281
|
+
- Example:
|
|
282
|
+
<ButtonGroup variant="outline" size="sm">
|
|
283
|
+
<Button>Bold</Button>
|
|
284
|
+
<Button>Italic</Button>
|
|
285
|
+
</ButtonGroup>
|
|
286
|
+
- Gotchas: Children can override variant/size individually
|
|
287
|
+
|
|
288
|
+
## Card
|
|
289
|
+
- Import: @devalok/shilp-sutra/ui/card
|
|
290
|
+
- Server-safe: No
|
|
291
|
+
- Props (Card root):
|
|
292
|
+
variant: "default" | "elevated" | "outline" | "flat"
|
|
293
|
+
interactive: boolean (enables hover shadow lift + pointer cursor)
|
|
294
|
+
- Defaults: variant="default"
|
|
295
|
+
- Compound:
|
|
296
|
+
Card (root)
|
|
297
|
+
CardHeader
|
|
298
|
+
CardTitle
|
|
299
|
+
CardDescription
|
|
300
|
+
CardContent
|
|
301
|
+
CardFooter
|
|
302
|
+
- Example:
|
|
303
|
+
<Card variant="elevated" interactive onClick={() => navigate(url)}>
|
|
304
|
+
<CardHeader>
|
|
305
|
+
<CardTitle>Project</CardTitle>
|
|
306
|
+
<CardDescription>Last updated 2h ago</CardDescription>
|
|
307
|
+
</CardHeader>
|
|
308
|
+
<CardContent><p>Content here</p></CardContent>
|
|
309
|
+
</Card>
|
|
310
|
+
|
|
311
|
+
## Checkbox
|
|
312
|
+
- Import: @devalok/shilp-sutra/ui/checkbox
|
|
313
|
+
- Server-safe: No
|
|
314
|
+
- Props:
|
|
315
|
+
checked: boolean | "indeterminate"
|
|
316
|
+
onCheckedChange: (checked: boolean | "indeterminate") => void
|
|
317
|
+
error: boolean (shows red border)
|
|
318
|
+
indeterminate: boolean (overrides checked, shows dash icon)
|
|
319
|
+
disabled: boolean
|
|
320
|
+
- Example:
|
|
321
|
+
<Checkbox checked={agreed} onCheckedChange={(v) => setAgreed(v === true)} />
|
|
322
|
+
- Gotchas: indeterminate overrides checked visually
|
|
323
|
+
|
|
324
|
+
## Chip
|
|
325
|
+
- Import: @devalok/shilp-sutra/ui/chip
|
|
326
|
+
- Server-safe: No
|
|
327
|
+
- Props:
|
|
328
|
+
label: string (REQUIRED — use this, NOT children)
|
|
329
|
+
variant: "subtle" | "outline"
|
|
330
|
+
color: "default" | "primary" | "success" | "error" | "warning" | "info" | "teal" | "amber" | "slate" | "indigo" | "cyan" | "orange" | "emerald"
|
|
331
|
+
size: "sm" | "md" | "lg"
|
|
332
|
+
icon: ReactNode
|
|
333
|
+
onClick: MouseEventHandler (renders as <button> when provided)
|
|
334
|
+
onDismiss: () => void (shows X button)
|
|
335
|
+
disabled: boolean
|
|
336
|
+
- Defaults: variant="subtle", size="md", color="default"
|
|
337
|
+
- Example:
|
|
338
|
+
<Chip label="In Progress" color="warning" />
|
|
339
|
+
<Chip label="React" color="info" onDismiss={() => removeFilter('react')} />
|
|
340
|
+
- Gotchas:
|
|
341
|
+
- MUST use label prop — children are NOT rendered
|
|
342
|
+
- <Chip>text</Chip> is WRONG — use <Chip label="text" />
|
|
343
|
+
|
|
344
|
+
## Code
|
|
345
|
+
- Import: @devalok/shilp-sutra/ui/code
|
|
346
|
+
- Server-safe: Yes
|
|
347
|
+
- Props:
|
|
348
|
+
variant: "inline" | "block"
|
|
349
|
+
children: ReactNode
|
|
350
|
+
- Defaults: variant="inline"
|
|
351
|
+
- Example:
|
|
352
|
+
<Code>onClick</Code>
|
|
353
|
+
<Code variant="block">{`const x = 1;\nconsole.log(x);`}</Code>
|
|
354
|
+
- Gotchas: "block" renders as <pre><code>, "inline" renders as <code>
|
|
355
|
+
|
|
356
|
+
## Collapsible
|
|
357
|
+
- Import: @devalok/shilp-sutra/ui/collapsible
|
|
358
|
+
- Server-safe: No
|
|
359
|
+
- Props: Standard Radix Collapsible props (open, onOpenChange, defaultOpen)
|
|
360
|
+
- Compound:
|
|
361
|
+
Collapsible (root)
|
|
362
|
+
CollapsibleTrigger
|
|
363
|
+
CollapsibleContent
|
|
364
|
+
- Example:
|
|
365
|
+
<Collapsible>
|
|
366
|
+
<CollapsibleTrigger>Toggle</CollapsibleTrigger>
|
|
367
|
+
<CollapsibleContent>Hidden content</CollapsibleContent>
|
|
368
|
+
</Collapsible>
|
|
369
|
+
|
|
370
|
+
## Combobox
|
|
371
|
+
- Import: @devalok/shilp-sutra/ui/combobox
|
|
372
|
+
- Server-safe: No
|
|
373
|
+
- Props:
|
|
374
|
+
options: ComboboxOption[] (REQUIRED) — { value: string, label: string, description?: string, icon?: ReactNode, disabled?: boolean }
|
|
375
|
+
value: string | string[]
|
|
376
|
+
onValueChange: (value: string | string[]) => void (REQUIRED)
|
|
377
|
+
placeholder: string (default: "Select...")
|
|
378
|
+
searchPlaceholder: string (default: "Search...")
|
|
379
|
+
emptyMessage: string (default: "No results found")
|
|
380
|
+
multiple: boolean (default: false)
|
|
381
|
+
disabled: boolean
|
|
382
|
+
triggerClassName: string
|
|
383
|
+
maxVisible: number (default: 6, max dropdown items before scroll)
|
|
384
|
+
renderOption: (option, selected) => ReactNode
|
|
385
|
+
- Example:
|
|
386
|
+
<Combobox
|
|
387
|
+
multiple
|
|
388
|
+
options={tagOptions}
|
|
389
|
+
value={selectedTags}
|
|
390
|
+
onValueChange={(v) => setSelectedTags(v as string[])}
|
|
391
|
+
placeholder="Select tags..."
|
|
392
|
+
/>
|
|
393
|
+
- Gotchas:
|
|
394
|
+
- Enforces selection from list (unlike Autocomplete which allows free text)
|
|
395
|
+
- In multi mode, selected items appear as pills with "+N more" overflow
|
|
396
|
+
|
|
397
|
+
## Container
|
|
398
|
+
- Import: @devalok/shilp-sutra/ui/container
|
|
399
|
+
- Server-safe: Yes
|
|
400
|
+
- Props:
|
|
401
|
+
maxWidth: "default" | "body" | "full"
|
|
402
|
+
as: ElementType (default: "div")
|
|
403
|
+
- Defaults: maxWidth="default"
|
|
404
|
+
- Example:
|
|
405
|
+
<Container maxWidth="body">
|
|
406
|
+
<p>Centered content</p>
|
|
407
|
+
</Container>
|
|
408
|
+
|
|
409
|
+
## ContextMenu
|
|
410
|
+
- Import: @devalok/shilp-sutra/ui/context-menu
|
|
411
|
+
- Server-safe: No
|
|
412
|
+
- Compound:
|
|
413
|
+
ContextMenu (root)
|
|
414
|
+
ContextMenuTrigger (right-click target)
|
|
415
|
+
ContextMenuContent
|
|
416
|
+
ContextMenuItem
|
|
417
|
+
ContextMenuCheckboxItem
|
|
418
|
+
ContextMenuRadioGroup > ContextMenuRadioItem
|
|
419
|
+
ContextMenuLabel
|
|
420
|
+
ContextMenuSeparator
|
|
421
|
+
ContextMenuSub > ContextMenuSubTrigger, ContextMenuSubContent
|
|
422
|
+
- Example:
|
|
423
|
+
<ContextMenu>
|
|
424
|
+
<ContextMenuTrigger>Right-click me</ContextMenuTrigger>
|
|
425
|
+
<ContextMenuContent>
|
|
426
|
+
<ContextMenuItem>Edit</ContextMenuItem>
|
|
427
|
+
<ContextMenuItem>Delete</ContextMenuItem>
|
|
428
|
+
</ContextMenuContent>
|
|
429
|
+
</ContextMenu>
|
|
430
|
+
|
|
431
|
+
## Dialog
|
|
432
|
+
- Import: @devalok/shilp-sutra/ui/dialog
|
|
433
|
+
- Server-safe: No
|
|
434
|
+
- Compound:
|
|
435
|
+
Dialog (root)
|
|
436
|
+
DialogTrigger
|
|
437
|
+
DialogContent
|
|
438
|
+
DialogHeader
|
|
439
|
+
DialogTitle
|
|
440
|
+
DialogDescription
|
|
441
|
+
[body content]
|
|
442
|
+
DialogFooter
|
|
443
|
+
DialogClose
|
|
444
|
+
- Example:
|
|
445
|
+
<Dialog>
|
|
446
|
+
<DialogTrigger asChild><Button>Open</Button></DialogTrigger>
|
|
447
|
+
<DialogContent>
|
|
448
|
+
<DialogHeader>
|
|
449
|
+
<DialogTitle>Edit Profile</DialogTitle>
|
|
450
|
+
<DialogDescription>Make changes to your profile.</DialogDescription>
|
|
451
|
+
</DialogHeader>
|
|
452
|
+
<div>Form fields here</div>
|
|
453
|
+
<DialogFooter>
|
|
454
|
+
<Button>Save</Button>
|
|
455
|
+
</DialogFooter>
|
|
456
|
+
</DialogContent>
|
|
457
|
+
</Dialog>
|
|
458
|
+
|
|
459
|
+
## DropdownMenu
|
|
460
|
+
- Import: @devalok/shilp-sutra/ui/dropdown-menu
|
|
461
|
+
- Server-safe: No
|
|
462
|
+
- Compound:
|
|
463
|
+
DropdownMenu (root)
|
|
464
|
+
DropdownMenuTrigger
|
|
465
|
+
DropdownMenuContent
|
|
466
|
+
DropdownMenuLabel
|
|
467
|
+
DropdownMenuSeparator
|
|
468
|
+
DropdownMenuItem (+ DropdownMenuShortcut for keyboard hints)
|
|
469
|
+
DropdownMenuCheckboxItem
|
|
470
|
+
DropdownMenuRadioGroup > DropdownMenuRadioItem
|
|
471
|
+
DropdownMenuGroup
|
|
472
|
+
DropdownMenuSub > DropdownMenuSubTrigger, DropdownMenuSubContent
|
|
473
|
+
- Example:
|
|
474
|
+
<DropdownMenu>
|
|
475
|
+
<DropdownMenuTrigger asChild><Button variant="ghost">Menu</Button></DropdownMenuTrigger>
|
|
476
|
+
<DropdownMenuContent>
|
|
477
|
+
<DropdownMenuItem>Profile</DropdownMenuItem>
|
|
478
|
+
<DropdownMenuSeparator />
|
|
479
|
+
<DropdownMenuItem>Logout</DropdownMenuItem>
|
|
480
|
+
</DropdownMenuContent>
|
|
481
|
+
</DropdownMenu>
|
|
482
|
+
|
|
483
|
+
## FileUpload
|
|
484
|
+
- Import: @devalok/shilp-sutra/ui/file-upload
|
|
485
|
+
- Server-safe: No
|
|
486
|
+
- Props:
|
|
487
|
+
onFiles: (files: File[]) => void (REQUIRED)
|
|
488
|
+
accept: string (MIME or extension, e.g. "image/*", ".pdf,.doc")
|
|
489
|
+
maxSize: number (bytes, default: 10MB)
|
|
490
|
+
multiple: boolean
|
|
491
|
+
uploading: boolean
|
|
492
|
+
progress: number (0-100)
|
|
493
|
+
error: string
|
|
494
|
+
compact: boolean (inline button mode vs drop zone)
|
|
495
|
+
disabled: boolean
|
|
496
|
+
label: string
|
|
497
|
+
sublabel: string
|
|
498
|
+
- Example:
|
|
499
|
+
<FileUpload
|
|
500
|
+
accept="image/*"
|
|
501
|
+
maxSize={2 * 1024 * 1024}
|
|
502
|
+
onFiles={(files) => uploadAvatar(files[0])}
|
|
503
|
+
label="Upload profile photo"
|
|
504
|
+
sublabel="PNG, JPG up to 2MB"
|
|
505
|
+
/>
|
|
506
|
+
- Gotchas:
|
|
507
|
+
- compact=true renders a small inline button; false (default) renders a large drag-and-drop zone
|
|
508
|
+
- Client-side validation: invalid files are rejected before onFiles is called
|
|
509
|
+
|
|
510
|
+
## FormField / FormHelperText / useFormField
|
|
511
|
+
- Import: @devalok/shilp-sutra/ui/form
|
|
512
|
+
- Server-safe: No
|
|
513
|
+
- Props (FormField):
|
|
514
|
+
state: "helper" | "error" | "warning" | "success" (default: "helper")
|
|
515
|
+
helperTextId: string (auto-generated if omitted)
|
|
516
|
+
required: boolean
|
|
517
|
+
- Props (FormHelperText):
|
|
518
|
+
state: "helper" | "error" | "warning" | "success" (inherits from FormField context)
|
|
519
|
+
- Hook: useFormField() => { state, helperTextId, required }
|
|
520
|
+
- Example:
|
|
521
|
+
<FormField state="error">
|
|
522
|
+
<Label htmlFor="email">Email</Label>
|
|
523
|
+
<Input id="email" state="error" />
|
|
524
|
+
<FormHelperText>Please enter a valid email.</FormHelperText>
|
|
525
|
+
</FormField>
|
|
526
|
+
- Gotchas:
|
|
527
|
+
- getFormFieldA11y() was REMOVED — use useFormField() hook instead
|
|
528
|
+
- FormHelperText auto-reads state and id from FormField context
|
|
529
|
+
- FormHelperText renders role="alert" when state="error"
|
|
530
|
+
|
|
531
|
+
## HoverCard
|
|
532
|
+
- Import: @devalok/shilp-sutra/ui/hover-card
|
|
533
|
+
- Server-safe: No
|
|
534
|
+
- Compound:
|
|
535
|
+
HoverCard (root)
|
|
536
|
+
HoverCardTrigger
|
|
537
|
+
HoverCardContent
|
|
538
|
+
- Example:
|
|
539
|
+
<HoverCard>
|
|
540
|
+
<HoverCardTrigger asChild><span>Hover me</span></HoverCardTrigger>
|
|
541
|
+
<HoverCardContent>Preview content</HoverCardContent>
|
|
542
|
+
</HoverCard>
|
|
543
|
+
|
|
544
|
+
## IconButton
|
|
545
|
+
- Import: @devalok/shilp-sutra/ui/icon-button
|
|
546
|
+
- Server-safe: No
|
|
547
|
+
- Props:
|
|
548
|
+
icon: ReactNode (REQUIRED)
|
|
549
|
+
aria-label: string (REQUIRED — WCAG AA mandatory)
|
|
550
|
+
shape: "square" | "circle" (default: "square")
|
|
551
|
+
size: "sm" | "md" | "lg" (default: "md")
|
|
552
|
+
variant: same as Button (solid, outline, ghost, link)
|
|
553
|
+
color: same as Button (default, error)
|
|
554
|
+
loading: boolean
|
|
555
|
+
disabled: boolean
|
|
556
|
+
- Example:
|
|
557
|
+
<IconButton icon={<IconEdit />} variant="ghost" aria-label="Edit item" />
|
|
558
|
+
<IconButton icon={<IconX />} shape="circle" variant="ghost" size="sm" aria-label="Close" />
|
|
559
|
+
- Gotchas:
|
|
560
|
+
- aria-label is enforced by TypeScript — you MUST provide it
|
|
561
|
+
- Prefer IconButton over Button with size="icon-*" for icon-only buttons
|
|
562
|
+
|
|
563
|
+
## Input
|
|
564
|
+
- Import: @devalok/shilp-sutra/ui/input
|
|
565
|
+
- Server-safe: No
|
|
566
|
+
- Props:
|
|
567
|
+
size: "sm" | "md" | "lg"
|
|
568
|
+
state: "default" | "error" | "warning" | "success"
|
|
569
|
+
startIcon: ReactNode
|
|
570
|
+
endIcon: ReactNode
|
|
571
|
+
(plus all standard HTML input attributes except native "size")
|
|
572
|
+
- Defaults: size="md"
|
|
573
|
+
- Example:
|
|
574
|
+
<Input type="email" placeholder="you@example.com" state="error" startIcon={<IconMail />} />
|
|
575
|
+
- Gotchas:
|
|
576
|
+
- HTML native "size" attribute is excluded — use CSS width instead
|
|
577
|
+
- state="error" sets aria-invalid automatically
|
|
578
|
+
|
|
579
|
+
## InputOTP
|
|
580
|
+
- Import: @devalok/shilp-sutra/ui/input-otp
|
|
581
|
+
- Server-safe: No
|
|
582
|
+
- Props (InputOTP): Standard input-otp props (maxLength, pattern, etc.)
|
|
583
|
+
- Compound:
|
|
584
|
+
InputOTP (root)
|
|
585
|
+
InputOTPGroup
|
|
586
|
+
InputOTPSlot (index: number, REQUIRED)
|
|
587
|
+
InputOTPSeparator
|
|
588
|
+
- Example:
|
|
589
|
+
<InputOTP maxLength={6}>
|
|
590
|
+
<InputOTPGroup>
|
|
591
|
+
<InputOTPSlot index={0} />
|
|
592
|
+
<InputOTPSlot index={1} />
|
|
593
|
+
<InputOTPSlot index={2} />
|
|
594
|
+
</InputOTPGroup>
|
|
595
|
+
<InputOTPSeparator />
|
|
596
|
+
<InputOTPGroup>
|
|
597
|
+
<InputOTPSlot index={3} />
|
|
598
|
+
<InputOTPSlot index={4} />
|
|
599
|
+
<InputOTPSlot index={5} />
|
|
600
|
+
</InputOTPGroup>
|
|
601
|
+
</InputOTP>
|
|
602
|
+
|
|
603
|
+
## Label
|
|
604
|
+
- Import: @devalok/shilp-sutra/ui/label
|
|
605
|
+
- Server-safe: No
|
|
606
|
+
- Props:
|
|
607
|
+
required: boolean (shows red asterisk)
|
|
608
|
+
htmlFor: string
|
|
609
|
+
(plus standard Radix Label props)
|
|
610
|
+
- Example:
|
|
611
|
+
<Label htmlFor="email" required>Email Address</Label>
|
|
612
|
+
|
|
613
|
+
## Link
|
|
614
|
+
- Import: @devalok/shilp-sutra/ui/link
|
|
615
|
+
- Server-safe: No
|
|
616
|
+
- Props:
|
|
617
|
+
inline: boolean (default: true — "inline" or "block" display)
|
|
618
|
+
asChild: boolean (merge with child element, e.g. Next.js Link)
|
|
619
|
+
(plus standard anchor attributes)
|
|
620
|
+
- Example:
|
|
621
|
+
<Link href="/docs">Documentation</Link>
|
|
622
|
+
<Link asChild><NextLink href="/about">About</NextLink></Link>
|
|
623
|
+
|
|
624
|
+
## Menubar
|
|
625
|
+
- Import: @devalok/shilp-sutra/ui/menubar
|
|
626
|
+
- Server-safe: No
|
|
627
|
+
- Compound:
|
|
628
|
+
Menubar (root)
|
|
629
|
+
MenubarMenu
|
|
630
|
+
MenubarTrigger
|
|
631
|
+
MenubarContent
|
|
632
|
+
MenubarItem (+ MenubarShortcut)
|
|
633
|
+
MenubarCheckboxItem
|
|
634
|
+
MenubarRadioGroup > MenubarRadioItem
|
|
635
|
+
MenubarLabel
|
|
636
|
+
MenubarSeparator
|
|
637
|
+
MenubarSub > MenubarSubTrigger, MenubarSubContent
|
|
638
|
+
- Example:
|
|
639
|
+
<Menubar>
|
|
640
|
+
<MenubarMenu>
|
|
641
|
+
<MenubarTrigger>File</MenubarTrigger>
|
|
642
|
+
<MenubarContent>
|
|
643
|
+
<MenubarItem>New<MenubarShortcut>Ctrl+N</MenubarShortcut></MenubarItem>
|
|
644
|
+
<MenubarSeparator />
|
|
645
|
+
<MenubarItem>Exit</MenubarItem>
|
|
646
|
+
</MenubarContent>
|
|
647
|
+
</MenubarMenu>
|
|
648
|
+
</Menubar>
|
|
649
|
+
|
|
650
|
+
## NavigationMenu
|
|
651
|
+
- Import: @devalok/shilp-sutra/ui/navigation-menu
|
|
652
|
+
- Server-safe: No
|
|
653
|
+
- Compound:
|
|
654
|
+
NavigationMenu (root)
|
|
655
|
+
NavigationMenuList
|
|
656
|
+
NavigationMenuItem
|
|
657
|
+
NavigationMenuTrigger (for items with content panels)
|
|
658
|
+
NavigationMenuContent (dropdown panel)
|
|
659
|
+
NavigationMenuLink (for direct links)
|
|
660
|
+
NavigationMenuIndicator
|
|
661
|
+
NavigationMenuViewport
|
|
662
|
+
- Example:
|
|
663
|
+
<NavigationMenu>
|
|
664
|
+
<NavigationMenuList>
|
|
665
|
+
<NavigationMenuItem>
|
|
666
|
+
<NavigationMenuTrigger>Products</NavigationMenuTrigger>
|
|
667
|
+
<NavigationMenuContent>
|
|
668
|
+
<ul>...</ul>
|
|
669
|
+
</NavigationMenuContent>
|
|
670
|
+
</NavigationMenuItem>
|
|
671
|
+
<NavigationMenuItem>
|
|
672
|
+
<NavigationMenuLink href="/about">About</NavigationMenuLink>
|
|
673
|
+
</NavigationMenuItem>
|
|
674
|
+
</NavigationMenuList>
|
|
675
|
+
</NavigationMenu>
|
|
676
|
+
|
|
677
|
+
## NumberInput
|
|
678
|
+
- Import: @devalok/shilp-sutra/ui/number-input
|
|
679
|
+
- Server-safe: No
|
|
680
|
+
- Props:
|
|
681
|
+
value: number (default: 0)
|
|
682
|
+
onValueChange: (value: number) => void
|
|
683
|
+
min: number
|
|
684
|
+
max: number
|
|
685
|
+
step: number (default: 1)
|
|
686
|
+
disabled: boolean
|
|
687
|
+
- Example:
|
|
688
|
+
<NumberInput value={qty} onValueChange={setQty} min={1} max={99} />
|
|
689
|
+
- Gotchas: Controlled only — buttons won't work without onValueChange
|
|
690
|
+
|
|
691
|
+
## Pagination
|
|
692
|
+
- Import: @devalok/shilp-sutra/ui/pagination
|
|
693
|
+
- Server-safe: No
|
|
694
|
+
- Compound:
|
|
695
|
+
PaginationRoot (nav)
|
|
696
|
+
PaginationContent (ul)
|
|
697
|
+
PaginationItem (li)
|
|
698
|
+
PaginationLink (isActive: boolean, asChild: boolean)
|
|
699
|
+
PaginationPrevious
|
|
700
|
+
PaginationNext
|
|
701
|
+
PaginationEllipsis
|
|
702
|
+
PaginationNav (convenience wrapper)
|
|
703
|
+
- Utility: generatePagination(current: number, total: number, siblingCount: number) => (number | 'ellipsis')[]
|
|
704
|
+
- Example:
|
|
705
|
+
<PaginationRoot>
|
|
706
|
+
<PaginationContent>
|
|
707
|
+
<PaginationItem><PaginationPrevious onClick={() => setPage(p - 1)} /></PaginationItem>
|
|
708
|
+
{generatePagination(page, totalPages, 1).map((item, i) =>
|
|
709
|
+
item === 'ellipsis'
|
|
710
|
+
? <PaginationItem key={i}><PaginationEllipsis /></PaginationItem>
|
|
711
|
+
: <PaginationItem key={i}><PaginationLink isActive={item === page} onClick={() => setPage(item)}>{item}</PaginationLink></PaginationItem>
|
|
712
|
+
)}
|
|
713
|
+
<PaginationItem><PaginationNext onClick={() => setPage(p + 1)} /></PaginationItem>
|
|
714
|
+
</PaginationContent>
|
|
715
|
+
</PaginationRoot>
|
|
716
|
+
- Gotchas: Root component is PaginationRoot (NOT Pagination)
|
|
717
|
+
|
|
718
|
+
## Popover
|
|
719
|
+
- Import: @devalok/shilp-sutra/ui/popover
|
|
720
|
+
- Server-safe: No
|
|
721
|
+
- Compound:
|
|
722
|
+
Popover (root)
|
|
723
|
+
PopoverTrigger
|
|
724
|
+
PopoverContent
|
|
725
|
+
PopoverAnchor (optional anchor point)
|
|
726
|
+
- Example:
|
|
727
|
+
<Popover>
|
|
728
|
+
<PopoverTrigger asChild><Button>Open</Button></PopoverTrigger>
|
|
729
|
+
<PopoverContent>Content here</PopoverContent>
|
|
730
|
+
</Popover>
|
|
731
|
+
|
|
732
|
+
## Progress
|
|
733
|
+
- Import: @devalok/shilp-sutra/ui/progress
|
|
734
|
+
- Server-safe: No
|
|
735
|
+
- Props:
|
|
736
|
+
value: number (0-100) — omit for indeterminate
|
|
737
|
+
size: "sm" | "md" | "lg" (track height)
|
|
738
|
+
color: "default" | "success" | "warning" | "error" (indicator color)
|
|
739
|
+
showLabel: boolean (shows percentage text)
|
|
740
|
+
indicatorClassName: string
|
|
741
|
+
- Defaults: size="md", color="default"
|
|
742
|
+
- Example:
|
|
743
|
+
<Progress value={75} color="success" showLabel />
|
|
744
|
+
<Progress size="sm" /> {/* indeterminate */}
|
|
745
|
+
- Gotchas: Omit value (or pass undefined) for indeterminate animation
|
|
746
|
+
|
|
747
|
+
## RadioGroup
|
|
748
|
+
- Import: @devalok/shilp-sutra/ui/radio
|
|
749
|
+
- Server-safe: No
|
|
750
|
+
- Compound:
|
|
751
|
+
RadioGroup (root, value, onValueChange, defaultValue)
|
|
752
|
+
RadioGroupItem (value: string, REQUIRED)
|
|
753
|
+
- Example:
|
|
754
|
+
<RadioGroup defaultValue="option-1" onValueChange={setValue}>
|
|
755
|
+
<div className="flex items-center gap-2">
|
|
756
|
+
<RadioGroupItem value="option-1" id="r1" />
|
|
757
|
+
<Label htmlFor="r1">Option 1</Label>
|
|
758
|
+
</div>
|
|
759
|
+
<div className="flex items-center gap-2">
|
|
760
|
+
<RadioGroupItem value="option-2" id="r2" />
|
|
761
|
+
<Label htmlFor="r2">Option 2</Label>
|
|
762
|
+
</div>
|
|
763
|
+
</RadioGroup>
|
|
764
|
+
|
|
765
|
+
## ScrollArea
|
|
766
|
+
- Import: @devalok/shilp-sutra/ui/scroll-area
|
|
767
|
+
- Server-safe: No
|
|
768
|
+
- Props: Standard Radix ScrollArea props
|
|
769
|
+
- Note: Provides custom-styled scrollbars
|
|
770
|
+
|
|
771
|
+
## SearchInput
|
|
772
|
+
- Import: @devalok/shilp-sutra/ui/search-input
|
|
773
|
+
- Server-safe: No
|
|
774
|
+
- Props:
|
|
775
|
+
size: "sm" | "md" | "lg" (default: "md")
|
|
776
|
+
loading: boolean (shows spinner instead of clear button)
|
|
777
|
+
onClear: () => void (shows X button when value is non-empty)
|
|
778
|
+
value: string
|
|
779
|
+
onChange: ChangeEventHandler
|
|
780
|
+
(plus standard input attributes except native "size")
|
|
781
|
+
- Example:
|
|
782
|
+
<SearchInput
|
|
783
|
+
value={query}
|
|
784
|
+
onChange={(e) => setQuery(e.target.value)}
|
|
785
|
+
onClear={() => setQuery('')}
|
|
786
|
+
placeholder="Search tasks..."
|
|
787
|
+
loading={isSearching}
|
|
788
|
+
/>
|
|
789
|
+
|
|
790
|
+
## SegmentedControl
|
|
791
|
+
- Import: @devalok/shilp-sutra/ui/segmented-control
|
|
792
|
+
- Server-safe: No
|
|
793
|
+
- Props (SegmentedControl root):
|
|
794
|
+
size: "sm" | "md" | "lg" (REQUIRED) — also accepts legacy "small" | "medium" | "big"
|
|
795
|
+
variant: "filled" | "tonal" (REQUIRED)
|
|
796
|
+
options: SegmentedControlOption[] (REQUIRED) — { id: string, text: string, icon?: ComponentType }
|
|
797
|
+
selectedId: string (REQUIRED)
|
|
798
|
+
onSelect: (id: string) => void (REQUIRED)
|
|
799
|
+
disabled: boolean
|
|
800
|
+
- Defaults: None — size and variant are required
|
|
801
|
+
- Example:
|
|
802
|
+
<SegmentedControl
|
|
803
|
+
size="md"
|
|
804
|
+
variant="tonal"
|
|
805
|
+
options={[
|
|
806
|
+
{ id: 'list', text: 'List' },
|
|
807
|
+
{ id: 'grid', text: 'Grid' },
|
|
808
|
+
]}
|
|
809
|
+
selectedId={viewMode}
|
|
810
|
+
onSelect={setViewMode}
|
|
811
|
+
/>
|
|
812
|
+
- Gotchas:
|
|
813
|
+
- Controlled only — selectedId + onSelect are required
|
|
814
|
+
- Uses data-driven API (options prop), not compound children
|
|
815
|
+
|
|
816
|
+
## Select
|
|
817
|
+
- Import: @devalok/shilp-sutra/ui/select
|
|
818
|
+
- Server-safe: No
|
|
819
|
+
- Props (SelectTrigger):
|
|
820
|
+
size: "sm" | "md" | "lg"
|
|
821
|
+
- Defaults: size="md" (on SelectTrigger)
|
|
822
|
+
- Compound:
|
|
823
|
+
Select (root — value, onValueChange, defaultValue)
|
|
824
|
+
SelectTrigger (size goes HERE, not on Select root)
|
|
825
|
+
SelectValue (placeholder)
|
|
826
|
+
SelectContent
|
|
827
|
+
SelectGroup (optional grouping)
|
|
828
|
+
SelectLabel (group header)
|
|
829
|
+
SelectItem (value: string, REQUIRED)
|
|
830
|
+
SelectSeparator
|
|
831
|
+
- Example:
|
|
832
|
+
<Select onValueChange={setValue}>
|
|
833
|
+
<SelectTrigger size="lg">
|
|
834
|
+
<SelectValue placeholder="Choose..." />
|
|
835
|
+
</SelectTrigger>
|
|
836
|
+
<SelectContent>
|
|
837
|
+
<SelectItem value="a">Option A</SelectItem>
|
|
838
|
+
<SelectItem value="b">Option B</SelectItem>
|
|
839
|
+
</SelectContent>
|
|
840
|
+
</Select>
|
|
841
|
+
- Gotchas:
|
|
842
|
+
- Size goes on SelectTrigger, NOT on Select root
|
|
843
|
+
- <Select size="lg"> is silently ignored (no TypeScript error)
|
|
844
|
+
|
|
845
|
+
## Separator
|
|
846
|
+
- Import: @devalok/shilp-sutra/ui/separator
|
|
847
|
+
- Server-safe: No
|
|
848
|
+
- Props:
|
|
849
|
+
orientation: "horizontal" | "vertical" (default: "horizontal")
|
|
850
|
+
decorative: boolean (default: true)
|
|
851
|
+
- Example:
|
|
852
|
+
<Separator />
|
|
853
|
+
<Separator orientation="vertical" className="h-6" />
|
|
854
|
+
|
|
855
|
+
## Sheet
|
|
856
|
+
- Import: @devalok/shilp-sutra/ui/sheet
|
|
857
|
+
- Server-safe: No
|
|
858
|
+
- Props (SheetContent):
|
|
859
|
+
side: "top" | "bottom" | "left" | "right"
|
|
860
|
+
- Compound:
|
|
861
|
+
Sheet (root)
|
|
862
|
+
SheetTrigger
|
|
863
|
+
SheetContent (side prop)
|
|
864
|
+
SheetHeader
|
|
865
|
+
SheetTitle
|
|
866
|
+
SheetDescription
|
|
867
|
+
[body content]
|
|
868
|
+
SheetFooter
|
|
869
|
+
SheetClose
|
|
870
|
+
- Example:
|
|
871
|
+
<Sheet>
|
|
872
|
+
<SheetTrigger asChild><Button>Open</Button></SheetTrigger>
|
|
873
|
+
<SheetContent side="right">
|
|
874
|
+
<SheetHeader>
|
|
875
|
+
<SheetTitle>Settings</SheetTitle>
|
|
876
|
+
</SheetHeader>
|
|
877
|
+
<div>Content</div>
|
|
878
|
+
</SheetContent>
|
|
879
|
+
</Sheet>
|
|
880
|
+
|
|
881
|
+
## Sidebar
|
|
882
|
+
- Import: @devalok/shilp-sutra/ui/sidebar
|
|
883
|
+
- Server-safe: No
|
|
884
|
+
- Compound (full tree):
|
|
885
|
+
SidebarProvider (context provider — must wrap everything)
|
|
886
|
+
Sidebar (root panel)
|
|
887
|
+
SidebarHeader
|
|
888
|
+
SidebarContent
|
|
889
|
+
SidebarGroup
|
|
890
|
+
SidebarGroupLabel
|
|
891
|
+
SidebarGroupAction
|
|
892
|
+
SidebarGroupContent
|
|
893
|
+
SidebarMenu
|
|
894
|
+
SidebarMenuItem
|
|
895
|
+
SidebarMenuButton (tooltip, isActive)
|
|
896
|
+
SidebarMenuAction
|
|
897
|
+
SidebarMenuBadge
|
|
898
|
+
SidebarMenuSub
|
|
899
|
+
SidebarMenuSubItem
|
|
900
|
+
SidebarMenuSubButton (isActive)
|
|
901
|
+
SidebarFooter
|
|
902
|
+
SidebarSeparator
|
|
903
|
+
SidebarRail
|
|
904
|
+
SidebarInset (main content area)
|
|
905
|
+
SidebarTrigger (hamburger button)
|
|
906
|
+
SidebarInput (search input in sidebar)
|
|
907
|
+
SidebarMenuSkeleton (loading placeholder)
|
|
908
|
+
- Hook: useSidebar() => { state, open, setOpen, openMobile, setOpenMobile, isMobile, toggleSidebar }
|
|
909
|
+
- Example:
|
|
910
|
+
<SidebarProvider>
|
|
911
|
+
<Sidebar>
|
|
912
|
+
<SidebarHeader>Logo</SidebarHeader>
|
|
913
|
+
<SidebarContent>
|
|
914
|
+
<SidebarGroup>
|
|
915
|
+
<SidebarGroupLabel>Navigation</SidebarGroupLabel>
|
|
916
|
+
<SidebarGroupContent>
|
|
917
|
+
<SidebarMenu>
|
|
918
|
+
<SidebarMenuItem>
|
|
919
|
+
<SidebarMenuButton isActive>
|
|
920
|
+
<IconHome /> Dashboard
|
|
921
|
+
</SidebarMenuButton>
|
|
922
|
+
</SidebarMenuItem>
|
|
923
|
+
</SidebarMenu>
|
|
924
|
+
</SidebarGroupContent>
|
|
925
|
+
</SidebarGroup>
|
|
926
|
+
</SidebarContent>
|
|
927
|
+
</Sidebar>
|
|
928
|
+
<SidebarInset>
|
|
929
|
+
<SidebarTrigger />
|
|
930
|
+
<main>Page content</main>
|
|
931
|
+
</SidebarInset>
|
|
932
|
+
</SidebarProvider>
|
|
933
|
+
- Gotchas:
|
|
934
|
+
- SidebarProvider MUST wrap both Sidebar and SidebarInset
|
|
935
|
+
- Use SidebarMenuButton for nav items (supports tooltip in collapsed state)
|
|
936
|
+
|
|
937
|
+
## Skeleton
|
|
938
|
+
- Import: @devalok/shilp-sutra/ui/skeleton
|
|
939
|
+
- Server-safe: Yes
|
|
940
|
+
- Props:
|
|
941
|
+
variant: "rectangle" | "circle" | "text"
|
|
942
|
+
animation: "pulse" | "shimmer" | "none"
|
|
943
|
+
- Defaults: variant="rectangle", animation="pulse"
|
|
944
|
+
- Example:
|
|
945
|
+
<Skeleton variant="text" className="w-3/4" />
|
|
946
|
+
<Skeleton variant="circle" className="h-12 w-12" />
|
|
947
|
+
<Skeleton variant="rectangle" animation="shimmer" className="h-48 w-full" />
|
|
948
|
+
- Gotchas: shimmer respects prefers-reduced-motion
|
|
949
|
+
|
|
950
|
+
## Slider
|
|
951
|
+
- Import: @devalok/shilp-sutra/ui/slider
|
|
952
|
+
- Server-safe: No
|
|
953
|
+
- Props: Standard Radix Slider props (value, onValueChange, defaultValue, min, max, step, aria-label)
|
|
954
|
+
- Example:
|
|
955
|
+
<Slider defaultValue={[50]} max={100} step={1} aria-label="Volume" />
|
|
956
|
+
- Gotchas: value is number[] (array), not a single number
|
|
957
|
+
|
|
958
|
+
## Spinner
|
|
959
|
+
- Import: @devalok/shilp-sutra/ui/spinner
|
|
960
|
+
- Server-safe: Yes
|
|
961
|
+
- Props:
|
|
962
|
+
size: "sm" | "md" | "lg"
|
|
963
|
+
- Defaults: size="md"
|
|
964
|
+
- Example:
|
|
965
|
+
<Spinner size="lg" />
|
|
966
|
+
- Gotchas:
|
|
967
|
+
- Renders role="status" with sr-only "Loading..." text — no need for aria-label
|
|
968
|
+
- Button has built-in loading prop — prefer that over manual Spinner composition
|
|
969
|
+
|
|
970
|
+
## Stack
|
|
971
|
+
- Import: @devalok/shilp-sutra/ui/stack
|
|
972
|
+
- Server-safe: Yes
|
|
973
|
+
- Props:
|
|
974
|
+
direction: "vertical" | "horizontal" | "row" | "column" (default: "vertical")
|
|
975
|
+
gap: SpacingToken | number — tokens: "ds-01".."ds-13", or numbers 0-13
|
|
976
|
+
align: "start" | "center" | "end" | "stretch" | "baseline"
|
|
977
|
+
justify: "start" | "center" | "end" | "between" | "around" | "evenly"
|
|
978
|
+
wrap: boolean
|
|
979
|
+
as: ElementType (default: "div")
|
|
980
|
+
- Example:
|
|
981
|
+
<Stack direction="horizontal" gap="ds-04" align="center">
|
|
982
|
+
<Avatar size="sm" />
|
|
983
|
+
<Text variant="body-md">User Name</Text>
|
|
984
|
+
</Stack>
|
|
985
|
+
|
|
986
|
+
## StatCard
|
|
987
|
+
- Import: @devalok/shilp-sutra/ui/stat-card
|
|
988
|
+
- Server-safe: No
|
|
989
|
+
- Props:
|
|
990
|
+
label: string (heading text) — alias: title
|
|
991
|
+
title: string (alias for label)
|
|
992
|
+
value: string | number (REQUIRED)
|
|
993
|
+
delta: { value: string, direction: "up" | "down" | "neutral" }
|
|
994
|
+
icon: ReactNode | ComponentType<{ className?: string }>
|
|
995
|
+
loading: boolean (renders skeleton)
|
|
996
|
+
- Example:
|
|
997
|
+
<StatCard
|
|
998
|
+
label="Revenue"
|
|
999
|
+
value="$48,200"
|
|
1000
|
+
delta={{ value: "+12%", direction: "up" }}
|
|
1001
|
+
icon={<IconCurrencyDollar />}
|
|
1002
|
+
/>
|
|
1003
|
+
- Gotchas: delta.direction "up" = green, "down" = red, "neutral" = grey
|
|
1004
|
+
|
|
1005
|
+
## Stepper / Step
|
|
1006
|
+
- Import: @devalok/shilp-sutra/ui/stepper
|
|
1007
|
+
- Server-safe: No
|
|
1008
|
+
- Props (Stepper):
|
|
1009
|
+
activeStep: number (REQUIRED, 0-indexed)
|
|
1010
|
+
orientation: "horizontal" | "vertical" (default: "horizontal")
|
|
1011
|
+
children: <Step> elements
|
|
1012
|
+
- Props (Step):
|
|
1013
|
+
label: string (REQUIRED)
|
|
1014
|
+
description: string
|
|
1015
|
+
icon: ReactNode (overrides default number/checkmark)
|
|
1016
|
+
- Example:
|
|
1017
|
+
<Stepper activeStep={1}>
|
|
1018
|
+
<Step label="Account" description="Create credentials" />
|
|
1019
|
+
<Step label="Profile" description="Add details" />
|
|
1020
|
+
<Step label="Review" />
|
|
1021
|
+
</Stepper>
|
|
1022
|
+
- Gotchas: Steps before activeStep are "completed", at activeStep is "active", after is "pending"
|
|
1023
|
+
|
|
1024
|
+
## Switch
|
|
1025
|
+
- Import: @devalok/shilp-sutra/ui/switch
|
|
1026
|
+
- Server-safe: No
|
|
1027
|
+
- Props:
|
|
1028
|
+
checked: boolean
|
|
1029
|
+
onCheckedChange: (checked: boolean) => void
|
|
1030
|
+
error: boolean (shows red border/bg)
|
|
1031
|
+
disabled: boolean
|
|
1032
|
+
- Example:
|
|
1033
|
+
<Switch checked={enabled} onCheckedChange={setEnabled} />
|
|
1034
|
+
|
|
1035
|
+
## Table
|
|
1036
|
+
- Import: @devalok/shilp-sutra/ui/table
|
|
1037
|
+
- Server-safe: Yes
|
|
1038
|
+
- Compound:
|
|
1039
|
+
Table (root <table>)
|
|
1040
|
+
TableHeader (<thead>)
|
|
1041
|
+
TableRow (<tr>)
|
|
1042
|
+
TableHead (<th>)
|
|
1043
|
+
TableBody (<tbody>)
|
|
1044
|
+
TableRow (<tr>)
|
|
1045
|
+
TableCell (<td>)
|
|
1046
|
+
TableFooter (<tfoot>)
|
|
1047
|
+
TableCaption (<caption>)
|
|
1048
|
+
- Example:
|
|
1049
|
+
<Table>
|
|
1050
|
+
<TableHeader>
|
|
1051
|
+
<TableRow>
|
|
1052
|
+
<TableHead>Name</TableHead>
|
|
1053
|
+
<TableHead>Status</TableHead>
|
|
1054
|
+
</TableRow>
|
|
1055
|
+
</TableHeader>
|
|
1056
|
+
<TableBody>
|
|
1057
|
+
<TableRow>
|
|
1058
|
+
<TableCell>Project Alpha</TableCell>
|
|
1059
|
+
<TableCell><Badge color="success">Active</Badge></TableCell>
|
|
1060
|
+
</TableRow>
|
|
1061
|
+
</TableBody>
|
|
1062
|
+
</Table>
|
|
1063
|
+
|
|
1064
|
+
## Tabs
|
|
1065
|
+
- Import: @devalok/shilp-sutra/ui/tabs
|
|
1066
|
+
- Server-safe: No
|
|
1067
|
+
- Props (Tabs root): defaultValue, value, onValueChange
|
|
1068
|
+
- Props (TabsList): variant: "line" | "contained" (default: "line")
|
|
1069
|
+
- Props (TabsTrigger): value: string (REQUIRED), variant (inherits from TabsList)
|
|
1070
|
+
- Props (TabsContent): value: string (REQUIRED)
|
|
1071
|
+
- Compound:
|
|
1072
|
+
Tabs (root)
|
|
1073
|
+
TabsList (variant)
|
|
1074
|
+
TabsTrigger (value)
|
|
1075
|
+
TabsContent (value)
|
|
1076
|
+
- Defaults: TabsList variant="line"
|
|
1077
|
+
- Example:
|
|
1078
|
+
<Tabs defaultValue="overview">
|
|
1079
|
+
<TabsList variant="contained">
|
|
1080
|
+
<TabsTrigger value="overview">Overview</TabsTrigger>
|
|
1081
|
+
<TabsTrigger value="activity">Activity</TabsTrigger>
|
|
1082
|
+
</TabsList>
|
|
1083
|
+
<TabsContent value="overview">Overview content</TabsContent>
|
|
1084
|
+
<TabsContent value="activity">Activity content</TabsContent>
|
|
1085
|
+
</Tabs>
|
|
1086
|
+
- Gotchas:
|
|
1087
|
+
- variant goes on TabsList, NOT on individual TabsTrigger (propagates via context)
|
|
1088
|
+
- DO NOT put variant on TabsTrigger — it inherits from TabsList
|
|
1089
|
+
|
|
1090
|
+
## Text
|
|
1091
|
+
- Import: @devalok/shilp-sutra/ui/text
|
|
1092
|
+
- Server-safe: Yes
|
|
1093
|
+
- Props:
|
|
1094
|
+
variant: "heading-2xl" | "heading-xl" | "heading-lg" | "heading-md" | "heading-sm" | "heading-xs" | "body-lg" | "body-md" | "body-sm" | "body-xs" | "label-lg" | "label-md" | "label-sm" | "label-xs" | "caption" | "overline"
|
|
1095
|
+
as: ElementType (override the auto-selected HTML element)
|
|
1096
|
+
- Defaults: variant="body-md"
|
|
1097
|
+
- Default element mapping:
|
|
1098
|
+
heading-2xl -> h1, heading-xl -> h2, heading-lg -> h3, heading-md -> h4, heading-sm -> h5, heading-xs -> h6
|
|
1099
|
+
body-* -> p, label-* -> span, caption -> span, overline -> span
|
|
1100
|
+
- Example:
|
|
1101
|
+
<Text variant="heading-2xl">Page Title</Text>
|
|
1102
|
+
<Text variant="body-sm" as="span">Inline text</Text>
|
|
1103
|
+
<Text variant="label-sm" className="text-text-secondary">SECTION LABEL</Text>
|
|
1104
|
+
- Gotchas:
|
|
1105
|
+
- label-* and overline variants are automatically uppercase
|
|
1106
|
+
- Use "as" prop to override the HTML element when needed
|
|
1107
|
+
|
|
1108
|
+
## Textarea
|
|
1109
|
+
- Import: @devalok/shilp-sutra/ui/textarea
|
|
1110
|
+
- Server-safe: No
|
|
1111
|
+
- Props:
|
|
1112
|
+
size: "sm" | "md" | "lg"
|
|
1113
|
+
state: "default" | "error" | "warning" | "success"
|
|
1114
|
+
(plus standard textarea attributes except native "size")
|
|
1115
|
+
- Defaults: size="md"
|
|
1116
|
+
- Example:
|
|
1117
|
+
<Textarea size="lg" state="error" placeholder="Describe the issue..." />
|
|
1118
|
+
- Gotchas: state="error" sets aria-invalid automatically; all sizes are vertically resizable
|
|
1119
|
+
|
|
1120
|
+
## Toast / Toaster
|
|
1121
|
+
- Import: @devalok/shilp-sutra/ui/toast, @devalok/shilp-sutra/ui/toaster
|
|
1122
|
+
- Server-safe: No
|
|
1123
|
+
- Props (Toast):
|
|
1124
|
+
color: "neutral" | "success" | "warning" | "error" | "info"
|
|
1125
|
+
- Defaults: color="neutral"
|
|
1126
|
+
- Compound (declarative — for tests/Storybook only):
|
|
1127
|
+
ToastProvider
|
|
1128
|
+
Toast (color)
|
|
1129
|
+
ToastTitle
|
|
1130
|
+
ToastDescription
|
|
1131
|
+
ToastAction (altText required)
|
|
1132
|
+
ToastClose
|
|
1133
|
+
ToastViewport
|
|
1134
|
+
- Imperative usage (recommended):
|
|
1135
|
+
// 1. Mount <Toaster /> once at root layout
|
|
1136
|
+
<Toaster />
|
|
1137
|
+
// 2. Call from anywhere:
|
|
1138
|
+
const { toast } = useToast()
|
|
1139
|
+
toast({ title: 'Saved!', description: 'Changes saved.', color: 'success' })
|
|
1140
|
+
// Or use the direct function (no hook):
|
|
1141
|
+
import { toast } from '@devalok/shilp-sutra/hooks/use-toast'
|
|
1142
|
+
toast({ title: 'Deleted', color: 'error' })
|
|
1143
|
+
- Gotchas:
|
|
1144
|
+
- DO NOT use color="destructive" — use color="error"
|
|
1145
|
+
- DO NOT use variant="destructive" — Toast has NO variant prop, only color
|
|
1146
|
+
- Max 2 toasts visible simultaneously (TOAST_LIMIT=2)
|
|
1147
|
+
- Auto-dismisses after 5 seconds (TOAST_REMOVE_DELAY=5000)
|
|
1148
|
+
|
|
1149
|
+
## Toggle
|
|
1150
|
+
- Import: @devalok/shilp-sutra/ui/toggle
|
|
1151
|
+
- Server-safe: No
|
|
1152
|
+
- Props:
|
|
1153
|
+
variant: "default" | "outline"
|
|
1154
|
+
size: "sm" | "md" | "lg"
|
|
1155
|
+
pressed: boolean
|
|
1156
|
+
onPressedChange: (pressed: boolean) => void
|
|
1157
|
+
defaultPressed: boolean
|
|
1158
|
+
- Defaults: variant="default", size="md"
|
|
1159
|
+
- Example:
|
|
1160
|
+
<Toggle aria-label="Toggle bold" pressed={isBold} onPressedChange={setIsBold}>
|
|
1161
|
+
<IconBold />
|
|
1162
|
+
</Toggle>
|
|
1163
|
+
|
|
1164
|
+
## ToggleGroup
|
|
1165
|
+
- Import: @devalok/shilp-sutra/ui/toggle-group
|
|
1166
|
+
- Server-safe: No
|
|
1167
|
+
- Props (ToggleGroup):
|
|
1168
|
+
type: "single" | "multiple"
|
|
1169
|
+
variant: "default" | "outline" (propagated to items)
|
|
1170
|
+
size: "sm" | "md" | "lg" (propagated to items)
|
|
1171
|
+
value: string | string[]
|
|
1172
|
+
onValueChange: (value) => void
|
|
1173
|
+
- Compound:
|
|
1174
|
+
ToggleGroup (root)
|
|
1175
|
+
ToggleGroupItem (value: string)
|
|
1176
|
+
- Example:
|
|
1177
|
+
<ToggleGroup type="single" variant="outline" size="sm" value={alignment} onValueChange={setAlignment}>
|
|
1178
|
+
<ToggleGroupItem value="left"><IconAlignLeft /></ToggleGroupItem>
|
|
1179
|
+
<ToggleGroupItem value="center"><IconAlignCenter /></ToggleGroupItem>
|
|
1180
|
+
<ToggleGroupItem value="right"><IconAlignRight /></ToggleGroupItem>
|
|
1181
|
+
</ToggleGroup>
|
|
1182
|
+
|
|
1183
|
+
## Tooltip
|
|
1184
|
+
- Import: @devalok/shilp-sutra/ui/tooltip
|
|
1185
|
+
- Server-safe: No
|
|
1186
|
+
- Compound:
|
|
1187
|
+
TooltipProvider (REQUIRED at layout root or wrapping tooltip usage, controls delay)
|
|
1188
|
+
Tooltip (root)
|
|
1189
|
+
TooltipTrigger
|
|
1190
|
+
TooltipContent
|
|
1191
|
+
- Example:
|
|
1192
|
+
<TooltipProvider>
|
|
1193
|
+
<Tooltip>
|
|
1194
|
+
<TooltipTrigger asChild><Button>Hover me</Button></TooltipTrigger>
|
|
1195
|
+
<TooltipContent>Tooltip text</TooltipContent>
|
|
1196
|
+
</Tooltip>
|
|
1197
|
+
</TooltipProvider>
|
|
1198
|
+
- Gotchas: TooltipProvider is REQUIRED — without it, tooltips won't show
|
|
1199
|
+
|
|
1200
|
+
## Transitions (Fade, Collapse, Grow, Slide)
|
|
1201
|
+
- Import: @devalok/shilp-sutra/ui/transitions
|
|
1202
|
+
- Server-safe: No
|
|
1203
|
+
- Props (shared TransitionProps):
|
|
1204
|
+
open: boolean (REQUIRED)
|
|
1205
|
+
duration: string (CSS duration, default: "var(--duration-moderate-02)")
|
|
1206
|
+
unmountOnClose: boolean (Fade, Grow, Slide only)
|
|
1207
|
+
children: ReactNode
|
|
1208
|
+
- Props (Slide only):
|
|
1209
|
+
direction: "up" | "down" | "left" | "right" (default: "up")
|
|
1210
|
+
- Example:
|
|
1211
|
+
<Fade open={isVisible}><div>Fading content</div></Fade>
|
|
1212
|
+
<Collapse open={isExpanded}><div>Collapsible content</div></Collapse>
|
|
1213
|
+
<Grow open={isVisible}><div>Scaling content</div></Grow>
|
|
1214
|
+
<Slide open={isVisible} direction="left"><div>Sliding panel</div></Slide>
|
|
1215
|
+
- Gotchas: All transitions respect prefers-reduced-motion (duration set to 0ms)
|
|
1216
|
+
|
|
1217
|
+
## TreeView / TreeItem
|
|
1218
|
+
- Import: @devalok/shilp-sutra/ui/tree-view
|
|
1219
|
+
- Server-safe: No
|
|
1220
|
+
- Props (TreeView):
|
|
1221
|
+
items: TreeNode[] (data-driven mode) — { id, label, icon?, disabled?, children? }
|
|
1222
|
+
defaultExpanded: string[]
|
|
1223
|
+
defaultSelected: string[]
|
|
1224
|
+
multiSelect: boolean
|
|
1225
|
+
checkboxes: boolean
|
|
1226
|
+
onSelect: (ids: string[]) => void
|
|
1227
|
+
onExpand: (ids: string[]) => void
|
|
1228
|
+
children: ReactNode (declarative mode)
|
|
1229
|
+
- Props (TreeItem):
|
|
1230
|
+
itemId: string (REQUIRED)
|
|
1231
|
+
label: string
|
|
1232
|
+
icon: ReactNode
|
|
1233
|
+
disabled: boolean
|
|
1234
|
+
depth: number
|
|
1235
|
+
children: ReactNode (nested TreeItems)
|
|
1236
|
+
- Hook: useTree({ defaultExpanded, defaultSelected, multiSelect, onSelect, onExpand })
|
|
1237
|
+
- Example (data-driven):
|
|
1238
|
+
<TreeView
|
|
1239
|
+
items={[
|
|
1240
|
+
{ id: '1', label: 'Folder', children: [
|
|
1241
|
+
{ id: '1.1', label: 'File A' },
|
|
1242
|
+
{ id: '1.2', label: 'File B' },
|
|
1243
|
+
]},
|
|
1244
|
+
]}
|
|
1245
|
+
defaultExpanded={['1']}
|
|
1246
|
+
onSelect={(ids) => console.log(ids)}
|
|
1247
|
+
/>
|
|
1248
|
+
- Example (declarative):
|
|
1249
|
+
<TreeView>
|
|
1250
|
+
<TreeItem itemId="1" label="Folder">
|
|
1251
|
+
<TreeItem itemId="1.1" label="File A" />
|
|
1252
|
+
</TreeItem>
|
|
1253
|
+
</TreeView>
|
|
1254
|
+
|
|
1255
|
+
## VisuallyHidden
|
|
1256
|
+
- Import: @devalok/shilp-sutra/ui/visually-hidden
|
|
1257
|
+
- Server-safe: Yes
|
|
1258
|
+
- Props: standard span attributes
|
|
1259
|
+
- Example:
|
|
1260
|
+
<VisuallyHidden>Screen reader only text</VisuallyHidden>
|
|
1261
|
+
|
|
1262
|
+
---
|
|
1263
|
+
|
|
1264
|
+
# COMPOSED COMPONENTS
|
|
1265
|
+
# Alphabetical within this section.
|
|
1266
|
+
# Import from: @devalok/shilp-sutra/composed/<kebab-name>
|
|
1267
|
+
|
|
1268
|
+
---
|
|
1269
|
+
|
|
1270
|
+
## AvatarGroup
|
|
1271
|
+
- Import: @devalok/shilp-sutra/composed/avatar-group
|
|
1272
|
+
- Server-safe: No
|
|
1273
|
+
- Props:
|
|
1274
|
+
users: AvatarUser[] (REQUIRED) — { name: string, image?: string | null }
|
|
1275
|
+
max: number (default: 4, overflow shows "+N" badge)
|
|
1276
|
+
size: "sm" | "md" | "lg"
|
|
1277
|
+
showTooltip: boolean (default: true)
|
|
1278
|
+
- Defaults: size="md", max=4, showTooltip=true
|
|
1279
|
+
- Example:
|
|
1280
|
+
<AvatarGroup
|
|
1281
|
+
users={[{ name: 'Alice', image: '/alice.jpg' }, { name: 'Bob' }]}
|
|
1282
|
+
max={3}
|
|
1283
|
+
size="md"
|
|
1284
|
+
/>
|
|
1285
|
+
- Gotchas: Wraps TooltipProvider internally — no need to add one
|
|
1286
|
+
|
|
1287
|
+
## CommandPalette
|
|
1288
|
+
- Import: @devalok/shilp-sutra/composed/command-palette
|
|
1289
|
+
- Server-safe: No
|
|
1290
|
+
- Props:
|
|
1291
|
+
groups: CommandGroup[] — { label: string, items: CommandItem[] }
|
|
1292
|
+
placeholder: string (default: "Search or jump to...")
|
|
1293
|
+
onSearch: (query: string) => void
|
|
1294
|
+
emptyMessage: string (default: "No results found.")
|
|
1295
|
+
- CommandItem shape: { id, label, description?, icon?, shortcut?, onSelect: () => void }
|
|
1296
|
+
- Example:
|
|
1297
|
+
<CommandPalette
|
|
1298
|
+
groups={[{
|
|
1299
|
+
label: 'Navigation',
|
|
1300
|
+
items: [{ id: 'dash', label: 'Dashboard', onSelect: () => navigate('/') }],
|
|
1301
|
+
}]}
|
|
1302
|
+
/>
|
|
1303
|
+
- Gotchas: Opens with Ctrl+K / Cmd+K by default
|
|
1304
|
+
|
|
1305
|
+
## ContentCard
|
|
1306
|
+
- Import: @devalok/shilp-sutra/composed/content-card
|
|
1307
|
+
- Server-safe: Yes
|
|
1308
|
+
- Props:
|
|
1309
|
+
variant: "default" | "outline" | "ghost"
|
|
1310
|
+
padding: "default" | "compact" | "spacious" | "none"
|
|
1311
|
+
header: ReactNode (custom header content)
|
|
1312
|
+
headerTitle: string (simple text header)
|
|
1313
|
+
headerActions: ReactNode (actions in header area)
|
|
1314
|
+
footer: ReactNode
|
|
1315
|
+
children: ReactNode (body)
|
|
1316
|
+
- Defaults: variant="default", padding="default"
|
|
1317
|
+
- Example:
|
|
1318
|
+
<ContentCard headerTitle="Team Members" headerActions={<Button size="sm">Add</Button>}>
|
|
1319
|
+
<p>Member list here</p>
|
|
1320
|
+
</ContentCard>
|
|
1321
|
+
|
|
1322
|
+
## DatePicker
|
|
1323
|
+
- Import: @devalok/shilp-sutra/composed/date-picker
|
|
1324
|
+
- Server-safe: No
|
|
1325
|
+
- Props:
|
|
1326
|
+
value: Date | null
|
|
1327
|
+
onChange: (date: Date | null) => void
|
|
1328
|
+
placeholder: string (default: "Pick a date")
|
|
1329
|
+
formatStr: string (default: "MMM d, yyyy")
|
|
1330
|
+
minDate: Date
|
|
1331
|
+
maxDate: Date
|
|
1332
|
+
disabledDates: (date: Date) => boolean
|
|
1333
|
+
className: string
|
|
1334
|
+
- Example:
|
|
1335
|
+
<DatePicker value={date} onChange={setDate} placeholder="Select date" />
|
|
1336
|
+
|
|
1337
|
+
## DateRangePicker
|
|
1338
|
+
- Import: @devalok/shilp-sutra/composed/date-picker
|
|
1339
|
+
- Server-safe: No
|
|
1340
|
+
- Props:
|
|
1341
|
+
startDate: Date | null
|
|
1342
|
+
endDate: Date | null
|
|
1343
|
+
onChange: (range: { start: Date | null, end: Date | null }) => void
|
|
1344
|
+
placeholder: string (default: "Pick a date range")
|
|
1345
|
+
formatStr: string (default: "MMM d, yyyy")
|
|
1346
|
+
minDate: Date
|
|
1347
|
+
maxDate: Date
|
|
1348
|
+
disabledDates: (date: Date) => boolean
|
|
1349
|
+
presets: PresetKey[] (shows quick-select sidebar)
|
|
1350
|
+
numberOfMonths: number (default: 1)
|
|
1351
|
+
- Example:
|
|
1352
|
+
<DateRangePicker
|
|
1353
|
+
startDate={start}
|
|
1354
|
+
endDate={end}
|
|
1355
|
+
onChange={({ start, end }) => { setStart(start); setEnd(end); }}
|
|
1356
|
+
/>
|
|
1357
|
+
|
|
1358
|
+
## DateTimePicker
|
|
1359
|
+
- Import: @devalok/shilp-sutra/composed/date-picker
|
|
1360
|
+
- Server-safe: No
|
|
1361
|
+
- Props:
|
|
1362
|
+
value: Date | null
|
|
1363
|
+
onChange: (date: Date | null) => void
|
|
1364
|
+
minDate: Date
|
|
1365
|
+
maxDate: Date
|
|
1366
|
+
disabledDates: (date: Date) => boolean
|
|
1367
|
+
timeFormat: "12h" | "24h"
|
|
1368
|
+
minuteStep: number
|
|
1369
|
+
placeholder: string
|
|
1370
|
+
className: string
|
|
1371
|
+
- Example:
|
|
1372
|
+
<DateTimePicker value={dateTime} onChange={setDateTime} timeFormat="12h" minuteStep={15} />
|
|
1373
|
+
|
|
1374
|
+
## EmptyState
|
|
1375
|
+
- Import: @devalok/shilp-sutra/composed/empty-state
|
|
1376
|
+
- Server-safe: Yes
|
|
1377
|
+
- Props:
|
|
1378
|
+
title: string (REQUIRED)
|
|
1379
|
+
description: string
|
|
1380
|
+
icon: ReactNode (default: Devalok chakra icon)
|
|
1381
|
+
action: ReactNode (e.g. a Button)
|
|
1382
|
+
compact: boolean (smaller layout)
|
|
1383
|
+
- Example:
|
|
1384
|
+
<EmptyState
|
|
1385
|
+
title="No tasks found"
|
|
1386
|
+
description="Create your first task to get started."
|
|
1387
|
+
action={<Button>Create Task</Button>}
|
|
1388
|
+
/>
|
|
1389
|
+
|
|
1390
|
+
## ErrorDisplay
|
|
1391
|
+
- Import: @devalok/shilp-sutra/composed/error-boundary
|
|
1392
|
+
- Server-safe: No
|
|
1393
|
+
- Props:
|
|
1394
|
+
error: unknown (REQUIRED — Error object, status object, or string)
|
|
1395
|
+
onReset: () => void (optional retry button)
|
|
1396
|
+
- Example:
|
|
1397
|
+
<ErrorDisplay error={error} onReset={() => refetch()} />
|
|
1398
|
+
- Gotchas:
|
|
1399
|
+
- Auto-detects HTTP status codes (404, 403, 500) and shows appropriate icon/message
|
|
1400
|
+
- Shows stack trace in development mode only
|
|
1401
|
+
|
|
1402
|
+
## GlobalLoading
|
|
1403
|
+
- Import: @devalok/shilp-sutra/composed/global-loading
|
|
1404
|
+
- Server-safe: No
|
|
1405
|
+
- Props:
|
|
1406
|
+
isLoading: boolean (REQUIRED)
|
|
1407
|
+
- Example:
|
|
1408
|
+
<GlobalLoading isLoading={isNavigating} />
|
|
1409
|
+
- Gotchas: Fixed-position bar at top of viewport (z-toast layer)
|
|
1410
|
+
|
|
1411
|
+
## LoadingSkeleton (CardSkeleton, TableSkeleton, BoardSkeleton, ListSkeleton)
|
|
1412
|
+
- Import: @devalok/shilp-sutra/composed/loading-skeleton
|
|
1413
|
+
- Server-safe: Yes
|
|
1414
|
+
- Props (CardSkeleton): className
|
|
1415
|
+
- Props (TableSkeleton): rows (default: 5), columns (default: 4), className
|
|
1416
|
+
- Props (BoardSkeleton): columns (default: 4), cardsPerColumn (default: 3), className
|
|
1417
|
+
- Props (ListSkeleton): rows (default: 6), showAvatar (default: true), className
|
|
1418
|
+
- Example:
|
|
1419
|
+
<CardSkeleton />
|
|
1420
|
+
<TableSkeleton rows={8} columns={5} />
|
|
1421
|
+
<BoardSkeleton columns={3} cardsPerColumn={4} />
|
|
1422
|
+
<ListSkeleton rows={10} showAvatar={false} />
|
|
1423
|
+
|
|
1424
|
+
## MemberPicker
|
|
1425
|
+
- Import: @devalok/shilp-sutra/composed/member-picker
|
|
1426
|
+
- Server-safe: No
|
|
1427
|
+
- Props:
|
|
1428
|
+
members: MemberPickerMember[] (REQUIRED) — { id, name, avatar? }
|
|
1429
|
+
selectedIds: string[] (REQUIRED)
|
|
1430
|
+
onSelect: (memberId: string) => void (REQUIRED)
|
|
1431
|
+
multiple: boolean (default: false)
|
|
1432
|
+
placeholder: string (default: "Search members...")
|
|
1433
|
+
children: ReactNode (trigger element)
|
|
1434
|
+
- Example:
|
|
1435
|
+
<MemberPicker members={teamMembers} selectedIds={assignees} onSelect={toggleAssignee} multiple>
|
|
1436
|
+
<Button variant="outline">Assign Members</Button>
|
|
1437
|
+
</MemberPicker>
|
|
1438
|
+
|
|
1439
|
+
## PageHeader
|
|
1440
|
+
- Import: @devalok/shilp-sutra/composed/page-header
|
|
1441
|
+
- Server-safe: Yes
|
|
1442
|
+
- Props:
|
|
1443
|
+
title: string (falls back to last breadcrumb label if omitted)
|
|
1444
|
+
subtitle: string
|
|
1445
|
+
actions: ReactNode (action buttons)
|
|
1446
|
+
breadcrumbs: Breadcrumb[] — { label: string, href?: string }
|
|
1447
|
+
titleClassName: string
|
|
1448
|
+
- Example:
|
|
1449
|
+
<PageHeader
|
|
1450
|
+
title="Project Settings"
|
|
1451
|
+
subtitle="Configure your project preferences"
|
|
1452
|
+
breadcrumbs={[
|
|
1453
|
+
{ label: 'Home', href: '/' },
|
|
1454
|
+
{ label: 'Projects', href: '/projects' },
|
|
1455
|
+
{ label: 'Settings' },
|
|
1456
|
+
]}
|
|
1457
|
+
actions={<Button>Save</Button>}
|
|
1458
|
+
/>
|
|
1459
|
+
|
|
1460
|
+
## PageSkeletons (DashboardSkeleton, ProjectListSkeleton, TaskDetailSkeleton)
|
|
1461
|
+
- Import: @devalok/shilp-sutra/composed/page-skeletons
|
|
1462
|
+
- Server-safe: Yes
|
|
1463
|
+
- Props: None (no configurable props)
|
|
1464
|
+
- Example:
|
|
1465
|
+
<DashboardSkeleton />
|
|
1466
|
+
<ProjectListSkeleton />
|
|
1467
|
+
<TaskDetailSkeleton />
|
|
1468
|
+
|
|
1469
|
+
## PriorityIndicator
|
|
1470
|
+
- Import: @devalok/shilp-sutra/composed/priority-indicator
|
|
1471
|
+
- Server-safe: Yes
|
|
1472
|
+
- Props:
|
|
1473
|
+
priority: "LOW" | "MEDIUM" | "HIGH" | "URGENT" (case-insensitive)
|
|
1474
|
+
display: "compact" | "full" (default: "full")
|
|
1475
|
+
- Example:
|
|
1476
|
+
<PriorityIndicator priority="HIGH" />
|
|
1477
|
+
<PriorityIndicator priority="low" display="compact" />
|
|
1478
|
+
- Gotchas: Case-insensitive — "low" and "LOW" both work
|
|
1479
|
+
|
|
1480
|
+
## RichTextEditor / RichTextViewer
|
|
1481
|
+
- Import: @devalok/shilp-sutra/composed/rich-text-editor
|
|
1482
|
+
- Server-safe: No
|
|
1483
|
+
- Props (RichTextEditor):
|
|
1484
|
+
content: string (HTML string)
|
|
1485
|
+
placeholder: string (default: "Start writing...")
|
|
1486
|
+
onChange: (html: string) => void
|
|
1487
|
+
className: string
|
|
1488
|
+
editable: boolean (default: true)
|
|
1489
|
+
- Props (RichTextViewer):
|
|
1490
|
+
content: string (REQUIRED, HTML string)
|
|
1491
|
+
className: string
|
|
1492
|
+
- Example:
|
|
1493
|
+
<RichTextEditor content={html} onChange={setHtml} placeholder="Write your message..." />
|
|
1494
|
+
<RichTextViewer content={savedHtml} />
|
|
1495
|
+
- Gotchas: Requires @tiptap/react, @tiptap/starter-kit, @tiptap/extension-placeholder as peer deps
|
|
1496
|
+
|
|
1497
|
+
## ScheduleView
|
|
1498
|
+
- Import: @devalok/shilp-sutra/composed/schedule-view
|
|
1499
|
+
- Server-safe: No
|
|
1500
|
+
- Props:
|
|
1501
|
+
view: "day" | "week" (REQUIRED)
|
|
1502
|
+
date: Date (REQUIRED — current day or any date in target week)
|
|
1503
|
+
events: ScheduleEvent[] (REQUIRED) — { id, title, start: Date, end: Date, color? }
|
|
1504
|
+
onEventClick: (event: ScheduleEvent) => void
|
|
1505
|
+
onSlotClick: (start: Date, end: Date) => void
|
|
1506
|
+
startHour: number (default: 8)
|
|
1507
|
+
endHour: number (default: 18, exclusive)
|
|
1508
|
+
slotDuration: number (minutes, default: 30)
|
|
1509
|
+
- Event colors: "primary" | "success" | "warning" | "error" | "info" | "neutral"
|
|
1510
|
+
- Example:
|
|
1511
|
+
<ScheduleView
|
|
1512
|
+
view="week"
|
|
1513
|
+
date={new Date()}
|
|
1514
|
+
events={calendarEvents}
|
|
1515
|
+
onEventClick={(e) => openEvent(e.id)}
|
|
1516
|
+
/>
|
|
1517
|
+
|
|
1518
|
+
## SimpleTooltip
|
|
1519
|
+
- Import: @devalok/shilp-sutra/composed/simple-tooltip
|
|
1520
|
+
- Server-safe: No
|
|
1521
|
+
- Props:
|
|
1522
|
+
content: ReactNode (REQUIRED — tooltip content)
|
|
1523
|
+
side: "top" | "right" | "bottom" | "left" (default: "top")
|
|
1524
|
+
align: "start" | "center" | "end" (default: "center")
|
|
1525
|
+
delayDuration: number (ms, default: 300)
|
|
1526
|
+
children: ReactNode (trigger element)
|
|
1527
|
+
- Example:
|
|
1528
|
+
<SimpleTooltip content="Edit this item">
|
|
1529
|
+
<IconButton icon={<IconEdit />} aria-label="Edit" />
|
|
1530
|
+
</SimpleTooltip>
|
|
1531
|
+
- Gotchas: Wraps the full Tooltip compound (Provider + Tooltip + Trigger + Content) into one component
|
|
1532
|
+
|
|
1533
|
+
## StatusBadge
|
|
1534
|
+
- Import: @devalok/shilp-sutra/composed/status-badge
|
|
1535
|
+
- Server-safe: Yes
|
|
1536
|
+
- Props:
|
|
1537
|
+
status: "active" | "pending" | "approved" | "rejected" | "completed" | "blocked" | "cancelled" | "draft"
|
|
1538
|
+
color: "success" | "warning" | "error" | "info" | "neutral" (overrides status styling when set)
|
|
1539
|
+
size: "sm" | "md"
|
|
1540
|
+
label: string (auto-derived from status/color if omitted)
|
|
1541
|
+
hideDot: boolean (default: false)
|
|
1542
|
+
- Defaults: size="md"
|
|
1543
|
+
- Example:
|
|
1544
|
+
<StatusBadge status="active" />
|
|
1545
|
+
<StatusBadge color="warning" label="In Review" size="sm" />
|
|
1546
|
+
- Gotchas: When color is set, it takes priority over status for styling
|
|
1547
|
+
|
|
1548
|
+
---
|
|
1549
|
+
|
|
1550
|
+
# SHELL COMPONENTS
|
|
1551
|
+
# Application-level layout components.
|
|
1552
|
+
# Import from: @devalok/shilp-sutra/shell/<kebab-name>
|
|
1553
|
+
|
|
1554
|
+
---
|
|
1555
|
+
|
|
1556
|
+
## AppCommandPalette
|
|
1557
|
+
- Import: @devalok/shilp-sutra/shell/app-command-palette
|
|
1558
|
+
- Server-safe: No
|
|
1559
|
+
- Props:
|
|
1560
|
+
user: AppCommandPaletteUser | null — { name, role? }
|
|
1561
|
+
extraGroups: CommandGroup[]
|
|
1562
|
+
onNavigate: (path: string) => void
|
|
1563
|
+
onSearch: (query: string) => void
|
|
1564
|
+
searchResults: SearchResult[] — { id, title, snippet?, entityType, projectId?, metadata? }
|
|
1565
|
+
onSelectResult: (result: SearchResult) => void
|
|
1566
|
+
- Example:
|
|
1567
|
+
<AppCommandPalette
|
|
1568
|
+
user={{ name: 'John', role: 'admin' }}
|
|
1569
|
+
onNavigate={(path) => router.push(path)}
|
|
1570
|
+
searchResults={results}
|
|
1571
|
+
onSelectResult={(r) => router.push(`/${r.entityType}/${r.id}`)}
|
|
1572
|
+
/>
|
|
1573
|
+
|
|
1574
|
+
## AppSidebar
|
|
1575
|
+
- Import: @devalok/shilp-sutra/shell/sidebar
|
|
1576
|
+
- Server-safe: No
|
|
1577
|
+
- Props:
|
|
1578
|
+
currentPath: string (highlights active nav item)
|
|
1579
|
+
user: SidebarUser | null — { name, email?, image?, designation?, role? }
|
|
1580
|
+
navGroups: NavGroup[] — { label: string, items: NavItem[] }
|
|
1581
|
+
logo: ReactNode
|
|
1582
|
+
footerLinks: Array<{ label: string, href: string }>
|
|
1583
|
+
className: string
|
|
1584
|
+
- NavItem shape: { title, href, icon: ReactNode, exact?: boolean }
|
|
1585
|
+
- Example:
|
|
1586
|
+
<AppSidebar
|
|
1587
|
+
currentPath="/dashboard"
|
|
1588
|
+
user={{ name: 'Jane', email: 'jane@example.com' }}
|
|
1589
|
+
navGroups={[{
|
|
1590
|
+
label: 'Main',
|
|
1591
|
+
items: [
|
|
1592
|
+
{ title: 'Dashboard', href: '/dashboard', icon: <IconHome /> },
|
|
1593
|
+
{ title: 'Projects', href: '/projects', icon: <IconFolder /> },
|
|
1594
|
+
],
|
|
1595
|
+
}]}
|
|
1596
|
+
/>
|
|
1597
|
+
- Gotchas: Must be wrapped in SidebarProvider (from ui/sidebar)
|
|
1598
|
+
|
|
1599
|
+
## BottomNavbar
|
|
1600
|
+
- Import: @devalok/shilp-sutra/shell/bottom-navbar
|
|
1601
|
+
- Server-safe: No
|
|
1602
|
+
- Props:
|
|
1603
|
+
currentPath: string
|
|
1604
|
+
user: BottomNavbarUser | null — { name, role? }
|
|
1605
|
+
primaryItems: BottomNavItem[] (max 4 recommended) — { title, href, icon, exact? }
|
|
1606
|
+
moreItems: BottomNavItem[] (overflow items in "More" menu)
|
|
1607
|
+
className: string
|
|
1608
|
+
- Example:
|
|
1609
|
+
<BottomNavbar
|
|
1610
|
+
currentPath="/dashboard"
|
|
1611
|
+
primaryItems={[
|
|
1612
|
+
{ title: 'Home', href: '/', icon: <IconHome /> },
|
|
1613
|
+
{ title: 'Tasks', href: '/tasks', icon: <IconChecklist /> },
|
|
1614
|
+
]}
|
|
1615
|
+
/>
|
|
1616
|
+
|
|
1617
|
+
## LinkProvider
|
|
1618
|
+
- Import: @devalok/shilp-sutra/shell/link-context
|
|
1619
|
+
- Server-safe: No
|
|
1620
|
+
- Props:
|
|
1621
|
+
component: ForwardRefComponent (e.g. Next.js Link, Remix Link)
|
|
1622
|
+
children: ReactNode
|
|
1623
|
+
- Hook: useLink() => LinkComponent
|
|
1624
|
+
- Example:
|
|
1625
|
+
import Link from 'next/link'
|
|
1626
|
+
<LinkProvider component={Link}>
|
|
1627
|
+
<AppSidebar ... />
|
|
1628
|
+
<BottomNavbar ... />
|
|
1629
|
+
</LinkProvider>
|
|
1630
|
+
- Gotchas: Without LinkProvider, shell components render plain <a> tags
|
|
1631
|
+
|
|
1632
|
+
## NotificationCenter
|
|
1633
|
+
- Import: @devalok/shilp-sutra/shell/notification-center
|
|
1634
|
+
- Server-safe: No
|
|
1635
|
+
- Props:
|
|
1636
|
+
notifications: Notification[] — { id, title, body?, tier: "INFO"|"IMPORTANT"|"CRITICAL", isRead, createdAt, entityType?, entityId?, projectId?, project? }
|
|
1637
|
+
unreadCount: number (derived from notifications if not provided)
|
|
1638
|
+
open: boolean (controlled mode)
|
|
1639
|
+
onOpenChange: (open: boolean) => void
|
|
1640
|
+
isLoading: boolean
|
|
1641
|
+
hasMore: boolean
|
|
1642
|
+
onFetchMore: () => void
|
|
1643
|
+
onMarkRead: (id: string) => void
|
|
1644
|
+
onMarkAllRead: () => void
|
|
1645
|
+
onNotificationClick: (notification: Notification) => void
|
|
1646
|
+
- Example:
|
|
1647
|
+
<NotificationCenter
|
|
1648
|
+
notifications={notifications}
|
|
1649
|
+
onMarkRead={markAsRead}
|
|
1650
|
+
onMarkAllRead={markAllAsRead}
|
|
1651
|
+
onNotificationClick={(n) => router.push(`/notifications/${n.id}`)}
|
|
1652
|
+
/>
|
|
1653
|
+
|
|
1654
|
+
## NotificationPreferences
|
|
1655
|
+
- Import: @devalok/shilp-sutra/shell/notification-preferences
|
|
1656
|
+
- Server-safe: No
|
|
1657
|
+
- Props:
|
|
1658
|
+
preferences: NotificationPreference[] — { id, userId?, projectId, channel, minTier, muted }
|
|
1659
|
+
projects: NotificationProject[] — { id, title }
|
|
1660
|
+
isLoading: boolean
|
|
1661
|
+
onSave: (preference: { projectId, channel, minTier, muted }) => void
|
|
1662
|
+
onDelete: (id: string) => void
|
|
1663
|
+
- Example:
|
|
1664
|
+
<NotificationPreferences
|
|
1665
|
+
preferences={prefs}
|
|
1666
|
+
projects={projectList}
|
|
1667
|
+
onSave={handleSavePref}
|
|
1668
|
+
onDelete={handleDeletePref}
|
|
1669
|
+
/>
|
|
1670
|
+
|
|
1671
|
+
## TopBar
|
|
1672
|
+
- Import: @devalok/shilp-sutra/shell/top-bar
|
|
1673
|
+
- Server-safe: No
|
|
1674
|
+
- Props:
|
|
1675
|
+
pageTitle: string
|
|
1676
|
+
user: TopBarUser | null — { name, email?, image? }
|
|
1677
|
+
onNavigate: (path: string) => void
|
|
1678
|
+
onLogout: () => void
|
|
1679
|
+
onSearchClick: () => void
|
|
1680
|
+
onAiChatClick: () => void
|
|
1681
|
+
mobileLogo: ReactNode
|
|
1682
|
+
notificationSlot: ReactNode (render NotificationCenter here)
|
|
1683
|
+
className: string
|
|
1684
|
+
- Example:
|
|
1685
|
+
<TopBar
|
|
1686
|
+
pageTitle="Dashboard"
|
|
1687
|
+
user={{ name: 'John', email: 'john@example.com' }}
|
|
1688
|
+
onNavigate={(p) => router.push(p)}
|
|
1689
|
+
onLogout={handleLogout}
|
|
1690
|
+
notificationSlot={<NotificationCenter notifications={notifications} />}
|
|
1691
|
+
/>
|
|
1692
|
+
|
|
1693
|
+
---
|
|
1694
|
+
|
|
1695
|
+
# HOOKS
|
|
1696
|
+
# Import from: @devalok/shilp-sutra/hooks/<hook-name>
|
|
1697
|
+
|
|
1698
|
+
---
|
|
1699
|
+
|
|
1700
|
+
## useToast
|
|
1701
|
+
- Import: @devalok/shilp-sutra/hooks/use-toast
|
|
1702
|
+
- Returns: { toasts, toast, dismiss }
|
|
1703
|
+
- toast(options): options = { title?, description?, color?, action? }
|
|
1704
|
+
color: "neutral" | "success" | "warning" | "error" | "info"
|
|
1705
|
+
Returns: { id, dismiss, update }
|
|
1706
|
+
- dismiss(toastId?: string): dismiss specific or all toasts
|
|
1707
|
+
- Example:
|
|
1708
|
+
const { toast } = useToast()
|
|
1709
|
+
toast({ title: 'Saved!', color: 'success' })
|
|
1710
|
+
toast({ title: 'Error', description: 'Something went wrong.', color: 'error' })
|
|
1711
|
+
- Also available: import { toast } from '@devalok/shilp-sutra/hooks/use-toast' (imperative, no hook)
|
|
1712
|
+
|
|
1713
|
+
## useColorMode
|
|
1714
|
+
- Import: @devalok/shilp-sutra/hooks/use-color-mode
|
|
1715
|
+
- Returns: { colorMode, setColorMode, toggleColorMode }
|
|
1716
|
+
- colorMode: "light" | "dark" | "system"
|
|
1717
|
+
- setColorMode(mode: "light" | "dark" | "system"): sets .dark class on html, persists to localStorage + cookie
|
|
1718
|
+
- toggleColorMode(): toggles between light and dark
|
|
1719
|
+
- Example:
|
|
1720
|
+
const { colorMode, toggleColorMode } = useColorMode()
|
|
1721
|
+
<Button onClick={toggleColorMode}>{colorMode === 'dark' ? 'Light' : 'Dark'}</Button>
|
|
1722
|
+
|
|
1723
|
+
## useIsMobile
|
|
1724
|
+
- Import: @devalok/shilp-sutra/hooks/use-mobile
|
|
1725
|
+
- Returns: boolean (true if viewport width < 768px)
|
|
1726
|
+
- Example:
|
|
1727
|
+
const isMobile = useIsMobile()
|
|
1728
|
+
{isMobile ? <BottomNavbar /> : <AppSidebar />}
|
|
1729
|
+
|
|
1730
|
+
---
|
|
1731
|
+
|
|
1732
|
+
# UTILITIES
|
|
1733
|
+
# Import from: @devalok/shilp-sutra (barrel) or per-component path
|
|
1734
|
+
|
|
1735
|
+
---
|
|
1736
|
+
|
|
1737
|
+
## cn (className merge utility)
|
|
1738
|
+
- Import: @devalok/shilp-sutra (barrel export)
|
|
1739
|
+
- Usage: cn('base-class', condition && 'conditional-class', className)
|
|
1740
|
+
- Merges Tailwind classes with clsx + twMerge
|
|
1741
|
+
|
|
1742
|
+
## getInitials
|
|
1743
|
+
- Import: @devalok/shilp-sutra/composed (barrel export)
|
|
1744
|
+
- Usage: getInitials("John Doe") => "JD"
|
|
1745
|
+
|
|
1746
|
+
## generatePagination
|
|
1747
|
+
- Import: @devalok/shilp-sutra/ui/pagination
|
|
1748
|
+
- Usage: generatePagination(currentPage, totalPages, siblingCount)
|
|
1749
|
+
- Returns: (number | 'ellipsis')[]
|
|
1750
|
+
|
|
1751
|
+
## motion / duration / easings / durations
|
|
1752
|
+
- Import: @devalok/shilp-sutra (barrel export)
|
|
1753
|
+
- Design system motion tokens for custom animations
|