@siyahi/ui 0.1.9-next.30 → 0.1.9-next.31
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 +81 -0
- package/agents/examples/bar-list-labels.tsx +1 -1
- package/agents/examples/card.tsx +2 -2
- package/agents/examples/change-grid.tsx +5 -4
- package/agents/examples/chart-bar-losses.tsx +1 -1
- package/agents/examples/chart-bar-teams.tsx +2 -2
- package/agents/examples/chart-bar-years.tsx +3 -1
- package/agents/examples/chart-bar.tsx +2 -2
- package/agents/examples/chart-line-events.tsx +1 -1
- package/agents/examples/chart-line-timeline.tsx +2 -2
- package/agents/examples/data-bar.tsx +9 -3
- package/agents/examples/data-table-compact.tsx +5 -8
- package/agents/examples/data-table-filters.tsx +4 -4
- package/agents/examples/data-table.tsx +2 -5
- package/agents/examples/distribution-bar-tug.tsx +1 -1
- package/agents/examples/heatmap-calendar.tsx +1 -1
- package/agents/examples/histogram-chart.tsx +1 -1
- package/agents/examples/metric-strip.tsx +1 -1
- package/agents/examples/overview-dashboard.tsx +2 -2
- package/agents/examples/range-position-table.tsx +26 -14
- package/agents/examples/target-rows.tsx +3 -3
- package/package.json +1 -1
package/DESIGN.md
CHANGED
|
@@ -100,6 +100,87 @@ An app page opens on its masthead, `PageHero`: a small mark and eyebrow in the
|
|
|
100
100
|
mono face and link colour, the title, one muted line saying what the page is,
|
|
101
101
|
the actions at the right, and a hairline under it. One per page; no hero card.
|
|
102
102
|
|
|
103
|
+
## Words
|
|
104
|
+
|
|
105
|
+
A page is read by someone who acts on its figures. Its words are plain English,
|
|
106
|
+
in sentence case, and explain themselves: a reader needs no other page to know
|
|
107
|
+
what a figure is, where it came from and what to do next. These rules bind what
|
|
108
|
+
a page's reader sees. The catalogue's own notes (the text that introduces a
|
|
109
|
+
component, its rules and its captions) teach the package and are exempt from
|
|
110
|
+
them, and so are the words in a demo that only name a component's variant or API
|
|
111
|
+
("Ghost", "moneyCompact") or show a bare state (a field disabled with no reason
|
|
112
|
+
given). Everything else in an example is bound like any app's copy, including
|
|
113
|
+
the error, empty, loading and busy text of a state demo. Each rule's example is
|
|
114
|
+
from the catalogue.
|
|
115
|
+
|
|
116
|
+
- **A title states the finding** when the block makes one point: "Search is
|
|
117
|
+
near its slowest of the month, login near its fastest". In an app, work its
|
|
118
|
+
figures out from the data the block draws, so it cannot go stale. When the
|
|
119
|
+
finding changes with the reader's choice (a toggle, a filter, a pick), the
|
|
120
|
+
title names the topic instead: "Revenue by channel and quarter". No full
|
|
121
|
+
stop, no pun or metaphor.
|
|
122
|
+
- **A description says what the figures are**: the measure, its scope and
|
|
123
|
+
period, how it was counted, whatever the title left out. A single fragment
|
|
124
|
+
takes no full stop: "Each month's revenue against its target". Once there are
|
|
125
|
+
two parts, each ends with one: "Each week's balance against the highest it
|
|
126
|
+
had been so far. At zero the bakery was at a new high." Page copy never names
|
|
127
|
+
a component, a token or a prop.
|
|
128
|
+
- **Explain what the reader cannot guess.** Say how to read a mark that is not
|
|
129
|
+
obvious ("Money in above the line, money out below it"), and define a term
|
|
130
|
+
on first use, in the description, a tooltip or a popover: "Churn counts
|
|
131
|
+
customers who did not renew this month."
|
|
132
|
+
- **Say where the figures come from and how fresh they are**: the system or
|
|
133
|
+
report they are read from, "From sar -r", and when, "Prices as of the last
|
|
134
|
+
market close."
|
|
135
|
+
- **Labels and column heads are short nouns, written in full.** The theme sets
|
|
136
|
+
them in small mono capitals, so write them in sentence case and never
|
|
137
|
+
abbreviate to fit: no "vs", "p95", "Qty". Name what the figure is, then its
|
|
138
|
+
period if the block has several: "Cash flow this year". A note on a plot or
|
|
139
|
+
beside a figure is lowercase: "new limit, 50%", "new this year".
|
|
140
|
+
- **Units.** A figure from the formatters carries its unit (₹2.4L, 14.2%,
|
|
141
|
+
14:30 IST). Any other unit is named once: after a label in brackets, "Hour of
|
|
142
|
+
day (IST)", or after a title with a comma, "Memory in use, GiB". Keep the
|
|
143
|
+
symbols readers know (%, ₹, ms, s, GiB); anything else is a word. A
|
|
144
|
+
difference between two percentages is in points, to one decimal, as no
|
|
145
|
+
formatter writes them: "Sales is 5.6 points under its budget".
|
|
146
|
+
- **Numbers** go through `useFormat()` and `Delta`. In the default locale that
|
|
147
|
+
is Indian grouping (1,53,110), compact rupees (₹3.1Cr) and amounts under a
|
|
148
|
+
lakh in full; a page under another locale keeps its forms. A figure on its
|
|
149
|
+
own (a cell, a tick, a tooltip) carries its sign, as Colour says; a sign you
|
|
150
|
+
write is `+` or the minus `−`, never a hyphen: "+₹21,300", "−₹18,400". In a
|
|
151
|
+
phrase a word may carry the direction instead: "5.6 points under". A range
|
|
152
|
+
takes an en dash, "0–12 GiB"; a multiple takes ×, "about 1.5× a month". A
|
|
153
|
+
missing figure is —, and a page that leaves items out says how many.
|
|
154
|
+
- **Dates and times** read as the formatters write them, in the default locale
|
|
155
|
+
day before month: "20 Oct 2026, 14:30 IST". Every block says its time zone
|
|
156
|
+
once, on its axis title, its description or each time: "Hour of day (IST)". A
|
|
157
|
+
period is in words, "Last 30 days", "April to March"; a financial year is
|
|
158
|
+
"FY26", with the block saying when it ends: "FY26 ends in March 2026". A
|
|
159
|
+
tooltip's heading says more than the tick under it: "Week ending 1 Nov 2026"
|
|
160
|
+
against "Nov".
|
|
161
|
+
- **A status names a state, not a task**, in a few words that match its tone:
|
|
162
|
+
"Paid", "Out for delivery", "Needs review".
|
|
163
|
+
- **The components write the chart and staleness notes**: FORECAST, TODAY and
|
|
164
|
+
"Stale · 3 Nov". The page passes the point or the date and nothing more, and
|
|
165
|
+
says in its description which stretch is a forecast: "November and December
|
|
166
|
+
are a forecast". It never writes a note of its own for the same thing.
|
|
167
|
+
- **A button or menu item says what happens**, as a verb, in one to three
|
|
168
|
+
words: "Export CSV", "Copy order number". A busy button keeps its label as
|
|
169
|
+
"Saving"; a finished one says so briefly, "Copied". An icon-only button names
|
|
170
|
+
its action in `aria-label`: "Refresh". A toggle names its options in words
|
|
171
|
+
("Cumulative growth", "Last year"); only a range toggle in a card's header
|
|
172
|
+
shortens them, to 1M, 6M, 1Y and All. A control disabled for a reason the
|
|
173
|
+
page does not show says why beside it, as a chart's Log button does.
|
|
174
|
+
- **Empty, loading and error states say what happened and what to do.** Empty
|
|
175
|
+
names what is missing and why: "No holding matches the search." Loading is
|
|
176
|
+
a `Skeleton` in the block's shape; long work names itself, "Orders imported",
|
|
177
|
+
with its progress, and a waiting line ends in one "…". An error's title says
|
|
178
|
+
what happened, "Import failed"; its line says why and what the reader can
|
|
179
|
+
do: "The CSV could not be read. Export it again and retry." A field's error
|
|
180
|
+
states the rule with an example: "A PIN code is six digits, such as 560001".
|
|
181
|
+
- **Address the reader as you and the business as we**: "What you own, what it
|
|
182
|
+
is worth today", "Our store".
|
|
183
|
+
|
|
103
184
|
## Labels and totals
|
|
104
185
|
|
|
105
186
|
- **Labels are small monospaced capitals**: a stat tile's label and footer
|
|
@@ -12,7 +12,7 @@ export default function BarListLabelsExample() {
|
|
|
12
12
|
<Card>
|
|
13
13
|
<CardHeader>
|
|
14
14
|
<CardTitle>Orders by category, ranked</CardTitle>
|
|
15
|
-
<CardDescription>
|
|
15
|
+
<CardDescription>Orders placed this month in each category</CardDescription>
|
|
16
16
|
</CardHeader>
|
|
17
17
|
<CardContent>
|
|
18
18
|
<BarList data={categories} valueFormatter={count} />
|
package/agents/examples/card.tsx
CHANGED
|
@@ -21,7 +21,7 @@ export default function CardExample() {
|
|
|
21
21
|
<CardAction>
|
|
22
22
|
<Button variant="outline" size="sm">
|
|
23
23
|
<DownloadIcon />
|
|
24
|
-
CSV
|
|
24
|
+
Export CSV
|
|
25
25
|
</Button>
|
|
26
26
|
</CardAction>
|
|
27
27
|
</CardHeader>
|
|
@@ -29,7 +29,7 @@ export default function CardExample() {
|
|
|
29
29
|
<p className="text-3xl font-semibold">{money(4512300)}</p>
|
|
30
30
|
</CardContent>
|
|
31
31
|
<CardFooter>
|
|
32
|
-
<p className="text-muted-foreground">Updated every night at midnight</p>
|
|
32
|
+
<p className="text-muted-foreground">Updated every night at midnight IST</p>
|
|
33
33
|
</CardFooter>
|
|
34
34
|
</Card>
|
|
35
35
|
);
|
|
@@ -4,16 +4,16 @@ import { Card, CardContent, CardDescription, CardHeader, CardTitle, ChangeGrid,
|
|
|
4
4
|
const months = ["Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", "Jan", "Feb", "Mar"];
|
|
5
5
|
const growth: Omit<ChangeGridRow, "total">[] = [
|
|
6
6
|
{
|
|
7
|
-
label: "
|
|
7
|
+
label: "FY25",
|
|
8
8
|
values: [0.031, 0.012, -0.024, 0.008, 0.017, -0.006, 0.041, 0.052, 0.063, -0.038, 0.004, 0.022],
|
|
9
9
|
},
|
|
10
10
|
{
|
|
11
|
-
label: "
|
|
11
|
+
label: "FY26",
|
|
12
12
|
values: [0.018, -0.011, -0.032, 0.014, 0.009, 0, 0.036, 0.047, 0.071, -0.044, -0.008, 0.019],
|
|
13
13
|
},
|
|
14
14
|
// The year is under way: months still to come have no figure.
|
|
15
15
|
{
|
|
16
|
-
label: "
|
|
16
|
+
label: "FY27",
|
|
17
17
|
values: [0.026, 0.015, -0.018, 0.021, 0.012, 0.007, 0.044, null, null, null, null, null],
|
|
18
18
|
},
|
|
19
19
|
];
|
|
@@ -31,7 +31,8 @@ export default function ChangeGridExample() {
|
|
|
31
31
|
<CardHeader>
|
|
32
32
|
<CardTitle>Sales growth by month</CardTitle>
|
|
33
33
|
<CardDescription>
|
|
34
|
-
Each month against the one before
|
|
34
|
+
Each month against the one before, by financial year; FY27 ends in March 2027. Deeper tints are larger
|
|
35
|
+
changes; the year compounds its months.
|
|
35
36
|
</CardDescription>
|
|
36
37
|
</CardHeader>
|
|
37
38
|
<CardContent>
|
|
@@ -17,7 +17,7 @@ export default function ChartBarLossesExample() {
|
|
|
17
17
|
return (
|
|
18
18
|
<BarChartCard
|
|
19
19
|
title="Operating loss by financial year"
|
|
20
|
-
description="A loss every year, so every bar runs down from zero"
|
|
20
|
+
description="FY26 ends in March 2026. A loss every year, so every bar runs down from zero."
|
|
21
21
|
data={data}
|
|
22
22
|
xKey="fy"
|
|
23
23
|
series={series}
|
|
@@ -33,7 +33,7 @@ export default function ChartBarTeamsExample() {
|
|
|
33
33
|
];
|
|
34
34
|
const gap = (row: Row) => {
|
|
35
35
|
const points = Math.abs(row.actual - row.comparison) * 100;
|
|
36
|
-
return row.actual >= row.comparison ?
|
|
36
|
+
return `${row.actual >= row.comparison ? "+" : "\u2212"}${points.toFixed(1)} points`;
|
|
37
37
|
};
|
|
38
38
|
return (
|
|
39
39
|
<BarChartCard
|
|
@@ -57,7 +57,7 @@ export default function ChartBarTeamsExample() {
|
|
|
57
57
|
horizontal
|
|
58
58
|
data={rows}
|
|
59
59
|
xKey="team"
|
|
60
|
-
xTickDetail={(row) => `${pct(row.actual)}
|
|
60
|
+
xTickDetail={(row) => `${pct(row.actual)} against ${pct(row.comparison)}, ${gap(row)}`}
|
|
61
61
|
series={series}
|
|
62
62
|
valueFormatter={(v) => pct(v)}
|
|
63
63
|
// A tick format puts the value axis along the bottom of the sideways bars.
|
|
@@ -24,7 +24,9 @@ export default function ChartBarYearsExample() {
|
|
|
24
24
|
<BarChartCard
|
|
25
25
|
title="Profit by financial year"
|
|
26
26
|
description={
|
|
27
|
-
year
|
|
27
|
+
year
|
|
28
|
+
? `${year} picked (April to March, before tax). Click it again for every year.`
|
|
29
|
+
: "April to March, before tax; FY26 ends in March 2026. Click a year to pick it."
|
|
28
30
|
}
|
|
29
31
|
data={data}
|
|
30
32
|
xKey="fy"
|
|
@@ -26,8 +26,8 @@ export default function ChartBarExample() {
|
|
|
26
26
|
title="Revenue by channel and quarter"
|
|
27
27
|
description={
|
|
28
28
|
layout === "grouped"
|
|
29
|
-
? "
|
|
30
|
-
: "
|
|
29
|
+
? "Each channel's revenue, side by side"
|
|
30
|
+
: "Each channel's revenue, stacked into the quarter's total"
|
|
31
31
|
}
|
|
32
32
|
action={
|
|
33
33
|
<ToggleGroup
|
|
@@ -22,7 +22,7 @@ export default function ChartLineEventsExample() {
|
|
|
22
22
|
return (
|
|
23
23
|
<LineChartCard
|
|
24
24
|
title="Memory pressure of a test group, %"
|
|
25
|
-
description="avg10, sampled every 15 s"
|
|
25
|
+
description="Share of time the group stalled waiting for memory, as the kernel's 10 s average (avg10), sampled every 15 s, times in IST"
|
|
26
26
|
data={data}
|
|
27
27
|
xKey="second"
|
|
28
28
|
// A numeric axis: ticks fall on whole minutes, 2 s after the first sample.
|
|
@@ -14,7 +14,7 @@ const data = [
|
|
|
14
14
|
|
|
15
15
|
const series: ChartSeries<(typeof data)[number]> = [
|
|
16
16
|
{ key: "used", label: "Used", color: "var(--chart-1)" },
|
|
17
|
-
{ key: "anon", label: "
|
|
17
|
+
{ key: "anon", label: "Program memory", color: "var(--chart-2)" },
|
|
18
18
|
];
|
|
19
19
|
|
|
20
20
|
const clock = (minute: number) => `${Math.floor(15 + minute / 60)}:${String(minute % 60).padStart(2, "0")}`;
|
|
@@ -23,7 +23,7 @@ export default function ChartLineTimelineExample() {
|
|
|
23
23
|
return (
|
|
24
24
|
<LineChartCard
|
|
25
25
|
title="Memory in use, GiB"
|
|
26
|
-
description="sar -r. Used excludes cache;
|
|
26
|
+
description="From sar -r, times in IST. Used excludes the page cache; program memory is what sar calls anon."
|
|
27
27
|
data={data}
|
|
28
28
|
xKey="minute"
|
|
29
29
|
// Ticks on both ends of the domain: the first sits at the plot's left edge.
|
|
@@ -41,6 +41,12 @@ function Money({ amount }: { amount: number }) {
|
|
|
41
41
|
return useFormat().money(amount);
|
|
42
42
|
}
|
|
43
43
|
|
|
44
|
+
// The formatters print a bare hyphen, so a change in plain text writes its own + or −.
|
|
45
|
+
function SignedMoney({ amount }: { amount: number }) {
|
|
46
|
+
const sign = amount > 0 ? "+" : amount < 0 ? "\u2212" : "";
|
|
47
|
+
return `${sign}${useFormat().money(Math.abs(amount))}`;
|
|
48
|
+
}
|
|
49
|
+
|
|
44
50
|
const header = () => <DataTableSortHeader />;
|
|
45
51
|
|
|
46
52
|
const columns: DataTableColumnDef<Store>[] = [
|
|
@@ -60,16 +66,16 @@ const columns: DataTableColumnDef<Store>[] = [
|
|
|
60
66
|
accessorKey: "change",
|
|
61
67
|
header,
|
|
62
68
|
size: 170,
|
|
63
|
-
meta: { label: "
|
|
69
|
+
meta: { label: "Change on last month", variant: "number" },
|
|
64
70
|
cell: ({ row }) => (
|
|
65
71
|
<DataBar value={row.original.change} max={largestChange} tone="sign">
|
|
66
|
-
<
|
|
72
|
+
<SignedMoney amount={row.original.change} />
|
|
67
73
|
</DataBar>
|
|
68
74
|
),
|
|
69
75
|
},
|
|
70
76
|
{
|
|
71
77
|
id: "daily",
|
|
72
|
-
header: "10 days",
|
|
78
|
+
header: "Last 10 days",
|
|
73
79
|
size: 110,
|
|
74
80
|
enableSorting: false,
|
|
75
81
|
cell: ({ row }) => {
|
|
@@ -36,7 +36,7 @@ const products: Product[] = [
|
|
|
36
36
|
|
|
37
37
|
const header = () => <DataTableSortHeader />;
|
|
38
38
|
|
|
39
|
-
// While the
|
|
39
|
+
// While the change column is hidden to fit, the week's change rides under the revenue it belongs to.
|
|
40
40
|
function RevenueCell({ product }: { product: Product }) {
|
|
41
41
|
const format = useFormat();
|
|
42
42
|
const { compactColumns } = useDataTable<Product>();
|
|
@@ -64,7 +64,7 @@ const columns: DataTableColumnDef<Product>[] = [
|
|
|
64
64
|
accessorKey: "change",
|
|
65
65
|
header,
|
|
66
66
|
size: 110,
|
|
67
|
-
meta: { label: "
|
|
67
|
+
meta: { label: "Change", variant: "number" },
|
|
68
68
|
cell: ({ row }) => <Delta value={row.original.change} />,
|
|
69
69
|
},
|
|
70
70
|
];
|
|
@@ -73,11 +73,8 @@ export default function DataTableCompactExample() {
|
|
|
73
73
|
return (
|
|
74
74
|
<Card>
|
|
75
75
|
<CardHeader>
|
|
76
|
-
<CardTitle>
|
|
77
|
-
<CardDescription>
|
|
78
|
-
Product and revenue are the key columns. When the columns do not fit, the others hide from the right, and the
|
|
79
|
-
week's change rides under the revenue once its column is gone; All columns brings back the rest
|
|
80
|
-
</CardDescription>
|
|
76
|
+
<CardTitle>Revenue by product</CardTitle>
|
|
77
|
+
<CardDescription>This week's revenue for each product, with the change on last week</CardDescription>
|
|
81
78
|
</CardHeader>
|
|
82
79
|
<CardContent>
|
|
83
80
|
<DataTableRoot data={products} columns={columns}>
|
|
@@ -85,7 +82,7 @@ export default function DataTableCompactExample() {
|
|
|
85
82
|
<DataTableCompactColumnsToggle />
|
|
86
83
|
<DataTableRowCount unit="products" />
|
|
87
84
|
</DataTableToolbarSection>
|
|
88
|
-
<DataTable aria-label="
|
|
85
|
+
<DataTable aria-label="Revenue by product">
|
|
89
86
|
<DataTableHeader />
|
|
90
87
|
<DataTableBody />
|
|
91
88
|
</DataTable>
|
|
@@ -77,22 +77,22 @@ export default function DataTableFiltersExample() {
|
|
|
77
77
|
return (
|
|
78
78
|
<Card>
|
|
79
79
|
<CardHeader>
|
|
80
|
-
<CardTitle>
|
|
81
|
-
<CardDescription>
|
|
80
|
+
<CardTitle>Open orders</CardTitle>
|
|
81
|
+
<CardDescription>Orders packed or out for delivery, by warehouse</CardDescription>
|
|
82
82
|
</CardHeader>
|
|
83
83
|
<CardContent>
|
|
84
84
|
{/* The closed filter panel is absent at first render, so configure its features explicitly. */}
|
|
85
85
|
<DataTableRoot data={data} columns={columns} config={{ enableFilters: true, enableSorting: true }}>
|
|
86
86
|
<DataTableToolbarSection>
|
|
87
87
|
<DataTableSearchFilter
|
|
88
|
-
label="Search
|
|
88
|
+
label="Search orders"
|
|
89
89
|
placeholder="Search orders…"
|
|
90
90
|
className="w-full flex-none sm:w-64"
|
|
91
91
|
/>
|
|
92
92
|
<Filters />
|
|
93
93
|
<DataTableRowCount unit="orders" />
|
|
94
94
|
</DataTableToolbarSection>
|
|
95
|
-
<DataTable aria-label="
|
|
95
|
+
<DataTable aria-label="Open orders">
|
|
96
96
|
<DataTableHeader />
|
|
97
97
|
<DataTableBody />
|
|
98
98
|
</DataTable>
|
|
@@ -58,7 +58,7 @@ const columns: DataTableColumnDef<Product>[] = [
|
|
|
58
58
|
accessorKey: "change",
|
|
59
59
|
header,
|
|
60
60
|
size: 110,
|
|
61
|
-
meta: { label: "
|
|
61
|
+
meta: { label: "Change", variant: "number" },
|
|
62
62
|
cell: ({ row }) => <Delta value={row.original.change} />,
|
|
63
63
|
},
|
|
64
64
|
];
|
|
@@ -68,10 +68,7 @@ export default function DataTableExample() {
|
|
|
68
68
|
<Card>
|
|
69
69
|
<CardHeader>
|
|
70
70
|
<CardTitle>Products</CardTitle>
|
|
71
|
-
<CardDescription>
|
|
72
|
-
Search every column, filter by category, sort any column in one click; the product stays put when the table
|
|
73
|
-
scrolls in wide panels. In narrow panels every column scrolls, leaving room for the figures
|
|
74
|
-
</CardDescription>
|
|
71
|
+
<CardDescription>This week's units and revenue for each product, with the change on last week</CardDescription>
|
|
75
72
|
</CardHeader>
|
|
76
73
|
<CardContent>
|
|
77
74
|
<DataTableRoot
|
|
@@ -30,7 +30,7 @@ export default function DistributionBarTugExample() {
|
|
|
30
30
|
return (
|
|
31
31
|
<Card>
|
|
32
32
|
<CardHeader>
|
|
33
|
-
<CardTitle>
|
|
33
|
+
<CardTitle>Campaigns earned ₹70,000 more than they lost</CardTitle>
|
|
34
34
|
<CardDescription>Each finished campaign, losses left of break-even and gains right</CardDescription>
|
|
35
35
|
</CardHeader>
|
|
36
36
|
<CardContent>
|
|
@@ -30,7 +30,7 @@ export default function HeatmapCalendarExample() {
|
|
|
30
30
|
<Card>
|
|
31
31
|
<CardHeader>
|
|
32
32
|
<CardTitle>Deploys per day</CardTitle>
|
|
33
|
-
<CardDescription>The last 12 months;
|
|
33
|
+
<CardDescription>The last 12 months; the stronger the colour, the more deploys</CardDescription>
|
|
34
34
|
</CardHeader>
|
|
35
35
|
<CardContent>
|
|
36
36
|
<CalendarHeatmap data={data} labels={{ heatmapLabel: "Deploys per day over the last 12 months" }}>
|
|
@@ -14,7 +14,7 @@ export default function HistogramChartExample() {
|
|
|
14
14
|
return (
|
|
15
15
|
<HistogramChartCard
|
|
16
16
|
title="Checkout response times"
|
|
17
|
-
description="The last 2,000 requests, counted in 100 ms buckets. Lines mark the median and the 95th percentile."
|
|
17
|
+
description="The last 2,000 requests, counted in 100 ms buckets. Lines mark the median and the 95th percentile (95 in 100 requests were faster)."
|
|
18
18
|
values={times}
|
|
19
19
|
valueFormatter={(ms) => `${count(ms)} ms`}
|
|
20
20
|
tickFormatter={count}
|
|
@@ -40,7 +40,7 @@ interface Holding {
|
|
|
40
40
|
const holdings: Holding[] = [
|
|
41
41
|
{ name: "Nifty 50 index fund", kind: "Equity", status: "On track", tone: "positive", value: 1840000, change: 0.124 },
|
|
42
42
|
{ name: "Flexi cap fund", kind: "Equity", status: "On track", tone: "positive", value: 962000, change: 0.087 },
|
|
43
|
-
{ name: "Gold bonds, 2031", kind: "Gold", status: "
|
|
43
|
+
{ name: "Gold bonds, 2031", kind: "Gold", status: "Needs review", tone: "warning", value: 410500, change: 0.211 },
|
|
44
44
|
{ name: "Short-term debt fund", kind: "Debt", status: "On track", tone: "positive", value: 655000, change: 0.031 },
|
|
45
45
|
{ name: "Small cap fund", kind: "Equity", status: "Below plan", tone: "negative", value: 318200, change: -0.064 },
|
|
46
46
|
];
|
|
@@ -76,7 +76,7 @@ function Holdings({ rows }: { rows: Holding[] }) {
|
|
|
76
76
|
<TableHead>Holding</TableHead>
|
|
77
77
|
<TableHead>Status</TableHead>
|
|
78
78
|
<TableHead data-numeric>Value</TableHead>
|
|
79
|
-
<TableHead data-numeric>12 months</TableHead>
|
|
79
|
+
<TableHead data-numeric>Change over 12 months</TableHead>
|
|
80
80
|
</TableRow>
|
|
81
81
|
</TableHeader>
|
|
82
82
|
<TableBody>
|
|
@@ -43,8 +43,28 @@ const services: Service[] = [
|
|
|
43
43
|
service("login", 175, 161, 203),
|
|
44
44
|
];
|
|
45
45
|
|
|
46
|
+
function useMilliseconds(): (value: number) => string {
|
|
47
|
+
const { count } = useFormat();
|
|
48
|
+
return (value) => `${count(value)} ms`;
|
|
49
|
+
}
|
|
50
|
+
|
|
46
51
|
function Milliseconds({ value }: { value: number }) {
|
|
47
|
-
return
|
|
52
|
+
return useMilliseconds()(value);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function Range({ row }: { row: Service }) {
|
|
56
|
+
const ms = useMilliseconds();
|
|
57
|
+
return (
|
|
58
|
+
<RangePosition
|
|
59
|
+
variant="compact"
|
|
60
|
+
low={row.low}
|
|
61
|
+
high={row.high}
|
|
62
|
+
value={row.today}
|
|
63
|
+
valueFormatter={ms}
|
|
64
|
+
lowLabel="30-day fastest"
|
|
65
|
+
highLabel="30-day slowest"
|
|
66
|
+
/>
|
|
67
|
+
);
|
|
48
68
|
}
|
|
49
69
|
|
|
50
70
|
const header = () => <DataTableSortHeader />;
|
|
@@ -54,8 +74,8 @@ const columns: DataTableColumnDef<Service>[] = [
|
|
|
54
74
|
{
|
|
55
75
|
accessorKey: "today",
|
|
56
76
|
header,
|
|
57
|
-
size:
|
|
58
|
-
meta: { label: "
|
|
77
|
+
size: 170,
|
|
78
|
+
meta: { label: "95th percentile today", variant: "number" },
|
|
59
79
|
cell: ({ row }) => <Milliseconds value={row.original.today} />,
|
|
60
80
|
},
|
|
61
81
|
{
|
|
@@ -63,16 +83,7 @@ const columns: DataTableColumnDef<Service>[] = [
|
|
|
63
83
|
header,
|
|
64
84
|
size: 200,
|
|
65
85
|
meta: { label: "30-day range", compact: true },
|
|
66
|
-
cell: ({ row }) =>
|
|
67
|
-
<RangePosition
|
|
68
|
-
variant="compact"
|
|
69
|
-
low={row.original.low}
|
|
70
|
-
high={row.original.high}
|
|
71
|
-
value={row.original.today}
|
|
72
|
-
lowLabel="30-day fastest"
|
|
73
|
-
highLabel="30-day slowest"
|
|
74
|
-
/>
|
|
75
|
-
),
|
|
86
|
+
cell: ({ row }) => <Range row={row.original} />,
|
|
76
87
|
},
|
|
77
88
|
];
|
|
78
89
|
|
|
@@ -82,7 +93,8 @@ export default function RangePositionTableExample() {
|
|
|
82
93
|
<CardHeader>
|
|
83
94
|
<CardTitle>Search is near its slowest of the month, login near its fastest</CardTitle>
|
|
84
95
|
<CardDescription>
|
|
85
|
-
Each service's response time today
|
|
96
|
+
Each service's 95th-percentile response time today (95 in 100 requests were faster) on a track from its
|
|
97
|
+
fastest day to its slowest; sort to rank them
|
|
86
98
|
</CardDescription>
|
|
87
99
|
</CardHeader>
|
|
88
100
|
<CardContent>
|
|
@@ -30,14 +30,14 @@ const teams: Team[] = [
|
|
|
30
30
|
const NEAR = 0.02;
|
|
31
31
|
const FAR = 0.05;
|
|
32
32
|
|
|
33
|
-
const
|
|
33
|
+
const points = (value: number) => `${(value * 100).toFixed(1)} points`;
|
|
34
34
|
|
|
35
35
|
function statusOf({ weight, target }: Team): TargetStatus {
|
|
36
36
|
const gap = weight - target;
|
|
37
|
-
const size = `${
|
|
37
|
+
const size = `${points(Math.abs(gap))} ${gap > 0 ? "over" : "under"}`;
|
|
38
38
|
if (Math.abs(gap) <= NEAR) return { kind: "met", label: "On target" };
|
|
39
39
|
if (Math.abs(gap) <= FAR) return { kind: "missed", label: size };
|
|
40
|
-
return { kind: "warning", label: `${size}, review` };
|
|
40
|
+
return { kind: "warning", label: `${size}, needs review` };
|
|
41
41
|
}
|
|
42
42
|
|
|
43
43
|
export default function TargetRowsExample() {
|