@siyahi/ui 0.1.9-next.2 → 0.1.9-next.21

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (196) hide show
  1. package/DESIGN.md +30 -0
  2. package/README.md +82 -176
  3. package/THIRD_PARTY_NOTICES.md +11 -11
  4. package/agents/examples/accordion.tsx +39 -0
  5. package/agents/examples/alert.tsx +23 -0
  6. package/agents/examples/app-sidebar.tsx +38 -0
  7. package/agents/examples/avatar.tsx +14 -0
  8. package/agents/examples/badge.tsx +18 -0
  9. package/agents/examples/bar-list-labels.tsx +22 -0
  10. package/agents/examples/bar-list-sides.tsx +38 -0
  11. package/agents/examples/bar-list.tsx +24 -0
  12. package/agents/examples/breadcrumb.tsx +28 -0
  13. package/agents/examples/button-states.tsx +23 -0
  14. package/agents/examples/button.tsx +21 -0
  15. package/agents/examples/calendar.tsx +15 -0
  16. package/agents/examples/callout.tsx +33 -0
  17. package/agents/examples/card.tsx +34 -0
  18. package/agents/examples/change-bar.tsx +104 -0
  19. package/agents/examples/chart-area.tsx +39 -0
  20. package/agents/examples/chart-bar-flows.tsx +32 -0
  21. package/agents/examples/chart-bar-hourly.tsx +28 -0
  22. package/agents/examples/chart-bar-losses.tsx +29 -0
  23. package/agents/examples/chart-bar-sectors.tsx +72 -0
  24. package/agents/examples/chart-bar-years.tsx +40 -0
  25. package/agents/examples/chart-bar.tsx +56 -0
  26. package/agents/examples/chart-category-ticks.tsx +50 -0
  27. package/agents/examples/chart-line-events.tsx +47 -0
  28. package/agents/examples/chart-line-timeline.tsx +41 -0
  29. package/agents/examples/chart-line.tsx +57 -0
  30. package/agents/examples/code-block.tsx +38 -0
  31. package/agents/examples/collapsible.tsx +16 -0
  32. package/agents/examples/command.tsx +27 -0
  33. package/agents/examples/context-menu.tsx +15 -0
  34. package/agents/examples/copy-button.tsx +8 -0
  35. package/agents/examples/data-bar.tsx +121 -0
  36. package/agents/examples/data-table-compact.tsx +95 -0
  37. package/agents/examples/data-table-filters.tsx +103 -0
  38. package/agents/examples/data-table.tsx +98 -0
  39. package/agents/examples/delta.tsx +13 -0
  40. package/agents/examples/dialog.tsx +28 -0
  41. package/agents/examples/distribution-bar-budget.tsx +30 -0
  42. package/agents/examples/distribution-bar-tug.tsx +72 -0
  43. package/agents/examples/dropdown-menu.tsx +31 -0
  44. package/agents/examples/formatters.tsx +42 -0
  45. package/agents/examples/heatmap-calendar.tsx +60 -0
  46. package/agents/examples/heatmap-status.tsx +57 -0
  47. package/agents/examples/heatmap-weekday.tsx +75 -0
  48. package/agents/examples/input-states.tsx +33 -0
  49. package/agents/examples/input.tsx +17 -0
  50. package/agents/examples/local-time.tsx +14 -0
  51. package/agents/examples/metric-strip.tsx +46 -0
  52. package/agents/examples/overview-dashboard.tsx +18 -0
  53. package/agents/examples/page-hero.tsx +38 -0
  54. package/agents/examples/page-section.tsx +52 -0
  55. package/agents/examples/popover.tsx +23 -0
  56. package/agents/examples/price-chart.tsx +32 -0
  57. package/agents/examples/progress-tone.tsx +28 -0
  58. package/agents/examples/progress.tsx +10 -0
  59. package/agents/examples/range-position-table.tsx +101 -0
  60. package/agents/examples/range-position.tsx +53 -0
  61. package/agents/examples/scroll-area.tsx +20 -0
  62. package/agents/examples/section-cards-select.tsx +30 -0
  63. package/agents/examples/section-cards-trend.tsx +30 -0
  64. package/agents/examples/section-cards.tsx +48 -0
  65. package/agents/examples/select.tsx +27 -0
  66. package/agents/examples/separator.tsx +13 -0
  67. package/agents/examples/sheet.tsx +15 -0
  68. package/agents/examples/skeleton.tsx +11 -0
  69. package/agents/examples/slider.tsx +5 -0
  70. package/agents/examples/surfaces.tsx +116 -0
  71. package/agents/examples/table.tsx +50 -0
  72. package/agents/examples/tabs.tsx +16 -0
  73. package/agents/examples/target-rows.tsx +70 -0
  74. package/agents/examples/toggle-group.tsx +12 -0
  75. package/agents/examples/toggle.tsx +11 -0
  76. package/agents/examples/tooltip.tsx +13 -0
  77. package/agents/examples/treemap.tsx +88 -0
  78. package/agents/manifest.json +1081 -0
  79. package/agents/report-rules.json +7 -0
  80. package/dist/components/registry/charts/area-chart-card.js +25 -7
  81. package/dist/components/registry/charts/bar-chart-card.d.ts +0 -1
  82. package/dist/components/registry/charts/bar-chart-card.js +40 -26
  83. package/dist/components/registry/charts/line-chart-card.js +2 -2
  84. package/dist/components/registry/dashboard-01/section-cards.d.ts +2 -0
  85. package/dist/components/registry/dashboard-01/section-cards.js +42 -35
  86. package/dist/components/registry/dashboardcn/bar-list.js +18 -11
  87. package/dist/components/registry/dashboardcn/distribution-bar.js +1 -1
  88. package/dist/components/registry/dashboardcn/kpi-row-card.d.ts +29 -0
  89. package/dist/components/registry/dashboardcn/kpi-row-card.js +81 -0
  90. package/dist/components/registry/dashboardcn/sparkline.d.ts +22 -0
  91. package/dist/components/registry/dashboardcn/sparkline.js +117 -0
  92. package/dist/components/registry/heatmap/calendar-heatmap.d.ts +4 -2
  93. package/dist/components/registry/heatmap/calendar-heatmap.js +74 -33
  94. package/dist/components/registry/heatmap/status-heatmap.d.ts +5 -3
  95. package/dist/components/registry/heatmap/status-heatmap.js +37 -15
  96. package/dist/components/registry/heatmap/weekday-heatmap.d.ts +9 -3
  97. package/dist/components/registry/heatmap/weekday-heatmap.js +82 -47
  98. package/dist/components/registry/niko-table/core/data-table-context.d.ts +18 -1
  99. package/dist/components/registry/niko-table/core/data-table-context.js +7 -1
  100. package/dist/components/registry/niko-table/core/data-table-grouped-row.js +3 -1
  101. package/dist/components/registry/niko-table/core/data-table-root.js +39 -4
  102. package/dist/components/registry/niko-table/core/data-table-structure.js +17 -12
  103. package/dist/components/registry/niko-table/core/data-table.d.ts +2 -2
  104. package/dist/components/registry/niko-table/core/data-table.js +5 -1
  105. package/dist/components/registry/niko-table/filters/table-faceted-filter.js +4 -4
  106. package/dist/components/registry/niko-table/filters/table-search-filter.js +1 -1
  107. package/dist/components/registry/niko-table/lib/flex-columns.d.ts +1 -1
  108. package/dist/components/registry/niko-table/lib/flex-columns.js +3 -3
  109. package/dist/components/registry/niko-table/lib/styles.d.ts +1 -1
  110. package/dist/components/registry/niko-table/lib/styles.js +3 -3
  111. package/dist/components/registry/niko-table/types/index.d.ts +9 -0
  112. package/dist/components/registry/niko-table/ui/table.js +2 -2
  113. package/dist/components/registry/sidebar-07/app-sidebar.d.ts +1 -1
  114. package/dist/components/registry/sidebar-07/app-sidebar.js +3 -1
  115. package/dist/components/registry/sidebar-07/nav-main.d.ts +2 -1
  116. package/dist/components/registry/sidebar-07/nav-main.js +16 -3
  117. package/dist/components/siyahi/callout.d.ts +24 -0
  118. package/dist/components/siyahi/callout.js +40 -0
  119. package/dist/components/siyahi/change-bar.d.ts +21 -0
  120. package/dist/components/siyahi/change-bar.js +35 -0
  121. package/dist/components/siyahi/data-bar.d.ts +19 -0
  122. package/dist/components/siyahi/data-bar.js +29 -0
  123. package/dist/components/siyahi/data-table-compact-columns-toggle.d.ts +4 -0
  124. package/dist/components/siyahi/data-table-compact-columns-toggle.js +19 -0
  125. package/dist/components/siyahi/data-table-footnote.d.ts +12 -0
  126. package/dist/components/siyahi/data-table-footnote.js +35 -0
  127. package/dist/components/siyahi/default-sidebar-open.d.ts +8 -0
  128. package/dist/components/siyahi/default-sidebar-open.js +13 -0
  129. package/dist/components/siyahi/page-hero.d.ts +25 -0
  130. package/dist/components/siyahi/page-hero.js +41 -0
  131. package/dist/components/siyahi/page-section.d.ts +4 -1
  132. package/dist/components/siyahi/page-section.js +6 -3
  133. package/dist/components/siyahi/page-toc.d.ts +20 -0
  134. package/dist/components/siyahi/page-toc.js +76 -0
  135. package/dist/components/siyahi/panel-grid.d.ts +22 -0
  136. package/dist/components/siyahi/panel-grid.js +34 -0
  137. package/dist/components/siyahi/quote.d.ts +12 -0
  138. package/dist/components/siyahi/quote.js +23 -0
  139. package/dist/components/siyahi/range-position.d.ts +24 -0
  140. package/dist/components/siyahi/range-position.js +105 -0
  141. package/dist/components/siyahi/siyahi-provider.d.ts +13 -2
  142. package/dist/components/siyahi/siyahi-provider.js +17 -4
  143. package/dist/components/siyahi/target-rows.d.ts +34 -0
  144. package/dist/components/siyahi/target-rows.js +97 -0
  145. package/dist/components/siyahi/treemap.d.ts +4 -1
  146. package/dist/components/siyahi/treemap.js +78 -28
  147. package/dist/components/siyahi/warning-alert.d.ts +10 -0
  148. package/dist/components/siyahi/warning-alert.js +20 -0
  149. package/dist/components/ui/accordion.js +5 -5
  150. package/dist/components/ui/alert.js +2 -2
  151. package/dist/components/ui/badge.js +1 -1
  152. package/dist/components/ui/breadcrumb.js +2 -2
  153. package/dist/components/ui/button.js +10 -10
  154. package/dist/components/ui/calendar.js +1 -1
  155. package/dist/components/ui/card.js +4 -4
  156. package/dist/components/ui/chart.js +1 -1
  157. package/dist/components/ui/code-block.js +13 -12
  158. package/dist/components/ui/command.js +7 -6
  159. package/dist/components/ui/context-menu.js +7 -7
  160. package/dist/components/ui/dialog.js +5 -5
  161. package/dist/components/ui/dropdown-menu.js +8 -8
  162. package/dist/components/ui/input-group.js +11 -11
  163. package/dist/components/ui/input.js +1 -1
  164. package/dist/components/ui/popover.js +3 -3
  165. package/dist/components/ui/progress.js +1 -1
  166. package/dist/components/ui/select.js +6 -6
  167. package/dist/components/ui/sheet.js +5 -5
  168. package/dist/components/ui/sidebar.js +17 -18
  169. package/dist/components/ui/skeleton.js +1 -1
  170. package/dist/components/ui/slider.js +2 -2
  171. package/dist/components/ui/table.js +2 -2
  172. package/dist/components/ui/tabs.js +2 -2
  173. package/dist/components/ui/textarea.js +1 -1
  174. package/dist/components/ui/toggle-group.js +2 -2
  175. package/dist/components/ui/toggle.js +4 -4
  176. package/dist/components/ui/tooltip.js +2 -2
  177. package/dist/index.d.ts +21 -7
  178. package/dist/index.js +18 -4
  179. package/dist/lib/chart-frame.d.ts +25 -7
  180. package/dist/lib/chart-frame.js +251 -127
  181. package/dist/lib/chart-series.d.ts +12 -6
  182. package/dist/lib/compact-columns.d.ts +19 -0
  183. package/dist/lib/compact-columns.js +55 -0
  184. package/dist/lib/format.d.ts +7 -2
  185. package/dist/lib/format.js +54 -16
  186. package/dist/lib/heatmap-navigation.d.ts +8 -0
  187. package/dist/lib/heatmap-navigation.js +49 -0
  188. package/dist/lib/treemap.js +6 -1
  189. package/dist/lib/use-element-width.d.ts +5 -0
  190. package/dist/lib/use-element-width.js +15 -0
  191. package/package.json +4 -1
  192. package/registry.lock.json +16 -0
  193. package/report.css +33 -32
  194. package/styles/siyahi.css +221 -39
  195. package/dist/lib/recharts.d.ts +0 -2
  196. package/dist/lib/recharts.js +0 -1
@@ -0,0 +1,30 @@
1
+ import { Card, CardContent, CardDescription, CardHeader, CardTitle, DistributionBar } from "@siyahi/ui";
2
+
3
+ // Where a memory budget sits, on a fixed 0 to 12 GiB scale; made-up figures.
4
+ const gib = (value: number) => `${value.toFixed(1)} GiB`;
5
+
6
+ export default function DistributionBarBudgetExample() {
7
+ return (
8
+ <Card>
9
+ <CardHeader>
10
+ <CardTitle>The agents group's budget</CardTitle>
11
+ <CardDescription>0 to 12 GiB; the lines mark its limits</CardDescription>
12
+ </CardHeader>
13
+ <CardContent>
14
+ <DistributionBar
15
+ max={12}
16
+ valueFormatter={gib}
17
+ markers={[
18
+ { at: 6, label: "High" },
19
+ { at: 8.7, label: "Max" },
20
+ ]}
21
+ segments={[
22
+ { name: "In RAM, under High", value: 6, color: "var(--chart-1)" },
23
+ { name: "In RAM, over High", value: 1.4, color: "var(--chart-2)" },
24
+ { name: "Pushed to zram", value: 2.6, color: "var(--chart-3)" },
25
+ ]}
26
+ />
27
+ </CardContent>
28
+ </Card>
29
+ );
30
+ }
@@ -0,0 +1,72 @@
1
+ import { useState } from "react";
2
+ import {
3
+ Card,
4
+ CardContent,
5
+ CardDescription,
6
+ CardFooter,
7
+ CardHeader,
8
+ CardTitle,
9
+ Delta,
10
+ DistributionBar,
11
+ type DistributionSegment,
12
+ } from "@siyahi/ui";
13
+
14
+ // Each closed position's net realized, ₹; made-up figures.
15
+ const positions = [
16
+ { name: "INFY", value: 61000 },
17
+ { name: "TCS", value: 38000 },
18
+ { name: "HDFCBANK", value: 17000 },
19
+ { name: "ITC", value: -26000 },
20
+ { name: "WIPRO", value: -14000 },
21
+ { name: "SBIN", value: -6000 },
22
+ ];
23
+ const won = positions.filter((p) => p.value > 0).reduce((sum, p) => sum + p.value, 0);
24
+ const lost = positions.filter((p) => p.value < 0).reduce((sum, p) => sum + p.value, 0);
25
+ // One scale both ways, so whichever side reaches farther is the net.
26
+ const reach = Math.max(won, -lost);
27
+
28
+ export default function DistributionBarTugExample() {
29
+ const [focus, setFocus] = useState<DistributionSegment | null>(null);
30
+ return (
31
+ <Card>
32
+ <CardHeader>
33
+ <CardTitle>The tug-of-war</CardTitle>
34
+ <CardDescription>Each closed position, losses left of break-even and gains right</CardDescription>
35
+ </CardHeader>
36
+ <CardContent className="flex flex-col gap-2">
37
+ <DistributionBar
38
+ min={-reach}
39
+ max={reach}
40
+ showLegend={false}
41
+ markers={[{ at: 0 }]}
42
+ highlighted={focus?.name ?? null}
43
+ onHover={setFocus}
44
+ segments={positions.map(({ name, value }) => ({
45
+ name,
46
+ value,
47
+ color: value > 0 ? "var(--positive)" : "var(--negative)",
48
+ }))}
49
+ />
50
+ <div className="grid grid-cols-3 text-xs text-muted-foreground tabular-nums">
51
+ <span>
52
+ losers <Delta value={lost} format="money" />
53
+ </span>
54
+ <span className="text-center">break-even</span>
55
+ <span className="text-right">
56
+ winners <Delta value={won} format="money" />
57
+ </span>
58
+ </div>
59
+ </CardContent>
60
+ <CardFooter className="min-h-12" aria-live="polite">
61
+ {focus ? (
62
+ <p className="flex items-center gap-2">
63
+ <span className="font-medium">{focus.name}</span>
64
+ <Delta value={focus.value} format="money" />
65
+ </p>
66
+ ) : (
67
+ <p className="text-muted-foreground">Hover a band to see its position.</p>
68
+ )}
69
+ </CardFooter>
70
+ </Card>
71
+ );
72
+ }
@@ -0,0 +1,31 @@
1
+ import {
2
+ Button,
3
+ DropdownMenu,
4
+ DropdownMenuContent,
5
+ DropdownMenuGroup,
6
+ DropdownMenuItem,
7
+ DropdownMenuLabel,
8
+ DropdownMenuSeparator,
9
+ DropdownMenuTrigger,
10
+ } from "@siyahi/ui";
11
+ import { EllipsisIcon } from "lucide-react";
12
+
13
+ export default function DropdownMenuExample() {
14
+ return (
15
+ <DropdownMenu>
16
+ <DropdownMenuTrigger render={<Button variant="outline" className="w-fit" />}>
17
+ <EllipsisIcon />
18
+ Actions
19
+ </DropdownMenuTrigger>
20
+ <DropdownMenuContent>
21
+ <DropdownMenuGroup>
22
+ <DropdownMenuLabel>Holding</DropdownMenuLabel>
23
+ <DropdownMenuItem>View lots</DropdownMenuItem>
24
+ <DropdownMenuItem>Export CSV</DropdownMenuItem>
25
+ </DropdownMenuGroup>
26
+ <DropdownMenuSeparator />
27
+ <DropdownMenuItem variant="destructive">Hide</DropdownMenuItem>
28
+ </DropdownMenuContent>
29
+ </DropdownMenu>
30
+ );
31
+ }
@@ -0,0 +1,42 @@
1
+ import { SiyahiProvider, Table, TableBody, TableCell, TableHead, TableHeader, TableRow, useFormat } from "@siyahi/ui";
2
+
3
+ function Row({ label }: { label: string }) {
4
+ const { count, money, moneyCompact, pct, time } = useFormat();
5
+ return (
6
+ <TableRow>
7
+ <TableCell>{label}</TableCell>
8
+ <TableCell className="text-right">{count(153110)}</TableCell>
9
+ <TableCell className="text-right">{money(153110)}</TableCell>
10
+ <TableCell className="text-right">{moneyCompact(99999)}</TableCell>
11
+ <TableCell className="text-right">{moneyCompact(240000)}</TableCell>
12
+ <TableCell className="text-right">{moneyCompact(400000000)}</TableCell>
13
+ <TableCell className="text-right">{pct(0.1234)}</TableCell>
14
+ <TableCell className="text-right">{time("2026-10-20T09:00:00Z")}</TableCell>
15
+ </TableRow>
16
+ );
17
+ }
18
+
19
+ export default function FormattersExample() {
20
+ return (
21
+ <Table>
22
+ <TableHeader>
23
+ <TableRow>
24
+ <TableHead>Locale</TableHead>
25
+ <TableHead className="text-right">count</TableHead>
26
+ <TableHead className="text-right">money</TableHead>
27
+ <TableHead className="text-right">moneyCompact</TableHead>
28
+ <TableHead className="text-right">moneyCompact</TableHead>
29
+ <TableHead className="text-right">moneyCompact</TableHead>
30
+ <TableHead className="text-right">pct</TableHead>
31
+ <TableHead className="text-right">time</TableHead>
32
+ </TableRow>
33
+ </TableHeader>
34
+ <TableBody>
35
+ <Row label="Default (en-IN, INR, Kolkata)" />
36
+ <SiyahiProvider locale={{ numberLocale: "en-US", currency: "USD", timeZone: "America/New_York" }}>
37
+ <Row label="en-US, USD, New York" />
38
+ </SiyahiProvider>
39
+ </TableBody>
40
+ </Table>
41
+ );
42
+ }
@@ -0,0 +1,60 @@
1
+ import { useState } from "react";
2
+ import {
3
+ CalendarHeatmap,
4
+ CalendarHeatmapBlock,
5
+ CalendarHeatmapBody,
6
+ CalendarHeatmapFooter,
7
+ CalendarHeatmapLegend,
8
+ useFormat,
9
+ Card,
10
+ CardContent,
11
+ CardDescription,
12
+ CardHeader,
13
+ CardTitle,
14
+ } from "@siyahi/ui";
15
+
16
+ // A year of trading days with a count of trades on each.
17
+ const data = Array.from({ length: 365 }, (_, i) => {
18
+ const day = new Date(Date.UTC(2025, 9, 20 + i));
19
+ const weekday = day.getUTCDay();
20
+ const value = weekday === 0 || weekday === 6 ? 0 : (i * 7 + weekday * 3) % 6;
21
+ return { date: day.toISOString().slice(0, 10), value };
22
+ });
23
+ const busiest = Math.max(...data.map((day) => day.value));
24
+
25
+ export default function HeatmapCalendarExample() {
26
+ // The page owns the pick: a second tap on the same day clears it.
27
+ const [picked, setPicked] = useState<(typeof data)[number] | null>(null);
28
+ const { count } = useFormat();
29
+ return (
30
+ <Card>
31
+ <CardHeader>
32
+ <CardTitle>Trades per day</CardTitle>
33
+ <CardDescription>The last 12 months; counts use a single-hue fade of --chart-1</CardDescription>
34
+ </CardHeader>
35
+ <CardContent>
36
+ <CalendarHeatmap data={data} labels={{ heatmapLabel: "Trades per day over the last 12 months" }}>
37
+ <CalendarHeatmapBody>
38
+ {({ activity, dayIndex, weekIndex }) => (
39
+ <CalendarHeatmapBlock
40
+ activity={activity}
41
+ dayIndex={dayIndex}
42
+ weekIndex={weekIndex}
43
+ selected={activity.date === picked?.date}
44
+ onCellClick={(cell) => setPicked(cell.date === picked?.date ? null : cell)}
45
+ />
46
+ )}
47
+ </CalendarHeatmapBody>
48
+ <CalendarHeatmapFooter className="items-center justify-between gap-x-6 gap-y-2 whitespace-normal">
49
+ <p aria-live="polite" className="text-sm">
50
+ {picked
51
+ ? `${picked.date}: ${count(picked.value)} trades`
52
+ : "Select a day for its trade count. Use arrow keys to move between days."}
53
+ </p>
54
+ <CalendarHeatmapLegend labels={{ less: "0 trades", more: `${count(busiest)} trades` }} />
55
+ </CalendarHeatmapFooter>
56
+ </CalendarHeatmap>
57
+ </CardContent>
58
+ </Card>
59
+ );
60
+ }
@@ -0,0 +1,57 @@
1
+ import { useState } from "react";
2
+ import {
3
+ Card,
4
+ CardContent,
5
+ CardDescription,
6
+ CardHeader,
7
+ CardTitle,
8
+ StatusHeatmap,
9
+ StatusHeatmapBlock,
10
+ StatusHeatmapBody,
11
+ StatusHeatmapFooter,
12
+ StatusHeatmapLegend,
13
+ } from "@siyahi/ui";
14
+
15
+ // One cell per day for the last 60 days, as a status the heatmap colours:
16
+ // 3 healthy (quiet grey), 2 degraded (--warning), 1 critical (--destructive).
17
+ const data = Array.from({ length: 60 }, (_, i) => {
18
+ const day = new Date(Date.UTC(2026, 7, 22 + i));
19
+ return { date: day.toISOString().slice(0, 10), value: i % 17 === 5 ? 1 : i % 11 === 3 ? 2 : 3 };
20
+ });
21
+
22
+ export default function HeatmapStatusExample() {
23
+ // The page owns the pick: a second tap on the same day clears it.
24
+ const [picked, setPicked] = useState<(typeof data)[number] | null>(null);
25
+
26
+ return (
27
+ <Card>
28
+ <CardHeader>
29
+ <CardTitle>Price feed</CardTitle>
30
+ <CardDescription>Daily health: healthy, degraded or critical</CardDescription>
31
+ </CardHeader>
32
+ <CardContent>
33
+ <StatusHeatmap data={data} statusValues={[3, 2, 1]}>
34
+ <StatusHeatmapBody>
35
+ {({ activity, dayIndex }) => (
36
+ <StatusHeatmapBlock
37
+ activity={activity}
38
+ dayIndex={dayIndex}
39
+ selected={activity.date === picked?.date}
40
+ onCellClick={(cell) => setPicked(cell.date === picked?.date ? null : cell)}
41
+ />
42
+ )}
43
+ </StatusHeatmapBody>
44
+ <StatusHeatmapFooter className="items-center justify-between gap-x-6 gap-y-2 whitespace-normal">
45
+ <p aria-live="polite" className="text-sm">
46
+ {picked
47
+ ? `${picked.date}: ${["unknown", "critical", "degraded", "healthy"][picked.value] ?? "unknown"}`
48
+ : "Select a day for its health. Use arrow keys to move between days."}
49
+ </p>
50
+ {/* No day lacks data, so the legend leaves out "No Data". */}
51
+ <StatusHeatmapLegend className="gap-3" />
52
+ </StatusHeatmapFooter>
53
+ </StatusHeatmap>
54
+ </CardContent>
55
+ </Card>
56
+ );
57
+ }
@@ -0,0 +1,75 @@
1
+ import { useState } from "react";
2
+ import {
3
+ useFormat,
4
+ Card,
5
+ CardContent,
6
+ CardDescription,
7
+ CardHeader,
8
+ CardTitle,
9
+ WeekdayHeatmap,
10
+ WeekdayHeatmapBlock,
11
+ WeekdayHeatmapBody,
12
+ WeekdayHeatmapLegend,
13
+ } from "@siyahi/ui";
14
+
15
+ // Orders placed by weekday and hour of the trading session.
16
+ const data = [1, 2, 3, 4, 5].flatMap((weekday) =>
17
+ [9, 10, 11, 12, 13, 14, 15].map((hour) => ({
18
+ weekday,
19
+ hour,
20
+ value: ((weekday * 5 + hour * 3) % 9) + (hour === 9 ? 6 : 0),
21
+ })),
22
+ );
23
+ const busiest = data.reduce((top, cell) => (cell.value > top.value ? cell : top));
24
+ const DAYS = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"];
25
+ const hourLabel = (hour: number) => `${String(hour).padStart(2, "0")}:00`;
26
+
27
+ export default function HeatmapWeekdayExample() {
28
+ // The page owns the pick: a second tap on the same cell clears it.
29
+ const [picked, setPicked] = useState<(typeof data)[number] | null>(null);
30
+ const isPicked = (cell: { weekday: number; hour: number }) =>
31
+ cell.weekday === picked?.weekday && cell.hour === picked.hour;
32
+ const { count } = useFormat();
33
+ return (
34
+ <Card>
35
+ <CardHeader>
36
+ <CardTitle>When orders are placed</CardTitle>
37
+ <CardDescription>Weekday by hour in India Standard Time, market hours only</CardDescription>
38
+ </CardHeader>
39
+ {/* Seven hours by five days is a small grid: capped, it keeps square cells, and the legend and the answer
40
+ sit beside it instead of a wide empty card. */}
41
+ <CardContent>
42
+ <WeekdayHeatmap
43
+ data={data}
44
+ hours={[9, 10, 11, 12, 13, 14, 15]}
45
+ weekdays={[1, 2, 3, 4, 5]}
46
+ className="gap-4 lg:flex-row lg:items-center lg:gap-8"
47
+ >
48
+ <WeekdayHeatmapBody className="lg:w-md lg:shrink-0">
49
+ {({ activity }) => (
50
+ <WeekdayHeatmapBlock
51
+ activity={activity}
52
+ selected={isPicked(activity)}
53
+ onCellClick={(cell) => setPicked(isPicked(cell) ? null : cell)}
54
+ />
55
+ )}
56
+ </WeekdayHeatmapBody>
57
+ <div className="flex flex-col items-start gap-3 text-sm">
58
+ <WeekdayHeatmapLegend
59
+ className="ml-0"
60
+ labels={{ less: "0 orders", more: `${count(busiest.value)} orders an hour` }}
61
+ />
62
+ <p>
63
+ Busiest: {DAYS[busiest.weekday]} at {hourLabel(busiest.hour)}, {count(busiest.value)} orders
64
+ </p>
65
+ <p aria-live="polite" className="text-muted-foreground">
66
+ {picked
67
+ ? `${DAYS[picked.weekday]}, ${hourLabel(picked.hour)} IST: ${count(picked.value)} orders`
68
+ : "Select a cell for its order count. Use arrow keys to move between cells."}
69
+ </p>
70
+ </div>
71
+ </WeekdayHeatmap>
72
+ </CardContent>
73
+ </Card>
74
+ );
75
+ }
@@ -0,0 +1,33 @@
1
+ import { Input } from "@siyahi/ui";
2
+
3
+ // A field is its label, the input and one line under it: a hint, or the error that replaces it.
4
+ export default function InputStatesExample() {
5
+ return (
6
+ <div className="grid max-w-sm gap-5">
7
+ <div className="grid gap-1.5">
8
+ <label htmlFor="budget" className="text-sm font-medium">
9
+ Monthly budget
10
+ </label>
11
+ <Input id="budget" inputMode="numeric" defaultValue="25,000" aria-describedby="budget-hint" />
12
+ <p id="budget-hint" className="text-sm text-muted-foreground">
13
+ In rupees, before brokerage
14
+ </p>
15
+ </div>
16
+ <div className="grid gap-1.5">
17
+ <label htmlFor="folio" className="text-sm font-medium">
18
+ Folio number
19
+ </label>
20
+ <Input id="folio" defaultValue="12/34" aria-invalid aria-describedby="folio-error" />
21
+ <p id="folio-error" className="text-sm text-destructive">
22
+ A folio is digits only, such as 1234567
23
+ </p>
24
+ </div>
25
+ <div className="grid gap-1.5">
26
+ <label htmlFor="broker" className="text-sm font-medium text-muted-foreground">
27
+ Broker
28
+ </label>
29
+ <Input id="broker" defaultValue="Kite" disabled />
30
+ </div>
31
+ </div>
32
+ );
33
+ }
@@ -0,0 +1,17 @@
1
+ import { Input, InputGroup, InputGroupAddon, InputGroupInput, Textarea } from "@siyahi/ui";
2
+ import { SearchIcon } from "lucide-react";
3
+
4
+ export default function InputExample() {
5
+ return (
6
+ <div className="flex max-w-sm flex-col gap-3">
7
+ <Input placeholder="Folio number" aria-label="Folio number" />
8
+ <InputGroup>
9
+ <InputGroupAddon>
10
+ <SearchIcon />
11
+ </InputGroupAddon>
12
+ <InputGroupInput placeholder="Search symbols" aria-label="Search symbols" />
13
+ </InputGroup>
14
+ <Textarea placeholder="Note for this trade" aria-label="Note" />
15
+ </div>
16
+ );
17
+ }
@@ -0,0 +1,14 @@
1
+ import { LocalTime } from "@siyahi/ui";
2
+
3
+ export default function LocalTimeExample() {
4
+ return (
5
+ <>
6
+ <p>
7
+ Prices as of <LocalTime value="2026-10-20T10:00:00Z" />. Hover the time to see it on your own clock.
8
+ </p>
9
+ <p>
10
+ Daily snapshot of <LocalTime value="2026-06-26T04:11:00Z" withDate />.
11
+ </p>
12
+ </>
13
+ );
14
+ }
@@ -0,0 +1,46 @@
1
+ import { MetricStrip, ToggleGroup, ToggleGroupItem, useFormat, type MetricStripItem } from "@siyahi/ui";
2
+
3
+ export default function MetricStripExample() {
4
+ const { money, moneyCompact, pct, count } = useFormat();
5
+ // trend is each figure's month-end run, oldest first.
6
+ const items: MetricStripItem[] = [
7
+ {
8
+ label: "Portfolio value",
9
+ value: money(4512300),
10
+ change: 0.031,
11
+ note: "vs Sep",
12
+ trend: [3820000, 3950000, 4110000, 4020000, 4380000, 4512300],
13
+ },
14
+ {
15
+ label: "Invested",
16
+ value: moneyCompact(4025000),
17
+ change: 85000,
18
+ changeFormat: "money",
19
+ trend: [3600000, 3685000, 3770000, 3855000, 3940000, 4025000],
20
+ },
21
+ { label: "XIRR", value: pct(0.142), change: -0.004, trend: [0.151, 0.149, 0.152, 0.144, 0.146, 0.142] },
22
+ {
23
+ label: "Realised",
24
+ value: moneyCompact(-61200),
25
+ change: -61200,
26
+ changeFormat: "money",
27
+ trend: [14200, -3800, 9100, 2600, -18400, -61200],
28
+ },
29
+ { label: "Dividends", value: moneyCompact(38400), change: 0.18, trend: [4100, 0, 12800, 3300, 9900, 8300] },
30
+ { label: "Holdings", value: count(42), note: "3 bought in Oct", trend: [38, 39, 39, 40, 39, 42] },
31
+ ];
32
+ return (
33
+ <MetricStrip
34
+ title="Portfolio up 3.1% in October"
35
+ description="Month-end figures with the six-month run behind each"
36
+ action={
37
+ <ToggleGroup defaultValue={["1M"]} variant="outline" size="sm" aria-label="Period">
38
+ <ToggleGroupItem value="1M">1M</ToggleGroupItem>
39
+ <ToggleGroupItem value="1Y">1Y</ToggleGroupItem>
40
+ </ToggleGroup>
41
+ }
42
+ items={items}
43
+ footer={<p className="text-muted-foreground">Valued at the last close, 31 Oct 2026</p>}
44
+ />
45
+ );
46
+ }
@@ -0,0 +1,18 @@
1
+ import ChartArea from "./chart-area";
2
+ import DataTableExample from "./data-table";
3
+ import MetricStrip from "./metric-strip";
4
+
5
+ // What a page built with Siyahi looks like: a metric strip, a chart card and a data table, each the catalogue's own example.
6
+ export default function OverviewDashboard() {
7
+ return (
8
+ <div className="flex flex-col gap-4">
9
+ <MetricStrip />
10
+ {/* Side by side only where the table's five columns fit beside the chart; at 1440 they would be cut off.
11
+ Each card keeps its own height: stretched to the taller one, the chart card ends in blank space. */}
12
+ <div className="grid items-start gap-4 2xl:grid-cols-2">
13
+ <ChartArea />
14
+ <DataTableExample />
15
+ </div>
16
+ </div>
17
+ );
18
+ }
@@ -0,0 +1,38 @@
1
+ import { Button, PageHero } from "@siyahi/ui";
2
+ import { ArrowRightIcon } from "lucide-react";
3
+
4
+ // The catalogue's own ink drop, painted with currentColor so the hero tints it.
5
+ function InkDrop() {
6
+ return (
7
+ <svg viewBox="0 0 16 16">
8
+ <path
9
+ fill="currentColor"
10
+ fillRule="evenodd"
11
+ d="M8 .5C8 .5 2.5 6.3 2.5 10a5.5 5.5 0 0 0 11 0C13.5 6.3 8 .5 8 .5ZM4.75 10A3.25 3.25 0 0 0 8 13.25v-1.5A1.75 1.75 0 0 1 6.25 10Z"
12
+ />
13
+ </svg>
14
+ );
15
+ }
16
+
17
+ export default function PageHeroExample() {
18
+ return (
19
+ <PageHero
20
+ eyebrow="@siyahi/ui"
21
+ title="Siyahi"
22
+ lead="Ink for numbers. A design system for pages people read figures from: portfolios, ledgers, research."
23
+ detail="Stock shadcn/ui on Base UI and Tailwind v4, with a finance theme, charts and tables that fit a phone, Indian-aware formatters and a lint. Siyahi is Hindi for ink."
24
+ actions={
25
+ <>
26
+ <Button nativeButton={false} render={<a href="#/data" />}>
27
+ Browse the data components
28
+ <ArrowRightIcon />
29
+ </Button>
30
+ <Button variant="outline" nativeButton={false} render={<a href="#/overview/install" />}>
31
+ Install
32
+ </Button>
33
+ </>
34
+ }
35
+ mark={<InkDrop />}
36
+ />
37
+ );
38
+ }
@@ -0,0 +1,52 @@
1
+ import {
2
+ Button,
3
+ Card,
4
+ CardContent,
5
+ CardHeader,
6
+ CardTitle,
7
+ DataList,
8
+ DataListItem,
9
+ InlineCode,
10
+ PageSection,
11
+ PageSubheading,
12
+ } from "@siyahi/ui";
13
+ import { ArrowDownIcon } from "lucide-react";
14
+
15
+ export default function PageSectionExample() {
16
+ return (
17
+ <PageSection
18
+ id="toolkit"
19
+ eyebrow="Part 2"
20
+ title="The toolkit"
21
+ lead={
22
+ <p>
23
+ One command, <InlineCode>vp</InlineCode>, builds, tests and lints; it replaces the separate config of each.
24
+ </p>
25
+ }
26
+ action={
27
+ <Button variant="outline" size="sm">
28
+ <ArrowDownIcon data-icon="inline-start" />
29
+ Skip to the commands
30
+ </Button>
31
+ }
32
+ >
33
+ <PageSubheading>What each tool does</PageSubheading>
34
+ <Card>
35
+ <CardHeader>
36
+ <CardTitle>Vite+</CardTitle>
37
+ </CardHeader>
38
+ <CardContent>
39
+ <DataList>
40
+ <DataListItem label="In Java terms">Maven, the compiler plugin and Surefire in one binary.</DataListItem>
41
+ <DataListItem label="How it is set up">
42
+ One <InlineCode>vite.config.ts</InlineCode> at the root of the repo.
43
+ </DataListItem>
44
+ <DataListItem label="Remember">
45
+ <InlineCode>vp check</InlineCode>
46
+ </DataListItem>
47
+ </DataList>
48
+ </CardContent>
49
+ </Card>
50
+ </PageSection>
51
+ );
52
+ }
@@ -0,0 +1,23 @@
1
+ import {
2
+ Button,
3
+ Popover,
4
+ PopoverContent,
5
+ PopoverDescription,
6
+ PopoverHeader,
7
+ PopoverTitle,
8
+ PopoverTrigger,
9
+ } from "@siyahi/ui";
10
+
11
+ export default function PopoverExample() {
12
+ return (
13
+ <Popover>
14
+ <PopoverTrigger render={<Button variant="outline" className="w-fit" />}>How is this priced?</PopoverTrigger>
15
+ <PopoverContent align="start">
16
+ <PopoverHeader>
17
+ <PopoverTitle>Last close</PopoverTitle>
18
+ <PopoverDescription>Holdings are valued at the exchange's last closing price.</PopoverDescription>
19
+ </PopoverHeader>
20
+ </PopoverContent>
21
+ </Popover>
22
+ );
23
+ }
@@ -0,0 +1,32 @@
1
+ import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, PriceChart } from "@siyahi/ui";
2
+
3
+ // A made-up daily close series for one year.
4
+ const data = Array.from({ length: 250 }, (_, i) => {
5
+ const day = new Date(Date.UTC(2025, 9, 20 + Math.floor((i * 7) / 5)));
6
+ return { time: day.toISOString().slice(0, 10), value: 1500 + i * 1.2 + Math.sin(i / 9) * 60 + Math.sin(i / 3) * 18 };
7
+ });
8
+
9
+ export default function PriceChartExample() {
10
+ return (
11
+ <Card>
12
+ <CardHeader>
13
+ <CardTitle>INFY</CardTitle>
14
+ <CardDescription>Daily close, one year</CardDescription>
15
+ </CardHeader>
16
+ <CardContent>
17
+ <PriceChart data={data} height={256} />
18
+ </CardContent>
19
+ <CardFooter className="text-xs text-muted-foreground">
20
+ <span>
21
+ Chart by{" "}
22
+ <a
23
+ className="text-link underline-offset-4 hover:underline"
24
+ href="https://www.tradingview.com/lightweight-charts/"
25
+ >
26
+ TradingView Lightweight Charts
27
+ </a>
28
+ </span>
29
+ </CardFooter>
30
+ </Card>
31
+ );
32
+ }