@seekalfred/primitives 0.7.1 → 0.7.4

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 (147) hide show
  1. package/README.md +255 -5
  2. package/dist/alert/alert.d.ts +49 -4
  3. package/dist/alert/alert.js +66 -38
  4. package/dist/alert/alert.js.map +1 -1
  5. package/dist/alert/index.d.ts +2 -2
  6. package/dist/alert/index.js +2 -2
  7. package/dist/alert-dialog/alert-dialog.d.ts +31 -4
  8. package/dist/alert-dialog/alert-dialog.js +75 -26
  9. package/dist/alert-dialog/alert-dialog.js.map +1 -1
  10. package/dist/announcement-bar/announcement-bar.d.ts +15 -4
  11. package/dist/announcement-bar/announcement-bar.js +31 -6
  12. package/dist/announcement-bar/announcement-bar.js.map +1 -1
  13. package/dist/avatar/avatar.d.ts +7 -0
  14. package/dist/avatar/avatar.js +33 -12
  15. package/dist/avatar/avatar.js.map +1 -1
  16. package/dist/badge/badge.d.ts +1 -1
  17. package/dist/bar-chart/bar-chart-series.d.ts +75 -0
  18. package/dist/bar-chart/bar-chart-series.js +401 -0
  19. package/dist/bar-chart/bar-chart-series.js.map +1 -0
  20. package/dist/bar-chart/bar-chart.d.ts +25 -1
  21. package/dist/bar-chart/bar-chart.js +26 -1
  22. package/dist/bar-chart/bar-chart.js.map +1 -1
  23. package/dist/bar-chart/index.d.ts +2 -1
  24. package/dist/button/button.d.ts +1 -1
  25. package/dist/calendar/calendar.d.ts +1 -1
  26. package/dist/card/card.d.ts +22 -1
  27. package/dist/card/card.js +60 -7
  28. package/dist/card/card.js.map +1 -1
  29. package/dist/carousel/carousel.d.ts +21 -0
  30. package/dist/carousel/carousel.js +34 -8
  31. package/dist/carousel/carousel.js.map +1 -1
  32. package/dist/chip-group/chip-group.d.ts +11 -0
  33. package/dist/chip-group/chip-group.js +50 -34
  34. package/dist/chip-group/chip-group.js.map +1 -1
  35. package/dist/chip-group/index.d.ts +2 -2
  36. package/dist/copy-button/copy-button.d.ts +37 -5
  37. package/dist/copy-button/copy-button.js +35 -11
  38. package/dist/copy-button/copy-button.js.map +1 -1
  39. package/dist/donut-chart/donut-chart.d.ts +22 -0
  40. package/dist/donut-chart/donut-chart.js +35 -18
  41. package/dist/donut-chart/donut-chart.js.map +1 -1
  42. package/dist/donut-chart/index.d.ts +2 -2
  43. package/dist/empty-state/empty-state.d.ts +21 -0
  44. package/dist/empty-state/empty-state.js +16 -4
  45. package/dist/empty-state/empty-state.js.map +1 -1
  46. package/dist/empty-state/index.d.ts +2 -2
  47. package/dist/empty-state/index.js +2 -2
  48. package/dist/file-drop-zone/file-drop-zone.d.ts +29 -3
  49. package/dist/file-drop-zone/file-drop-zone.js +26 -8
  50. package/dist/file-drop-zone/file-drop-zone.js.map +1 -1
  51. package/dist/filter-bar/filter-bar.d.ts +4 -1
  52. package/dist/filter-bar/filter-bar.js +53 -9
  53. package/dist/filter-bar/filter-bar.js.map +1 -1
  54. package/dist/gauge/gauge.d.ts +18 -1
  55. package/dist/gauge/gauge.js +35 -21
  56. package/dist/gauge/gauge.js.map +1 -1
  57. package/dist/gauge/index.d.ts +2 -2
  58. package/dist/index.d.ts +15 -14
  59. package/dist/index.js +9 -9
  60. package/dist/input/input.d.ts +7 -0
  61. package/dist/input/input.js +7 -5
  62. package/dist/input/input.js.map +1 -1
  63. package/dist/lib/field-shell.js +6 -3
  64. package/dist/lib/field-shell.js.map +1 -1
  65. package/dist/lib/field-surface.js +22 -1
  66. package/dist/lib/field-surface.js.map +1 -1
  67. package/dist/lib/required-marker.js +20 -0
  68. package/dist/lib/required-marker.js.map +1 -0
  69. package/dist/lib/use-copy-feedback.js +17 -3
  70. package/dist/lib/use-copy-feedback.js.map +1 -1
  71. package/dist/line-chart/index.d.ts +2 -2
  72. package/dist/line-chart/line-chart.d.ts +144 -2
  73. package/dist/line-chart/line-chart.js +314 -46
  74. package/dist/line-chart/line-chart.js.map +1 -1
  75. package/dist/money-input/index.d.ts +2 -2
  76. package/dist/money-input/money-input.d.ts +36 -1
  77. package/dist/money-input/money-input.js +30 -14
  78. package/dist/money-input/money-input.js.map +1 -1
  79. package/dist/morph-select/morph-select.d.ts +26 -1
  80. package/dist/morph-select/morph-select.js +28 -6
  81. package/dist/morph-select/morph-select.js.map +1 -1
  82. package/dist/multi-select/index.d.ts +2 -2
  83. package/dist/multi-select/index.js +2 -2
  84. package/dist/multi-select/multi-select.d.ts +74 -2
  85. package/dist/multi-select/multi-select.js +213 -39
  86. package/dist/multi-select/multi-select.js.map +1 -1
  87. package/dist/password-field/password-field.d.ts +16 -0
  88. package/dist/password-field/password-field.js +37 -8
  89. package/dist/password-field/password-field.js.map +1 -1
  90. package/dist/popover/index.d.ts +2 -2
  91. package/dist/popover/index.js +2 -2
  92. package/dist/popover/popover.d.ts +39 -2
  93. package/dist/popover/popover.js +29 -2
  94. package/dist/popover/popover.js.map +1 -1
  95. package/dist/progress/index.d.ts +2 -2
  96. package/dist/progress/index.js +2 -2
  97. package/dist/progress/progress.d.ts +68 -3
  98. package/dist/progress/progress.js +156 -39
  99. package/dist/progress/progress.js.map +1 -1
  100. package/dist/radio-group/index.d.ts +2 -2
  101. package/dist/radio-group/index.js +2 -2
  102. package/dist/radio-group/radio-group.d.ts +33 -3
  103. package/dist/radio-group/radio-group.js +109 -9
  104. package/dist/radio-group/radio-group.js.map +1 -1
  105. package/dist/scroll-area/scroll-area.js +4 -3
  106. package/dist/scroll-area/scroll-area.js.map +1 -1
  107. package/dist/scrub-input/scrub-input.d.ts +11 -1
  108. package/dist/scrub-input/scrub-input.js +22 -10
  109. package/dist/scrub-input/scrub-input.js.map +1 -1
  110. package/dist/search-field/search-field.d.ts +22 -1
  111. package/dist/search-field/search-field.js +22 -7
  112. package/dist/search-field/search-field.js.map +1 -1
  113. package/dist/select/select.d.ts +10 -1
  114. package/dist/select/select.js +11 -8
  115. package/dist/select/select.js.map +1 -1
  116. package/dist/separator/separator.d.ts +21 -9
  117. package/dist/separator/separator.js +33 -11
  118. package/dist/separator/separator.js.map +1 -1
  119. package/dist/sparkline/sparkline.d.ts +27 -2
  120. package/dist/sparkline/sparkline.js +115 -18
  121. package/dist/sparkline/sparkline.js.map +1 -1
  122. package/dist/stat-card/index.d.ts +2 -2
  123. package/dist/stat-card/index.js +2 -2
  124. package/dist/stat-card/stat-card.d.ts +65 -1
  125. package/dist/stat-card/stat-card.js +98 -25
  126. package/dist/stat-card/stat-card.js.map +1 -1
  127. package/dist/styles.css +1 -1
  128. package/dist/table/index.d.ts +2 -2
  129. package/dist/table/index.js +2 -2
  130. package/dist/table/table.d.ts +115 -5
  131. package/dist/table/table.js +374 -94
  132. package/dist/table/table.js.map +1 -1
  133. package/dist/text-shimmer/text-shimmer.d.ts +18 -2
  134. package/dist/text-shimmer/text-shimmer.js +5 -2
  135. package/dist/text-shimmer/text-shimmer.js.map +1 -1
  136. package/dist/textarea/textarea.d.ts +7 -0
  137. package/dist/textarea/textarea.js +3 -1
  138. package/dist/textarea/textarea.js.map +1 -1
  139. package/dist/time-picker/time-picker.d.ts +36 -1
  140. package/dist/time-picker/time-picker.js +33 -5
  141. package/dist/time-picker/time-picker.js.map +1 -1
  142. package/dist/toast/toast.d.ts +2 -2
  143. package/dist/usage-meter/index.d.ts +2 -2
  144. package/dist/usage-meter/usage-meter.d.ts +30 -0
  145. package/dist/usage-meter/usage-meter.js +56 -38
  146. package/dist/usage-meter/usage-meter.js.map +1 -1
  147. package/package.json +3 -3
@@ -6,15 +6,18 @@ import { SeparatorProps } from "react-aria-components";
6
6
  * Census: 7 files, 6 uses, one of them `orientation="vertical"`. No other
7
7
  * prop is ever passed.
8
8
  *
9
- * `decorative` is NOT ported -- zero call sites pass it, and React Aria has
10
- * no equivalent. That does change the semantics of all six existing uses,
11
- * which is worth saying out loud rather than burying: the legacy defaulted
12
- * to `decorative: true` and therefore rendered `role="none"`, so every
13
- * divider in the app is currently invisible to assistive technology. React
14
- * Aria renders an `<hr>` for a horizontal separator, whose implicit role IS
15
- * separator. A visible line between two groups of content is a separator;
16
- * announcing it is the correct behaviour, and it is what the element the
17
- * legacy chose (a `div`) was pretending not to be.
9
+ * The legacy defaulted to `decorative: true` and therefore rendered
10
+ * `role="none"`, so every divider in the app was invisible to assistive
11
+ * technology. Here the default is the opposite, which is worth saying out
12
+ * loud rather than burying: React Aria renders an `<hr>` for a horizontal
13
+ * separator, whose implicit role IS separator. A visible line between two
14
+ * groups of content is a separator; announcing it is the correct behaviour.
15
+ *
16
+ * `decorative` is the opt-in for the other case: a rule between inline
17
+ * metadata ("12 posts | 3 drafts") that only spaces items, where announcing
18
+ * "separator" between every pair is noise. React Aria has no equivalent and
19
+ * its Separator always carries a separator role, so a decorative one is a
20
+ * plain `aria-hidden` div drawn with the same classes.
18
21
  *
19
22
  * Every string in the cva blocks below is a STATIC literal. Tailwind scans
20
23
  * source text, so a class assembled at runtime is invisible to it and would
@@ -33,6 +36,15 @@ export declare const separatorVariants: (props?: ({
33
36
  interface SeparatorProps$1 extends Omit<SeparatorProps, "className" | "style"> {
34
37
  /** @default "horizontal" */
35
38
  orientation?: "horizontal" | "vertical";
39
+ /**
40
+ * Draw the line without announcing it: a plain `div` with
41
+ * `aria-hidden="true"` instead of a separator. For a rule that only spaces
42
+ * inline items (metadata, breadcrumbs-like rows), where a screen reader
43
+ * saying "separator" between each pair is noise. Leave it off for a line
44
+ * between two groups of content.
45
+ * @default false
46
+ */
47
+ decorative?: boolean;
36
48
  /** Extra classes. Merged last, so they win conflicts with ours. */
37
49
  className?: string;
38
50
  }
@@ -11,22 +11,25 @@ import { jsx } from "react/jsx-runtime";
11
11
  * Census: 7 files, 6 uses, one of them `orientation="vertical"`. No other
12
12
  * prop is ever passed.
13
13
  *
14
- * `decorative` is NOT ported -- zero call sites pass it, and React Aria has
15
- * no equivalent. That does change the semantics of all six existing uses,
16
- * which is worth saying out loud rather than burying: the legacy defaulted
17
- * to `decorative: true` and therefore rendered `role="none"`, so every
18
- * divider in the app is currently invisible to assistive technology. React
19
- * Aria renders an `<hr>` for a horizontal separator, whose implicit role IS
20
- * separator. A visible line between two groups of content is a separator;
21
- * announcing it is the correct behaviour, and it is what the element the
22
- * legacy chose (a `div`) was pretending not to be.
14
+ * The legacy defaulted to `decorative: true` and therefore rendered
15
+ * `role="none"`, so every divider in the app was invisible to assistive
16
+ * technology. Here the default is the opposite, which is worth saying out
17
+ * loud rather than burying: React Aria renders an `<hr>` for a horizontal
18
+ * separator, whose implicit role IS separator. A visible line between two
19
+ * groups of content is a separator; announcing it is the correct behaviour.
20
+ *
21
+ * `decorative` is the opt-in for the other case: a rule between inline
22
+ * metadata ("12 posts | 3 drafts") that only spaces items, where announcing
23
+ * "separator" between every pair is noise. React Aria has no equivalent and
24
+ * its Separator always carries a separator role, so a decorative one is a
25
+ * plain `aria-hidden` div drawn with the same classes.
23
26
  *
24
27
  * Every string in the cva blocks below is a STATIC literal. Tailwind scans
25
28
  * source text, so a class assembled at runtime is invisible to it and would
26
29
  * simply never be emitted.
27
30
  */
28
31
  const separatorVariants = cva([
29
- "shrink-0 bg-border border-none",
32
+ "shrink-0 bg-separator border-none",
30
33
  "m-0",
31
34
  "box-border"
32
35
  ], {
@@ -36,7 +39,26 @@ const separatorVariants = cva([
36
39
  } },
37
40
  defaultVariants: { orientation: "horizontal" }
38
41
  });
39
- const Separator$1 = forwardRef(function Separator$2({ className, orientation = "horizontal", ...props }, ref) {
42
+ /** Props a decorative separator does not forward to its div (see below). */
43
+ const DECORATIVE_DROPPED = [
44
+ "elementType",
45
+ "slot",
46
+ "render",
47
+ "aria-label",
48
+ "aria-labelledby"
49
+ ];
50
+ const Separator$1 = forwardRef(function Separator$2({ className, orientation = "horizontal", decorative = false, ...props }, ref) {
51
+ if (decorative) {
52
+ const rest = { ...props };
53
+ for (const key of DECORATIVE_DROPPED) delete rest[key];
54
+ return /* @__PURE__ */ jsx("div", {
55
+ ref,
56
+ ...rest,
57
+ "aria-hidden": "true",
58
+ "data-slot": "separator",
59
+ className: cn(separatorVariants({ orientation }), className)
60
+ });
61
+ }
40
62
  return /* @__PURE__ */ jsx(Separator, {
41
63
  ref,
42
64
  ...props,
@@ -1 +1 @@
1
- {"version":3,"file":"separator.js","names":["Separator","AriaSeparator"],"sources":["../../src/separator/separator.tsx"],"sourcesContent":["\"use client\";\n\nimport { cva } from \"class-variance-authority\";\nimport { forwardRef } from \"react\";\nimport {\n Separator as AriaSeparator,\n type SeparatorProps as AriaSeparatorProps,\n} from \"react-aria-components\";\n\nimport { cn } from \"../lib/cn.js\";\n\n/**\n * Separator, ported from the legacy component onto React Aria's Separator.\n *\n * Census: 7 files, 6 uses, one of them `orientation=\"vertical\"`. No other\n * prop is ever passed.\n *\n * `decorative` is NOT ported -- zero call sites pass it, and React Aria has\n * no equivalent. That does change the semantics of all six existing uses,\n * which is worth saying out loud rather than burying: the legacy defaulted\n * to `decorative: true` and therefore rendered `role=\"none\"`, so every\n * divider in the app is currently invisible to assistive technology. React\n * Aria renders an `<hr>` for a horizontal separator, whose implicit role IS\n * separator. A visible line between two groups of content is a separator;\n * announcing it is the correct behaviour, and it is what the element the\n * legacy chose (a `div`) was pretending not to be.\n *\n * Every string in the cva blocks below is a STATIC literal. Tailwind scans\n * source text, so a class assembled at runtime is invisible to it and would\n * simply never be emitted.\n */\nexport const separatorVariants = cva(\n [\n // border-none, not border-0. `border-0` sets the WIDTH to zero but leaves\n // border-style at solid, so the computed style still claims a border that\n // simply measures nothing -- the same bookkeeping the Checkbox parity run\n // turned up on the legacy input. `border-none` says what is true.\n \"shrink-0 bg-border border-none\",\n // The user-agent <hr> ships a block margin and a 1px inset border, and\n // React Aria renders an <hr> for the horizontal case. The reset in\n // @seekalfred/styles is OPT-IN, so a consumer may load none -- unreset,\n // every divider is a grooved double line with space around it.\n \"m-0\",\n \"box-border\",\n ],\n {\n variants: {\n orientation: {\n // h-px / w-px, not the legacy h-[1px] / w-[1px]. Those are bracket\n // values that check:hardcoding rejects, and `px` is a first-class\n // Tailwind step rather than an arbitrary one.\n horizontal: \"h-px w-full\",\n vertical: \"h-full w-px\",\n },\n },\n defaultVariants: { orientation: \"horizontal\" },\n },\n);\n\n/**\n * A visual divider between two groups of content.\n *\n * @example\n * <Separator />\n * <Separator orientation=\"vertical\" />\n */\nexport interface SeparatorProps extends Omit<AriaSeparatorProps, \"className\" | \"style\"> {\n /** @default \"horizontal\" */\n orientation?: \"horizontal\" | \"vertical\";\n /** Extra classes. Merged last, so they win conflicts with ours. */\n className?: string;\n}\n\nexport const Separator = forwardRef<HTMLElement, SeparatorProps>(function Separator(\n { className, orientation = \"horizontal\", ...props },\n ref,\n) {\n return (\n <AriaSeparator\n ref={ref}\n {...props}\n orientation={orientation}\n data-slot=\"separator\"\n className={cn(separatorVariants({ orientation }), className)}\n />\n );\n});\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;AA+BA,MAAa,oBAAoB,IAC/B;CAKE;CAKA;CACA;AACF,GACA;CACE,UAAU,EACR,aAAa;EAIX,YAAY;EACZ,UAAU;CACZ,EACF;CACA,iBAAiB,EAAE,aAAa,aAAa;AAC/C,CACF;AAgBA,MAAaA,cAAY,WAAwC,SAASA,YACxE,EAAE,WAAW,cAAc,cAAc,GAAG,SAC5C,KACA;CACA,OACE,oBAACC,WAAD;EACO;EACL,GAAI;EACS;EACb,aAAU;EACV,WAAW,GAAG,kBAAkB,EAAE,YAAY,CAAC,GAAG,SAAS;CAC5D,CAAA;AAEL,CAAC"}
1
+ {"version":3,"file":"separator.js","names":["Separator","AriaSeparator"],"sources":["../../src/separator/separator.tsx"],"sourcesContent":["\"use client\";\n\nimport { cva } from \"class-variance-authority\";\nimport { forwardRef, type Ref } from \"react\";\nimport {\n Separator as AriaSeparator,\n type SeparatorProps as AriaSeparatorProps,\n} from \"react-aria-components\";\n\nimport { cn } from \"../lib/cn.js\";\n\n/**\n * Separator, ported from the legacy component onto React Aria's Separator.\n *\n * Census: 7 files, 6 uses, one of them `orientation=\"vertical\"`. No other\n * prop is ever passed.\n *\n * The legacy defaulted to `decorative: true` and therefore rendered\n * `role=\"none\"`, so every divider in the app was invisible to assistive\n * technology. Here the default is the opposite, which is worth saying out\n * loud rather than burying: React Aria renders an `<hr>` for a horizontal\n * separator, whose implicit role IS separator. A visible line between two\n * groups of content is a separator; announcing it is the correct behaviour.\n *\n * `decorative` is the opt-in for the other case: a rule between inline\n * metadata (\"12 posts | 3 drafts\") that only spaces items, where announcing\n * \"separator\" between every pair is noise. React Aria has no equivalent and\n * its Separator always carries a separator role, so a decorative one is a\n * plain `aria-hidden` div drawn with the same classes.\n *\n * Every string in the cva blocks below is a STATIC literal. Tailwind scans\n * source text, so a class assembled at runtime is invisible to it and would\n * simply never be emitted.\n */\nexport const separatorVariants = cva(\n [\n // border-none, not border-0. `border-0` sets the WIDTH to zero but leaves\n // border-style at solid, so the computed style still claims a border that\n // simply measures nothing -- the same bookkeeping the Checkbox parity run\n // turned up on the legacy input. `border-none` says what is true.\n // The separator role, not `border`: `border` is the outline of a card or\n // field, `separator` the hairline between rows and groups inside one.\n \"shrink-0 bg-separator border-none\",\n // The user-agent <hr> ships a block margin and a 1px inset border, and\n // React Aria renders an <hr> for the horizontal case. The reset in\n // @seekalfred/styles is OPT-IN, so a consumer may load none -- unreset,\n // every divider is a grooved double line with space around it.\n \"m-0\",\n \"box-border\",\n ],\n {\n variants: {\n orientation: {\n // h-px / w-px, not the legacy h-[1px] / w-[1px]. Those are bracket\n // values that check:hardcoding rejects, and `px` is a first-class\n // Tailwind step rather than an arbitrary one.\n horizontal: \"h-px w-full\",\n vertical: \"h-full w-px\",\n },\n },\n defaultVariants: { orientation: \"horizontal\" },\n },\n);\n\n/**\n * A visual divider between two groups of content.\n *\n * @example\n * <Separator />\n * <Separator orientation=\"vertical\" />\n */\nexport interface SeparatorProps extends Omit<AriaSeparatorProps, \"className\" | \"style\"> {\n /** @default \"horizontal\" */\n orientation?: \"horizontal\" | \"vertical\";\n /**\n * Draw the line without announcing it: a plain `div` with\n * `aria-hidden=\"true\"` instead of a separator. For a rule that only spaces\n * inline items (metadata, breadcrumbs-like rows), where a screen reader\n * saying \"separator\" between each pair is noise. Leave it off for a line\n * between two groups of content.\n * @default false\n */\n decorative?: boolean;\n /** Extra classes. Merged last, so they win conflicts with ours. */\n className?: string;\n}\n\n/** Props a decorative separator does not forward to its div (see below). */\nconst DECORATIVE_DROPPED = [\n \"elementType\",\n \"slot\",\n \"render\",\n \"aria-label\",\n \"aria-labelledby\",\n] as const;\n\nexport const Separator = forwardRef<HTMLElement, SeparatorProps>(function Separator(\n { className, orientation = \"horizontal\", decorative = false, ...props },\n ref,\n) {\n if (decorative) {\n // Everything else the caller passed (id, data-*, handlers, dir...) reaches\n // the div, as it reaches React Aria's Separator below. Dropped: the props\n // that only mean something to React Aria (`elementType`, `slot`, `render`)\n // and a name, which an aria-hidden line has no use for.\n const rest = { ...props };\n for (const key of DECORATIVE_DROPPED) delete rest[key];\n return (\n <div\n ref={ref as Ref<HTMLDivElement>}\n {...(rest as Omit<typeof rest, (typeof DECORATIVE_DROPPED)[number]>)}\n aria-hidden=\"true\"\n data-slot=\"separator\"\n className={cn(separatorVariants({ orientation }), className)}\n />\n );\n }\n return (\n <AriaSeparator\n ref={ref}\n {...props}\n orientation={orientation}\n data-slot=\"separator\"\n className={cn(separatorVariants({ orientation }), className)}\n />\n );\n});\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAkCA,MAAa,oBAAoB,IAC/B;CAOE;CAKA;CACA;AACF,GACA;CACE,UAAU,EACR,aAAa;EAIX,YAAY;EACZ,UAAU;CACZ,EACF;CACA,iBAAiB,EAAE,aAAa,aAAa;AAC/C,CACF;;AA0BA,MAAM,qBAAqB;CACzB;CACA;CACA;CACA;CACA;AACF;AAEA,MAAaA,cAAY,WAAwC,SAASA,YACxE,EAAE,WAAW,cAAc,cAAc,aAAa,OAAO,GAAG,SAChE,KACA;CACA,IAAI,YAAY;EAKd,MAAM,OAAO,EAAE,GAAG,MAAM;EACxB,KAAK,MAAM,OAAO,oBAAoB,OAAO,KAAK;EAClD,OACE,oBAAC,OAAD;GACO;GACL,GAAK;GACL,eAAY;GACZ,aAAU;GACV,WAAW,GAAG,kBAAkB,EAAE,YAAY,CAAC,GAAG,SAAS;EAC5D,CAAA;CAEL;CACA,OACE,oBAACC,WAAD;EACO;EACL,GAAI;EACS;EACb,aAAU;EACV,WAAW,GAAG,kBAAkB,EAAE,YAAY,CAAC,GAAG,SAAS;CAC5D,CAAA;AAEL,CAAC"}
@@ -7,15 +7,24 @@
7
7
  * keys scrub through the points: a cursor rides the line, the stretch after it
8
8
  * dims, and the headline and context show that point.
9
9
  *
10
+ * A missing point (`null`, `undefined` or not a finite number) is a gap: the
11
+ * line breaks around it and the scale ignores it. A point with missing
12
+ * neighbours on both sides is drawn as a dot.
13
+ *
10
14
  * Plain SVG, plain CSS transitions and one small spring tween for the two
11
15
  * things CSS cannot animate (the shape, and the cursor riding it). No chart or
12
16
  * animation library.
13
17
  */
14
18
  export declare const sparklineVariants: (props?: import("class-variance-authority/types").ClassProp | undefined) => string;
15
19
  export type SparklineTone = "accent" | "success" | "warning" | "danger";
20
+ export type SparklineSize = "sm" | "md";
16
21
  export interface SparklineProps {
17
- /** The series, oldest first. One value draws a flat line. */
18
- data: number[];
22
+ /**
23
+ * The series, oldest first. One value draws a flat line. A missing point
24
+ * (`null`, `undefined` or not a finite number) breaks the line, is left out
25
+ * of the scale and reads as `missingLabel` when scrubbed.
26
+ */
27
+ data: (number | null | undefined)[];
19
28
  /** What the line is. Its accessible name starts with it. */
20
29
  label: string;
21
30
  /** The headline figure, such as "1,284". Scrubbing replaces it with the scrubbed point. */
@@ -24,6 +33,22 @@ export interface SparklineProps {
24
33
  change?: string;
25
34
  /** @default "accent" */
26
35
  tone?: SparklineTone;
36
+ /**
37
+ * Any CSS colour for the line, its fill and its dot, such as a series token
38
+ * (`var(--color-chart-2)`). Overrides the tone's line colour; the change text
39
+ * keeps the tone's legible colour.
40
+ */
41
+ color?: string;
42
+ /**
43
+ * Hides the caption (label, headline and change), leaving the line alone,
44
+ * for a table cell or a card that shows its own figures. The label still
45
+ * names the figure and the slider. @default false
46
+ */
47
+ captionHidden?: boolean;
48
+ /** `"sm"` draws a thinner line and smaller dot, for a short line in a table cell. @default "md" */
49
+ size?: SparklineSize;
50
+ /** Read in place of a missing point's value. @default "No data" */
51
+ missingLabel?: string;
27
52
  /** Aspect of the drawing; the line fills the width and keeps this ratio. @default 160 */
28
53
  width?: number;
29
54
  /** @default 52 */
@@ -16,14 +16,23 @@ import { jsx, jsxs } from "react/jsx-runtime";
16
16
  * keys scrub through the points: a cursor rides the line, the stretch after it
17
17
  * dims, and the headline and context show that point.
18
18
  *
19
+ * A missing point (`null`, `undefined` or not a finite number) is a gap: the
20
+ * line breaks around it and the scale ignores it. A point with missing
21
+ * neighbours on both sides is drawn as a dot.
22
+ *
19
23
  * Plain SVG, plain CSS transitions and one small spring tween for the two
20
24
  * things CSS cannot animate (the shape, and the cursor riding it). No chart or
21
25
  * animation library.
22
26
  */
23
27
  const sparklineVariants = cva("grid min-w-0 gap-3 m-0 box-border font-sans text-foreground");
24
28
  /** Space kept above the highest and below the lowest point, and half the line width. */
25
- const INSET = 5;
29
+ const INSET = {
30
+ md: 5,
31
+ sm: 3
32
+ };
26
33
  const CAP = 1.25;
34
+ /** Radius of a lone point's dot, about the line's width. */
35
+ const DOT = 2.5;
27
36
  const GRID = Array.from({ length: 161 }, (_, index) => index / 160);
28
37
  const GROUPED = new Intl.NumberFormat("en-US");
29
38
  /** The first draw: one stroke, slow at both ends like a pen (480ms x 1.75). */
@@ -53,6 +62,31 @@ function curveFor(data) {
53
62
  points.push([1, ys[last]]);
54
63
  return points;
55
64
  }
65
+ /** Fills each missing point straight between its present neighbours (or with the nearest one at the ends), so a gapped line still has a shape to draw and morph. */
66
+ function fillGaps(values) {
67
+ const present = values.flatMap((value, index) => value === null ? [] : [index]);
68
+ if (!present.length) return values.map(() => 0);
69
+ return values.map((value, index) => {
70
+ if (value !== null) return value;
71
+ const after = present.find((at) => at > index);
72
+ const before = present.findLast((at) => at < index);
73
+ if (before === void 0) return values[after];
74
+ if (after === void 0) return values[before];
75
+ return values[before] + (values[after] - values[before]) * (index - before) / (after - before);
76
+ });
77
+ }
78
+ /** The stretches of x (0 to 1) where the line is drawn: from each present point to the next, split where missing points sit between. */
79
+ function runsFor(values) {
80
+ const last = values.length - 1;
81
+ const runs = [];
82
+ values.forEach((value, index) => {
83
+ if (value === null) return;
84
+ const run = runs[runs.length - 1];
85
+ if (run && values[index - 1] !== null && index > 0) run[1] = index / last;
86
+ else runs.push([index / last, index / last]);
87
+ });
88
+ return runs;
89
+ }
56
90
  /** Height of the sampled line at x, straight between samples. */
57
91
  function heightAt(shape, x) {
58
92
  let low = 0;
@@ -69,8 +103,8 @@ function heightAt(shape, x) {
69
103
  return x1 === x0 ? y1 : y0 + (y1 - y0) * (x - x0) / (x1 - x0);
70
104
  }
71
105
  /** Every drawn string for one frame, in the chart's own pixels so strokes and dots stay round at any width. */
72
- function geometryOf(shape, w, h, at) {
73
- const toY = (y) => INSET + (1 - y) * (h - 10);
106
+ function geometryOf(shape, w, h, at, inset) {
107
+ const toY = (y) => inset + (1 - y) * (h - inset * 2);
74
108
  const pairs = shape.map(([x, y]) => `${(x * w).toFixed(2)},${toY(y).toFixed(2)}`);
75
109
  const cx = at * w;
76
110
  const cy = toY(heightAt(shape, at));
@@ -88,11 +122,15 @@ const TONE = {
88
122
  warning: "text-warning-strong [--spark-stroke:var(--color-warning-strong)]",
89
123
  danger: "text-destructive-strong [--spark-stroke:var(--color-destructive-strong)]"
90
124
  };
91
- const Sparkline = forwardRef(function Sparkline({ data, label, value, change, tone = "accent", width = 160, height = 52, labels, formatValue, area = true, interactive = true, className }, ref) {
92
- const series = data.length > 1 ? data : [data[0] ?? 0, data[0] ?? 0];
125
+ const Sparkline = forwardRef(function Sparkline({ data, label, value, change, tone = "accent", color, captionHidden = false, size: scale = "md", missingLabel = "No data", width = 160, height = 52, labels, formatValue, area = true, interactive = true, className }, ref) {
126
+ const points = data.map((point) => typeof point === "number" && Number.isFinite(point) ? point : null);
127
+ const values = points.length > 1 ? points : [points[0] ?? null, points[0] ?? null];
128
+ const series = fillGaps(values);
93
129
  const last = series.length - 1;
94
130
  const key = series.join(",");
95
131
  const target = useMemo(() => curveFor(key.split(",").map(Number)), [key]);
132
+ const gapped = values.includes(null);
133
+ const inset = INSET[scale];
96
134
  const uid = `spark${useId().replace(/[^a-zA-Z0-9_-]/g, "")}`;
97
135
  const figure = useRef(null);
98
136
  const refs = useMemo(() => mergeRefs(ref, figure), [ref]);
@@ -102,7 +140,8 @@ const Sparkline = forwardRef(function Sparkline({ data, label, value, change, to
102
140
  const cursorAt = useRef(1);
103
141
  const box = useRef({
104
142
  w: width,
105
- h: height
143
+ h: height,
144
+ inset
106
145
  });
107
146
  const [size, setSize] = useState({
108
147
  w: width,
@@ -120,18 +159,22 @@ const Sparkline = forwardRef(function Sparkline({ data, label, value, change, to
120
159
  index,
121
160
  dir: index === null || moved.index === null ? 1 : Math.sign(index - moved.index) || 1
122
161
  });
123
- const [first] = useState(() => geometryOf(target, width, height, 1));
162
+ const [first] = useState(() => geometryOf(target, width, height, 1, inset));
124
163
  /** One writer for every drawn attribute, straight onto the elements: no re-render per frame. */
125
164
  const paint = useCallback(() => {
126
165
  const root = svg.current;
127
166
  if (!root) return;
128
- const { w, h } = box.current;
129
- const g = geometryOf(shape.current, w, h, cursorAt.current);
167
+ const { w, h, inset: room } = box.current;
168
+ const g = geometryOf(shape.current, w, h, cursorAt.current, room);
130
169
  const q = (name) => root.querySelector(`[data-part="${name}"]`);
131
170
  q("line")?.setAttribute("points", g.line);
132
171
  q("trail")?.setAttribute("d", g.trail);
133
172
  q("area")?.setAttribute("d", g.fill);
134
173
  q("dot")?.setAttribute("points", g.dot);
174
+ for (const node of root.querySelectorAll("[data-part=\"point\"]")) {
175
+ const y = heightAt(shape.current, Number(node.dataset.at));
176
+ node.setAttribute("cy", String(room + (1 - y) * (h - room * 2)));
177
+ }
135
178
  q("lit")?.setAttribute("width", String(g.cx + CAP + 16));
136
179
  q("dim")?.setAttribute("x", String(g.cx + CAP));
137
180
  q("cursor")?.setAttribute("x1", String(g.cx));
@@ -144,6 +187,7 @@ const Sparkline = forwardRef(function Sparkline({ data, label, value, change, to
144
187
  const w = node.clientWidth;
145
188
  if (!w || Math.abs(box.current.w - w) < .5) return;
146
189
  box.current = {
190
+ ...box.current,
147
191
  w,
148
192
  h: w * height / width
149
193
  };
@@ -152,7 +196,14 @@ const Sparkline = forwardRef(function Sparkline({ data, label, value, change, to
152
196
  observer.observe(node);
153
197
  return () => observer.disconnect();
154
198
  }, [height, width]);
155
- useIsomorphicLayoutEffect(paint, [size, paint]);
199
+ useIsomorphicLayoutEffect(() => {
200
+ box.current.inset = inset;
201
+ paint();
202
+ }, [
203
+ size,
204
+ inset,
205
+ paint
206
+ ]);
156
207
  useEffect(() => {
157
208
  const node = figure.current;
158
209
  if (!node) return;
@@ -202,12 +253,20 @@ const Sparkline = forwardRef(function Sparkline({ data, label, value, change, to
202
253
  paint
203
254
  ]);
204
255
  const scrubbing = index !== null;
205
- const format = (at) => formatValue ? formatValue(series[at], at) : GROUPED.format(series[at]);
256
+ const format = (at) => values[at] === null ? missingLabel : formatValue ? formatValue(values[at], at) : GROUPED.format(values[at]);
206
257
  const point = format(index ?? last);
207
258
  const when = labels?.[index ?? last];
208
259
  const headline = value === void 0 ? void 0 : scrubbing ? point : value;
209
260
  const aside = index === null ? change ?? "" : value === void 0 ? [point, when].filter(Boolean).join(" · ") : when ?? `${index + 1} of ${last + 1}`;
210
- const description = `${label}: ${value ?? series[last]}. ${change ?? ""}`.trim();
261
+ const description = `${label}: ${value ?? values[last] ?? missingLabel}. ${change ?? ""}`.trim();
262
+ const runs = gapped ? runsFor(values) : [];
263
+ const gaps = gapped ? `url(#${uid}-gaps)` : void 0;
264
+ const toY = (y) => inset + (1 - y) * (size.h - inset * 2);
265
+ const lone = runs.filter(([from, to]) => from === to).map(([at]) => ({
266
+ at,
267
+ cx: at * size.w,
268
+ cy: toY(heightAt(shape.current, at))
269
+ }));
211
270
  const pointAt = (clientX) => {
212
271
  const rect = plot.current?.getBoundingClientRect();
213
272
  return rect?.width ? Math.max(0, Math.min(last, Math.round((clientX - rect.left) / rect.width * last))) : last;
@@ -260,9 +319,10 @@ const Sparkline = forwardRef(function Sparkline({ data, label, value, change, to
260
319
  "data-drawn": drawn || void 0,
261
320
  "data-landed": landed || void 0,
262
321
  "data-scrubbing": scrubbing || void 0,
322
+ "data-size": scale === "md" ? void 0 : scale,
263
323
  "aria-label": description,
264
324
  className: cn(sparklineVariants(), className),
265
- children: [/* @__PURE__ */ jsxs("figcaption", {
325
+ children: [captionHidden ? null : /* @__PURE__ */ jsxs("figcaption", {
266
326
  className: "flex min-w-0 items-baseline gap-2 text-sm text-text-secondary",
267
327
  children: [
268
328
  /* @__PURE__ */ jsx("span", {
@@ -303,6 +363,10 @@ const Sparkline = forwardRef(function Sparkline({ data, label, value, change, to
303
363
  "aria-hidden": "true",
304
364
  focusable: "false",
305
365
  className: cnAll("block h-auto w-full overflow-visible", TONE[tone]),
366
+ style: color ? {
367
+ color,
368
+ "--spark-stroke": color
369
+ } : void 0,
306
370
  children: [
307
371
  /* @__PURE__ */ jsxs("defs", { children: [
308
372
  /* @__PURE__ */ jsx("clipPath", {
@@ -338,13 +402,35 @@ const Sparkline = forwardRef(function Sparkline({ data, label, value, change, to
338
402
  height: size.h + 32,
339
403
  style: { width: drawn ? size.w + 32 : 0 }
340
404
  })
341
- })
405
+ }),
406
+ gapped ? /* @__PURE__ */ jsx("mask", {
407
+ id: `${uid}-gaps`,
408
+ maskUnits: "userSpaceOnUse",
409
+ x: -16,
410
+ y: -16,
411
+ width: size.w + 32,
412
+ height: size.h + 32,
413
+ children: runs.map(([from, to]) => {
414
+ const reach = from === to ? DOT : CAP;
415
+ const x = from === 0 ? -16 : from * size.w - reach;
416
+ const end = to === 1 ? size.w + 16 : to * size.w + reach;
417
+ return /* @__PURE__ */ jsx("rect", {
418
+ "data-part": "run",
419
+ x,
420
+ y: -16,
421
+ width: end - x,
422
+ height: size.h + 32,
423
+ fill: "white"
424
+ }, from);
425
+ })
426
+ }) : null
342
427
  ] }),
343
428
  area ? /* @__PURE__ */ jsx("path", {
344
429
  "data-part": "area",
345
430
  className: "sparkline-area",
346
431
  d: first.fill,
347
- clipPath: `url(#${uid}-fill)`
432
+ clipPath: `url(#${uid}-fill)`,
433
+ mask: gaps
348
434
  }) : null,
349
435
  /* @__PURE__ */ jsx("line", {
350
436
  className: "sparkline-baseline",
@@ -357,15 +443,25 @@ const Sparkline = forwardRef(function Sparkline({ data, label, value, change, to
357
443
  "data-part": "trail",
358
444
  className: "sparkline-trail",
359
445
  d: first.trail,
360
- clipPath: `url(#${uid}-dim)`
446
+ clipPath: `url(#${uid}-dim)`,
447
+ mask: gaps
361
448
  }),
362
449
  /* @__PURE__ */ jsx("polyline", {
363
450
  "data-part": "line",
364
451
  className: "sparkline-line",
365
452
  points: first.line,
366
453
  pathLength: 1,
367
- clipPath: `url(#${uid}-lit)`
454
+ clipPath: `url(#${uid}-lit)`,
455
+ mask: gaps
368
456
  }),
457
+ lone.map(({ at, cx, cy }) => /* @__PURE__ */ jsx("circle", {
458
+ "data-part": "point",
459
+ "data-at": at,
460
+ className: "sparkline-point",
461
+ cx,
462
+ cy,
463
+ r: DOT
464
+ }, cx)),
369
465
  /* @__PURE__ */ jsx("line", {
370
466
  "data-part": "cursor",
371
467
  className: "sparkline-cursor",
@@ -377,7 +473,8 @@ const Sparkline = forwardRef(function Sparkline({ data, label, value, change, to
377
473
  /* @__PURE__ */ jsx("polyline", {
378
474
  "data-part": "dot",
379
475
  className: "sparkline-dot",
380
- points: first.dot
476
+ points: first.dot,
477
+ mask: gaps
381
478
  })
382
479
  ]
383
480
  })
@@ -1 +1 @@
1
- {"version":3,"file":"sparkline.js","names":[],"sources":["../../src/sparkline/sparkline.tsx"],"sourcesContent":["\"use client\";\n\nimport { cva } from \"class-variance-authority\";\nimport {\n forwardRef,\n useCallback,\n useEffect,\n useId,\n useMemo,\n useRef,\n useState,\n type KeyboardEvent,\n type PointerEvent,\n} from \"react\";\n\nimport { cn, cnAll } from \"../lib/cn.js\";\nimport { mergeRefs } from \"../lib/merge-refs.js\";\nimport { prefersReducedMotion, useIsomorphicLayoutEffect } from \"../lib/motion.js\";\nimport { SMOOTH, SNAPPY, tween } from \"../lib/spring-tween.js\";\nimport { SwapText } from \"../lib/swap-text.js\";\n\n/**\n * Sparkline: a small trend line with its label, an optional headline value and\n * a line of context (the change). The line draws itself left to right the first\n * time it is seen, the fill follows the pen, and the end dot lands. New data\n * morphs the line from the shape on screen. Hovering, dragging or the arrow\n * keys scrub through the points: a cursor rides the line, the stretch after it\n * dims, and the headline and context show that point.\n *\n * Plain SVG, plain CSS transitions and one small spring tween for the two\n * things CSS cannot animate (the shape, and the cursor riding it). No chart or\n * animation library.\n */\nexport const sparklineVariants = cva(\"grid min-w-0 gap-3 m-0 box-border font-sans text-foreground\");\n\nexport type SparklineTone = \"accent\" | \"success\" | \"warning\" | \"danger\";\n\nexport interface SparklineProps {\n /** The series, oldest first. One value draws a flat line. */\n data: number[];\n /** What the line is. Its accessible name starts with it. */\n label: string;\n /** The headline figure, such as \"1,284\". Scrubbing replaces it with the scrubbed point. */\n value?: string;\n /** Context beside the headline, such as \"+12% this week\". Coloured by `tone`. */\n change?: string;\n /** @default \"accent\" */\n tone?: SparklineTone;\n /** Aspect of the drawing; the line fills the width and keeps this ratio. @default 160 */\n width?: number;\n /** @default 52 */\n height?: number;\n /** One label per point, such as a date. It replaces the change while that point is scrubbed. */\n labels?: string[];\n /** Formats a scrubbed point for the headline. Defaults to a grouped number. */\n formatValue?: (value: number, index: number) => string;\n /** A faint fill under the line. @default true */\n area?: boolean;\n /** Pointer and keyboard scrubbing through the points. @default true */\n interactive?: boolean;\n /** Extra classes for the root. Merged last. */\n className?: string;\n}\n\ntype Point = [number, number];\n\n/** Space kept above the highest and below the lowest point, and half the line width. */\nconst INSET = 5;\nconst CAP = 1.25;\nconst GRID = Array.from({ length: 161 }, (_, index) => index / 160);\nconst GROUPED = new Intl.NumberFormat(\"en-US\");\n/** The first draw: one stroke, slow at both ends like a pen (480ms x 1.75). */\nexport const SPARKLINE_DRAW_MS = 840;\n\n/** A smooth line through every point, sampled densely in unit space (x and y 0 to 1, y up). Monotone cubic tangents keep it from swinging past the data. */\nfunction curveFor(data: number[]): Point[] {\n const min = Math.min(...data);\n const span = Math.max(...data) - min;\n const ys = data.map((value) => (span ? (value - min) / span : 0.5));\n const last = ys.length - 1;\n const step = 1 / last;\n const slopes = ys.slice(1).map((y, index) => (y - ys[index]!) / step);\n const tangents = ys.map((_, index) =>\n index === 0 || index === last\n ? slopes[0]!\n : (Math.sign(slopes[index - 1]!) + Math.sign(slopes[index]!)) *\n Math.min(\n Math.abs(slopes[index - 1]!),\n Math.abs(slopes[index]!),\n Math.abs(slopes[index - 1]! + slopes[index]!) / 4,\n ) || 0,\n );\n if (last > 1) {\n tangents[0] = (3 * slopes[0]! - tangents[1]!) / 2;\n tangents[last] = (3 * slopes[last - 1]! - tangents[last - 1]!) / 2;\n }\n const samples = Math.max(1, Math.min(16, Math.round(128 / last)));\n const points: Point[] = [];\n for (let index = 0; index < last; index++) {\n for (let sample = 0; sample < samples; sample++) {\n const t = sample / samples;\n const t2 = t * t;\n const t3 = t2 * t;\n const y =\n (2 * t3 - 3 * t2 + 1) * ys[index]! +\n (t3 - 2 * t2 + t) * step * tangents[index]! +\n (3 * t2 - 2 * t3) * ys[index + 1]! +\n (t3 - t2) * step * tangents[index + 1]!;\n points.push([(index + t) * step, Math.min(1, Math.max(0, y))]);\n }\n }\n points.push([1, ys[last]!]);\n return points;\n}\n\n/** Height of the sampled line at x, straight between samples. */\nfunction heightAt(shape: Point[], x: number) {\n let low = 0;\n let high = shape.length - 1;\n if (x <= shape[low]![0]) return shape[low]![1];\n if (x >= shape[high]![0]) return shape[high]![1];\n while (high - low > 1) {\n const middle = (low + high) >> 1;\n if (shape[middle]![0] < x) low = middle;\n else high = middle;\n }\n const [x0, y0] = shape[low]!;\n const [x1, y1] = shape[high]!;\n return x1 === x0 ? y1 : y0 + ((y1 - y0) * (x - x0)) / (x1 - x0);\n}\n\n/** Every drawn string for one frame, in the chart's own pixels so strokes and dots stay round at any width. */\nfunction geometryOf(shape: Point[], w: number, h: number, at: number) {\n const toY = (y: number) => INSET + (1 - y) * (h - INSET * 2);\n const pairs = shape.map(([x, y]) => `${(x * w).toFixed(2)},${toY(y).toFixed(2)}`);\n const cx = at * w;\n const cy = toY(heightAt(shape, at));\n return {\n line: pairs.join(\" \"),\n trail: `M${pairs.join(\"L\")}`,\n fill: `M${pairs.join(\"L\")}L${w},${h}L0,${h}Z`,\n dot: `${cx},${cy} ${cx},${cy}`,\n cx,\n };\n}\n\nconst TONE: Record<SparklineTone, string> = {\n accent: \"text-primary-strong [--spark-stroke:var(--color-primary)]\",\n success: \"text-success-strong [--spark-stroke:var(--color-success-strong)]\",\n warning: \"text-warning-strong [--spark-stroke:var(--color-warning-strong)]\",\n danger: \"text-destructive-strong [--spark-stroke:var(--color-destructive-strong)]\",\n};\n\nexport const Sparkline = forwardRef<HTMLElement, SparklineProps>(function Sparkline(\n {\n data,\n label,\n value,\n change,\n tone = \"accent\",\n width = 160,\n height = 52,\n labels,\n formatValue,\n area = true,\n interactive = true,\n className,\n },\n ref,\n) {\n const series = data.length > 1 ? data : [data[0] ?? 0, data[0] ?? 0];\n const last = series.length - 1;\n const key = series.join(\",\");\n const target = useMemo(() => curveFor(key.split(\",\").map(Number)), [key]);\n const uid = `spark${useId().replace(/[^a-zA-Z0-9_-]/g, \"\")}`;\n\n const figure = useRef<HTMLElement | null>(null);\n const refs = useMemo(() => mergeRefs(ref, figure), [ref]);\n const plot = useRef<HTMLDivElement>(null);\n const svg = useRef<SVGSVGElement>(null);\n const shape = useRef(target);\n const cursorAt = useRef(1);\n const box = useRef({ w: width, h: height });\n const [size, setSize] = useState({ w: width, h: height });\n const [drawn, setDrawn] = useState(false);\n const [landed, setLanded] = useState(false);\n const [active, setActive] = useState<number | null>(null);\n const index = active === null ? null : Math.min(active, last);\n const [moved, setMoved] = useState({ index, dir: 1 });\n if (moved.index !== index) {\n setMoved({\n index,\n dir: index === null || moved.index === null ? 1 : Math.sign(index - moved.index) || 1,\n });\n }\n\n // The first frame, also what the server renders.\n const [first] = useState(() => geometryOf(target, width, height, 1));\n\n /** One writer for every drawn attribute, straight onto the elements: no re-render per frame. */\n const paint = useCallback(() => {\n const root = svg.current;\n if (!root) return;\n const { w, h } = box.current;\n const g = geometryOf(shape.current, w, h, cursorAt.current);\n const q = (name: string) => root.querySelector<SVGElement>(`[data-part=\"${name}\"]`);\n q(\"line\")?.setAttribute(\"points\", g.line);\n q(\"trail\")?.setAttribute(\"d\", g.trail);\n q(\"area\")?.setAttribute(\"d\", g.fill);\n q(\"dot\")?.setAttribute(\"points\", g.dot);\n q(\"lit\")?.setAttribute(\"width\", String(g.cx + CAP + 16));\n q(\"dim\")?.setAttribute(\"x\", String(g.cx + CAP));\n q(\"cursor\")?.setAttribute(\"x1\", String(g.cx));\n q(\"cursor\")?.setAttribute(\"x2\", String(g.cx));\n }, []);\n\n // The chart draws in its own pixels: measure the width, keep the height in proportion.\n useIsomorphicLayoutEffect(() => {\n const node = plot.current;\n if (!node || typeof ResizeObserver === \"undefined\") return;\n const observer = new ResizeObserver(() => {\n const w = node.clientWidth;\n if (!w || Math.abs(box.current.w - w) < 0.5) return;\n box.current = { w, h: (w * height) / width };\n setSize(box.current);\n });\n observer.observe(node);\n return () => observer.disconnect();\n }, [height, width]);\n useIsomorphicLayoutEffect(paint, [size, paint]);\n\n // The line draws once it is half in view; the fill follows the pen, then the end dot lands.\n useEffect(() => {\n const node = figure.current;\n if (!node) return;\n if (prefersReducedMotion() || typeof IntersectionObserver === \"undefined\") {\n setDrawn(true);\n setLanded(true);\n return;\n }\n let timer = 0;\n const observer = new IntersectionObserver(\n ([entry]) => {\n if (!entry?.isIntersecting) return;\n observer.disconnect();\n setDrawn(true);\n timer = window.setTimeout(() => setLanded(true), SPARKLINE_DRAW_MS);\n },\n { threshold: 0.5 },\n );\n observer.observe(node);\n return () => {\n observer.disconnect();\n window.clearTimeout(timer);\n };\n }, []);\n\n // New data morphs the line from the shape on screen, sampled on shared x positions so a series that gains or loses points still flows.\n useEffect(() => {\n const from = shape.current;\n if (from === target) return;\n const xs = [...new Set([...GRID, ...target.map(([x]) => x)])].sort((a, b) => a - b);\n const start = xs.map((x) => heightAt(from, x));\n const goal = xs.map((x) => heightAt(target, x));\n return tween(\n SMOOTH,\n (p) => {\n shape.current = xs.map((x, at): Point => [x, start[at]! + (goal[at]! - start[at]!) * p]);\n paint();\n },\n () => {\n shape.current = target;\n paint();\n },\n );\n }, [paint, target]);\n\n // The cursor springs to the scrubbed point and rides the line; leaving sends it home to the latest value.\n useEffect(() => {\n const from = cursorAt.current;\n const to = index === null ? 1 : index / last;\n if (from === to) return;\n return tween(SNAPPY, (p) => {\n cursorAt.current = from + (to - from) * p;\n paint();\n });\n }, [index, last, paint]);\n\n const scrubbing = index !== null;\n const format = (at: number) =>\n formatValue ? formatValue(series[at]!, at) : GROUPED.format(series[at]!);\n const point = format(index ?? last);\n const when = labels?.[index ?? last];\n const headline = value === undefined ? undefined : scrubbing ? point : value;\n const aside =\n index === null\n ? (change ?? \"\")\n : value === undefined\n ? [point, when].filter(Boolean).join(\" · \")\n : (when ?? `${index + 1} of ${last + 1}`);\n const description = `${label}: ${value ?? series[last]}. ${change ?? \"\"}`.trim();\n\n const pointAt = (clientX: number) => {\n const rect = plot.current?.getBoundingClientRect();\n return rect?.width\n ? Math.max(0, Math.min(last, Math.round(((clientX - rect.left) / rect.width) * last)))\n : last;\n };\n const onKeyDown = (event: KeyboardEvent<HTMLDivElement>) => {\n if (event.key === \"Escape\" && scrubbing) {\n event.preventDefault();\n setActive(null);\n return;\n }\n const from = index ?? last;\n const page = Math.max(1, Math.round((last + 1) / 6));\n const next = (\n {\n ArrowLeft: from - 1,\n ArrowDown: from - 1,\n ArrowRight: from + 1,\n ArrowUp: from + 1,\n PageDown: from - page,\n PageUp: from + page,\n Home: 0,\n End: last,\n } as Record<string, number>\n )[event.key];\n if (next === undefined) return;\n event.preventDefault();\n setActive(Math.max(0, Math.min(last, next)));\n };\n const scrub = interactive\n ? {\n role: \"slider\",\n tabIndex: 0,\n \"aria-label\": `${label}, explore values`,\n \"aria-valuemin\": 1,\n \"aria-valuemax\": last + 1,\n \"aria-valuenow\": (index ?? last) + 1,\n \"aria-valuetext\": `${when ?? (index === null ? \"Latest\" : `Point ${index + 1} of ${last + 1}`)}: ${point}`,\n onPointerDown: (event: PointerEvent<HTMLDivElement>) => setActive(pointAt(event.clientX)),\n onPointerMove: (event: PointerEvent<HTMLDivElement>) => {\n if (event.pointerType === \"mouse\" || event.buttons) setActive(pointAt(event.clientX));\n },\n onPointerUp: (event: PointerEvent<HTMLDivElement>) => {\n if (event.pointerType !== \"mouse\") setActive(null);\n },\n onPointerLeave: () => setActive(null),\n onPointerCancel: () => setActive(null),\n onBlur: () => setActive(null),\n onKeyDown,\n }\n : {};\n\n return (\n <figure\n ref={refs}\n data-slot=\"sparkline\"\n data-drawn={drawn || undefined}\n data-landed={landed || undefined}\n data-scrubbing={scrubbing || undefined}\n aria-label={description}\n className={cn(sparklineVariants(), className)}\n >\n <figcaption className=\"flex min-w-0 items-baseline gap-2 text-sm text-text-secondary\">\n <span data-slot=\"sparkline-label\" className=\"min-w-0 truncate\">\n {label}\n </span>\n {headline === undefined ? null : (\n <strong\n data-slot=\"sparkline-value\"\n className=\"ms-auto text-lg font-medium tabular-nums text-foreground\"\n >\n <SwapText swapKey={headline} dir={moved.dir} motion=\"rise\">\n {headline}\n </SwapText>\n </strong>\n )}\n <small\n data-slot=\"sparkline-change\"\n // The scrubbed date is context, not a change, so it turns neutral.\n className={cnAll(\n \"flex-none text-xs font-medium tabular-nums sparkline-change\",\n scrubbing ? \"text-text-secondary\" : TONE[tone],\n )}\n >\n <SwapText swapKey={aside} dir={moved.dir} motion=\"rise\">\n {aside}\n </SwapText>\n </small>\n </figcaption>\n <div\n ref={plot}\n data-slot=\"sparkline-plot\"\n data-interactive={interactive || undefined}\n className=\"sparkline-plot\"\n {...scrub}\n >\n <svg\n ref={svg}\n viewBox={`0 0 ${size.w} ${size.h}`}\n aria-hidden=\"true\"\n focusable=\"false\"\n className={cnAll(\"block h-auto w-full overflow-visible\", TONE[tone])}\n >\n <defs>\n <clipPath id={`${uid}-lit`} clipPathUnits=\"userSpaceOnUse\">\n <rect data-part=\"lit\" x={-16} y={-16} width={first.cx + CAP + 16} height={size.h + 32} />\n </clipPath>\n <clipPath id={`${uid}-dim`} clipPathUnits=\"userSpaceOnUse\">\n <rect data-part=\"dim\" x={first.cx + CAP} y={-16} width={size.w + 32} height={size.h + 32} />\n </clipPath>\n <clipPath id={`${uid}-fill`} clipPathUnits=\"userSpaceOnUse\">\n <rect\n data-part=\"reveal\"\n className=\"sparkline-reveal\"\n x={-16}\n y={-16}\n height={size.h + 32}\n style={{ width: drawn ? size.w + 32 : 0 }}\n />\n </clipPath>\n </defs>\n {area ? (\n <path data-part=\"area\" className=\"sparkline-area\" d={first.fill} clipPath={`url(#${uid}-fill)`} />\n ) : null}\n <line\n className=\"sparkline-baseline\"\n x1=\"0\"\n y1={size.h - 0.5}\n x2={size.w}\n y2={size.h - 0.5}\n />\n {/* While scrubbing, the stretch after the cursor dims, as if the later points have not happened yet. */}\n <path data-part=\"trail\" className=\"sparkline-trail\" d={first.trail} clipPath={`url(#${uid}-dim)`} />\n <polyline\n data-part=\"line\"\n className=\"sparkline-line\"\n points={first.line}\n pathLength={1}\n clipPath={`url(#${uid}-lit)`}\n />\n <line data-part=\"cursor\" className=\"sparkline-cursor\" x1={first.cx} x2={first.cx} y1={0} y2={size.h} />\n {/* A zero-length round-capped stroke is a true circle in the line's colour; it rests on the latest point and becomes the scrub handle. */}\n <polyline data-part=\"dot\" className=\"sparkline-dot\" points={first.dot} />\n </svg>\n </div>\n </figure>\n );\n});\n"],"mappings":"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"}
1
+ {"version":3,"file":"sparkline.js","names":[],"sources":["../../src/sparkline/sparkline.tsx"],"sourcesContent":["\"use client\";\n\nimport { cva } from \"class-variance-authority\";\nimport {\n forwardRef,\n useCallback,\n useEffect,\n useId,\n useMemo,\n useRef,\n useState,\n type CSSProperties,\n type KeyboardEvent,\n type PointerEvent,\n} from \"react\";\n\nimport { cn, cnAll } from \"../lib/cn.js\";\nimport { mergeRefs } from \"../lib/merge-refs.js\";\nimport { prefersReducedMotion, useIsomorphicLayoutEffect } from \"../lib/motion.js\";\nimport { SMOOTH, SNAPPY, tween } from \"../lib/spring-tween.js\";\nimport { SwapText } from \"../lib/swap-text.js\";\n\n/**\n * Sparkline: a small trend line with its label, an optional headline value and\n * a line of context (the change). The line draws itself left to right the first\n * time it is seen, the fill follows the pen, and the end dot lands. New data\n * morphs the line from the shape on screen. Hovering, dragging or the arrow\n * keys scrub through the points: a cursor rides the line, the stretch after it\n * dims, and the headline and context show that point.\n *\n * A missing point (`null`, `undefined` or not a finite number) is a gap: the\n * line breaks around it and the scale ignores it. A point with missing\n * neighbours on both sides is drawn as a dot.\n *\n * Plain SVG, plain CSS transitions and one small spring tween for the two\n * things CSS cannot animate (the shape, and the cursor riding it). No chart or\n * animation library.\n */\nexport const sparklineVariants = cva(\"grid min-w-0 gap-3 m-0 box-border font-sans text-foreground\");\n\nexport type SparklineTone = \"accent\" | \"success\" | \"warning\" | \"danger\";\nexport type SparklineSize = \"sm\" | \"md\";\n\nexport interface SparklineProps {\n /**\n * The series, oldest first. One value draws a flat line. A missing point\n * (`null`, `undefined` or not a finite number) breaks the line, is left out\n * of the scale and reads as `missingLabel` when scrubbed.\n */\n data: (number | null | undefined)[];\n /** What the line is. Its accessible name starts with it. */\n label: string;\n /** The headline figure, such as \"1,284\". Scrubbing replaces it with the scrubbed point. */\n value?: string;\n /** Context beside the headline, such as \"+12% this week\". Coloured by `tone`. */\n change?: string;\n /** @default \"accent\" */\n tone?: SparklineTone;\n /**\n * Any CSS colour for the line, its fill and its dot, such as a series token\n * (`var(--color-chart-2)`). Overrides the tone's line colour; the change text\n * keeps the tone's legible colour.\n */\n color?: string;\n /**\n * Hides the caption (label, headline and change), leaving the line alone,\n * for a table cell or a card that shows its own figures. The label still\n * names the figure and the slider. @default false\n */\n captionHidden?: boolean;\n /** `\"sm\"` draws a thinner line and smaller dot, for a short line in a table cell. @default \"md\" */\n size?: SparklineSize;\n /** Read in place of a missing point's value. @default \"No data\" */\n missingLabel?: string;\n /** Aspect of the drawing; the line fills the width and keeps this ratio. @default 160 */\n width?: number;\n /** @default 52 */\n height?: number;\n /** One label per point, such as a date. It replaces the change while that point is scrubbed. */\n labels?: string[];\n /** Formats a scrubbed point for the headline. Defaults to a grouped number. */\n formatValue?: (value: number, index: number) => string;\n /** A faint fill under the line. @default true */\n area?: boolean;\n /** Pointer and keyboard scrubbing through the points. @default true */\n interactive?: boolean;\n /** Extra classes for the root. Merged last. */\n className?: string;\n}\n\ntype Point = [number, number];\n\n/** Space kept above the highest and below the lowest point, and half the line width. */\nconst INSET: Record<SparklineSize, number> = { md: 5, sm: 3 };\nconst CAP = 1.25;\n/** Radius of a lone point's dot, about the line's width. */\nconst DOT = 2.5;\nconst GRID = Array.from({ length: 161 }, (_, index) => index / 160);\nconst GROUPED = new Intl.NumberFormat(\"en-US\");\n/** The first draw: one stroke, slow at both ends like a pen (480ms x 1.75). */\nexport const SPARKLINE_DRAW_MS = 840;\n\n/** A smooth line through every point, sampled densely in unit space (x and y 0 to 1, y up). Monotone cubic tangents keep it from swinging past the data. */\nfunction curveFor(data: number[]): Point[] {\n const min = Math.min(...data);\n const span = Math.max(...data) - min;\n const ys = data.map((value) => (span ? (value - min) / span : 0.5));\n const last = ys.length - 1;\n const step = 1 / last;\n const slopes = ys.slice(1).map((y, index) => (y - ys[index]!) / step);\n const tangents = ys.map((_, index) =>\n index === 0 || index === last\n ? slopes[0]!\n : (Math.sign(slopes[index - 1]!) + Math.sign(slopes[index]!)) *\n Math.min(\n Math.abs(slopes[index - 1]!),\n Math.abs(slopes[index]!),\n Math.abs(slopes[index - 1]! + slopes[index]!) / 4,\n ) || 0,\n );\n if (last > 1) {\n tangents[0] = (3 * slopes[0]! - tangents[1]!) / 2;\n tangents[last] = (3 * slopes[last - 1]! - tangents[last - 1]!) / 2;\n }\n const samples = Math.max(1, Math.min(16, Math.round(128 / last)));\n const points: Point[] = [];\n for (let index = 0; index < last; index++) {\n for (let sample = 0; sample < samples; sample++) {\n const t = sample / samples;\n const t2 = t * t;\n const t3 = t2 * t;\n const y =\n (2 * t3 - 3 * t2 + 1) * ys[index]! +\n (t3 - 2 * t2 + t) * step * tangents[index]! +\n (3 * t2 - 2 * t3) * ys[index + 1]! +\n (t3 - t2) * step * tangents[index + 1]!;\n points.push([(index + t) * step, Math.min(1, Math.max(0, y))]);\n }\n }\n points.push([1, ys[last]!]);\n return points;\n}\n\n/** Fills each missing point straight between its present neighbours (or with the nearest one at the ends), so a gapped line still has a shape to draw and morph. */\nfunction fillGaps(values: (number | null)[]): number[] {\n const present = values.flatMap((value, index) => (value === null ? [] : [index]));\n if (!present.length) return values.map(() => 0);\n return values.map((value, index) => {\n if (value !== null) return value;\n const after = present.find((at) => at > index);\n const before = present.findLast((at) => at < index);\n if (before === undefined) return values[after!]!;\n if (after === undefined) return values[before]!;\n return (\n values[before]! + ((values[after]! - values[before]!) * (index - before)) / (after - before)\n );\n });\n}\n\n/** The stretches of x (0 to 1) where the line is drawn: from each present point to the next, split where missing points sit between. */\nfunction runsFor(values: (number | null)[]): [from: number, to: number][] {\n const last = values.length - 1;\n const runs: [number, number][] = [];\n values.forEach((value, index) => {\n if (value === null) return;\n const run = runs[runs.length - 1];\n if (run && values[index - 1] !== null && index > 0) run[1] = index / last;\n else runs.push([index / last, index / last]);\n });\n return runs;\n}\n\n/** Height of the sampled line at x, straight between samples. */\nfunction heightAt(shape: Point[], x: number) {\n let low = 0;\n let high = shape.length - 1;\n if (x <= shape[low]![0]) return shape[low]![1];\n if (x >= shape[high]![0]) return shape[high]![1];\n while (high - low > 1) {\n const middle = (low + high) >> 1;\n if (shape[middle]![0] < x) low = middle;\n else high = middle;\n }\n const [x0, y0] = shape[low]!;\n const [x1, y1] = shape[high]!;\n return x1 === x0 ? y1 : y0 + ((y1 - y0) * (x - x0)) / (x1 - x0);\n}\n\n/** Every drawn string for one frame, in the chart's own pixels so strokes and dots stay round at any width. */\nfunction geometryOf(shape: Point[], w: number, h: number, at: number, inset: number) {\n const toY = (y: number) => inset + (1 - y) * (h - inset * 2);\n const pairs = shape.map(([x, y]) => `${(x * w).toFixed(2)},${toY(y).toFixed(2)}`);\n const cx = at * w;\n const cy = toY(heightAt(shape, at));\n return {\n line: pairs.join(\" \"),\n trail: `M${pairs.join(\"L\")}`,\n fill: `M${pairs.join(\"L\")}L${w},${h}L0,${h}Z`,\n dot: `${cx},${cy} ${cx},${cy}`,\n cx,\n };\n}\n\nconst TONE: Record<SparklineTone, string> = {\n accent: \"text-primary-strong [--spark-stroke:var(--color-primary)]\",\n success: \"text-success-strong [--spark-stroke:var(--color-success-strong)]\",\n warning: \"text-warning-strong [--spark-stroke:var(--color-warning-strong)]\",\n danger: \"text-destructive-strong [--spark-stroke:var(--color-destructive-strong)]\",\n};\n\nexport const Sparkline = forwardRef<HTMLElement, SparklineProps>(function Sparkline(\n {\n data,\n label,\n value,\n change,\n tone = \"accent\",\n color,\n captionHidden = false,\n size: scale = \"md\",\n missingLabel = \"No data\",\n width = 160,\n height = 52,\n labels,\n formatValue,\n area = true,\n interactive = true,\n className,\n },\n ref,\n) {\n const points = data.map((point) =>\n typeof point === \"number\" && Number.isFinite(point) ? point : null,\n );\n const values = points.length > 1 ? points : [points[0] ?? null, points[0] ?? null];\n const series = fillGaps(values);\n const last = series.length - 1;\n const key = series.join(\",\");\n const target = useMemo(() => curveFor(key.split(\",\").map(Number)), [key]);\n const gapped = values.includes(null);\n const inset = INSET[scale];\n const uid = `spark${useId().replace(/[^a-zA-Z0-9_-]/g, \"\")}`;\n\n const figure = useRef<HTMLElement | null>(null);\n const refs = useMemo(() => mergeRefs(ref, figure), [ref]);\n const plot = useRef<HTMLDivElement>(null);\n const svg = useRef<SVGSVGElement>(null);\n const shape = useRef(target);\n const cursorAt = useRef(1);\n const box = useRef({ w: width, h: height, inset });\n const [size, setSize] = useState({ w: width, h: height });\n const [drawn, setDrawn] = useState(false);\n const [landed, setLanded] = useState(false);\n const [active, setActive] = useState<number | null>(null);\n const index = active === null ? null : Math.min(active, last);\n const [moved, setMoved] = useState({ index, dir: 1 });\n if (moved.index !== index) {\n setMoved({\n index,\n dir: index === null || moved.index === null ? 1 : Math.sign(index - moved.index) || 1,\n });\n }\n\n // The first frame, also what the server renders.\n const [first] = useState(() => geometryOf(target, width, height, 1, inset));\n\n /** One writer for every drawn attribute, straight onto the elements: no re-render per frame. */\n const paint = useCallback(() => {\n const root = svg.current;\n if (!root) return;\n const { w, h, inset: room } = box.current;\n const g = geometryOf(shape.current, w, h, cursorAt.current, room);\n const q = (name: string) => root.querySelector<SVGElement>(`[data-part=\"${name}\"]`);\n q(\"line\")?.setAttribute(\"points\", g.line);\n q(\"trail\")?.setAttribute(\"d\", g.trail);\n q(\"area\")?.setAttribute(\"d\", g.fill);\n q(\"dot\")?.setAttribute(\"points\", g.dot);\n // A lone point's dot rides the morphing line, at the height the line has under it.\n for (const node of root.querySelectorAll<SVGElement>('[data-part=\"point\"]')) {\n const y = heightAt(shape.current, Number(node.dataset.at));\n node.setAttribute(\"cy\", String(room + (1 - y) * (h - room * 2)));\n }\n q(\"lit\")?.setAttribute(\"width\", String(g.cx + CAP + 16));\n q(\"dim\")?.setAttribute(\"x\", String(g.cx + CAP));\n q(\"cursor\")?.setAttribute(\"x1\", String(g.cx));\n q(\"cursor\")?.setAttribute(\"x2\", String(g.cx));\n }, []);\n\n // The chart draws in its own pixels: measure the width, keep the height in proportion.\n useIsomorphicLayoutEffect(() => {\n const node = plot.current;\n if (!node || typeof ResizeObserver === \"undefined\") return;\n const observer = new ResizeObserver(() => {\n const w = node.clientWidth;\n if (!w || Math.abs(box.current.w - w) < 0.5) return;\n box.current = { ...box.current, w, h: (w * height) / width };\n setSize(box.current);\n });\n observer.observe(node);\n return () => observer.disconnect();\n }, [height, width]);\n useIsomorphicLayoutEffect(() => {\n box.current.inset = inset;\n paint();\n }, [size, inset, paint]);\n\n // The line draws once it is half in view; the fill follows the pen, then the end dot lands.\n useEffect(() => {\n const node = figure.current;\n if (!node) return;\n if (prefersReducedMotion() || typeof IntersectionObserver === \"undefined\") {\n setDrawn(true);\n setLanded(true);\n return;\n }\n let timer = 0;\n const observer = new IntersectionObserver(\n ([entry]) => {\n if (!entry?.isIntersecting) return;\n observer.disconnect();\n setDrawn(true);\n timer = window.setTimeout(() => setLanded(true), SPARKLINE_DRAW_MS);\n },\n { threshold: 0.5 },\n );\n observer.observe(node);\n return () => {\n observer.disconnect();\n window.clearTimeout(timer);\n };\n }, []);\n\n // New data morphs the line from the shape on screen, sampled on shared x positions so a series that gains or loses points still flows.\n useEffect(() => {\n const from = shape.current;\n if (from === target) return;\n const xs = [...new Set([...GRID, ...target.map(([x]) => x)])].sort((a, b) => a - b);\n const start = xs.map((x) => heightAt(from, x));\n const goal = xs.map((x) => heightAt(target, x));\n return tween(\n SMOOTH,\n (p) => {\n shape.current = xs.map((x, at): Point => [x, start[at]! + (goal[at]! - start[at]!) * p]);\n paint();\n },\n () => {\n shape.current = target;\n paint();\n },\n );\n }, [paint, target]);\n\n // The cursor springs to the scrubbed point and rides the line; leaving sends it home to the latest value.\n useEffect(() => {\n const from = cursorAt.current;\n const to = index === null ? 1 : index / last;\n if (from === to) return;\n return tween(SNAPPY, (p) => {\n cursorAt.current = from + (to - from) * p;\n paint();\n });\n }, [index, last, paint]);\n\n const scrubbing = index !== null;\n const format = (at: number) =>\n values[at] === null\n ? missingLabel\n : formatValue\n ? formatValue(values[at]!, at)\n : GROUPED.format(values[at]!);\n const point = format(index ?? last);\n const when = labels?.[index ?? last];\n const headline = value === undefined ? undefined : scrubbing ? point : value;\n const aside =\n index === null\n ? (change ?? \"\")\n : value === undefined\n ? [point, when].filter(Boolean).join(\" · \")\n : (when ?? `${index + 1} of ${last + 1}`);\n const description = `${label}: ${value ?? values[last] ?? missingLabel}. ${change ?? \"\"}`.trim();\n\n // Where the line is drawn, in the chart's pixels. Each stretch reaches half a line width past its end points so their round caps show; a lone point keeps room for its dot.\n const runs = gapped ? runsFor(values) : [];\n const gaps = gapped ? `url(#${uid}-gaps)` : undefined;\n const toY = (y: number) => inset + (1 - y) * (size.h - inset * 2);\n // At the height of the line on screen, not of its target: `paint` carries the dot from there with the morph.\n const lone = runs\n .filter(([from, to]) => from === to)\n .map(([at]) => ({ at, cx: at * size.w, cy: toY(heightAt(shape.current, at)) }));\n\n const pointAt = (clientX: number) => {\n const rect = plot.current?.getBoundingClientRect();\n return rect?.width\n ? Math.max(0, Math.min(last, Math.round(((clientX - rect.left) / rect.width) * last)))\n : last;\n };\n const onKeyDown = (event: KeyboardEvent<HTMLDivElement>) => {\n if (event.key === \"Escape\" && scrubbing) {\n event.preventDefault();\n setActive(null);\n return;\n }\n const from = index ?? last;\n const page = Math.max(1, Math.round((last + 1) / 6));\n const next = (\n {\n ArrowLeft: from - 1,\n ArrowDown: from - 1,\n ArrowRight: from + 1,\n ArrowUp: from + 1,\n PageDown: from - page,\n PageUp: from + page,\n Home: 0,\n End: last,\n } as Record<string, number>\n )[event.key];\n if (next === undefined) return;\n event.preventDefault();\n setActive(Math.max(0, Math.min(last, next)));\n };\n const scrub = interactive\n ? {\n role: \"slider\",\n tabIndex: 0,\n \"aria-label\": `${label}, explore values`,\n \"aria-valuemin\": 1,\n \"aria-valuemax\": last + 1,\n \"aria-valuenow\": (index ?? last) + 1,\n \"aria-valuetext\": `${when ?? (index === null ? \"Latest\" : `Point ${index + 1} of ${last + 1}`)}: ${point}`,\n onPointerDown: (event: PointerEvent<HTMLDivElement>) => setActive(pointAt(event.clientX)),\n onPointerMove: (event: PointerEvent<HTMLDivElement>) => {\n if (event.pointerType === \"mouse\" || event.buttons) setActive(pointAt(event.clientX));\n },\n onPointerUp: (event: PointerEvent<HTMLDivElement>) => {\n if (event.pointerType !== \"mouse\") setActive(null);\n },\n onPointerLeave: () => setActive(null),\n onPointerCancel: () => setActive(null),\n onBlur: () => setActive(null),\n onKeyDown,\n }\n : {};\n\n return (\n <figure\n ref={refs}\n data-slot=\"sparkline\"\n data-drawn={drawn || undefined}\n data-landed={landed || undefined}\n data-scrubbing={scrubbing || undefined}\n data-size={scale === \"md\" ? undefined : scale}\n aria-label={description}\n className={cn(sparklineVariants(), className)}\n >\n {captionHidden ? null : (\n <figcaption className=\"flex min-w-0 items-baseline gap-2 text-sm text-text-secondary\">\n <span data-slot=\"sparkline-label\" className=\"min-w-0 truncate\">\n {label}\n </span>\n {headline === undefined ? null : (\n <strong\n data-slot=\"sparkline-value\"\n className=\"ms-auto text-lg font-medium tabular-nums text-foreground\"\n >\n <SwapText swapKey={headline} dir={moved.dir} motion=\"rise\">\n {headline}\n </SwapText>\n </strong>\n )}\n <small\n data-slot=\"sparkline-change\"\n // The scrubbed date is context, not a change, so it turns neutral.\n className={cnAll(\n \"flex-none text-xs font-medium tabular-nums sparkline-change\",\n scrubbing ? \"text-text-secondary\" : TONE[tone],\n )}\n >\n <SwapText swapKey={aside} dir={moved.dir} motion=\"rise\">\n {aside}\n </SwapText>\n </small>\n </figcaption>\n )}\n <div\n ref={plot}\n data-slot=\"sparkline-plot\"\n data-interactive={interactive || undefined}\n className=\"sparkline-plot\"\n {...scrub}\n >\n <svg\n ref={svg}\n viewBox={`0 0 ${size.w} ${size.h}`}\n aria-hidden=\"true\"\n focusable=\"false\"\n className={cnAll(\"block h-auto w-full overflow-visible\", TONE[tone])}\n style={color ? ({ color, \"--spark-stroke\": color } as CSSProperties) : undefined}\n >\n <defs>\n <clipPath id={`${uid}-lit`} clipPathUnits=\"userSpaceOnUse\">\n <rect data-part=\"lit\" x={-16} y={-16} width={first.cx + CAP + 16} height={size.h + 32} />\n </clipPath>\n <clipPath id={`${uid}-dim`} clipPathUnits=\"userSpaceOnUse\">\n <rect data-part=\"dim\" x={first.cx + CAP} y={-16} width={size.w + 32} height={size.h + 32} />\n </clipPath>\n <clipPath id={`${uid}-fill`} clipPathUnits=\"userSpaceOnUse\">\n <rect\n data-part=\"reveal\"\n className=\"sparkline-reveal\"\n x={-16}\n y={-16}\n height={size.h + 32}\n style={{ width: drawn ? size.w + 32 : 0 }}\n />\n </clipPath>\n {gapped ? (\n <mask\n id={`${uid}-gaps`}\n maskUnits=\"userSpaceOnUse\"\n x={-16}\n y={-16}\n width={size.w + 32}\n height={size.h + 32}\n >\n {runs.map(([from, to]) => {\n const reach = from === to ? DOT : CAP;\n const x = from === 0 ? -16 : from * size.w - reach;\n const end = to === 1 ? size.w + 16 : to * size.w + reach;\n return (\n <rect\n key={from}\n data-part=\"run\"\n x={x}\n y={-16}\n width={end - x}\n height={size.h + 32}\n fill=\"white\"\n />\n );\n })}\n </mask>\n ) : null}\n </defs>\n {area ? (\n <path\n data-part=\"area\"\n className=\"sparkline-area\"\n d={first.fill}\n clipPath={`url(#${uid}-fill)`}\n mask={gaps}\n />\n ) : null}\n <line\n className=\"sparkline-baseline\"\n x1=\"0\"\n y1={size.h - 0.5}\n x2={size.w}\n y2={size.h - 0.5}\n />\n {/* While scrubbing, the stretch after the cursor dims, as if the later points have not happened yet. */}\n <path\n data-part=\"trail\"\n className=\"sparkline-trail\"\n d={first.trail}\n clipPath={`url(#${uid}-dim)`}\n mask={gaps}\n />\n <polyline\n data-part=\"line\"\n className=\"sparkline-line\"\n points={first.line}\n pathLength={1}\n clipPath={`url(#${uid}-lit)`}\n mask={gaps}\n />\n {lone.map(({ at, cx, cy }) => (\n <circle\n key={cx}\n data-part=\"point\"\n data-at={at}\n className=\"sparkline-point\"\n cx={cx}\n cy={cy}\n r={DOT}\n />\n ))}\n <line data-part=\"cursor\" className=\"sparkline-cursor\" x1={first.cx} x2={first.cx} y1={0} y2={size.h} />\n {/* A zero-length round-capped stroke is a true circle in the line's colour; it rests on the latest point and becomes the scrub handle. Inside a gap it is masked away. */}\n <polyline data-part=\"dot\" className=\"sparkline-dot\" points={first.dot} mask={gaps} />\n </svg>\n </div>\n </figure>\n );\n});\n"],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- import { StatCard, StatCardProps, StatCardTone, statCardHeaderVariants, statCardLabelVariants, statCardSubVariants, statCardValueVariants, statCardVariants } from "./stat-card.js";
2
- export { StatCard, type StatCardProps, type StatCardTone, statCardHeaderVariants, statCardLabelVariants, statCardSubVariants, statCardValueVariants, statCardVariants };
1
+ import { StatCard, StatCardChangeTone, StatCardProps, StatCardTone, StatCardTrend, statCardHeaderVariants, statCardIconVariants, statCardLabelVariants, statCardSkeletonVariants, statCardSubVariants, statCardValueVariants, statCardVariants } from "./stat-card.js";
2
+ export { StatCard, type StatCardChangeTone, type StatCardProps, type StatCardTone, type StatCardTrend, statCardHeaderVariants, statCardIconVariants, statCardLabelVariants, statCardSkeletonVariants, statCardSubVariants, statCardValueVariants, statCardVariants };
@@ -1,2 +1,2 @@
1
- import { StatCard, statCardHeaderVariants, statCardLabelVariants, statCardSubVariants, statCardValueVariants, statCardVariants } from "./stat-card.js";
2
- export { StatCard, statCardHeaderVariants, statCardLabelVariants, statCardSubVariants, statCardValueVariants, statCardVariants };
1
+ import { StatCard, statCardHeaderVariants, statCardIconVariants, statCardLabelVariants, statCardSkeletonVariants, statCardSubVariants, statCardValueVariants, statCardVariants } from "./stat-card.js";
2
+ export { StatCard, statCardHeaderVariants, statCardIconVariants, statCardLabelVariants, statCardSkeletonVariants, statCardSubVariants, statCardValueVariants, statCardVariants };