@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.
Files changed (46) hide show
  1. package/DESIGN.md +79 -10
  2. package/agents/examples/avatar.tsx +15 -5
  3. package/agents/examples/chart-line-forecast.tsx +36 -0
  4. package/agents/examples/chart-line-quiet.tsx +114 -0
  5. package/agents/examples/chart-panels.tsx +94 -0
  6. package/agents/examples/code-block.tsx +5 -3
  7. package/agents/examples/field.tsx +108 -0
  8. package/agents/examples/formatters.tsx +14 -14
  9. package/agents/examples/input-states.tsx +12 -22
  10. package/agents/examples/overview-dashboard.tsx +6 -6
  11. package/agents/examples/stale-blocks.tsx +56 -0
  12. package/agents/examples/table.tsx +6 -6
  13. package/agents/manifest.json +104 -3
  14. package/dist/components/registry/charts/area-chart-card.d.ts +7 -3
  15. package/dist/components/registry/charts/area-chart-card.js +52 -25
  16. package/dist/components/registry/charts/bar-chart-card.d.ts +1 -0
  17. package/dist/components/registry/charts/bar-chart-card.js +8 -24
  18. package/dist/components/registry/charts/line-chart-card.d.ts +2 -2
  19. package/dist/components/registry/charts/line-chart-card.js +42 -7
  20. package/dist/components/registry/dashboard-01/section-cards.d.ts +2 -0
  21. package/dist/components/registry/dashboard-01/section-cards.js +3 -0
  22. package/dist/components/registry/heatmap/calendar-heatmap.js +2 -2
  23. package/dist/components/registry/heatmap/status-heatmap.js +2 -2
  24. package/dist/components/registry/heatmap/weekday-heatmap.js +2 -2
  25. package/dist/components/registry/niko-table/filters/table-faceted-filter.js +1 -1
  26. package/dist/components/siyahi/histogram-chart.js +8 -8
  27. package/dist/components/siyahi/scatter-chart.js +1 -1
  28. package/dist/components/siyahi/slope-chart.js +1 -1
  29. package/dist/components/siyahi/stale-note.d.ts +9 -0
  30. package/dist/components/siyahi/stale-note.js +12 -0
  31. package/dist/components/siyahi/treemap.js +1 -1
  32. package/dist/components/siyahi/waterfall-chart.js +4 -4
  33. package/dist/components/ui/field.d.ts +26 -0
  34. package/dist/components/ui/field.js +109 -0
  35. package/dist/components/ui/label.d.ts +5 -0
  36. package/dist/components/ui/label.js +13 -0
  37. package/dist/index.d.ts +5 -2
  38. package/dist/index.js +5 -2
  39. package/dist/lib/chart-frame.d.ts +28 -4
  40. package/dist/lib/chart-frame.js +256 -153
  41. package/dist/lib/chart-marks.d.ts +83 -0
  42. package/dist/lib/chart-marks.js +216 -0
  43. package/dist/lib/chart-series.d.ts +15 -0
  44. package/package.json +1 -1
  45. package/report.css +6 -4
  46. 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 className="text-right">In</TableHead>
36
- <TableHead className="text-right">Out</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 className="text-right">{money(row.in)}</TableCell>
45
- <TableCell className="text-right">{money(row.out)}</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 className="text-right">{money(total("in"))}</TableCell>
53
- <TableCell className="text-right">{money(total("out"))}</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>
@@ -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
- "AvatarFallback"
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
- /** Values over an ordered axis as filled areas: one series, or several overlapping or stacked. */
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 { useId } from "react";
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
- /** Values over an ordered axis as filled areas: one series, or several overlapping or stacked. */
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 id = useId();
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: [/* @__PURE__ */ jsx("defs", { children: props.series.map(({ key, color }, index) => /* @__PURE__ */ jsxs("linearGradient", {
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: stacked || index === 0 ? `url(#${id}-${index})` : "none",
33
- stroke: color,
34
- strokeWidth: 2,
35
- dot: endDot(color, props.data.length),
36
- stackId: stacked ? "stack" : void 0
37
- }, key))]
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, index) => {
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
- radius: !stacked ? 2 : index === props.series.length - 1 ? props.horizontal ? [
30
- 0,
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
- return negativeColor ? /* @__PURE__ */ jsx(Cell, {
44
- fill: signedFill(tone, isSelected),
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.5
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
- return /* @__PURE__ */ jsx(ChartFrame, {
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: props.series.map(({ key, color }) => /* @__PURE__ */ jsx(Line, {
18
+ children: [forecast && series.map(({ key }) => /* @__PURE__ */ jsx(Line, {
12
19
  dataKey: key,
13
20
  type: curve,
14
- stroke: color,
15
- strokeWidth: 2,
16
- dot: endDot(color, props.data.length)
17
- }, key))
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 focus-visible:outline-none focus-visible:stroke-ring focus-visible:stroke-2", className),
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 focus-visible:outline-none focus-visible:stroke-ring focus-visible:stroke-2", className),
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 focus-visible:outline-none focus-visible:stroke-ring focus-visible:stroke-2", className),
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
- radius: [
125
- 2,
126
- 2,
127
- 0,
128
- 0
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 outline-none focus-visible:stroke-ring focus-visible:stroke-2" : void 0,
282
+ className: onSelect ? "cursor-pointer" : void 0,
283
283
  onKeyDown: (event) => onKeyDown(event, payload),
284
284
  onClick: (event) => {
285
285
  if (!onSelect) return;