@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.
- package/DESIGN.md +30 -0
- package/README.md +82 -176
- package/THIRD_PARTY_NOTICES.md +11 -11
- package/agents/examples/accordion.tsx +39 -0
- package/agents/examples/alert.tsx +23 -0
- package/agents/examples/app-sidebar.tsx +38 -0
- package/agents/examples/avatar.tsx +14 -0
- package/agents/examples/badge.tsx +18 -0
- package/agents/examples/bar-list-labels.tsx +22 -0
- package/agents/examples/bar-list-sides.tsx +38 -0
- package/agents/examples/bar-list.tsx +24 -0
- package/agents/examples/breadcrumb.tsx +28 -0
- package/agents/examples/button-states.tsx +23 -0
- package/agents/examples/button.tsx +21 -0
- package/agents/examples/calendar.tsx +15 -0
- package/agents/examples/callout.tsx +33 -0
- package/agents/examples/card.tsx +34 -0
- package/agents/examples/change-bar.tsx +104 -0
- package/agents/examples/chart-area.tsx +39 -0
- package/agents/examples/chart-bar-flows.tsx +32 -0
- package/agents/examples/chart-bar-hourly.tsx +28 -0
- package/agents/examples/chart-bar-losses.tsx +29 -0
- package/agents/examples/chart-bar-sectors.tsx +72 -0
- package/agents/examples/chart-bar-years.tsx +40 -0
- package/agents/examples/chart-bar.tsx +56 -0
- package/agents/examples/chart-category-ticks.tsx +50 -0
- package/agents/examples/chart-line-events.tsx +47 -0
- package/agents/examples/chart-line-timeline.tsx +41 -0
- package/agents/examples/chart-line.tsx +57 -0
- package/agents/examples/code-block.tsx +38 -0
- package/agents/examples/collapsible.tsx +16 -0
- package/agents/examples/command.tsx +27 -0
- package/agents/examples/context-menu.tsx +15 -0
- package/agents/examples/copy-button.tsx +8 -0
- package/agents/examples/data-bar.tsx +121 -0
- package/agents/examples/data-table-compact.tsx +95 -0
- package/agents/examples/data-table-filters.tsx +103 -0
- package/agents/examples/data-table.tsx +98 -0
- package/agents/examples/delta.tsx +13 -0
- package/agents/examples/dialog.tsx +28 -0
- package/agents/examples/distribution-bar-budget.tsx +30 -0
- package/agents/examples/distribution-bar-tug.tsx +72 -0
- package/agents/examples/dropdown-menu.tsx +31 -0
- package/agents/examples/formatters.tsx +42 -0
- package/agents/examples/heatmap-calendar.tsx +60 -0
- package/agents/examples/heatmap-status.tsx +57 -0
- package/agents/examples/heatmap-weekday.tsx +75 -0
- package/agents/examples/input-states.tsx +33 -0
- package/agents/examples/input.tsx +17 -0
- package/agents/examples/local-time.tsx +14 -0
- package/agents/examples/metric-strip.tsx +46 -0
- package/agents/examples/overview-dashboard.tsx +18 -0
- package/agents/examples/page-hero.tsx +38 -0
- package/agents/examples/page-section.tsx +52 -0
- package/agents/examples/popover.tsx +23 -0
- package/agents/examples/price-chart.tsx +32 -0
- package/agents/examples/progress-tone.tsx +28 -0
- package/agents/examples/progress.tsx +10 -0
- package/agents/examples/range-position-table.tsx +101 -0
- package/agents/examples/range-position.tsx +53 -0
- package/agents/examples/scroll-area.tsx +20 -0
- package/agents/examples/section-cards-select.tsx +30 -0
- package/agents/examples/section-cards-trend.tsx +30 -0
- package/agents/examples/section-cards.tsx +48 -0
- package/agents/examples/select.tsx +27 -0
- package/agents/examples/separator.tsx +13 -0
- package/agents/examples/sheet.tsx +15 -0
- package/agents/examples/skeleton.tsx +11 -0
- package/agents/examples/slider.tsx +5 -0
- package/agents/examples/surfaces.tsx +116 -0
- package/agents/examples/table.tsx +50 -0
- package/agents/examples/tabs.tsx +16 -0
- package/agents/examples/target-rows.tsx +70 -0
- package/agents/examples/toggle-group.tsx +12 -0
- package/agents/examples/toggle.tsx +11 -0
- package/agents/examples/tooltip.tsx +13 -0
- package/agents/examples/treemap.tsx +88 -0
- package/agents/manifest.json +1081 -0
- package/agents/report-rules.json +7 -0
- package/dist/components/registry/charts/area-chart-card.js +25 -7
- package/dist/components/registry/charts/bar-chart-card.d.ts +0 -1
- package/dist/components/registry/charts/bar-chart-card.js +40 -26
- package/dist/components/registry/charts/line-chart-card.js +2 -2
- package/dist/components/registry/dashboard-01/section-cards.d.ts +2 -0
- package/dist/components/registry/dashboard-01/section-cards.js +42 -35
- package/dist/components/registry/dashboardcn/bar-list.js +18 -11
- package/dist/components/registry/dashboardcn/distribution-bar.js +1 -1
- package/dist/components/registry/dashboardcn/kpi-row-card.d.ts +29 -0
- package/dist/components/registry/dashboardcn/kpi-row-card.js +81 -0
- package/dist/components/registry/dashboardcn/sparkline.d.ts +22 -0
- package/dist/components/registry/dashboardcn/sparkline.js +117 -0
- package/dist/components/registry/heatmap/calendar-heatmap.d.ts +4 -2
- package/dist/components/registry/heatmap/calendar-heatmap.js +74 -33
- package/dist/components/registry/heatmap/status-heatmap.d.ts +5 -3
- package/dist/components/registry/heatmap/status-heatmap.js +37 -15
- package/dist/components/registry/heatmap/weekday-heatmap.d.ts +9 -3
- package/dist/components/registry/heatmap/weekday-heatmap.js +82 -47
- package/dist/components/registry/niko-table/core/data-table-context.d.ts +18 -1
- package/dist/components/registry/niko-table/core/data-table-context.js +7 -1
- package/dist/components/registry/niko-table/core/data-table-grouped-row.js +3 -1
- package/dist/components/registry/niko-table/core/data-table-root.js +39 -4
- package/dist/components/registry/niko-table/core/data-table-structure.js +17 -12
- package/dist/components/registry/niko-table/core/data-table.d.ts +2 -2
- package/dist/components/registry/niko-table/core/data-table.js +5 -1
- package/dist/components/registry/niko-table/filters/table-faceted-filter.js +4 -4
- package/dist/components/registry/niko-table/filters/table-search-filter.js +1 -1
- package/dist/components/registry/niko-table/lib/flex-columns.d.ts +1 -1
- package/dist/components/registry/niko-table/lib/flex-columns.js +3 -3
- package/dist/components/registry/niko-table/lib/styles.d.ts +1 -1
- package/dist/components/registry/niko-table/lib/styles.js +3 -3
- package/dist/components/registry/niko-table/types/index.d.ts +9 -0
- package/dist/components/registry/niko-table/ui/table.js +2 -2
- package/dist/components/registry/sidebar-07/app-sidebar.d.ts +1 -1
- package/dist/components/registry/sidebar-07/app-sidebar.js +3 -1
- package/dist/components/registry/sidebar-07/nav-main.d.ts +2 -1
- package/dist/components/registry/sidebar-07/nav-main.js +16 -3
- package/dist/components/siyahi/callout.d.ts +24 -0
- package/dist/components/siyahi/callout.js +40 -0
- package/dist/components/siyahi/change-bar.d.ts +21 -0
- package/dist/components/siyahi/change-bar.js +35 -0
- package/dist/components/siyahi/data-bar.d.ts +19 -0
- package/dist/components/siyahi/data-bar.js +29 -0
- package/dist/components/siyahi/data-table-compact-columns-toggle.d.ts +4 -0
- package/dist/components/siyahi/data-table-compact-columns-toggle.js +19 -0
- package/dist/components/siyahi/data-table-footnote.d.ts +12 -0
- package/dist/components/siyahi/data-table-footnote.js +35 -0
- package/dist/components/siyahi/default-sidebar-open.d.ts +8 -0
- package/dist/components/siyahi/default-sidebar-open.js +13 -0
- package/dist/components/siyahi/page-hero.d.ts +25 -0
- package/dist/components/siyahi/page-hero.js +41 -0
- package/dist/components/siyahi/page-section.d.ts +4 -1
- package/dist/components/siyahi/page-section.js +6 -3
- package/dist/components/siyahi/page-toc.d.ts +20 -0
- package/dist/components/siyahi/page-toc.js +76 -0
- package/dist/components/siyahi/panel-grid.d.ts +22 -0
- package/dist/components/siyahi/panel-grid.js +34 -0
- package/dist/components/siyahi/quote.d.ts +12 -0
- package/dist/components/siyahi/quote.js +23 -0
- package/dist/components/siyahi/range-position.d.ts +24 -0
- package/dist/components/siyahi/range-position.js +105 -0
- package/dist/components/siyahi/siyahi-provider.d.ts +13 -2
- package/dist/components/siyahi/siyahi-provider.js +17 -4
- package/dist/components/siyahi/target-rows.d.ts +34 -0
- package/dist/components/siyahi/target-rows.js +97 -0
- package/dist/components/siyahi/treemap.d.ts +4 -1
- package/dist/components/siyahi/treemap.js +78 -28
- package/dist/components/siyahi/warning-alert.d.ts +10 -0
- package/dist/components/siyahi/warning-alert.js +20 -0
- package/dist/components/ui/accordion.js +5 -5
- package/dist/components/ui/alert.js +2 -2
- package/dist/components/ui/badge.js +1 -1
- package/dist/components/ui/breadcrumb.js +2 -2
- package/dist/components/ui/button.js +10 -10
- package/dist/components/ui/calendar.js +1 -1
- package/dist/components/ui/card.js +4 -4
- package/dist/components/ui/chart.js +1 -1
- package/dist/components/ui/code-block.js +13 -12
- package/dist/components/ui/command.js +7 -6
- package/dist/components/ui/context-menu.js +7 -7
- package/dist/components/ui/dialog.js +5 -5
- package/dist/components/ui/dropdown-menu.js +8 -8
- package/dist/components/ui/input-group.js +11 -11
- package/dist/components/ui/input.js +1 -1
- package/dist/components/ui/popover.js +3 -3
- package/dist/components/ui/progress.js +1 -1
- package/dist/components/ui/select.js +6 -6
- package/dist/components/ui/sheet.js +5 -5
- package/dist/components/ui/sidebar.js +17 -18
- package/dist/components/ui/skeleton.js +1 -1
- package/dist/components/ui/slider.js +2 -2
- package/dist/components/ui/table.js +2 -2
- package/dist/components/ui/tabs.js +2 -2
- package/dist/components/ui/textarea.js +1 -1
- package/dist/components/ui/toggle-group.js +2 -2
- package/dist/components/ui/toggle.js +4 -4
- package/dist/components/ui/tooltip.js +2 -2
- package/dist/index.d.ts +21 -7
- package/dist/index.js +18 -4
- package/dist/lib/chart-frame.d.ts +25 -7
- package/dist/lib/chart-frame.js +251 -127
- package/dist/lib/chart-series.d.ts +12 -6
- package/dist/lib/compact-columns.d.ts +19 -0
- package/dist/lib/compact-columns.js +55 -0
- package/dist/lib/format.d.ts +7 -2
- package/dist/lib/format.js +54 -16
- package/dist/lib/heatmap-navigation.d.ts +8 -0
- package/dist/lib/heatmap-navigation.js +49 -0
- package/dist/lib/treemap.js +6 -1
- package/dist/lib/use-element-width.d.ts +5 -0
- package/dist/lib/use-element-width.js +15 -0
- package/package.json +4 -1
- package/registry.lock.json +16 -0
- package/report.css +33 -32
- package/styles/siyahi.css +221 -39
- package/dist/lib/recharts.d.ts +0 -2
- 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
|
+
}
|