@siyahi/ui 0.1.9-next.29 → 0.1.9-next.30
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 +79 -10
- package/agents/examples/avatar.tsx +15 -5
- package/agents/examples/chart-line-forecast.tsx +36 -0
- package/agents/examples/chart-line-quiet.tsx +114 -0
- package/agents/examples/chart-panels.tsx +94 -0
- package/agents/examples/code-block.tsx +5 -3
- package/agents/examples/field.tsx +108 -0
- package/agents/examples/formatters.tsx +14 -14
- package/agents/examples/input-states.tsx +12 -22
- package/agents/examples/overview-dashboard.tsx +6 -6
- package/agents/examples/stale-blocks.tsx +56 -0
- package/agents/examples/table.tsx +6 -6
- package/agents/manifest.json +104 -3
- package/dist/components/registry/charts/area-chart-card.d.ts +7 -3
- package/dist/components/registry/charts/area-chart-card.js +52 -25
- package/dist/components/registry/charts/bar-chart-card.d.ts +1 -0
- package/dist/components/registry/charts/bar-chart-card.js +8 -24
- package/dist/components/registry/charts/line-chart-card.d.ts +2 -2
- package/dist/components/registry/charts/line-chart-card.js +42 -7
- package/dist/components/registry/dashboard-01/section-cards.d.ts +2 -0
- package/dist/components/registry/dashboard-01/section-cards.js +3 -0
- package/dist/components/registry/heatmap/calendar-heatmap.js +2 -2
- package/dist/components/registry/heatmap/status-heatmap.js +2 -2
- package/dist/components/registry/heatmap/weekday-heatmap.js +2 -2
- package/dist/components/registry/niko-table/filters/table-faceted-filter.js +1 -1
- package/dist/components/siyahi/histogram-chart.js +8 -8
- package/dist/components/siyahi/scatter-chart.js +1 -1
- package/dist/components/siyahi/slope-chart.js +1 -1
- package/dist/components/siyahi/stale-note.d.ts +9 -0
- package/dist/components/siyahi/stale-note.js +12 -0
- package/dist/components/siyahi/treemap.js +1 -1
- package/dist/components/siyahi/waterfall-chart.js +4 -4
- package/dist/components/ui/field.d.ts +26 -0
- package/dist/components/ui/field.js +109 -0
- package/dist/components/ui/label.d.ts +5 -0
- package/dist/components/ui/label.js +13 -0
- package/dist/index.d.ts +5 -2
- package/dist/index.js +5 -2
- package/dist/lib/chart-frame.d.ts +28 -4
- package/dist/lib/chart-frame.js +256 -153
- package/dist/lib/chart-marks.d.ts +83 -0
- package/dist/lib/chart-marks.js +216 -0
- package/dist/lib/chart-series.d.ts +15 -0
- package/package.json +1 -1
- package/report.css +6 -4
- package/styles/siyahi.css +260 -18
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { AreaChartCard, Delta, SectionCards, Sparkline, useFormat, type SectionCard } from "@siyahi/ui";
|
|
2
|
+
|
|
3
|
+
const value = [
|
|
4
|
+
{ month: "May", equity: 2150000, debt: 600000, gold: 320000, intl: 390000, cash: 150000 },
|
|
5
|
+
{ month: "Jun", equity: 2230000, debt: 612000, gold: 334000, intl: 402000, cash: 160000 },
|
|
6
|
+
{ month: "Jul", equity: 2390000, debt: 625000, gold: 352000, intl: 421000, cash: 172000 },
|
|
7
|
+
{ month: "Aug", equity: 2310000, debt: 637000, gold: 371000, intl: 447000, cash: 185000 },
|
|
8
|
+
{ month: "Sep", equity: 2620000, debt: 648000, gold: 390000, intl: 462000, cash: 198000 },
|
|
9
|
+
{ month: "Oct", equity: 2800000, debt: 655000, gold: 410500, intl: 486300, cash: 212000 },
|
|
10
|
+
];
|
|
11
|
+
const kinds = [
|
|
12
|
+
{ key: "equity", label: "Equity", color: "var(--chart-1)" },
|
|
13
|
+
{ key: "debt", label: "Debt", color: "var(--chart-2)" },
|
|
14
|
+
{ key: "gold", label: "Gold", color: "var(--chart-3)" },
|
|
15
|
+
{ key: "intl", label: "International", color: "var(--chart-4)" },
|
|
16
|
+
{ key: "cash", label: "Cash", color: "var(--chart-5)" },
|
|
17
|
+
] as const;
|
|
18
|
+
|
|
19
|
+
// The gift card sales and the holdings' values last came in on 3 November: the page marks both blocks stale, and each
|
|
20
|
+
// loses its colour and says since when. The page decides; nothing is inferred from dates.
|
|
21
|
+
export default function StaleBlocksExample() {
|
|
22
|
+
const { money, moneyCompact } = useFormat();
|
|
23
|
+
const tiles: SectionCard[] = [
|
|
24
|
+
{
|
|
25
|
+
label: "Revenue",
|
|
26
|
+
value: money(4512300),
|
|
27
|
+
badge: <Delta value={0.031} />,
|
|
28
|
+
trend: <Sparkline data={[3820000, 3950000, 4110000, 4020000, 4380000, 4512300]} />,
|
|
29
|
+
detail: "Month-end, last six months",
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
label: "Gift cards this year",
|
|
33
|
+
value: moneyCompact(38400),
|
|
34
|
+
badge: <Delta value={0.18} />,
|
|
35
|
+
trend: <Sparkline data={[4100, 0, 12800, 3300, 9900, 8300]} variant="line" curve="linear" />,
|
|
36
|
+
detail: "Sold each month",
|
|
37
|
+
stale: "3 Nov",
|
|
38
|
+
},
|
|
39
|
+
];
|
|
40
|
+
return (
|
|
41
|
+
<div className="flex flex-col gap-6">
|
|
42
|
+
<SectionCards items={tiles} />
|
|
43
|
+
<AreaChartCard
|
|
44
|
+
title="Value by kind"
|
|
45
|
+
description="Month-end value of each kind of holding"
|
|
46
|
+
data={value}
|
|
47
|
+
xKey="month"
|
|
48
|
+
series={kinds}
|
|
49
|
+
valueFormatter={money}
|
|
50
|
+
yTickFormatter={moneyCompact}
|
|
51
|
+
height={256}
|
|
52
|
+
stale="3 Nov"
|
|
53
|
+
/>
|
|
54
|
+
</div>
|
|
55
|
+
);
|
|
56
|
+
}
|
|
@@ -32,8 +32,8 @@ export default function TableExample() {
|
|
|
32
32
|
<TableHeader>
|
|
33
33
|
<TableRow>
|
|
34
34
|
<TableHead>Month</TableHead>
|
|
35
|
-
<TableHead
|
|
36
|
-
<TableHead
|
|
35
|
+
<TableHead data-numeric>In</TableHead>
|
|
36
|
+
<TableHead data-numeric>Out</TableHead>
|
|
37
37
|
</TableRow>
|
|
38
38
|
</TableHeader>
|
|
39
39
|
<TableBody>
|
|
@@ -41,16 +41,16 @@ export default function TableExample() {
|
|
|
41
41
|
// August's spending needs a look: the page marks the row, and the theme washes it amber.
|
|
42
42
|
<TableRow key={row.month} data-tone={row.out > 50000 ? "warning" : undefined}>
|
|
43
43
|
<TableCell>{row.month}</TableCell>
|
|
44
|
-
<TableCell
|
|
45
|
-
<TableCell
|
|
44
|
+
<TableCell data-numeric>{money(row.in)}</TableCell>
|
|
45
|
+
<TableCell data-numeric>{money(row.out)}</TableCell>
|
|
46
46
|
</TableRow>
|
|
47
47
|
))}
|
|
48
48
|
</TableBody>
|
|
49
49
|
<TableFooter>
|
|
50
50
|
<TableRow>
|
|
51
51
|
<TableCell>Total</TableCell>
|
|
52
|
-
<TableCell
|
|
53
|
-
<TableCell
|
|
52
|
+
<TableCell data-numeric>{money(total("in"))}</TableCell>
|
|
53
|
+
<TableCell data-numeric>{money(total("out"))}</TableCell>
|
|
54
54
|
</TableRow>
|
|
55
55
|
</TableFooter>
|
|
56
56
|
</Table>
|
package/agents/manifest.json
CHANGED
|
@@ -268,10 +268,12 @@
|
|
|
268
268
|
},
|
|
269
269
|
{
|
|
270
270
|
"need": "A command or file to copy, wrapped or scrolling",
|
|
271
|
-
"guidance": "`CodeBlock` with `CodeBlockCopyButton`",
|
|
271
|
+
"guidance": "`CodeBlock` with `CodeBlockCopyButton` in `CodeBlockActions` inside a `CodeBlockHeader`, never floating over the code",
|
|
272
272
|
"components": [
|
|
273
273
|
"CodeBlock",
|
|
274
|
-
"CodeBlockCopyButton"
|
|
274
|
+
"CodeBlockCopyButton",
|
|
275
|
+
"CodeBlockActions",
|
|
276
|
+
"CodeBlockHeader"
|
|
275
277
|
]
|
|
276
278
|
},
|
|
277
279
|
{
|
|
@@ -295,6 +297,37 @@
|
|
|
295
297
|
"SiyahiProvider"
|
|
296
298
|
]
|
|
297
299
|
},
|
|
300
|
+
{
|
|
301
|
+
"need": "A forecast stretch after the latest actual figure, or a marker for today",
|
|
302
|
+
"guidance": "`LineChartCard` or `AreaChartCard` with `forecastFrom` and `today` set to x values",
|
|
303
|
+
"components": [
|
|
304
|
+
"LineChartCard",
|
|
305
|
+
"AreaChartCard"
|
|
306
|
+
]
|
|
307
|
+
},
|
|
308
|
+
{
|
|
309
|
+
"need": "More than five lines on one chart",
|
|
310
|
+
"guidance": "chart colours for the few the page is about, `var(--muted-foreground)` for the rest; every line keeps its own name",
|
|
311
|
+
"components": []
|
|
312
|
+
},
|
|
313
|
+
{
|
|
314
|
+
"need": "A chart or stat tile whose data is old",
|
|
315
|
+
"guidance": "`stale` on the chart card or the `SectionCards` item, with since when (\"3 Nov\"); `StaleNote` for another kind of block",
|
|
316
|
+
"components": [
|
|
317
|
+
"SectionCards",
|
|
318
|
+
"StaleNote"
|
|
319
|
+
]
|
|
320
|
+
},
|
|
321
|
+
{
|
|
322
|
+
"need": "A form field with its label, a hint or an error",
|
|
323
|
+
"guidance": "`Field` with `FieldLabel`, the control, then `FieldDescription` or `FieldError`; `aria-invalid` on an invalid control",
|
|
324
|
+
"components": [
|
|
325
|
+
"Field",
|
|
326
|
+
"FieldLabel",
|
|
327
|
+
"FieldDescription",
|
|
328
|
+
"FieldError"
|
|
329
|
+
]
|
|
330
|
+
},
|
|
298
331
|
{
|
|
299
332
|
"need": "A command, file or identifier inside prose",
|
|
300
333
|
"guidance": "`InlineCode`",
|
|
@@ -378,7 +411,9 @@
|
|
|
378
411
|
"file": "agents/examples/avatar.tsx",
|
|
379
412
|
"components": [
|
|
380
413
|
"Avatar",
|
|
381
|
-
"
|
|
414
|
+
"AvatarBadge",
|
|
415
|
+
"AvatarFallback",
|
|
416
|
+
"AvatarGroup"
|
|
382
417
|
]
|
|
383
418
|
},
|
|
384
419
|
{
|
|
@@ -599,6 +634,13 @@
|
|
|
599
634
|
"LineChartCard"
|
|
600
635
|
]
|
|
601
636
|
},
|
|
637
|
+
{
|
|
638
|
+
"file": "agents/examples/chart-line-forecast.tsx",
|
|
639
|
+
"components": [
|
|
640
|
+
"LineChartCard",
|
|
641
|
+
"useFormat"
|
|
642
|
+
]
|
|
643
|
+
},
|
|
602
644
|
{
|
|
603
645
|
"file": "agents/examples/chart-line-log.tsx",
|
|
604
646
|
"components": [
|
|
@@ -607,6 +649,13 @@
|
|
|
607
649
|
"useFormat"
|
|
608
650
|
]
|
|
609
651
|
},
|
|
652
|
+
{
|
|
653
|
+
"file": "agents/examples/chart-line-quiet.tsx",
|
|
654
|
+
"components": [
|
|
655
|
+
"LineChartCard",
|
|
656
|
+
"useFormat"
|
|
657
|
+
]
|
|
658
|
+
},
|
|
610
659
|
{
|
|
611
660
|
"file": "agents/examples/chart-line-timeline.tsx",
|
|
612
661
|
"components": [
|
|
@@ -622,6 +671,16 @@
|
|
|
622
671
|
"useFormat"
|
|
623
672
|
]
|
|
624
673
|
},
|
|
674
|
+
{
|
|
675
|
+
"file": "agents/examples/chart-panels.tsx",
|
|
676
|
+
"components": [
|
|
677
|
+
"AreaChartCard",
|
|
678
|
+
"BarChartCard",
|
|
679
|
+
"LineChartCard",
|
|
680
|
+
"PanelGrid",
|
|
681
|
+
"useFormat"
|
|
682
|
+
]
|
|
683
|
+
},
|
|
625
684
|
{
|
|
626
685
|
"file": "agents/examples/code-block.tsx",
|
|
627
686
|
"components": [
|
|
@@ -821,6 +880,34 @@
|
|
|
821
880
|
"DropdownMenuTrigger"
|
|
822
881
|
]
|
|
823
882
|
},
|
|
883
|
+
{
|
|
884
|
+
"file": "agents/examples/field.tsx",
|
|
885
|
+
"components": [
|
|
886
|
+
"Button",
|
|
887
|
+
"Card",
|
|
888
|
+
"CardContent",
|
|
889
|
+
"CardDescription",
|
|
890
|
+
"CardHeader",
|
|
891
|
+
"CardTitle",
|
|
892
|
+
"Field",
|
|
893
|
+
"FieldDescription",
|
|
894
|
+
"FieldError",
|
|
895
|
+
"FieldLabel",
|
|
896
|
+
"Input",
|
|
897
|
+
"InputGroup",
|
|
898
|
+
"InputGroupAddon",
|
|
899
|
+
"InputGroupInput",
|
|
900
|
+
"Select",
|
|
901
|
+
"SelectContent",
|
|
902
|
+
"SelectGroup",
|
|
903
|
+
"SelectItem",
|
|
904
|
+
"SelectTrigger",
|
|
905
|
+
"SelectValue",
|
|
906
|
+
"Textarea",
|
|
907
|
+
"ToggleGroup",
|
|
908
|
+
"ToggleGroupItem"
|
|
909
|
+
]
|
|
910
|
+
},
|
|
824
911
|
{
|
|
825
912
|
"file": "agents/examples/formatters.tsx",
|
|
826
913
|
"components": [
|
|
@@ -906,6 +993,10 @@
|
|
|
906
993
|
{
|
|
907
994
|
"file": "agents/examples/input-states.tsx",
|
|
908
995
|
"components": [
|
|
996
|
+
"Field",
|
|
997
|
+
"FieldDescription",
|
|
998
|
+
"FieldError",
|
|
999
|
+
"FieldLabel",
|
|
909
1000
|
"Input"
|
|
910
1001
|
]
|
|
911
1002
|
},
|
|
@@ -1173,6 +1264,16 @@
|
|
|
1173
1264
|
"SlopeChart"
|
|
1174
1265
|
]
|
|
1175
1266
|
},
|
|
1267
|
+
{
|
|
1268
|
+
"file": "agents/examples/stale-blocks.tsx",
|
|
1269
|
+
"components": [
|
|
1270
|
+
"AreaChartCard",
|
|
1271
|
+
"Delta",
|
|
1272
|
+
"SectionCards",
|
|
1273
|
+
"Sparkline",
|
|
1274
|
+
"useFormat"
|
|
1275
|
+
]
|
|
1276
|
+
},
|
|
1176
1277
|
{
|
|
1177
1278
|
"file": "agents/examples/surfaces.tsx",
|
|
1178
1279
|
"components": [
|
|
@@ -1,10 +1,14 @@
|
|
|
1
|
-
import { ChartCurve, NumericXProps, SeriesChartProps, ValueScaleProps } from "../../../lib/chart-series.js";
|
|
1
|
+
import { ChartCurve, LineMarksProps, NumericXProps, SeriesChartProps, ValueScaleProps } from "../../../lib/chart-series.js";
|
|
2
2
|
//#region src/components/registry/charts/area-chart-card.d.ts
|
|
3
|
-
export interface AreaChartCardProps<T extends object, K extends keyof T> extends SeriesChartProps<T, K>, NumericXProps, ValueScaleProps {
|
|
3
|
+
export interface AreaChartCardProps<T extends object, K extends keyof T> extends SeriesChartProps<T, K>, NumericXProps, ValueScaleProps, LineMarksProps<T, K> {
|
|
4
4
|
/** Stack the series into a total instead of overlapping them. */
|
|
5
5
|
stacked?: boolean;
|
|
6
6
|
curve?: ChartCurve;
|
|
7
7
|
}
|
|
8
|
-
/**
|
|
8
|
+
/**
|
|
9
|
+
* Values over an ordered axis: overlapping series drawn as lines, or stacked into a total as tinted bands. A
|
|
10
|
+
* stack keeps solid edges through a forecast stretch, since a dashed copy of a band would stack twice; the hatched
|
|
11
|
+
* band still marks it.
|
|
12
|
+
*/
|
|
9
13
|
export declare function AreaChartCard<T extends object, K extends keyof T>({ stacked, curve, ...props }: AreaChartCardProps<T, K>): import("react").JSX.Element;
|
|
10
14
|
//#endregion
|
|
@@ -1,40 +1,67 @@
|
|
|
1
1
|
"use client";
|
|
2
|
+
import { QUIET, drawOrder } from "../../../lib/chart-marks.js";
|
|
2
3
|
import { ChartFrame, endDot } from "../../../lib/chart-frame.js";
|
|
3
4
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
4
|
-
import {
|
|
5
|
+
import { Fragment as Fragment$1 } from "react";
|
|
5
6
|
import { Area, AreaChart } from "recharts";
|
|
6
7
|
//#region src/components/registry/charts/area-chart-card.tsx
|
|
7
|
-
/**
|
|
8
|
+
/**
|
|
9
|
+
* Values over an ordered axis: overlapping series drawn as lines, or stacked into a total as tinted bands. A
|
|
10
|
+
* stack keeps solid edges through a forecast stretch, since a dashed copy of a band would stack twice; the hatched
|
|
11
|
+
* band still marks it.
|
|
12
|
+
*/
|
|
8
13
|
function AreaChartCard({ stacked = false, curve = "monotone", ...props }) {
|
|
9
|
-
const
|
|
14
|
+
const forecastEnd = props.forecastFrom !== void 0;
|
|
15
|
+
const forecast = !stacked && forecastEnd;
|
|
16
|
+
const count = props.data.length;
|
|
17
|
+
const series = stacked ? props.series : drawOrder(props.series);
|
|
10
18
|
return /* @__PURE__ */ jsxs(ChartFrame, {
|
|
11
19
|
chart: AreaChart,
|
|
12
20
|
logBlocked: stacked ? "Log cannot show stacked areas" : void 0,
|
|
21
|
+
endLabels: !stacked,
|
|
22
|
+
forecastHatch: !stacked,
|
|
23
|
+
curve,
|
|
13
24
|
...props,
|
|
14
|
-
children: [
|
|
15
|
-
id: `${id}-${index}`,
|
|
16
|
-
x1: "0",
|
|
17
|
-
y1: "0",
|
|
18
|
-
x2: "0",
|
|
19
|
-
y2: "1",
|
|
20
|
-
children: [/* @__PURE__ */ jsx("stop", {
|
|
21
|
-
offset: "0%",
|
|
22
|
-
stopColor: color,
|
|
23
|
-
stopOpacity: stacked ? .5 : .32
|
|
24
|
-
}), /* @__PURE__ */ jsx("stop", {
|
|
25
|
-
offset: "100%",
|
|
26
|
-
stopColor: color,
|
|
27
|
-
stopOpacity: stacked ? .15 : .02
|
|
28
|
-
})]
|
|
29
|
-
}, key)) }), props.series.map(({ key, color }, index) => /* @__PURE__ */ jsx(Area, {
|
|
25
|
+
children: [forecast && series.map(({ key }) => /* @__PURE__ */ jsx(Area, {
|
|
30
26
|
dataKey: key,
|
|
31
27
|
type: curve,
|
|
32
|
-
fill:
|
|
33
|
-
stroke:
|
|
34
|
-
strokeWidth:
|
|
35
|
-
dot:
|
|
36
|
-
|
|
37
|
-
|
|
28
|
+
fill: "none",
|
|
29
|
+
stroke: "var(--card)",
|
|
30
|
+
strokeWidth: 6,
|
|
31
|
+
dot: false,
|
|
32
|
+
activeDot: false,
|
|
33
|
+
tooltipType: "none",
|
|
34
|
+
legendType: "none",
|
|
35
|
+
className: "chart-forecast",
|
|
36
|
+
animationDuration: 1300
|
|
37
|
+
}, `halo-${key}`)), series.map(({ key, color }) => {
|
|
38
|
+
const quiet = color === QUIET;
|
|
39
|
+
return /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx(Area, {
|
|
40
|
+
dataKey: key,
|
|
41
|
+
type: curve,
|
|
42
|
+
fill: stacked ? color : "none",
|
|
43
|
+
fillOpacity: stacked ? .25 : void 0,
|
|
44
|
+
stroke: color,
|
|
45
|
+
strokeWidth: 2,
|
|
46
|
+
dot: quiet || forecast ? false : endDot(color, count, forecastEnd),
|
|
47
|
+
stackId: stacked ? "stack" : void 0,
|
|
48
|
+
className: forecast ? "chart-actual" : void 0,
|
|
49
|
+
animationDuration: 1300
|
|
50
|
+
}), forecast && /* @__PURE__ */ jsx(Area, {
|
|
51
|
+
dataKey: key,
|
|
52
|
+
type: curve,
|
|
53
|
+
fill: "none",
|
|
54
|
+
stroke: color,
|
|
55
|
+
strokeWidth: 2,
|
|
56
|
+
strokeDasharray: "5 4",
|
|
57
|
+
dot: quiet ? false : endDot(color, count, true),
|
|
58
|
+
activeDot: false,
|
|
59
|
+
tooltipType: "none",
|
|
60
|
+
legendType: "none",
|
|
61
|
+
className: "chart-forecast",
|
|
62
|
+
animationDuration: 1300
|
|
63
|
+
})] }, key);
|
|
64
|
+
})]
|
|
38
65
|
});
|
|
39
66
|
}
|
|
40
67
|
//#endregion
|
|
@@ -10,5 +10,6 @@ export interface BarChartCardProps<T extends object, K extends keyof T> extends
|
|
|
10
10
|
/** Called with a clicked category's x value, or null when the picked one is clicked again. */
|
|
11
11
|
onSelect?: (value: T[K] | null) => void;
|
|
12
12
|
}
|
|
13
|
+
/** Values over an ordered axis as bars: one series, or several grouped or stacked. */
|
|
13
14
|
export declare function BarChartCard<T extends object, K extends keyof T>({ stacked, selected, onSelect, ...props }: BarChartCardProps<T, K>): import("react").JSX.Element;
|
|
14
15
|
//#endregion
|
|
@@ -1,12 +1,10 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { ChartFrame, seriesFill } from "../../../lib/chart-frame.js";
|
|
2
|
+
import { ChartFrame, barFill, seriesFill } from "../../../lib/chart-frame.js";
|
|
3
3
|
import { jsx } from "react/jsx-runtime";
|
|
4
4
|
import { Bar, BarChart, Cell } from "recharts";
|
|
5
5
|
//#region src/components/registry/charts/bar-chart-card.tsx
|
|
6
6
|
const DOWN_FROM_ZERO = ["auto", 0];
|
|
7
7
|
/** Values over an ordered axis as bars: one series, or several grouped or stacked. */
|
|
8
|
-
const dimUnselected = (color, isSelected) => isSelected ? color : `color-mix(in oklab, ${color} 55%, var(--card))`;
|
|
9
|
-
const signedFill = (color, isSelected) => `color-mix(in oklab, ${color} ${isSelected ? 35 : 15}%, var(--card))`;
|
|
10
8
|
function BarChartCard({ stacked = false, selected = null, onSelect, ...props }) {
|
|
11
9
|
return /* @__PURE__ */ jsx(ChartFrame, {
|
|
12
10
|
chart: BarChart,
|
|
@@ -18,7 +16,7 @@ function BarChartCard({ stacked = false, selected = null, onSelect, ...props })
|
|
|
18
16
|
const row = props.data[Number(activeTooltipIndex)];
|
|
19
17
|
if (row) onSelect(row[props.xKey] === selected ? null : row[props.xKey]);
|
|
20
18
|
} : void 0,
|
|
21
|
-
children: props.series.map((item
|
|
19
|
+
children: props.series.map((item) => {
|
|
22
20
|
const { key, label, color, negativeColor } = item;
|
|
23
21
|
return /* @__PURE__ */ jsx(Bar, {
|
|
24
22
|
dataKey: key,
|
|
@@ -26,28 +24,14 @@ function BarChartCard({ stacked = false, selected = null, onSelect, ...props })
|
|
|
26
24
|
fill: seriesFill(item, props.data),
|
|
27
25
|
maxBarSize: props.horizontal ? 24 : void 0,
|
|
28
26
|
stackId: stacked ? "stack" : void 0,
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
2,
|
|
32
|
-
2,
|
|
33
|
-
0
|
|
34
|
-
] : [
|
|
35
|
-
2,
|
|
36
|
-
2,
|
|
37
|
-
0,
|
|
38
|
-
0
|
|
39
|
-
] : 0,
|
|
40
|
-
children: (negativeColor || selected !== null) && props.data.map((row, i) => {
|
|
41
|
-
const isSelected = selected === null || row[props.xKey] === selected;
|
|
27
|
+
isAnimationActive: false,
|
|
28
|
+
children: props.data.map((row, i) => {
|
|
42
29
|
const tone = negativeColor && Number(row[key]) < 0 ? negativeColor : color;
|
|
43
|
-
|
|
44
|
-
|
|
30
|
+
const dim = selected !== null && row[props.xKey] !== selected;
|
|
31
|
+
return /* @__PURE__ */ jsx(Cell, {
|
|
32
|
+
fill: barFill(tone, dim),
|
|
45
33
|
stroke: tone,
|
|
46
|
-
strokeWidth: 1
|
|
47
|
-
}, i) : /* @__PURE__ */ jsx(Cell, {
|
|
48
|
-
fill: dimUnselected(tone, isSelected),
|
|
49
|
-
stroke: tone,
|
|
50
|
-
strokeWidth: 1.5
|
|
34
|
+
strokeWidth: 1
|
|
51
35
|
}, i);
|
|
52
36
|
})
|
|
53
37
|
}, key);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { ChartCurve, NumericXProps, SeriesChartProps, ValueScaleProps } from "../../../lib/chart-series.js";
|
|
1
|
+
import { ChartCurve, LineMarksProps, NumericXProps, SeriesChartProps, ValueScaleProps } from "../../../lib/chart-series.js";
|
|
2
2
|
//#region src/components/registry/charts/line-chart-card.d.ts
|
|
3
|
-
export interface LineChartCardProps<T extends object, K extends keyof T> extends SeriesChartProps<T, K>, NumericXProps, ValueScaleProps {
|
|
3
|
+
export interface LineChartCardProps<T extends object, K extends keyof T> extends SeriesChartProps<T, K>, NumericXProps, ValueScaleProps, LineMarksProps<T, K> {
|
|
4
4
|
curve?: ChartCurve;
|
|
5
5
|
}
|
|
6
6
|
/** Values over an ordered axis as lines: one series, or several to compare, such as sales against a target. */
|
|
@@ -1,20 +1,55 @@
|
|
|
1
1
|
"use client";
|
|
2
|
+
import { drawOrder } from "../../../lib/chart-marks.js";
|
|
2
3
|
import { ChartFrame, endDot } from "../../../lib/chart-frame.js";
|
|
3
|
-
import { jsx } from "react/jsx-runtime";
|
|
4
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
5
|
+
import { Fragment as Fragment$1 } from "react";
|
|
4
6
|
import { Line, LineChart } from "recharts";
|
|
5
7
|
//#region src/components/registry/charts/line-chart-card.tsx
|
|
6
8
|
/** Values over an ordered axis as lines: one series, or several to compare, such as sales against a target. */
|
|
7
9
|
function LineChartCard({ curve = "monotone", ...props }) {
|
|
8
|
-
|
|
10
|
+
const forecast = props.forecastFrom !== void 0;
|
|
11
|
+
const count = props.data.length;
|
|
12
|
+
const series = drawOrder(props.series);
|
|
13
|
+
return /* @__PURE__ */ jsxs(ChartFrame, {
|
|
9
14
|
chart: LineChart,
|
|
15
|
+
endLabels: true,
|
|
16
|
+
curve,
|
|
10
17
|
...props,
|
|
11
|
-
children:
|
|
18
|
+
children: [forecast && series.map(({ key }) => /* @__PURE__ */ jsx(Line, {
|
|
12
19
|
dataKey: key,
|
|
13
20
|
type: curve,
|
|
14
|
-
stroke:
|
|
15
|
-
strokeWidth:
|
|
16
|
-
dot:
|
|
17
|
-
|
|
21
|
+
stroke: "var(--card)",
|
|
22
|
+
strokeWidth: 6,
|
|
23
|
+
dot: false,
|
|
24
|
+
activeDot: false,
|
|
25
|
+
tooltipType: "none",
|
|
26
|
+
legendType: "none",
|
|
27
|
+
className: "chart-forecast",
|
|
28
|
+
animationDuration: 1300
|
|
29
|
+
}, `halo-${key}`)), series.map(({ key, color }) => {
|
|
30
|
+
const dot = color === "var(--muted-foreground)" || forecast ? false : endDot(color, count);
|
|
31
|
+
return /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx(Line, {
|
|
32
|
+
dataKey: key,
|
|
33
|
+
type: curve,
|
|
34
|
+
stroke: color,
|
|
35
|
+
strokeWidth: 2,
|
|
36
|
+
dot,
|
|
37
|
+
className: forecast ? "chart-actual" : void 0,
|
|
38
|
+
animationDuration: 1300
|
|
39
|
+
}), forecast && /* @__PURE__ */ jsx(Line, {
|
|
40
|
+
dataKey: key,
|
|
41
|
+
type: curve,
|
|
42
|
+
stroke: color,
|
|
43
|
+
strokeWidth: 2,
|
|
44
|
+
strokeDasharray: "5 4",
|
|
45
|
+
dot: color === "var(--muted-foreground)" ? false : endDot(color, count, true),
|
|
46
|
+
activeDot: false,
|
|
47
|
+
tooltipType: "none",
|
|
48
|
+
legendType: "none",
|
|
49
|
+
className: "chart-forecast",
|
|
50
|
+
animationDuration: 1300
|
|
51
|
+
})] }, key);
|
|
52
|
+
})]
|
|
18
53
|
});
|
|
19
54
|
}
|
|
20
55
|
//#endregion
|
|
@@ -16,6 +16,8 @@ export interface SectionCard {
|
|
|
16
16
|
disabled?: boolean;
|
|
17
17
|
/** Marks the tile as the exception on the page: a faint wash of the colour and an outline of it. */
|
|
18
18
|
tone?: "negative" | "warning";
|
|
19
|
+
/** Marks the tile's data as old, with since when, such as "3 Nov": the tile greys out and says so. */
|
|
20
|
+
stale?: React$1.ReactNode;
|
|
19
21
|
}
|
|
20
22
|
export interface SectionCardsProps {
|
|
21
23
|
items: SectionCard[];
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import { Badge } from "../../ui/badge.js";
|
|
3
3
|
import { Card, CardAction, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from "../../ui/card.js";
|
|
4
4
|
import { cn } from "../../../lib/utils.js";
|
|
5
|
+
import { StaleNote } from "../../siyahi/stale-note.js";
|
|
5
6
|
import { useSurface } from "../../siyahi/siyahi-provider.js";
|
|
6
7
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
7
8
|
import "react";
|
|
@@ -26,6 +27,7 @@ function SectionCards({ items, selected, onSelect }) {
|
|
|
26
27
|
className: cn("min-w-0", ruled && "border-e border-b"),
|
|
27
28
|
children: /* @__PURE__ */ jsxs(Card, {
|
|
28
29
|
"data-tone": item.tone,
|
|
30
|
+
"data-stale": item.stale ? "" : void 0,
|
|
29
31
|
className: cn("@container/card h-full [--card-spacing:--spacing(3)]", onSelect && "relative", item.label === selected && "ring-2 ring-primary!", item.label === selected && ruled && "ring-inset", onSelect && item.disabled && "opacity-60"),
|
|
30
32
|
children: [
|
|
31
33
|
onSelect && /* @__PURE__ */ jsx("button", {
|
|
@@ -41,6 +43,7 @@ function SectionCards({ items, selected, onSelect }) {
|
|
|
41
43
|
className: "font-mono text-2xs tracking-wider uppercase",
|
|
42
44
|
children: item.label
|
|
43
45
|
}),
|
|
46
|
+
item.stale && /* @__PURE__ */ jsx(StaleNote, { since: item.stale }),
|
|
44
47
|
/* @__PURE__ */ jsx(CardTitle, {
|
|
45
48
|
className: "text-2xl font-semibold tabular-nums",
|
|
46
49
|
children: item.value
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { cn } from "../../../lib/utils.js";
|
|
2
|
-
import { useHeatmapNavigation } from "../../../lib/heatmap-navigation.js";
|
|
3
2
|
import { useElementWidth } from "../../../lib/use-element-width.js";
|
|
3
|
+
import { useHeatmapNavigation } from "../../../lib/heatmap-navigation.js";
|
|
4
4
|
import { heat, heatFill } from "../../../lib/heat.js";
|
|
5
5
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
6
6
|
import { Fragment as Fragment$1, createContext, use, useLayoutEffect, useMemo } from "react";
|
|
@@ -352,7 +352,7 @@ const CalendarHeatmapBlock = ({ ref, activity, dayIndex, weekIndex, highlighted
|
|
|
352
352
|
"aria-pressed": onCellClick ? selected : void 0,
|
|
353
353
|
tabIndex: onCellClick ? 0 : -1,
|
|
354
354
|
"aria-label": ariaLabel,
|
|
355
|
-
className: cn("hover:stroke-foreground hover:stroke-1", highlighted && "stroke-foreground stroke-1", selected && "stroke-foreground stroke-2 hover:stroke-2", onCellClick && "cursor-pointer
|
|
355
|
+
className: cn("hover:stroke-foreground hover:stroke-1", highlighted && "stroke-foreground stroke-1", selected && "stroke-foreground stroke-2 hover:stroke-2", onCellClick && "cursor-pointer", className),
|
|
356
356
|
"data-value": activity.value,
|
|
357
357
|
"data-date": activity.date,
|
|
358
358
|
"data-level": level,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { cn } from "../../../lib/utils.js";
|
|
2
|
-
import { useHeatmapNavigation } from "../../../lib/heatmap-navigation.js";
|
|
3
2
|
import { useElementWidth } from "../../../lib/use-element-width.js";
|
|
3
|
+
import { useHeatmapNavigation } from "../../../lib/heatmap-navigation.js";
|
|
4
4
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
5
5
|
import { Fragment as Fragment$1, createContext, use, useId, useMemo } from "react";
|
|
6
6
|
import { format } from "date-fns";
|
|
@@ -208,7 +208,7 @@ const StatusHeatmapBlock = ({ ref, activity, dayIndex, highlighted = false, sele
|
|
|
208
208
|
"aria-pressed": onCellClick ? selected : void 0,
|
|
209
209
|
tabIndex: onCellClick ? 0 : -1,
|
|
210
210
|
"aria-label": ariaLabel,
|
|
211
|
-
className: cn(edge && "stroke-input stroke-1", "hover:stroke-foreground hover:stroke-1", highlighted && "stroke-foreground stroke-1", selected && "stroke-foreground stroke-2 hover:stroke-2", onCellClick && "cursor-pointer
|
|
211
|
+
className: cn(edge && "stroke-input stroke-1", "hover:stroke-foreground hover:stroke-1", highlighted && "stroke-foreground stroke-1", selected && "stroke-foreground stroke-2 hover:stroke-2", onCellClick && "cursor-pointer", className),
|
|
212
212
|
"data-value": activity.value,
|
|
213
213
|
"data-date": activity.date,
|
|
214
214
|
"data-highlighted": highlighted || void 0,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { cn } from "../../../lib/utils.js";
|
|
2
|
-
import { useHeatmapNavigation } from "../../../lib/heatmap-navigation.js";
|
|
3
2
|
import { useElementWidth } from "../../../lib/use-element-width.js";
|
|
3
|
+
import { useHeatmapNavigation } from "../../../lib/heatmap-navigation.js";
|
|
4
4
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
5
5
|
import { Fragment as Fragment$1, createContext, use, useMemo } from "react";
|
|
6
6
|
import { format } from "date-fns";
|
|
@@ -258,7 +258,7 @@ const WeekdayHeatmapBlock = ({ ref, activity, extra, highlighted = false, select
|
|
|
258
258
|
"aria-pressed": onCellClick ? selected : void 0,
|
|
259
259
|
tabIndex: onCellClick ? 0 : -1,
|
|
260
260
|
"aria-label": ariaLabel,
|
|
261
|
-
className: cn("hover:stroke-foreground hover:stroke-1", highlighted && "stroke-foreground stroke-1", selected && "stroke-foreground stroke-2 hover:stroke-2", onCellClick && "cursor-pointer
|
|
261
|
+
className: cn("hover:stroke-foreground hover:stroke-1", highlighted && "stroke-foreground stroke-1", selected && "stroke-foreground stroke-2 hover:stroke-2", onCellClick && "cursor-pointer", className),
|
|
262
262
|
"data-value": activity.value,
|
|
263
263
|
"data-weekday": isExtraRow ? "extra" : activity.weekday,
|
|
264
264
|
"data-hour": isExtraColumn ? "extra" : activity.hour,
|
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
import { Badge } from "../../../ui/badge.js";
|
|
3
3
|
import { Button } from "../../../ui/button.js";
|
|
4
4
|
import { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList, CommandSeparator } from "../../../ui/command.js";
|
|
5
|
-
import { Popover, PopoverContent, PopoverTrigger } from "../../../ui/popover.js";
|
|
6
5
|
import { Separator } from "../../../ui/separator.js";
|
|
6
|
+
import { Popover, PopoverContent, PopoverTrigger } from "../../../ui/popover.js";
|
|
7
7
|
import { cn } from "../../../../lib/utils.js";
|
|
8
8
|
import { FILTER_OPERATORS, FILTER_VARIANTS, JOIN_OPERATORS } from "../lib/constants.js";
|
|
9
9
|
import { Check, PlusCircle, XCircle } from "lucide-react";
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { useLocale } from "./siyahi-provider.js";
|
|
2
2
|
import { linearTicks } from "../../lib/ticks.js";
|
|
3
|
-
import { ChartFrame } from "../../lib/chart-frame.js";
|
|
3
|
+
import { ChartFrame, barFill } from "../../lib/chart-frame.js";
|
|
4
4
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
5
5
|
import { useMemo } from "react";
|
|
6
|
-
import { Bar, BarChart } from "recharts";
|
|
6
|
+
import { Bar, BarChart, Cell } from "recharts";
|
|
7
7
|
//#region src/components/siyahi/histogram-chart.tsx
|
|
8
8
|
/**
|
|
9
9
|
* Buckets of one round width (1, 2, 2.5 or 5 × 10ⁿ) covering the values, about `target` of them. Values that are not
|
|
@@ -121,12 +121,12 @@ function HistogramChartCard({ values, bins: target = 12, valueFormatter, tickFor
|
|
|
121
121
|
dataKey: "count",
|
|
122
122
|
name: countLabel,
|
|
123
123
|
fill: "var(--chart-1)",
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
124
|
+
isAnimationActive: false,
|
|
125
|
+
children: bins.map((bin) => /* @__PURE__ */ jsx(Cell, {
|
|
126
|
+
fill: barFill("var(--chart-1)"),
|
|
127
|
+
stroke: "var(--chart-1)",
|
|
128
|
+
strokeWidth: 1
|
|
129
|
+
}, bin.key))
|
|
130
130
|
})
|
|
131
131
|
});
|
|
132
132
|
}
|
|
@@ -279,7 +279,7 @@ function Dot({ cx, cy, payload, index, "data-recharts-item-id": itemId, selected
|
|
|
279
279
|
tabIndex: onSelect ? key === focusKey ? 0 : -1 : void 0,
|
|
280
280
|
"aria-label": describe(payload),
|
|
281
281
|
"aria-pressed": onSelect ? isSelected : void 0,
|
|
282
|
-
className: onSelect ? "cursor-pointer
|
|
282
|
+
className: onSelect ? "cursor-pointer" : void 0,
|
|
283
283
|
onKeyDown: (event) => onKeyDown(event, payload),
|
|
284
284
|
onClick: (event) => {
|
|
285
285
|
if (!onSelect) return;
|