@devalok/shilp-sutra 0.16.1 → 0.17.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/ui/table.js CHANGED
@@ -1,22 +1,22 @@
1
1
  import { jsx as o } from "react/jsx-runtime";
2
2
  import * as l from "react";
3
- import { c as r } from "../_chunks/utils.js";
4
- const s = l.forwardRef(({ className: e, ...a }, t) => /* @__PURE__ */ o("div", { className: "relative w-full overflow-auto", children: /* @__PURE__ */ o(
3
+ import { c as s } from "../_chunks/utils.js";
4
+ const r = l.forwardRef(({ className: e, ...a }, t) => /* @__PURE__ */ o("div", { className: "relative w-full overflow-auto", children: /* @__PURE__ */ o(
5
5
  "table",
6
6
  {
7
7
  ref: t,
8
- className: r("w-full caption-bottom text-ds-md", e),
8
+ className: s("w-full caption-bottom text-ds-md", e),
9
9
  ...a
10
10
  }
11
11
  ) }));
12
- s.displayName = "Table";
13
- const d = l.forwardRef(({ className: e, ...a }, t) => /* @__PURE__ */ o("thead", { ref: t, className: r("", e), ...a }));
12
+ r.displayName = "Table";
13
+ const d = l.forwardRef(({ className: e, ...a }, t) => /* @__PURE__ */ o("thead", { ref: t, className: s("", e), ...a }));
14
14
  d.displayName = "TableHeader";
15
15
  const c = l.forwardRef(({ className: e, ...a }, t) => /* @__PURE__ */ o(
16
16
  "tbody",
17
17
  {
18
18
  ref: t,
19
- className: r("[&_tr:last-child]:border-0", e),
19
+ className: s("[&_tr:last-child]:border-0", e),
20
20
  ...a
21
21
  }
22
22
  ));
@@ -25,7 +25,7 @@ const m = l.forwardRef(({ className: e, ...a }, t) => /* @__PURE__ */ o(
25
25
  "tfoot",
26
26
  {
27
27
  ref: t,
28
- className: r(
28
+ className: s(
29
29
  "bg-[color-mix(in_srgb,var(--color-layer-02)_50%,transparent)] font-medium [&>tr]:last:border-b-0",
30
30
  e
31
31
  ),
@@ -37,7 +37,7 @@ const b = l.forwardRef(({ className: e, ...a }, t) => /* @__PURE__ */ o(
37
37
  "tr",
38
38
  {
39
39
  ref: t,
40
- className: r(
40
+ className: s(
41
41
  "transition-colors hover:bg-layer-02 data-[state=selected]:bg-interactive-selected",
42
42
  e
43
43
  ),
@@ -50,7 +50,7 @@ const i = l.forwardRef(({ className: e, ...a }, t) => /* @__PURE__ */ o(
50
50
  {
51
51
  ref: t,
52
52
  scope: "col",
53
- className: r(
53
+ className: s(
54
54
  "h-ds-md px-ds-03 text-left font-medium text-text-secondary [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]",
55
55
  e
56
56
  ),
@@ -62,8 +62,8 @@ const f = l.forwardRef(({ className: e, ...a }, t) => /* @__PURE__ */ o(
62
62
  "td",
63
63
  {
64
64
  ref: t,
65
- className: r(
66
- "py-ds-03 px-0 [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]",
65
+ className: s(
66
+ "py-ds-03 px-ds-03 [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]",
67
67
  e
68
68
  ),
69
69
  ...a
@@ -74,13 +74,13 @@ const n = l.forwardRef(({ className: e, ...a }, t) => /* @__PURE__ */ o(
74
74
  "caption",
75
75
  {
76
76
  ref: t,
77
- className: r("mt-ds-05 text-ds-md text-text-secondary", e),
77
+ className: s("mt-ds-05 text-ds-md text-text-secondary", e),
78
78
  ...a
79
79
  }
80
80
  ));
81
81
  n.displayName = "TableCaption";
82
82
  export {
83
- s as Table,
83
+ r as Table,
84
84
  c as TableBody,
85
85
  n as TableCaption,
86
86
  f as TableCell,
package/llms-full.txt CHANGED
@@ -187,6 +187,7 @@ Note: getFormFieldA11y() was removed in favor of useFormField() hook.
187
187
  <AvatarImage src={user.photoUrl} alt={user.name} />
188
188
  <AvatarFallback>JD</AvatarFallback>
189
189
  </Avatar>
190
+ - Type: AvatarStatus = 'online' | 'offline' | 'busy' | 'away'
190
191
  - Gotchas:
191
192
  - Status dot renders with role="img" and aria-label (accessible, not decorative)
192
193
  - Dot size scales automatically with avatar size
@@ -534,6 +535,7 @@ Note: getFormFieldA11y() was removed in favor of useFormField() hook.
534
535
  required: boolean
535
536
  - Props (FormHelperText):
536
537
  state: "helper" | "error" | "warning" | "success" (inherits from FormField context)
538
+ - Type: FormHelperState = 'helper' | 'error' | 'warning' | 'success'
537
539
  - Hook: useFormField() => { state, helperTextId, required }
538
540
  - Example:
539
541
  <FormField state="error">
@@ -581,9 +583,10 @@ Note: getFormFieldA11y() was removed in favor of useFormField() hook.
581
583
  ## Input
582
584
  - Import: @devalok/shilp-sutra/ui/input
583
585
  - Server-safe: No
586
+ - Type: InputState = 'default' | 'error' | 'warning' | 'success'
584
587
  - Props:
585
588
  size: "sm" | "md" | "lg"
586
- state: "default" | "error" | "warning" | "success"
589
+ state: InputState
587
590
  startIcon: ReactNode
588
591
  endIcon: ReactNode
589
592
  (plus all standard HTML input attributes except native "size")
@@ -814,10 +817,13 @@ Note: getFormFieldA11y() was removed in favor of useFormField() hook.
814
817
  - Props (SegmentedControl root):
815
818
  size: "sm" | "md" | "lg" (REQUIRED) — also accepts legacy "small" | "medium" | "big"
816
819
  variant: "filled" | "tonal" (REQUIRED)
817
- options: SegmentedControlOption[] (REQUIRED) — { id: string, text: string, icon?: ComponentType }
820
+ options: SegmentedControlOption[] (REQUIRED)
818
821
  selectedId: string (REQUIRED)
819
822
  onSelect: (id: string) => void (REQUIRED)
820
823
  disabled: boolean
824
+ - Type: SegmentedControlOption = { id: string, text: string, icon?: ComponentType<{ className?: string }> }
825
+ - Type: SegmentedControlSize = 'sm' | 'md' | 'lg' | 'small' | 'medium' | 'big' (legacy aliases)
826
+ - Type: SegmentedControlVariant = 'filled' | 'tonal'
821
827
  - Defaults: None — size and variant are required
822
828
  - Example:
823
829
  <SegmentedControl
@@ -1111,8 +1117,9 @@ Note: getFormFieldA11y() was removed in favor of useFormField() hook.
1111
1117
  ## Text
1112
1118
  - Import: @devalok/shilp-sutra/ui/text
1113
1119
  - Server-safe: Yes
1120
+ - Type: TextVariant = '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'
1114
1121
  - Props:
1115
- 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"
1122
+ variant: TextVariant
1116
1123
  as: ElementType (override the auto-selected HTML element)
1117
1124
  - Defaults: variant="body-md"
1118
1125
  - Default element mapping:
@@ -1481,6 +1488,124 @@ Note: getFormFieldA11y() was removed in favor of useFormField() hook.
1481
1488
  - Example:
1482
1489
  <DateTimePicker value={dateTime} onChange={setDateTime} timeFormat="12h" minuteStep={15} />
1483
1490
 
1491
+ ## TimePicker
1492
+ - Import: @devalok/shilp-sutra/composed/date-picker
1493
+ - Server-safe: No
1494
+ - Props:
1495
+ value: Date | null (time stored as a Date object)
1496
+ onChange: (date: Date) => void
1497
+ format: "12h" | "24h" (default: "12h")
1498
+ minuteStep: number (default: 1) — step interval for minutes column
1499
+ secondStep: number (default: 1) — step interval for seconds column
1500
+ showSeconds: boolean (default: false) — whether to show the seconds column
1501
+ placeholder: string (default: "Pick a time")
1502
+ disabled: boolean (default: false)
1503
+ className: string
1504
+ - Defaults: format="12h", minuteStep=1, secondStep=1, showSeconds=false
1505
+ - Example:
1506
+ <TimePicker value={time} onChange={setTime} format="24h" minuteStep={15} />
1507
+ <TimePicker value={time} onChange={setTime} showSeconds />
1508
+ - Gotchas:
1509
+ - Time is stored inside a Date object — only hours/minutes/seconds are meaningful
1510
+ - Opens as a popover with scrollable columns for hours, minutes, (optional) seconds, and AM/PM
1511
+ - When no value is set, selecting a time part creates a Date from the current date/time
1512
+
1513
+ ## CalendarGrid
1514
+ - Import: @devalok/shilp-sutra/composed/date-picker
1515
+ - Server-safe: No
1516
+ - Props:
1517
+ currentMonth: Date (REQUIRED) — which month to display
1518
+ selected: Date | null — single selected date
1519
+ rangeStart: Date | null — range selection start
1520
+ rangeEnd: Date | null — range selection end
1521
+ hoverDate: Date | null — date currently hovered (for range preview)
1522
+ onSelect: (date: Date) => void (REQUIRED) — called when user clicks a date
1523
+ onHover: (date: Date | null) => void — called on mouse enter/leave
1524
+ onMonthChange: (date: Date) => void (REQUIRED) — called for prev/next month navigation
1525
+ onHeaderClick: () => void — called when month/year header is clicked (e.g. to switch to month/year picker view)
1526
+ disabledDates: (date: Date) => boolean
1527
+ minDate: Date
1528
+ maxDate: Date
1529
+ hidePrevNav: boolean — hide previous-month arrow
1530
+ hideNextNav: boolean — hide next-month arrow
1531
+ events: CalendarEvent[] — dot indicators on dates. CalendarEvent: { date: Date, color?: string, label?: string }
1532
+ - Example:
1533
+ <CalendarGrid
1534
+ currentMonth={currentMonth}
1535
+ selected={selectedDate}
1536
+ onSelect={setSelectedDate}
1537
+ onMonthChange={setCurrentMonth}
1538
+ events={[{ date: new Date('2026-03-15'), color: 'var(--color-success)' }]}
1539
+ />
1540
+ - Gotchas:
1541
+ - This is a low-level building block — prefer DatePicker/DateRangePicker for most use cases
1542
+ - Full keyboard navigation: ArrowLeft/Right/Up/Down, Home/End, Enter/Space
1543
+ - Up to 3 event dots render per day cell
1544
+
1545
+ ## YearPicker
1546
+ - Import: @devalok/shilp-sutra/composed/date-picker
1547
+ - Server-safe: No
1548
+ - Props:
1549
+ currentYear: number (REQUIRED) — centers the decade grid
1550
+ selectedYear: number — highlights the selected year
1551
+ onYearSelect: (year: number) => void (REQUIRED)
1552
+ minDate: Date — disables years before this date's year
1553
+ maxDate: Date — disables years after this date's year
1554
+ - Example:
1555
+ <YearPicker currentYear={2026} selectedYear={2026} onYearSelect={handleYear} />
1556
+ - Gotchas:
1557
+ - Displays a 12-year grid (3 columns x 4 rows) based on the decade of currentYear
1558
+ - This is a low-level building block used internally by DatePicker
1559
+
1560
+ ## MonthPicker
1561
+ - Import: @devalok/shilp-sutra/composed/date-picker
1562
+ - Server-safe: No
1563
+ - Props:
1564
+ currentYear: number (REQUIRED) — which year's months to display
1565
+ selectedMonth: number (0-11) — highlights the selected month
1566
+ onMonthSelect: (month: number) => void (REQUIRED) — returns 0-11
1567
+ minDate: Date — disables months before this date
1568
+ maxDate: Date — disables months after this date
1569
+ - Example:
1570
+ <MonthPicker currentYear={2026} selectedMonth={2} onMonthSelect={handleMonth} />
1571
+ - Gotchas:
1572
+ - Month values are 0-indexed (Jan=0, Dec=11) — same as JavaScript Date.getMonth()
1573
+ - Displays a 4-column grid of abbreviated month names (Jan, Feb, Mar, ...)
1574
+ - This is a low-level building block used internally by DatePicker
1575
+
1576
+ ## Presets (Date Range Presets)
1577
+ - Import: @devalok/shilp-sutra/composed/date-picker
1578
+ - Server-safe: No
1579
+ - Types:
1580
+ PresetKey: 'today' | 'yesterday' | 'last7days' | 'last30days' | 'thisMonth' | 'lastMonth' | 'thisYear'
1581
+ - Props:
1582
+ presets: PresetKey[] (REQUIRED) — which preset buttons to show
1583
+ onSelect: (start: Date, end: Date) => void (REQUIRED) — called with the computed date range
1584
+ className: string
1585
+ - Example:
1586
+ <Presets
1587
+ presets={['today', 'last7days', 'last30days', 'thisMonth']}
1588
+ onSelect={(start, end) => setRange({ start, end })}
1589
+ />
1590
+ - Gotchas:
1591
+ - Typically used alongside DateRangePicker (pass presets prop to DateRangePicker for built-in sidebar)
1592
+ - Each preset computes start-of-day / end-of-day boundaries using date-fns
1593
+
1594
+ ## useCalendar
1595
+ - Import: @devalok/shilp-sutra/composed/date-picker
1596
+ - Hook: useCalendar(initialMonth?: Date)
1597
+ - Returns:
1598
+ currentMonth: Date — the current displayed month
1599
+ setCurrentMonth: (date: Date) => void
1600
+ goToPreviousMonth: () => void
1601
+ goToNextMonth: () => void
1602
+ goToMonth: (month: number) => void — 0-indexed month
1603
+ goToYear: (year: number) => void
1604
+ - Example:
1605
+ const { currentMonth, goToNextMonth, goToPreviousMonth, goToMonth, goToYear } = useCalendar()
1606
+ <CalendarGrid currentMonth={currentMonth} onMonthChange={setCurrentMonth} ... />
1607
+ - Gotchas: This is a convenience hook for managing calendar month state — used internally by DatePicker
1608
+
1484
1609
  ## EmptyState
1485
1610
  - Import: @devalok/shilp-sutra/composed/empty-state
1486
1611
  - Server-safe: Yes
@@ -1513,6 +1638,7 @@ Note: getFormFieldA11y() was removed in favor of useFormField() hook.
1513
1638
  emptyState: ReactNode — empty state content
1514
1639
  compact: boolean — tighter spacing, no avatars, smaller text
1515
1640
  maxInitialItems: number — truncate with "Show all (N)" toggle
1641
+ - Type: ActivityItem = { id: string, actor?: { name: string, image?: string }, action: string | ReactNode, timestamp: Date | string, icon?: ReactNode, color?: 'default' | 'success' | 'warning' | 'error' | 'info', detail?: ReactNode }
1516
1642
  - Example:
1517
1643
  <ActivityFeed
1518
1644
  items={[
@@ -1606,8 +1732,9 @@ Note: getFormFieldA11y() was removed in favor of useFormField() hook.
1606
1732
  ## PriorityIndicator
1607
1733
  - Import: @devalok/shilp-sutra/composed/priority-indicator
1608
1734
  - Server-safe: Yes
1735
+ - Type: Priority = 'LOW' | 'MEDIUM' | 'HIGH' | 'URGENT' | 'low' | 'medium' | 'high' | 'urgent'
1609
1736
  - Props:
1610
- priority: "LOW" | "MEDIUM" | "HIGH" | "URGENT" (case-insensitive)
1737
+ priority: Priority
1611
1738
  display: "compact" | "full" (default: "full")
1612
1739
  - Example:
1613
1740
  <PriorityIndicator priority="HIGH" />
@@ -1710,6 +1837,42 @@ Note: getFormFieldA11y() was removed in favor of useFormField() hook.
1710
1837
  <StatusBadge color="warning" label="In Review" size="sm" />
1711
1838
  - Gotchas: When color is set, it takes priority over status for styling
1712
1839
 
1840
+ ## UploadProgress
1841
+ - Import: @devalok/shilp-sutra/composed/upload-progress
1842
+ - Server-safe: No
1843
+ - Types:
1844
+ UploadFile: { id: string, name: string, size: number (bytes), progress?: number (0-100, undefined = indeterminate), status: 'pending' | 'uploading' | 'processing' | 'complete' | 'error', error?: string, previewUrl?: string }
1845
+ - Props:
1846
+ files: UploadFile[] (REQUIRED)
1847
+ onRemove: (fileId: string) => void — called when user cancels/removes a file
1848
+ onRetry: (fileId: string) => void — called when user retries a failed upload
1849
+ onDismissAll: () => void — "Dismiss all" button appears when all files are terminal (complete or error)
1850
+ variant: "default" | "compact" (default: "default") — default = two-line layout with progress bar; compact = single-line layout
1851
+ showSize: boolean (default: true) — show file size (default variant only)
1852
+ className: string
1853
+ - Also exported: formatFileSize(bytes: number) => string — utility to format bytes as human-readable (B, KB, MB, GB)
1854
+ - Defaults: variant="default", showSize=true
1855
+ - Example:
1856
+ <UploadProgress
1857
+ files={[
1858
+ { id: '1', name: 'report.pdf', size: 1048576, status: 'uploading', progress: 45 },
1859
+ { id: '2', name: 'photo.jpg', size: 204800, status: 'complete', previewUrl: '/thumb.jpg' },
1860
+ { id: '3', name: 'data.csv', size: 51200, status: 'error', error: 'Network timeout' },
1861
+ ]}
1862
+ onRemove={(id) => removeFile(id)}
1863
+ onRetry={(id) => retryUpload(id)}
1864
+ onDismissAll={() => clearAll()}
1865
+ />
1866
+ <UploadProgress files={uploads} variant="compact" onRemove={removeFile} />
1867
+ - Gotchas:
1868
+ - Returns null when files array is empty
1869
+ - Summary header shows completion count and error count
1870
+ - Border color changes based on state: green when all complete, red tint when all terminal with errors
1871
+ - File icon auto-detects image files by extension or previewUrl presence
1872
+ - Status 'processing' renders a spinner icon and indeterminate progress
1873
+ - Screen reader announcements via aria-live region for completion/error events
1874
+ - Staggered slide-up animation (30ms delay per file)
1875
+
1713
1876
  ---
1714
1877
 
1715
1878
  # SHELL COMPONENTS
@@ -1727,9 +1890,11 @@ Note: getFormFieldA11y() was removed in favor of useFormField() hook.
1727
1890
  extraGroups: CommandGroup[]
1728
1891
  onNavigate: (path: string) => void
1729
1892
  onSearch: (query: string) => void
1730
- searchResults: SearchResult[] — { id, title, snippet?, entityType, projectId?, metadata? }
1893
+ searchResults: SearchResult[]
1731
1894
  isSearching: boolean (shows loading state while search is in progress)
1732
1895
  onSearchResultSelect: (result: SearchResult) => void
1896
+ - Type: SearchResult = { id: string, title: string, snippet?: string, entityType: string, projectId?: string | null, metadata?: Record<string, unknown> }
1897
+ - Type: AppCommandPaletteUser = { name: string, role?: string }
1733
1898
  - Example:
1734
1899
  <AppCommandPalette
1735
1900
  user={{ name: 'John', role: 'admin' }}
@@ -1749,7 +1914,10 @@ Note: getFormFieldA11y() was removed in favor of useFormField() hook.
1749
1914
  logo: ReactNode
1750
1915
  footerLinks: Array<{ label: string, href: string }>
1751
1916
  className: string
1752
- - NavItem shape: { title, href, icon: ReactNode, exact?: boolean }
1917
+ - Type: NavItem = { title: string, href: string, icon: ReactNode, exact?: boolean, badge?: string | number, children?: NavSubItem[], defaultOpen?: boolean }
1918
+ - Type: NavSubItem = { title: string, href: string, icon?: ReactNode, exact?: boolean }
1919
+ - Type: NavGroup = { label: string, items: NavItem[], action?: ReactNode }
1920
+ - Type: SidebarUser = { name: string, email?: string, image?: string | null, designation?: string, role?: string }
1753
1921
  - Example:
1754
1922
  <AppSidebar
1755
1923
  currentPath="/dashboard"
@@ -1867,10 +2035,12 @@ Deprecated props:
1867
2035
  - Server-safe: No
1868
2036
  - Props:
1869
2037
  currentPath: string
1870
- user: BottomNavbarUser | null (optional) — { name, role? }. Not required to render.
1871
- primaryItems: BottomNavItem[] (max 4 recommended) — { title, href, icon, exact?, badge?: number }
2038
+ user: BottomNavbarUser | null (optional). Not required to render.
2039
+ primaryItems: BottomNavItem[] (max 4 recommended)
1872
2040
  moreItems: BottomNavItem[] (overflow items in "More" menu)
1873
2041
  className: string
2042
+ - Type: BottomNavItem = { title: string, href: string, icon: ReactNode, exact?: boolean, badge?: number }
2043
+ - Type: BottomNavbarUser = { name: string, role?: string }
1874
2044
  - Example:
1875
2045
  <BottomNavbar
1876
2046
  currentPath="/dashboard"
@@ -1899,8 +2069,9 @@ Deprecated props:
1899
2069
  - Import: @devalok/shilp-sutra/shell/notification-center
1900
2070
  - Server-safe: No
1901
2071
  - Props:
1902
- notifications: Notification[] — { id, title, body?, tier: "INFO"|"IMPORTANT"|"CRITICAL", isRead, createdAt, entityType?, entityId?, projectId?, project?, actions?: NotificationAction[] }
1903
- NotificationAction: { label: string, variant?: "primary"|"default"|"danger", onClick: (id: string) => void }
2072
+ notifications: Notification[]
2073
+ - Type: Notification = { id: string, title: string, body?: string | null, tier: 'INFO' | 'IMPORTANT' | 'CRITICAL', isRead: boolean, createdAt: string, entityType?: string | null, entityId?: string | null, projectId?: string | null, project?: { title: string } | null, actions?: NotificationAction[] }
2074
+ - Type: NotificationAction = { label: string, variant?: 'primary' | 'default' | 'danger', onClick: (id: string) => void }
1904
2075
  unreadCount: number (derived from notifications if not provided)
1905
2076
  open: boolean (controlled mode)
1906
2077
  onOpenChange: (open: boolean) => void
@@ -1934,8 +2105,10 @@ Deprecated props:
1934
2105
  - Import: @devalok/shilp-sutra/shell/notification-preferences
1935
2106
  - Server-safe: No
1936
2107
  - Props:
1937
- preferences: NotificationPreference[] — { id, userId?, projectId, channel, minTier, muted }
1938
- projects: NotificationProject[] — { id, title }
2108
+ preferences: NotificationPreference[]
2109
+ projects: NotificationProject[]
2110
+ - Type: NotificationPreference = { id: string, userId?: string, projectId: string | null, channel: string, minTier: string, muted: boolean }
2111
+ - Type: NotificationProject = { id: string, title: string }
1939
2112
  isLoading: boolean
1940
2113
  onSave: (preference: { projectId, channel, minTier, muted }) => void | Promise<void>
1941
2114
  onToggleMute: (preference: NotificationPreference) => void | Promise<void>
@@ -1966,7 +2139,8 @@ Deprecated props:
1966
2139
  notificationSlot: ReactNode (render NotificationCenter here)
1967
2140
  userMenuItems?: UserMenuItem[] — custom items between Profile and Dark/Light Mode toggle
1968
2141
  className: string
1969
- - UserMenuItem: { label: string, icon?: ReactNode, href?: string, onClick?: () => void, separator?: boolean, color?: string, badge?: string | boolean, disabled?: boolean }
2142
+ - Type: TopBarUser = { name: string, email?: string, image?: string | null }
2143
+ - Type: UserMenuItem = { label: string, icon?: ReactNode, href?: string, onClick?: () => void, separator?: boolean, color?: string, badge?: string | boolean, disabled?: boolean }
1970
2144
  href — navigates via onNavigate callback
1971
2145
  onClick — custom action (takes precedence over href)
1972
2146
  separator — renders a DropdownMenuSeparator before this item
@@ -2047,3 +2221,10 @@ Deprecated props:
2047
2221
  ## motion / duration / easings / durations
2048
2222
  - Import: @devalok/shilp-sutra (barrel export)
2049
2223
  - Design system motion tokens for custom animations
2224
+ - Type: MotionMode = 'productive' | 'expressive'
2225
+ - Type: MotionCategory = 'standard' | 'entrance' | 'exit'
2226
+ - Type: DurationToken = 'instant' | 'fast-01' | 'fast-02' | 'moderate-01' | 'moderate-02' | 'slow-01' | 'slow-02'
2227
+ - Function: motion(category: MotionCategory, mode: MotionMode) => string — returns CSS cubic-bezier string
2228
+ - Function: duration(token: DurationToken) => string — returns CSS duration string (e.g. '150ms')
2229
+ - Object: easings — Record<MotionMode, Record<MotionCategory, string>> — all cubic-bezier values
2230
+ - Object: durations — Record<DurationToken, string> — { instant: '0ms', fast-01: '70ms', fast-02: '110ms', moderate-01: '150ms', moderate-02: '240ms', slow-01: '400ms', slow-02: '700ms' }
package/llms.txt CHANGED
@@ -108,7 +108,7 @@ Components with two-axis system: Button, Badge, Alert, Chip, Toast, Banner, Prog
108
108
  ### Inputs & Controls
109
109
  - Button: variant(solid|default|outline|ghost|link) color(default|error) size(sm|md|lg|icon) + loading, startIcon, endIcon, asChild
110
110
  - IconButton: icon(ReactNode, required) shape(square|circle) size(sm|md|lg) + aria-label required
111
- - Input: size(sm|md|lg) state(default|error|warning|success) + startIcon, endIcon
111
+ - Input: size(sm|md|lg) state(InputState) + startIcon, endIcon. Type: InputState = 'default' | 'error' | 'warning' | 'success'
112
112
  - SearchInput: size(sm|md|lg) + loading, onClear
113
113
  - NumberInput: value + onValueChange, min, max, step (controlled only)
114
114
  - Textarea: size(sm|md|lg) state(default|error|warning|success)
@@ -119,7 +119,7 @@ Components with two-axis system: Button, Badge, Alert, Chip, Toast, Banner, Prog
119
119
  - Select > SelectTrigger(size) > SelectValue; SelectContent > SelectItem(value)
120
120
  - Toggle: variant(default|outline) size(sm|md|lg)
121
121
  - ToggleGroup > ToggleGroupItem (variant/size propagate from root)
122
- - SegmentedControl > SegmentedControlItem: variant(filled|tonal) size(sm|md|lg)
122
+ - SegmentedControl > SegmentedControlItem: variant(filled|tonal) size(sm|md|lg). Types: SegmentedControlOption = { id, text, icon? }, SegmentedControlSize = 'sm' | 'md' | 'lg', SegmentedControlVariant = 'filled' | 'tonal'
123
123
  - Slider: standard Radix slider
124
124
 
125
125
  ### Feedback & Notifications
@@ -137,10 +137,10 @@ NOTIFICATION SELECTION GUIDE:
137
137
  ### Data Display
138
138
  - Badge: variant(subtle|solid|outline) color(default|info|success|error|warning|brand|accent + 7 category colors) size(xs|sm|md|lg) + onDismiss
139
139
  - Chip: variant(subtle|outline) color(default|primary|success|error|warning|info + 7 category) size(sm|md|lg) label(string, REQUIRED) + onDismiss, onClick
140
- - Avatar: size(xs|sm|md|lg|xl) shape(circle|square|rounded) status(online|offline|busy|away) > AvatarImage + AvatarFallback
140
+ - Avatar: size(xs|sm|md|lg|xl) shape(circle|square|rounded) status(AvatarStatus) > AvatarImage + AvatarFallback. Type: AvatarStatus = 'online' | 'offline' | 'busy' | 'away'
141
141
  - Card: variant(default|elevated|outline|flat) interactive(boolean) > CardHeader > CardTitle, CardDescription; CardContent; CardFooter
142
142
  - Table > TableHeader > TableRow > TableHead; TableBody > TableRow > TableCell; TableFooter; TableCaption
143
- - Text: 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) as(element)
143
+ - Text: variant(TextVariant) as(element). Type: TextVariant = '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'
144
144
  - Code: variant(inline|block)
145
145
  - Skeleton: variant(rectangle|circle|text) animation(pulse|shimmer|none)
146
146
  - StatCard: title, value, description, trend, icon
@@ -171,7 +171,7 @@ NOTIFICATION SELECTION GUIDE:
171
171
  - Sidebar: complex — see llms-full.txt for complete tree
172
172
 
173
173
  ### Form Pattern
174
- - FormField: state(helper|error|warning|success) > Label + Input + FormHelperText
174
+ - FormField: state(FormHelperState) > Label + Input + FormHelperText. Type: FormHelperState = 'helper' | 'error' | 'warning' | 'success'
175
175
  - useFormField() hook returns { state, helperTextId, required } from FormField context
176
176
  - Wire accessibility: const { state, helperTextId } = useFormField(); then aria-describedby={helperTextId}, aria-invalid={state === 'error'}
177
177
  - Input/Textarea auto-wire from FormField context (no manual hookup needed). Explicit props override.
@@ -183,18 +183,27 @@ NOTIFICATION SELECTION GUIDE:
183
183
  - StatusBadge: DISCRIMINATED UNION — pass status OR color, not both. status(active|pending|approved|rejected|completed|blocked|cancelled|draft) color(success|warning|error|info|neutral) size(sm|md)
184
184
  - ContentCard: variant(default|outline|ghost) padding(default|compact|spacious|none)
185
185
  - EmptyState: icon(ReactNode or ComponentType), title(required), description, action(ReactNode), compact
186
- - PriorityIndicator: priority(LOW|MEDIUM|HIGH|URGENT) display(compact|full)
186
+ - PriorityIndicator: priority(Priority) display(compact|full). Type: Priority = 'LOW' | 'MEDIUM' | 'HIGH' | 'URGENT' | 'low' | 'medium' | 'high' | 'urgent'
187
187
  - SimpleTooltip: wraps Tooltip compound into single component
188
- - DatePicker, DateRangePicker, DateTimePicker, TimePicker
188
+ - DatePicker, DateRangePicker, DateTimePicker
189
+ - TimePicker: standalone time selector — value(Date|null), onChange, format('12h'|'24h'), minuteStep, showSeconds, disabled
190
+ - CalendarGrid: low-level calendar widget — currentMonth, selected, rangeStart/End, onSelect, onMonthChange, events(CalendarEvent[])
191
+ - YearPicker: decade year grid — currentYear, selectedYear, onYearSelect, minDate, maxDate
192
+ - MonthPicker: month grid — currentYear, selectedMonth(0-11), onMonthSelect, minDate, maxDate
193
+ - Presets: date range quick-select buttons — presets(PresetKey[]), onSelect(start, end). Keys: today, yesterday, last7days, last30days, thisMonth, lastMonth, thisYear
194
+ - useCalendar: hook for calendar month state — returns currentMonth, goToPreviousMonth, goToNextMonth, goToMonth, goToYear
195
+ - UploadProgress: file upload tracker — files(UploadFile[]), onRemove, onRetry, onDismissAll, variant(default|compact), showSize. Status: pending|uploading|processing|complete|error. Also exports formatFileSize()
189
196
  - RichTextEditor: Tiptap editor — bold/italic/underline/strike/highlight, headings, blockquote, lists (bullet/ordered/task), code, links, images (paste/drop/upload), file attachments, @mentions, emoji picker + :shortcode:, text alignment, HR. Props: onImageUpload?, onFileUpload?, mentions?, onMentionSearch?, onMentionSelect?
190
197
  - RichTextViewer: read-only renderer for RichTextEditor HTML content (renders all above content types)
198
+ - ActivityFeed: items(ActivityItem[]), onLoadMore, loading, hasMore, emptyState, compact, maxInitialItems. Type: ActivityItem = { id, actor?, action, timestamp, icon?, color?, detail? }
191
199
  - CommandPalette, MemberPicker
192
200
  - ErrorDisplay, GlobalLoading
193
- - Loading skeletons: CardSkeleton, TableSkeleton, BoardSkeleton, ListSkeleton, DashboardSkeleton, etc.
201
+ - Loading skeletons: CardSkeleton, TableSkeleton, BoardSkeleton, ListSkeleton
202
+ - Page skeletons: DashboardSkeleton, ProjectListSkeleton, TaskDetailSkeleton (no props, server-safe)
194
203
 
195
204
  ### Shell Components (app-level layout)
196
- - TopBar: pageTitle, user, onNavigate, onLogout, notificationSlot, mobileLogo, userMenuItems?(UserMenuItem[] custom items between Profile and theme toggle, supports icon, href, onClick, separator, badge, disabled, color)
197
- - AppSidebar: navigation tree with NavItem[], NavGroup[]
205
+ - TopBar: pageTitle, user, onNavigate, onLogout, notificationSlot, mobileLogo, userMenuItems?(UserMenuItem[]). Types: TopBarUser = { name, email?, image? }, UserMenuItem = { label, icon?, href?, onClick?, separator?, color?, badge?, disabled? }
206
+ - AppSidebar: navigation tree with NavItem[], NavGroup[]. Types: NavItem = { title, href, icon, exact?, badge?, children?, defaultOpen? }, NavSubItem = { title, href, icon?, exact? }, NavGroup = { label, items, action? }, SidebarUser = { name, email?, image?, designation?, role? }
198
207
 
199
208
  ### AppSidebar (v0.10.0 additions)
200
209
  - NavItem.children?: NavSubItem[] — collapsible sub-list with chevron toggle
@@ -206,14 +215,20 @@ NOTIFICATION SELECTION GUIDE:
206
215
  - preFooterSlot?: ReactNode — content between navigation and footer
207
216
  - renderItem?: (item, defaultRender) => ReactNode | null — custom item rendering
208
217
 
209
- - BottomNavbar: mobile navigation, user is optional (not required to render)
210
- - NotificationCenter: notifications[], onMarkRead, onMarkAllRead, onNavigate, getNotificationRoute?(returns string|null, fallback null), footerSlot?, emptyState?, headerActions?, popoverClassName?, onDismiss?(id). Notification.actions?: NotificationAction[] for inline buttons (e.g. Approve/Deny)
211
- - AppCommandPalette: user, isAdmin, onNavigate, onSearch, searchResults, isSearching, onSearchResultSelect
218
+ - BottomNavbar: mobile navigation, user is optional. Types: BottomNavItem = { title, href, icon, exact?, badge? }, BottomNavbarUser = { name, role? }
219
+ - NotificationCenter: notifications[], onMarkRead, onMarkAllRead, onNavigate, getNotificationRoute?, footerSlot?, emptyState?, headerActions?, popoverClassName?, onDismiss?(id). Types: Notification = { id, title, body?, tier, isRead, createdAt, entityType?, entityId?, projectId?, project?, actions? }, NotificationAction = { label, variant?, onClick }
220
+ - NotificationPreferences: preferences[], projects[], onSave, onToggleMute, onUpdateTier, onDelete. Types: NotificationPreference = { id, userId?, projectId, channel, minTier, muted }, NotificationProject = { id, title }
221
+ - AppCommandPalette: user, isAdmin, onNavigate, onSearch, searchResults, isSearching, onSearchResultSelect. Types: SearchResult = { id, title, snippet?, entityType, projectId?, metadata? }, AppCommandPaletteUser = { name, role? }
222
+ - LinkProvider: wraps app with router-agnostic Link component — component(ForwardRefComponent), children. useLink() hook returns the Link component.
223
+
224
+ ### Motion Tokens
225
+ - motion(category, mode) => CSS cubic-bezier string. duration(token) => CSS duration string. easings: Record<MotionMode, Record<MotionCategory, string>>. durations: Record<DurationToken, string> = { instant: '0ms', fast-01: '70ms', fast-02: '110ms', moderate-01: '150ms', moderate-02: '240ms', slow-01: '400ms', slow-02: '700ms' }
212
226
 
213
227
  ### Hooks
214
- - useToast(): returns { toast, toasts, dismiss } — toast({ title, description, color })
228
+ - useToast(): returns { toast, toasts, dismiss } — toast({ title, description, color }). Also: import { toast } from '@devalok/shilp-sutra/hooks/use-toast' for imperative usage without hook
215
229
  - useColorMode(): returns { colorMode, setColorMode, toggleColorMode }
216
230
  - useMobile(): returns boolean (true if viewport < 768px)
231
+ - useLink(): returns router-agnostic Link component from LinkProvider context (shell/link-context)
217
232
 
218
233
  ## Server-Safe Components (no "use client")
219
234
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@devalok/shilp-sutra",
3
- "version": "0.16.1",
3
+ "version": "0.17.1",
4
4
  "description": "Devalok Design System — tokens, components, and patterns for Next.js",
5
5
  "license": "MIT",
6
6
  "type": "module",