@siyahi/ui 0.1.9-next.17 → 0.1.9-next.18
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/README.md +77 -207
- package/agents/examples/accordion.tsx +39 -0
- package/agents/examples/alert.tsx +25 -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 +30 -0
- package/agents/examples/bar-list.tsx +24 -0
- package/agents/examples/breadcrumb.tsx +28 -0
- package/agents/examples/button.tsx +21 -0
- package/agents/examples/calendar.tsx +15 -0
- package/agents/examples/card.tsx +34 -0
- package/agents/examples/chart-area.tsx +38 -0
- package/agents/examples/chart-bar-flows.tsx +31 -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 +70 -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-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 +50 -0
- package/agents/examples/heatmap-status.tsx +51 -0
- package/agents/examples/heatmap-weekday.tsx +53 -0
- package/agents/examples/input.tsx +17 -0
- package/agents/examples/local-time.tsx +14 -0
- package/agents/examples/metric-strip.tsx +40 -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/scroll-area.tsx +20 -0
- package/agents/examples/section-cards-select.tsx +27 -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 +115 -0
- package/agents/examples/table.tsx +50 -0
- package/agents/examples/tabs.tsx +16 -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 +87 -0
- package/agents/manifest.json +931 -0
- package/agents/report-rules.json +7 -0
- package/dist/components/registry/charts/bar-chart-card.js +1 -0
- package/dist/components/registry/dashboardcn/bar-list.js +8 -7
- package/dist/components/registry/heatmap/calendar-heatmap.js +8 -1
- package/dist/components/registry/heatmap/status-heatmap.js +8 -1
- package/dist/components/registry/heatmap/weekday-heatmap.d.ts +6 -2
- package/dist/components/registry/heatmap/weekday-heatmap.js +56 -37
- package/dist/components/registry/niko-table/core/data-table-context.d.ts +2 -0
- package/dist/components/registry/niko-table/core/data-table-context.js +21 -0
- package/dist/components/registry/niko-table/core/data-table-grouped-row.js +3 -1
- package/dist/components/registry/niko-table/core/data-table-structure.js +15 -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-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 +2 -2
- package/dist/components/registry/sidebar-07/nav-main.js +7 -1
- package/dist/components/siyahi/treemap.js +46 -4
- package/dist/lib/chart-frame.js +180 -124
- package/dist/lib/chart-series.d.ts +1 -1
- package/dist/lib/format.js +32 -13
- package/dist/lib/heatmap-navigation.d.ts +8 -0
- package/dist/lib/heatmap-navigation.js +49 -0
- package/package.json +3 -1
- package/styles/siyahi.css +24 -0
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Card,
|
|
3
|
+
CardContent,
|
|
4
|
+
CardDescription,
|
|
5
|
+
CardHeader,
|
|
6
|
+
CardTitle,
|
|
7
|
+
DataBar,
|
|
8
|
+
DataTable,
|
|
9
|
+
DataTableBody,
|
|
10
|
+
DataTableHeader,
|
|
11
|
+
DataTableRoot,
|
|
12
|
+
DataTableSortHeader,
|
|
13
|
+
Sparkline,
|
|
14
|
+
useFormat,
|
|
15
|
+
type DataTableColumnDef,
|
|
16
|
+
} from "@siyahi/ui";
|
|
17
|
+
|
|
18
|
+
interface Holding {
|
|
19
|
+
symbol: string;
|
|
20
|
+
value: number;
|
|
21
|
+
gain: number;
|
|
22
|
+
/** Closing prices over the last ten sessions, oldest first. */
|
|
23
|
+
closes: number[];
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const holdings: Holding[] = [
|
|
27
|
+
{ symbol: "ITC", value: 276000, gain: -18400, closes: [478, 474, 470, 471, 466, 463, 465, 461, 459, 460] },
|
|
28
|
+
{
|
|
29
|
+
symbol: "HDFCBANK",
|
|
30
|
+
value: 262500,
|
|
31
|
+
gain: 21300,
|
|
32
|
+
closes: [1712, 1718, 1726, 1720, 1731, 1744, 1739, 1748, 1752, 1750],
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
symbol: "ICICIBANK",
|
|
36
|
+
value: 258000,
|
|
37
|
+
gain: 44100,
|
|
38
|
+
closes: [1250, 1262, 1258, 1271, 1266, 1280, 1284, 1279, 1288, 1290],
|
|
39
|
+
},
|
|
40
|
+
{ symbol: "NIFTYBEES", value: 255600, gain: 12800, closes: [281, 282, 280, 283, 284, 283, 285, 284, 286, 284] },
|
|
41
|
+
{ symbol: "INFY", value: 224400, gain: 30900, closes: [1820, 1834, 1829, 1841, 1852, 1848, 1860, 1855, 1866, 1870] },
|
|
42
|
+
{ symbol: "TCS", value: 166000, gain: -2600, closes: [4180, 4172, 4165, 4170, 4158, 4150, 4161, 4155, 4148, 4150] },
|
|
43
|
+
];
|
|
44
|
+
|
|
45
|
+
// Each bar is a share of its column's largest figure, so the rows draw to one scale.
|
|
46
|
+
const largestValue = Math.max(...holdings.map((h) => h.value));
|
|
47
|
+
const largestGain = Math.max(...holdings.map((h) => Math.abs(h.gain)));
|
|
48
|
+
|
|
49
|
+
function Money({ amount }: { amount: number }) {
|
|
50
|
+
return useFormat().money(amount);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const header = () => <DataTableSortHeader />;
|
|
54
|
+
|
|
55
|
+
const columns: DataTableColumnDef<Holding>[] = [
|
|
56
|
+
{ accessorKey: "symbol", header, size: 120, meta: { label: "Symbol" } },
|
|
57
|
+
{
|
|
58
|
+
accessorKey: "value",
|
|
59
|
+
header,
|
|
60
|
+
size: 170,
|
|
61
|
+
meta: { label: "Value", variant: "number" },
|
|
62
|
+
cell: ({ row }) => (
|
|
63
|
+
<DataBar value={row.original.value} max={largestValue}>
|
|
64
|
+
<Money amount={row.original.value} />
|
|
65
|
+
</DataBar>
|
|
66
|
+
),
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
accessorKey: "gain",
|
|
70
|
+
header,
|
|
71
|
+
size: 170,
|
|
72
|
+
meta: { label: "Gain", variant: "number" },
|
|
73
|
+
cell: ({ row }) => (
|
|
74
|
+
<DataBar value={row.original.gain} max={largestGain} tone="sign">
|
|
75
|
+
<Money amount={row.original.gain} />
|
|
76
|
+
</DataBar>
|
|
77
|
+
),
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
id: "closes",
|
|
81
|
+
header: "10 days",
|
|
82
|
+
size: 110,
|
|
83
|
+
enableSorting: false,
|
|
84
|
+
cell: ({ row }) => {
|
|
85
|
+
const { closes } = row.original;
|
|
86
|
+
const rising = (closes.at(-1) ?? 0) >= (closes[0] ?? 0);
|
|
87
|
+
return (
|
|
88
|
+
<Sparkline
|
|
89
|
+
data={closes}
|
|
90
|
+
variant="line"
|
|
91
|
+
color={rising ? "var(--positive)" : "var(--negative)"}
|
|
92
|
+
animate={false}
|
|
93
|
+
className="h-5 w-20"
|
|
94
|
+
/>
|
|
95
|
+
);
|
|
96
|
+
},
|
|
97
|
+
},
|
|
98
|
+
];
|
|
99
|
+
|
|
100
|
+
export default function DataBarExample() {
|
|
101
|
+
return (
|
|
102
|
+
<Card>
|
|
103
|
+
<CardHeader>
|
|
104
|
+
<CardTitle>Holdings</CardTitle>
|
|
105
|
+
<CardDescription>Bars give each figure's size against the column's largest</CardDescription>
|
|
106
|
+
</CardHeader>
|
|
107
|
+
<CardContent>
|
|
108
|
+
<DataTableRoot
|
|
109
|
+
data={holdings}
|
|
110
|
+
columns={columns}
|
|
111
|
+
initialState={{ columnPinning: { start: ["symbol"], end: [] } }}
|
|
112
|
+
>
|
|
113
|
+
<DataTable>
|
|
114
|
+
<DataTableHeader />
|
|
115
|
+
<DataTableBody />
|
|
116
|
+
</DataTable>
|
|
117
|
+
</DataTableRoot>
|
|
118
|
+
</CardContent>
|
|
119
|
+
</Card>
|
|
120
|
+
);
|
|
121
|
+
}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Button,
|
|
3
|
+
Card,
|
|
4
|
+
CardContent,
|
|
5
|
+
CardDescription,
|
|
6
|
+
CardHeader,
|
|
7
|
+
CardTitle,
|
|
8
|
+
DataTable,
|
|
9
|
+
DataTableBody,
|
|
10
|
+
DataTableFacetedFilter,
|
|
11
|
+
DataTableHeader,
|
|
12
|
+
DataTableRoot,
|
|
13
|
+
DataTableRowCount,
|
|
14
|
+
DataTableSearchFilter,
|
|
15
|
+
DataTableSortHeader,
|
|
16
|
+
DataTableToolbarSection,
|
|
17
|
+
Sheet,
|
|
18
|
+
SheetClose,
|
|
19
|
+
SheetContent,
|
|
20
|
+
SheetDescription,
|
|
21
|
+
SheetFooter,
|
|
22
|
+
SheetHeader,
|
|
23
|
+
SheetTitle,
|
|
24
|
+
SheetTrigger,
|
|
25
|
+
useDataTable,
|
|
26
|
+
type DataTableColumnDef,
|
|
27
|
+
} from "@siyahi/ui";
|
|
28
|
+
|
|
29
|
+
interface Holding {
|
|
30
|
+
symbol: string;
|
|
31
|
+
sector: string;
|
|
32
|
+
broker: string;
|
|
33
|
+
}
|
|
34
|
+
const data: Holding[] = [
|
|
35
|
+
{ symbol: "INFY", sector: "Information technology", broker: "Kite" },
|
|
36
|
+
{ symbol: "HDFCBANK", sector: "Banks", broker: "Dhan" },
|
|
37
|
+
{ symbol: "TCS", sector: "Information technology", broker: "Dhan" },
|
|
38
|
+
];
|
|
39
|
+
const header = () => <DataTableSortHeader />;
|
|
40
|
+
const columns: DataTableColumnDef<Holding>[] = [
|
|
41
|
+
{ accessorKey: "symbol", header, size: 140, meta: { label: "Instrument" } },
|
|
42
|
+
{ accessorKey: "sector", header, meta: { label: "Sector" } },
|
|
43
|
+
{ accessorKey: "broker", header, meta: { label: "Broker" } },
|
|
44
|
+
];
|
|
45
|
+
|
|
46
|
+
function Filters() {
|
|
47
|
+
const { table, isCompact } = useDataTable<Holding>();
|
|
48
|
+
const active = table.state.columnFilters.length;
|
|
49
|
+
const controls = (
|
|
50
|
+
<div className="flex flex-wrap gap-2">
|
|
51
|
+
<DataTableFacetedFilter accessorKey="sector" title="Sector" />
|
|
52
|
+
<DataTableFacetedFilter accessorKey="broker" title="Broker" />
|
|
53
|
+
</div>
|
|
54
|
+
);
|
|
55
|
+
if (!isCompact) return controls;
|
|
56
|
+
return (
|
|
57
|
+
<Sheet>
|
|
58
|
+
<SheetTrigger render={<Button variant="outline" size="sm" />}>Filters{active ? ` (${active})` : ""}</SheetTrigger>
|
|
59
|
+
<SheetContent>
|
|
60
|
+
<SheetHeader>
|
|
61
|
+
<SheetTitle>Holding filters</SheetTitle>
|
|
62
|
+
<SheetDescription>Selections stay applied when this panel closes.</SheetDescription>
|
|
63
|
+
</SheetHeader>
|
|
64
|
+
<div className="overflow-y-auto px-4">{controls}</div>
|
|
65
|
+
<SheetFooter>
|
|
66
|
+
<Button variant="outline" disabled={!active} onClick={() => table.resetColumnFilters()}>
|
|
67
|
+
Clear filters
|
|
68
|
+
</Button>
|
|
69
|
+
<SheetClose render={<Button />}>Done</SheetClose>
|
|
70
|
+
</SheetFooter>
|
|
71
|
+
</SheetContent>
|
|
72
|
+
</Sheet>
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export default function DataTableFiltersExample() {
|
|
77
|
+
return (
|
|
78
|
+
<Card>
|
|
79
|
+
<CardHeader>
|
|
80
|
+
<CardTitle>Holdings with compact filters</CardTitle>
|
|
81
|
+
<CardDescription>Search stays visible; secondary filters move into a panel in narrow space.</CardDescription>
|
|
82
|
+
</CardHeader>
|
|
83
|
+
<CardContent>
|
|
84
|
+
{/* The closed filter panel is absent at first render, so configure its features explicitly. */}
|
|
85
|
+
<DataTableRoot data={data} columns={columns} config={{ enableFilters: true, enableSorting: true }}>
|
|
86
|
+
<DataTableToolbarSection>
|
|
87
|
+
<DataTableSearchFilter
|
|
88
|
+
label="Search compact example"
|
|
89
|
+
placeholder="Search holdings…"
|
|
90
|
+
className="w-full sm:w-64"
|
|
91
|
+
/>
|
|
92
|
+
<Filters />
|
|
93
|
+
<DataTableRowCount unit="holdings" />
|
|
94
|
+
</DataTableToolbarSection>
|
|
95
|
+
<DataTable aria-label="Holdings with compact filters">
|
|
96
|
+
<DataTableHeader />
|
|
97
|
+
<DataTableBody />
|
|
98
|
+
</DataTable>
|
|
99
|
+
</DataTableRoot>
|
|
100
|
+
</CardContent>
|
|
101
|
+
</Card>
|
|
102
|
+
);
|
|
103
|
+
}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Card,
|
|
3
|
+
CardContent,
|
|
4
|
+
CardDescription,
|
|
5
|
+
CardHeader,
|
|
6
|
+
CardTitle,
|
|
7
|
+
DataTable,
|
|
8
|
+
DataTableBody,
|
|
9
|
+
DataTableFacetedFilter,
|
|
10
|
+
DataTableHeader,
|
|
11
|
+
DataTableRoot,
|
|
12
|
+
DataTableRowCount,
|
|
13
|
+
DataTableSearchFilter,
|
|
14
|
+
DataTableSortHeader,
|
|
15
|
+
DataTableToolbarSection,
|
|
16
|
+
Delta,
|
|
17
|
+
useFormat,
|
|
18
|
+
type DataTableColumnDef,
|
|
19
|
+
} from "@siyahi/ui";
|
|
20
|
+
|
|
21
|
+
interface Holding {
|
|
22
|
+
symbol: string;
|
|
23
|
+
sector: string;
|
|
24
|
+
quantity: number;
|
|
25
|
+
value: number;
|
|
26
|
+
change: number;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const holdings: Holding[] = [
|
|
30
|
+
{ symbol: "INFY", sector: "IT", quantity: 120, value: 224400, change: 0.018 },
|
|
31
|
+
{ symbol: "HDFCBANK", sector: "Banks", quantity: 150, value: 262500, change: -0.006 },
|
|
32
|
+
{ symbol: "TCS", sector: "IT", quantity: 40, value: 166000, change: 0.004 },
|
|
33
|
+
{ symbol: "ITC", sector: "Fast Moving Consumer Goods", quantity: 600, value: 276000, change: -0.012 },
|
|
34
|
+
{ symbol: "ICICIBANK", sector: "Banks", quantity: 200, value: 258000, change: 0.022 },
|
|
35
|
+
{ symbol: "NIFTYBEES", sector: "ETF", quantity: 900, value: 255600, change: 0.009 },
|
|
36
|
+
];
|
|
37
|
+
|
|
38
|
+
// One click sorts largest (or Z) first; the title comes from meta.label.
|
|
39
|
+
const header = () => <DataTableSortHeader />;
|
|
40
|
+
|
|
41
|
+
function Money({ amount }: { amount: number }) {
|
|
42
|
+
return useFormat().money(amount);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const columns: DataTableColumnDef<Holding>[] = [
|
|
46
|
+
{ accessorKey: "symbol", header, size: 140, meta: { label: "Symbol" } },
|
|
47
|
+
{ accessorKey: "sector", header, size: 120, meta: { label: "Sector" } },
|
|
48
|
+
{ accessorKey: "quantity", header, size: 100, meta: { label: "Qty", variant: "number" } },
|
|
49
|
+
{
|
|
50
|
+
accessorKey: "value",
|
|
51
|
+
header,
|
|
52
|
+
size: 140,
|
|
53
|
+
meta: { label: "Value", variant: "number" },
|
|
54
|
+
cell: ({ row }) => <Money amount={row.original.value} />,
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
accessorKey: "change",
|
|
58
|
+
header,
|
|
59
|
+
size: 110,
|
|
60
|
+
meta: { label: "Day", variant: "number" },
|
|
61
|
+
cell: ({ row }) => <Delta value={row.original.change} />,
|
|
62
|
+
},
|
|
63
|
+
];
|
|
64
|
+
|
|
65
|
+
export default function DataTableExample() {
|
|
66
|
+
return (
|
|
67
|
+
<Card>
|
|
68
|
+
<CardHeader>
|
|
69
|
+
<CardTitle>Holdings</CardTitle>
|
|
70
|
+
<CardDescription>
|
|
71
|
+
Search every column, filter by sector, sort any column in one click; the symbol stays put when the table
|
|
72
|
+
scrolls in wide panels. In narrow panels every column scrolls, leaving room for the values
|
|
73
|
+
</CardDescription>
|
|
74
|
+
</CardHeader>
|
|
75
|
+
<CardContent>
|
|
76
|
+
<DataTableRoot
|
|
77
|
+
data={holdings}
|
|
78
|
+
columns={columns}
|
|
79
|
+
initialState={{ columnPinning: { start: ["symbol"], end: [] } }}
|
|
80
|
+
>
|
|
81
|
+
<DataTableToolbarSection>
|
|
82
|
+
<DataTableSearchFilter
|
|
83
|
+
placeholder="Search holdings…"
|
|
84
|
+
label="Filter holdings"
|
|
85
|
+
className="flex-none sm:w-64"
|
|
86
|
+
/>
|
|
87
|
+
<DataTableFacetedFilter accessorKey="sector" title="Sector" />
|
|
88
|
+
<DataTableRowCount unit="holdings" />
|
|
89
|
+
</DataTableToolbarSection>
|
|
90
|
+
<DataTable aria-label="Holdings">
|
|
91
|
+
<DataTableHeader />
|
|
92
|
+
<DataTableBody />
|
|
93
|
+
</DataTable>
|
|
94
|
+
</DataTableRoot>
|
|
95
|
+
</CardContent>
|
|
96
|
+
</Card>
|
|
97
|
+
);
|
|
98
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { Delta } from "@siyahi/ui";
|
|
2
|
+
|
|
3
|
+
export default function DeltaExample() {
|
|
4
|
+
return (
|
|
5
|
+
<div className="flex flex-wrap items-center gap-6">
|
|
6
|
+
<Delta value={0.0234} />
|
|
7
|
+
<Delta value={-0.0108} />
|
|
8
|
+
<Delta value={0} />
|
|
9
|
+
<Delta value={18250} format="money" />
|
|
10
|
+
<Delta value={-4300} format="money" />
|
|
11
|
+
</div>
|
|
12
|
+
);
|
|
13
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Button,
|
|
3
|
+
Dialog,
|
|
4
|
+
DialogClose,
|
|
5
|
+
DialogContent,
|
|
6
|
+
DialogDescription,
|
|
7
|
+
DialogFooter,
|
|
8
|
+
DialogHeader,
|
|
9
|
+
DialogTitle,
|
|
10
|
+
DialogTrigger,
|
|
11
|
+
} from "@siyahi/ui";
|
|
12
|
+
|
|
13
|
+
export default function DialogExample() {
|
|
14
|
+
return (
|
|
15
|
+
<Dialog>
|
|
16
|
+
<DialogTrigger render={<Button variant="outline" className="w-fit" />}>Show fills</DialogTrigger>
|
|
17
|
+
<DialogContent>
|
|
18
|
+
<DialogHeader>
|
|
19
|
+
<DialogTitle>Fills for this sell</DialogTitle>
|
|
20
|
+
<DialogDescription>Each lot this sale was matched against, oldest first.</DialogDescription>
|
|
21
|
+
</DialogHeader>
|
|
22
|
+
<DialogFooter>
|
|
23
|
+
<DialogClose render={<Button variant="outline" />}>Close</DialogClose>
|
|
24
|
+
</DialogFooter>
|
|
25
|
+
</DialogContent>
|
|
26
|
+
</Dialog>
|
|
27
|
+
);
|
|
28
|
+
}
|
|
@@ -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,50 @@
|
|
|
1
|
+
import { useState } from "react";
|
|
2
|
+
import {
|
|
3
|
+
CalendarHeatmap,
|
|
4
|
+
CalendarHeatmapBlock,
|
|
5
|
+
CalendarHeatmapBody,
|
|
6
|
+
useFormat,
|
|
7
|
+
Card,
|
|
8
|
+
CardContent,
|
|
9
|
+
CardDescription,
|
|
10
|
+
CardHeader,
|
|
11
|
+
CardTitle,
|
|
12
|
+
} from "@siyahi/ui";
|
|
13
|
+
|
|
14
|
+
// A year of trading days with a count of trades on each.
|
|
15
|
+
const data = Array.from({ length: 365 }, (_, i) => {
|
|
16
|
+
const day = new Date(Date.UTC(2025, 9, 20 + i));
|
|
17
|
+
const weekday = day.getUTCDay();
|
|
18
|
+
const value = weekday === 0 || weekday === 6 ? 0 : (i * 7 + weekday * 3) % 6;
|
|
19
|
+
return { date: day.toISOString().slice(0, 10), value };
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
export default function HeatmapCalendarExample() {
|
|
23
|
+
const [selected, setSelected] = useState<string | null>(null);
|
|
24
|
+
const { count } = useFormat();
|
|
25
|
+
return (
|
|
26
|
+
<Card>
|
|
27
|
+
<CardHeader>
|
|
28
|
+
<CardTitle>Trades per day</CardTitle>
|
|
29
|
+
<CardDescription>Counts use a single-hue fade of --chart-1</CardDescription>
|
|
30
|
+
</CardHeader>
|
|
31
|
+
<CardContent>
|
|
32
|
+
<CalendarHeatmap data={data}>
|
|
33
|
+
<CalendarHeatmapBody>
|
|
34
|
+
{({ activity, dayIndex, weekIndex }) => (
|
|
35
|
+
<CalendarHeatmapBlock
|
|
36
|
+
activity={activity}
|
|
37
|
+
dayIndex={dayIndex}
|
|
38
|
+
weekIndex={weekIndex}
|
|
39
|
+
onCellClick={(cell) => setSelected(`${cell.date}: ${count(cell.value)} trades`)}
|
|
40
|
+
/>
|
|
41
|
+
)}
|
|
42
|
+
</CalendarHeatmapBody>
|
|
43
|
+
</CalendarHeatmap>
|
|
44
|
+
<p aria-live="polite" className="mt-3 text-sm">
|
|
45
|
+
{selected ?? "Select a day for its trade count. Use arrow keys to move between days."}
|
|
46
|
+
</p>
|
|
47
|
+
</CardContent>
|
|
48
|
+
</Card>
|
|
49
|
+
);
|
|
50
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { useState } from "react";
|
|
2
|
+
import {
|
|
3
|
+
Card,
|
|
4
|
+
CardContent,
|
|
5
|
+
CardDescription,
|
|
6
|
+
CardHeader,
|
|
7
|
+
CardTitle,
|
|
8
|
+
StatusHeatmap,
|
|
9
|
+
StatusHeatmapBlock,
|
|
10
|
+
StatusHeatmapBody,
|
|
11
|
+
} from "@siyahi/ui";
|
|
12
|
+
|
|
13
|
+
// One cell per day for the last 60 days, as a status the heatmap colours:
|
|
14
|
+
// 3 healthy (--positive), 2 degraded (--warning), 1 critical (--negative).
|
|
15
|
+
const data = Array.from({ length: 60 }, (_, i) => {
|
|
16
|
+
const day = new Date(Date.UTC(2026, 7, 22 + i));
|
|
17
|
+
return { date: day.toISOString().slice(0, 10), value: i % 17 === 5 ? 1 : i % 11 === 3 ? 2 : 3 };
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
export default function HeatmapStatusExample() {
|
|
21
|
+
const [selected, setSelected] = useState<string | null>(null);
|
|
22
|
+
|
|
23
|
+
return (
|
|
24
|
+
<Card>
|
|
25
|
+
<CardHeader>
|
|
26
|
+
<CardTitle>Price feed</CardTitle>
|
|
27
|
+
<CardDescription>Daily health: healthy, degraded or critical</CardDescription>
|
|
28
|
+
</CardHeader>
|
|
29
|
+
<CardContent>
|
|
30
|
+
<StatusHeatmap data={data}>
|
|
31
|
+
<StatusHeatmapBody>
|
|
32
|
+
{({ activity, dayIndex }) => (
|
|
33
|
+
<StatusHeatmapBlock
|
|
34
|
+
activity={activity}
|
|
35
|
+
dayIndex={dayIndex}
|
|
36
|
+
onCellClick={(cell) =>
|
|
37
|
+
setSelected(
|
|
38
|
+
`${cell.date}: ${["unknown", "critical", "degraded", "healthy"][cell.value] ?? "unknown"}`,
|
|
39
|
+
)
|
|
40
|
+
}
|
|
41
|
+
/>
|
|
42
|
+
)}
|
|
43
|
+
</StatusHeatmapBody>
|
|
44
|
+
</StatusHeatmap>
|
|
45
|
+
<p aria-live="polite" className="mt-3 text-sm">
|
|
46
|
+
{selected ?? "Select a day for its health. Use arrow keys to move between days."}
|
|
47
|
+
</p>
|
|
48
|
+
</CardContent>
|
|
49
|
+
</Card>
|
|
50
|
+
);
|
|
51
|
+
}
|