@siyahi/ui 0.1.9-next.3 → 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 (254) hide show
  1. package/DESIGN.md +170 -0
  2. package/README.md +94 -177
  3. package/THIRD_PARTY_NOTICES.md +11 -11
  4. package/agents/examples/accordion.tsx +39 -0
  5. package/agents/examples/alert.tsx +24 -0
  6. package/agents/examples/app-sidebar.tsx +40 -0
  7. package/agents/examples/avatar.tsx +24 -0
  8. package/agents/examples/badge.tsx +26 -0
  9. package/agents/examples/bar-list-labels.tsx +22 -0
  10. package/agents/examples/bar-list-sides.tsx +40 -0
  11. package/agents/examples/bar-list.tsx +24 -0
  12. package/agents/examples/breadcrumb.tsx +28 -0
  13. package/agents/examples/button-states.tsx +23 -0
  14. package/agents/examples/button.tsx +21 -0
  15. package/agents/examples/calendar.tsx +11 -0
  16. package/agents/examples/callout.tsx +33 -0
  17. package/agents/examples/card.tsx +36 -0
  18. package/agents/examples/change-bar.tsx +104 -0
  19. package/agents/examples/change-grid-money.tsx +36 -0
  20. package/agents/examples/change-grid.tsx +42 -0
  21. package/agents/examples/chart-area-log.tsx +45 -0
  22. package/agents/examples/chart-area.tsx +39 -0
  23. package/agents/examples/chart-bar-flows.tsx +32 -0
  24. package/agents/examples/chart-bar-hourly.tsx +28 -0
  25. package/agents/examples/chart-bar-losses.tsx +29 -0
  26. package/agents/examples/chart-bar-teams.tsx +72 -0
  27. package/agents/examples/chart-bar-years.tsx +40 -0
  28. package/agents/examples/chart-bar.tsx +56 -0
  29. package/agents/examples/chart-category-ticks.tsx +50 -0
  30. package/agents/examples/chart-line-events.tsx +47 -0
  31. package/agents/examples/chart-line-forecast.tsx +36 -0
  32. package/agents/examples/chart-line-log.tsx +39 -0
  33. package/agents/examples/chart-line-quiet.tsx +114 -0
  34. package/agents/examples/chart-line-timeline.tsx +41 -0
  35. package/agents/examples/chart-line.tsx +57 -0
  36. package/agents/examples/chart-panels.tsx +94 -0
  37. package/agents/examples/code-block.tsx +40 -0
  38. package/agents/examples/collapsible.tsx +18 -0
  39. package/agents/examples/command.tsx +29 -0
  40. package/agents/examples/context-menu.tsx +17 -0
  41. package/agents/examples/copy-button.tsx +8 -0
  42. package/agents/examples/data-bar.tsx +108 -0
  43. package/agents/examples/data-table-compact.tsx +96 -0
  44. package/agents/examples/data-table-filters.tsx +103 -0
  45. package/agents/examples/data-table.tsx +99 -0
  46. package/agents/examples/delta.tsx +18 -0
  47. package/agents/examples/dialog.tsx +28 -0
  48. package/agents/examples/distribution-bar-budget.tsx +30 -0
  49. package/agents/examples/distribution-bar-tug.tsx +74 -0
  50. package/agents/examples/drawdown-chart.tsx +35 -0
  51. package/agents/examples/dropdown-menu.tsx +31 -0
  52. package/agents/examples/field.tsx +108 -0
  53. package/agents/examples/formatters.tsx +42 -0
  54. package/agents/examples/heatmap-calendar-signed.tsx +67 -0
  55. package/agents/examples/heatmap-calendar.tsx +62 -0
  56. package/agents/examples/heatmap-status.tsx +61 -0
  57. package/agents/examples/heatmap-weekday.tsx +72 -0
  58. package/agents/examples/histogram-chart.tsx +28 -0
  59. package/agents/examples/input-states.tsx +23 -0
  60. package/agents/examples/input.tsx +17 -0
  61. package/agents/examples/local-time.tsx +14 -0
  62. package/agents/examples/metric-strip.tsx +46 -0
  63. package/agents/examples/overview-dashboard.tsx +180 -0
  64. package/agents/examples/page-hero.tsx +38 -0
  65. package/agents/examples/page-section.tsx +52 -0
  66. package/agents/examples/popover.tsx +23 -0
  67. package/agents/examples/price-chart.tsx +32 -0
  68. package/agents/examples/progress-tone.tsx +29 -0
  69. package/agents/examples/progress.tsx +10 -0
  70. package/agents/examples/range-position-table.tsx +101 -0
  71. package/agents/examples/range-position.tsx +55 -0
  72. package/agents/examples/scatter-chart-quadrants.tsx +63 -0
  73. package/agents/examples/scatter-chart-scales.tsx +46 -0
  74. package/agents/examples/scatter-chart.tsx +78 -0
  75. package/agents/examples/scroll-area.tsx +22 -0
  76. package/agents/examples/section-cards-select.tsx +28 -0
  77. package/agents/examples/section-cards-trend.tsx +32 -0
  78. package/agents/examples/section-cards.tsx +48 -0
  79. package/agents/examples/select.tsx +27 -0
  80. package/agents/examples/separator.tsx +13 -0
  81. package/agents/examples/sheet.tsx +15 -0
  82. package/agents/examples/skeleton.tsx +11 -0
  83. package/agents/examples/slider.tsx +5 -0
  84. package/agents/examples/slope-chart-market.tsx +33 -0
  85. package/agents/examples/slope-chart.tsx +26 -0
  86. package/agents/examples/stale-blocks.tsx +56 -0
  87. package/agents/examples/surfaces.tsx +116 -0
  88. package/agents/examples/table.tsx +60 -0
  89. package/agents/examples/tabs.tsx +16 -0
  90. package/agents/examples/target-rows.tsx +74 -0
  91. package/agents/examples/toggle-group.tsx +12 -0
  92. package/agents/examples/toggle.tsx +11 -0
  93. package/agents/examples/tooltip.tsx +13 -0
  94. package/agents/examples/treemap.tsx +93 -0
  95. package/agents/examples/waterfall-chart-stores.tsx +27 -0
  96. package/agents/examples/waterfall-chart.tsx +37 -0
  97. package/agents/manifest.json +1391 -0
  98. package/agents/report-rules.json +7 -0
  99. package/dist/components/registry/charts/area-chart-card.d.ts +7 -3
  100. package/dist/components/registry/charts/area-chart-card.js +56 -10
  101. package/dist/components/registry/charts/bar-chart-card.d.ts +1 -1
  102. package/dist/components/registry/charts/bar-chart-card.js +20 -22
  103. package/dist/components/registry/charts/line-chart-card.d.ts +3 -3
  104. package/dist/components/registry/charts/line-chart-card.js +44 -9
  105. package/dist/components/registry/dashboard-01/section-cards.d.ts +6 -0
  106. package/dist/components/registry/dashboard-01/section-cards.js +51 -35
  107. package/dist/components/registry/dashboardcn/bar-list.js +17 -10
  108. package/dist/components/registry/dashboardcn/distribution-bar.js +6 -4
  109. package/dist/components/registry/dashboardcn/kpi-row-card.d.ts +29 -0
  110. package/dist/components/registry/dashboardcn/kpi-row-card.js +81 -0
  111. package/dist/components/registry/dashboardcn/sparkline.d.ts +22 -0
  112. package/dist/components/registry/dashboardcn/sparkline.js +117 -0
  113. package/dist/components/registry/heatmap/calendar-heatmap.d.ts +11 -3
  114. package/dist/components/registry/heatmap/calendar-heatmap.js +164 -45
  115. package/dist/components/registry/heatmap/status-heatmap.d.ts +5 -3
  116. package/dist/components/registry/heatmap/status-heatmap.js +115 -54
  117. package/dist/components/registry/heatmap/weekday-heatmap.d.ts +9 -3
  118. package/dist/components/registry/heatmap/weekday-heatmap.js +83 -53
  119. package/dist/components/registry/niko-table/core/data-table-context.d.ts +18 -1
  120. package/dist/components/registry/niko-table/core/data-table-context.js +7 -1
  121. package/dist/components/registry/niko-table/core/data-table-grouped-row.js +3 -1
  122. package/dist/components/registry/niko-table/core/data-table-root.js +39 -4
  123. package/dist/components/registry/niko-table/core/data-table-structure.js +17 -12
  124. package/dist/components/registry/niko-table/core/data-table.d.ts +2 -2
  125. package/dist/components/registry/niko-table/core/data-table.js +5 -1
  126. package/dist/components/registry/niko-table/filters/table-faceted-filter.js +5 -5
  127. package/dist/components/registry/niko-table/filters/table-search-filter.js +1 -1
  128. package/dist/components/registry/niko-table/lib/flex-columns.d.ts +1 -1
  129. package/dist/components/registry/niko-table/lib/flex-columns.js +3 -3
  130. package/dist/components/registry/niko-table/lib/styles.d.ts +1 -1
  131. package/dist/components/registry/niko-table/lib/styles.js +3 -3
  132. package/dist/components/registry/niko-table/types/index.d.ts +9 -0
  133. package/dist/components/registry/niko-table/ui/table.js +2 -2
  134. package/dist/components/registry/sidebar-07/app-sidebar.d.ts +1 -1
  135. package/dist/components/registry/sidebar-07/app-sidebar.js +3 -1
  136. package/dist/components/registry/sidebar-07/nav-main.d.ts +2 -1
  137. package/dist/components/registry/sidebar-07/nav-main.js +16 -3
  138. package/dist/components/siyahi/callout.d.ts +17 -0
  139. package/dist/components/siyahi/callout.js +34 -0
  140. package/dist/components/siyahi/change-bar.d.ts +21 -0
  141. package/dist/components/siyahi/change-bar.js +35 -0
  142. package/dist/components/siyahi/change-grid.d.ts +31 -0
  143. package/dist/components/siyahi/change-grid.js +74 -0
  144. package/dist/components/siyahi/data-bar.d.ts +19 -0
  145. package/dist/components/siyahi/data-bar.js +29 -0
  146. package/dist/components/siyahi/data-table-compact-columns-toggle.d.ts +4 -0
  147. package/dist/components/siyahi/data-table-compact-columns-toggle.js +19 -0
  148. package/dist/components/siyahi/data-table-footnote.d.ts +12 -0
  149. package/dist/components/siyahi/data-table-footnote.js +35 -0
  150. package/dist/components/siyahi/default-sidebar-open.d.ts +8 -0
  151. package/dist/components/siyahi/default-sidebar-open.js +13 -0
  152. package/dist/components/siyahi/delta.d.ts +5 -1
  153. package/dist/components/siyahi/delta.js +14 -9
  154. package/dist/components/siyahi/drawdown-chart.d.ts +34 -0
  155. package/dist/components/siyahi/drawdown-chart.js +102 -0
  156. package/dist/components/siyahi/histogram-chart.d.ts +57 -0
  157. package/dist/components/siyahi/histogram-chart.js +134 -0
  158. package/dist/components/siyahi/page-hero.d.ts +23 -0
  159. package/dist/components/siyahi/page-hero.js +43 -0
  160. package/dist/components/siyahi/page-section.d.ts +4 -1
  161. package/dist/components/siyahi/page-section.js +6 -3
  162. package/dist/components/siyahi/page-toc.d.ts +20 -0
  163. package/dist/components/siyahi/page-toc.js +76 -0
  164. package/dist/components/siyahi/panel-grid.d.ts +24 -0
  165. package/dist/components/siyahi/panel-grid.js +37 -0
  166. package/dist/components/siyahi/quote.d.ts +12 -0
  167. package/dist/components/siyahi/quote.js +23 -0
  168. package/dist/components/siyahi/range-position.d.ts +24 -0
  169. package/dist/components/siyahi/range-position.js +105 -0
  170. package/dist/components/siyahi/scale-toggle.d.ts +32 -0
  171. package/dist/components/siyahi/scale-toggle.js +61 -0
  172. package/dist/components/siyahi/scatter-chart.d.ts +69 -0
  173. package/dist/components/siyahi/scatter-chart.js +395 -0
  174. package/dist/components/siyahi/siyahi-provider.d.ts +15 -2
  175. package/dist/components/siyahi/siyahi-provider.js +17 -4
  176. package/dist/components/siyahi/slope-chart.d.ts +35 -0
  177. package/dist/components/siyahi/slope-chart.js +159 -0
  178. package/dist/components/siyahi/stale-note.d.ts +9 -0
  179. package/dist/components/siyahi/stale-note.js +12 -0
  180. package/dist/components/siyahi/status-alert.d.ts +15 -0
  181. package/dist/components/siyahi/status-alert.js +29 -0
  182. package/dist/components/siyahi/status-badge.d.ts +15 -0
  183. package/dist/components/siyahi/status-badge.js +24 -0
  184. package/dist/components/siyahi/target-rows.d.ts +34 -0
  185. package/dist/components/siyahi/target-rows.js +97 -0
  186. package/dist/components/siyahi/tone-progress.d.ts +1 -1
  187. package/dist/components/siyahi/tone-progress.js +1 -1
  188. package/dist/components/siyahi/treemap.d.ts +8 -5
  189. package/dist/components/siyahi/treemap.js +80 -34
  190. package/dist/components/siyahi/waterfall-chart.d.ts +51 -0
  191. package/dist/components/siyahi/waterfall-chart.js +147 -0
  192. package/dist/components/ui/accordion.js +5 -5
  193. package/dist/components/ui/alert.js +2 -2
  194. package/dist/components/ui/badge.js +1 -1
  195. package/dist/components/ui/breadcrumb.js +2 -2
  196. package/dist/components/ui/button.js +10 -10
  197. package/dist/components/ui/calendar.js +1 -1
  198. package/dist/components/ui/card.js +4 -4
  199. package/dist/components/ui/chart.js +1 -1
  200. package/dist/components/ui/code-block.js +13 -12
  201. package/dist/components/ui/command.js +7 -6
  202. package/dist/components/ui/context-menu.js +7 -7
  203. package/dist/components/ui/dialog.js +5 -5
  204. package/dist/components/ui/dropdown-menu.js +8 -8
  205. package/dist/components/ui/field.d.ts +26 -0
  206. package/dist/components/ui/field.js +109 -0
  207. package/dist/components/ui/input-group.js +11 -11
  208. package/dist/components/ui/input.js +1 -1
  209. package/dist/components/ui/label.d.ts +5 -0
  210. package/dist/components/ui/label.js +13 -0
  211. package/dist/components/ui/popover.js +3 -3
  212. package/dist/components/ui/progress.js +1 -1
  213. package/dist/components/ui/select.js +6 -6
  214. package/dist/components/ui/sheet.js +5 -5
  215. package/dist/components/ui/sidebar.js +17 -18
  216. package/dist/components/ui/skeleton.js +1 -1
  217. package/dist/components/ui/slider.js +2 -2
  218. package/dist/components/ui/table.js +2 -2
  219. package/dist/components/ui/tabs.js +2 -2
  220. package/dist/components/ui/textarea.js +1 -1
  221. package/dist/components/ui/toggle-group.js +2 -2
  222. package/dist/components/ui/toggle.js +4 -4
  223. package/dist/components/ui/tooltip.js +2 -2
  224. package/dist/index.d.ts +35 -8
  225. package/dist/index.js +30 -4
  226. package/dist/lib/chart-frame.d.ts +51 -7
  227. package/dist/lib/chart-frame.js +390 -133
  228. package/dist/lib/chart-marks.d.ts +83 -0
  229. package/dist/lib/chart-marks.js +216 -0
  230. package/dist/lib/chart-series.d.ts +42 -10
  231. package/dist/lib/compact-columns.d.ts +19 -0
  232. package/dist/lib/compact-columns.js +55 -0
  233. package/dist/lib/fit-text.d.ts +6 -0
  234. package/dist/lib/fit-text.js +11 -0
  235. package/dist/lib/format.d.ts +8 -3
  236. package/dist/lib/format.js +55 -17
  237. package/dist/lib/heat.d.ts +7 -0
  238. package/dist/lib/heat.js +14 -0
  239. package/dist/lib/heatmap-navigation.d.ts +8 -0
  240. package/dist/lib/heatmap-navigation.js +49 -0
  241. package/dist/lib/scatter.d.ts +88 -0
  242. package/dist/lib/scatter.js +179 -0
  243. package/dist/lib/ticks.d.ts +27 -0
  244. package/dist/lib/ticks.js +67 -0
  245. package/dist/lib/tone.d.ts +36 -0
  246. package/dist/lib/tone.js +35 -0
  247. package/dist/lib/treemap.d.ts +0 -5
  248. package/dist/lib/treemap.js +1 -11
  249. package/dist/lib/use-element-width.d.ts +5 -0
  250. package/dist/lib/use-element-width.js +15 -0
  251. package/package.json +5 -1
  252. package/registry.lock.json +16 -0
  253. package/report.css +63 -47
  254. package/styles/siyahi.css +708 -55
@@ -1,24 +1,29 @@
1
1
  import { useFormat } from "../../lib/format.js";
2
2
  import { cn } from "cn";
3
+ import { MinusIcon, TrendingDownIcon, TrendingUpIcon } from "lucide-react";
3
4
  import { jsx, jsxs } from "react/jsx-runtime";
4
5
  //#region src/components/siyahi/delta.tsx
5
- /** A signed figure: ▲ in --positive, ▼ in --negative, so colour is never the only cue. */
6
+ const MINUS = "−";
7
+ /**
8
+ * A signed change: the figure in the surrounding text colour with its sign, and a trend icon in --positive or
9
+ * --negative. The sign and the icon's shape carry the direction, so colour is never the only cue, and a column of
10
+ * changes reads as figures rather than a wall of red and green.
11
+ */
6
12
  function Delta({ value, format = "pct", className }) {
7
13
  const f = useFormat();
8
14
  const size = format === "money" ? f.money(Math.abs(value)) : f.pct(Math.abs(value));
9
- const direction = value > 0 ? "up" : value < 0 ? "down" : null;
15
+ const direction = value > 0 ? "up" : value < 0 ? "down" : "none";
16
+ const Icon = direction === "up" ? TrendingUpIcon : direction === "down" ? TrendingDownIcon : MinusIcon;
10
17
  return /* @__PURE__ */ jsxs("span", {
11
18
  "data-slot": "delta",
12
- className: cn("inline-flex items-center gap-1 tabular-nums", direction === "up" && "text-positive", direction === "down" && "text-negative", direction === null && "text-muted-foreground", className),
19
+ "data-direction": direction,
20
+ className: cn("inline-flex items-baseline gap-1 whitespace-nowrap", className),
13
21
  children: [
14
- direction && /* @__PURE__ */ jsx("span", {
22
+ /* @__PURE__ */ jsx(Icon, {
15
23
  "aria-hidden": "true",
16
- children: direction === "up" ? "▲" : "▼"
17
- }),
18
- direction && /* @__PURE__ */ jsx("span", {
19
- className: "sr-only",
20
- children: direction
24
+ className: cn("size-3.5 shrink-0 self-center", direction === "up" && "text-positive", direction === "down" && "text-negative", direction === "none" && "text-muted-foreground")
21
25
  }),
26
+ direction === "up" ? "+" : direction === "down" ? MINUS : "",
22
27
  size
23
28
  ]
24
29
  });
@@ -0,0 +1,34 @@
1
+ import { NumericKey, SeriesChartProps } from "../../lib/chart-series.js";
2
+ //#region src/components/siyahi/drawdown-chart.d.ts
3
+ /** One point as drawn: how far the level sits below the highest it had reached, and that high. */
4
+ export interface DrawdownPoint<X> {
5
+ x: X;
6
+ /** The fall from the peak as a fraction, 0 at a new high and −0.25 a quarter below it; null when the level is unknown. */
7
+ drawdown: number | null;
8
+ /** The highest level so far. */
9
+ peak: number;
10
+ level: number | null;
11
+ }
12
+ /**
13
+ * Walks the levels in order, keeping the highest so far. A level that is not a finite number has no drawdown and
14
+ * leaves the peak as it was; the first level is the first peak.
15
+ */
16
+ export declare function drawdowns<X>(levels: readonly {
17
+ x: X;
18
+ level: number | null;
19
+ }[]): DrawdownPoint<X>[];
20
+ export interface DrawdownChartCardProps<T extends object, K extends keyof T> extends Pick<SeriesChartProps<T, K>, "title" | "description" | "action" | "data" | "xKey" | "height" | "xFormatter" | "xTooltipFormatter" | "xTicks" | "placeholder" | "footer"> {
21
+ /** The field holding the level, such as the day's cash balance or a fund's value. */
22
+ levelKey: NumericKey<T>;
23
+ /** Writes the level and the peak in the tooltip. Defaults to useFormat().money. */
24
+ levelFormatter?: (value: number) => string;
25
+ /** Names the fall in the legend and tooltip. */
26
+ label?: string;
27
+ }
28
+ /**
29
+ * How far a level sits below the highest it had reached, over time: a fund's or a cash balance's fall from its
30
+ * peak. The area hangs from zero in the loss colour, so the deepest and longest falls read at once, and a dashed
31
+ * line marks the deepest one. Levels must be above zero: a fall is a share of the peak.
32
+ */
33
+ export declare function DrawdownChartCard<T extends object, K extends keyof T>({ data, xKey, levelKey, levelFormatter, label, xFormatter, xTooltipFormatter, xTicks, ...props }: DrawdownChartCardProps<T, K>): import("react").JSX.Element;
34
+ //#endregion
@@ -0,0 +1,102 @@
1
+ import { linearTicks } from "../../lib/ticks.js";
2
+ import { ChartFrame } from "../../lib/chart-frame.js";
3
+ import { useFormat } from "../../lib/format.js";
4
+ import { jsx, jsxs } from "react/jsx-runtime";
5
+ import { useMemo } from "react";
6
+ import { Area, AreaChart } from "recharts";
7
+ //#region src/components/siyahi/drawdown-chart.tsx
8
+ /**
9
+ * Walks the levels in order, keeping the highest so far. A level that is not a finite number has no drawdown and
10
+ * leaves the peak as it was; the first level is the first peak.
11
+ */
12
+ function drawdowns(levels) {
13
+ let peak = Number.NEGATIVE_INFINITY;
14
+ return levels.map(({ x, level }) => {
15
+ if (level === null || !Number.isFinite(level)) return {
16
+ x,
17
+ level: null,
18
+ drawdown: null,
19
+ peak
20
+ };
21
+ peak = Math.max(peak, level);
22
+ return {
23
+ x,
24
+ level,
25
+ peak,
26
+ drawdown: peak > 0 ? level / peak - 1 : 0
27
+ };
28
+ });
29
+ }
30
+ /**
31
+ * How far a level sits below the highest it had reached, over time: a fund's or a cash balance's fall from its
32
+ * peak. The area hangs from zero in the loss colour, so the deepest and longest falls read at once, and a dashed
33
+ * line marks the deepest one. Levels must be above zero: a fall is a share of the peak.
34
+ */
35
+ function DrawdownChartCard({ data, xKey, levelKey, levelFormatter, label = "Below peak", xFormatter = String, xTooltipFormatter, xTicks, ...props }) {
36
+ const f = useFormat();
37
+ const formatLevel = levelFormatter ?? ((value) => f.money(value));
38
+ const points = useMemo(() => drawdowns(data.map((row) => ({
39
+ x: row[xKey],
40
+ level: row[levelKey]
41
+ }))), [
42
+ data,
43
+ xKey,
44
+ levelKey
45
+ ]);
46
+ const fall = (value) => value < 0 ? `−${f.pct(-value)}` : f.pct(0);
47
+ const deepest = Math.min(0, ...points.flatMap(({ drawdown }) => drawdown ?? []));
48
+ const ticks = linearTicks(Math.min(deepest, -.01), 0);
49
+ return /* @__PURE__ */ jsx(ChartFrame, {
50
+ chart: AreaChart,
51
+ ...props,
52
+ data: points,
53
+ xKey: "x",
54
+ xFormatter: (x) => xFormatter(x),
55
+ xTooltipFormatter: (x) => (xTooltipFormatter ?? xFormatter)(x),
56
+ xTicks,
57
+ series: [{
58
+ key: "drawdown",
59
+ label,
60
+ color: "var(--negative)"
61
+ }],
62
+ yDomain: [Math.min(deepest, ticks[0] ?? deepest), 0],
63
+ yTicks: ticks,
64
+ yTickFormatter: (value) => fall(value),
65
+ referenceLines: deepest < 0 ? [{
66
+ y: deepest,
67
+ label: `Deepest ${fall(deepest)}`,
68
+ labelPosition: "insideBottomRight"
69
+ }] : [],
70
+ tooltipRow: (_value, _name, item) => {
71
+ const point = item.payload;
72
+ return (point.drawdown === null ? [[label, "—"]] : [
73
+ [label, fall(point.drawdown)],
74
+ ["Level", formatLevel(point.level ?? 0)],
75
+ ["Peak so far", formatLevel(point.peak)]
76
+ ]).map(([name, figure]) => /* @__PURE__ */ jsxs("div", {
77
+ className: "flex w-full items-center gap-2",
78
+ children: [/* @__PURE__ */ jsx("span", {
79
+ className: "text-muted-foreground",
80
+ children: name
81
+ }), /* @__PURE__ */ jsx("span", {
82
+ className: "ml-auto font-mono font-medium text-foreground tabular-nums",
83
+ children: figure
84
+ })]
85
+ }, name));
86
+ },
87
+ children: /* @__PURE__ */ jsx(Area, {
88
+ dataKey: "drawdown",
89
+ name: label,
90
+ type: "linear",
91
+ baseValue: 0,
92
+ stroke: "var(--negative)",
93
+ strokeWidth: 2,
94
+ fill: "color-mix(in oklab, var(--negative) 25%, var(--card))",
95
+ fillOpacity: 1,
96
+ dot: false,
97
+ connectNulls: false
98
+ })
99
+ });
100
+ }
101
+ //#endregion
102
+ export { DrawdownChartCard, drawdowns };
@@ -0,0 +1,57 @@
1
+ import { ChartReferenceLine, SeriesChartProps } from "../../lib/chart-series.js";
2
+ //#region src/components/siyahi/histogram-chart.d.ts
3
+ /** One bucket: the values from `from` up to, not including, `to`; the last bucket takes its upper edge too. */
4
+ export interface HistogramBin {
5
+ key: string;
6
+ from: number;
7
+ to: number;
8
+ count: number;
9
+ }
10
+ /**
11
+ * Buckets of one round width (1, 2, 2.5 or 5 × 10ⁿ) covering the values, about `target` of them. Values that are not
12
+ * finite numbers are left out.
13
+ */
14
+ export declare function histogram(values: readonly number[], target?: number): HistogramBin[];
15
+ /**
16
+ * The bucket a value falls in, read against the buckets' own edges: dividing by the width instead put 0.3 in the
17
+ * 0.2–0.3 bucket, as 0.3 / 0.1 is 2.9999999999999996. A value past the last edge belongs to the last bucket.
18
+ */
19
+ export declare function bucketOf(bins: readonly HistogramBin[], value: number): number;
20
+ /** The value below which `share` of the values fall, interpolating between the two nearest, as spreadsheets do. */
21
+ export declare function percentile(values: readonly number[], share: number): number | null;
22
+ declare const MARKS: {
23
+ readonly median: 0.5;
24
+ readonly p90: 0.9;
25
+ readonly p95: 0.95;
26
+ readonly p99: 0.99;
27
+ };
28
+ export type HistogramMark = keyof typeof MARKS;
29
+ /**
30
+ * One line per bucket that holds a mark, named by its marks: marks that share a bucket share a line and a name,
31
+ * "median, p90", rather than drawing over each other.
32
+ */
33
+ export declare function markLines(bins: readonly HistogramBin[], marked: readonly {
34
+ mark: HistogramMark;
35
+ value: number;
36
+ }[]): ChartReferenceLine[];
37
+ export interface HistogramChartCardProps extends Pick<SeriesChartProps<HistogramBin, "key">, "title" | "description" | "action" | "height" | "xLabel" | "yLabel" | "placeholder" | "footer"> {
38
+ /** The raw values, one per item, such as each request's response time. */
39
+ values: readonly number[];
40
+ /** About how many buckets; the width is rounded, so the count varies a little. */
41
+ bins?: number;
42
+ /** Writes the bucket edges and the marks, such as (v) => `${v} ms`. Defaults to the locale's numbers. */
43
+ valueFormatter?: (value: number) => string;
44
+ /** The bucket edges under the axis, when they should be shorter than valueFormatter: 1,000 against 1,000 ms. */
45
+ tickFormatter?: (value: number) => string;
46
+ /** What is counted, in the legend and tooltip, such as "Requests". */
47
+ countLabel?: string;
48
+ /** Percentiles to mark with a line at their bucket; their exact values are written under the chart. */
49
+ marks?: readonly HistogramMark[];
50
+ }
51
+ /**
52
+ * How values spread: counts in buckets of one round width, such as response times by 50 ms, with the median or a
53
+ * high percentile marked. The page gives the raw values; the card buckets them.
54
+ */
55
+ export declare function HistogramChartCard({ values, bins: target, valueFormatter, tickFormatter, countLabel, marks, ...props }: HistogramChartCardProps): import("react").JSX.Element;
56
+ //#endregion
57
+ export {};
@@ -0,0 +1,134 @@
1
+ import { useLocale } from "./siyahi-provider.js";
2
+ import { linearTicks } from "../../lib/ticks.js";
3
+ import { ChartFrame, barFill } from "../../lib/chart-frame.js";
4
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
5
+ import { useMemo } from "react";
6
+ import { Bar, BarChart, Cell } from "recharts";
7
+ //#region src/components/siyahi/histogram-chart.tsx
8
+ /**
9
+ * Buckets of one round width (1, 2, 2.5 or 5 × 10ⁿ) covering the values, about `target` of them. Values that are not
10
+ * finite numbers are left out.
11
+ */
12
+ function histogram(values, target = 12) {
13
+ const known = values.filter(Number.isFinite);
14
+ if (known.length === 0) return [];
15
+ let low = known[0];
16
+ let high = low;
17
+ for (const value of known) {
18
+ low = Math.min(low, value);
19
+ high = Math.max(high, value);
20
+ }
21
+ const edges = linearTicks(low, high, target + 1);
22
+ if (edges.length < 2) return [{
23
+ key: "0",
24
+ from: low,
25
+ to: high,
26
+ count: known.length
27
+ }];
28
+ const bins = edges.slice(0, -1).map((from, i) => ({
29
+ key: String(i),
30
+ from,
31
+ to: edges[i + 1],
32
+ count: 0
33
+ }));
34
+ for (const value of known) bins[bucketOf(bins, value)].count += 1;
35
+ return bins;
36
+ }
37
+ /**
38
+ * The bucket a value falls in, read against the buckets' own edges: dividing by the width instead put 0.3 in the
39
+ * 0.2–0.3 bucket, as 0.3 / 0.1 is 2.9999999999999996. A value past the last edge belongs to the last bucket.
40
+ */
41
+ function bucketOf(bins, value) {
42
+ const index = bins.findIndex((bin) => value < bin.to);
43
+ return index === -1 ? bins.length - 1 : index;
44
+ }
45
+ /** The value below which `share` of the values fall, interpolating between the two nearest, as spreadsheets do. */
46
+ function percentile(values, share) {
47
+ const sorted = values.filter(Number.isFinite).toSorted((a, b) => a - b);
48
+ if (sorted.length === 0) return null;
49
+ const at = (sorted.length - 1) * share;
50
+ const low = Math.floor(at);
51
+ return sorted[low] + (sorted[Math.ceil(at)] - sorted[low]) * (at - low);
52
+ }
53
+ const MARKS = {
54
+ median: .5,
55
+ p90: .9,
56
+ p95: .95,
57
+ p99: .99
58
+ };
59
+ /**
60
+ * One line per bucket that holds a mark, named by its marks: marks that share a bucket share a line and a name,
61
+ * "median, p90", rather than drawing over each other.
62
+ */
63
+ function markLines(bins, marked) {
64
+ return [...Map.groupBy(marked, ({ value }) => bucketOf(bins, value))].flatMap(([index, group]) => {
65
+ const bin = bins[index];
66
+ if (!bin) return [];
67
+ const right = index >= bins.length / 2;
68
+ const label = group.map(({ mark }) => mark).join(", ");
69
+ return [{
70
+ x: bin.key,
71
+ label,
72
+ emphasis: "strong",
73
+ labelPosition: right ? "insideTopRight" : "insideTopLeft"
74
+ }];
75
+ });
76
+ }
77
+ const NO_MARKS = [];
78
+ /**
79
+ * How values spread: counts in buckets of one round width, such as response times by 50 ms, with the median or a
80
+ * high percentile marked. The page gives the raw values; the card buckets them.
81
+ */
82
+ function HistogramChartCard({ values, bins: target = 12, valueFormatter, tickFormatter, countLabel = "Count", marks = NO_MARKS, ...props }) {
83
+ const { numberLocale } = useLocale();
84
+ const format = valueFormatter ?? new Intl.NumberFormat(numberLocale).format;
85
+ const bins = useMemo(() => histogram(values, target), [values, target]);
86
+ const range = (bin) => `${format(bin.from)}–${format(bin.to)}`;
87
+ const names = new Map(bins.map((bin) => [bin.key, bin]));
88
+ const marked = marks.flatMap((mark) => {
89
+ const value = percentile(values, MARKS[mark]);
90
+ return value === null ? [] : [{
91
+ mark,
92
+ value
93
+ }];
94
+ });
95
+ const lines = markLines(bins, marked);
96
+ return /* @__PURE__ */ jsx(ChartFrame, {
97
+ chart: BarChart,
98
+ ...props,
99
+ data: bins,
100
+ xKey: "key",
101
+ xFormatter: (key) => {
102
+ const bin = names.get(key);
103
+ return bin ? (tickFormatter ?? format)(bin.from) : key;
104
+ },
105
+ xTooltipFormatter: (key) => {
106
+ const bin = names.get(key);
107
+ return bin ? range(bin) : key;
108
+ },
109
+ series: [{
110
+ key: "count",
111
+ label: countLabel,
112
+ color: "var(--chart-1)"
113
+ }],
114
+ referenceLines: lines,
115
+ footer: marked.length > 0 || props.footer ? /* @__PURE__ */ jsxs(Fragment, { children: [marked.length > 0 && /* @__PURE__ */ jsx("p", {
116
+ "data-slot": "histogram-marks",
117
+ className: "tabular-nums",
118
+ children: marked.map(({ mark, value }) => `${mark} ${format(value)}`).join(" · ")
119
+ }), props.footer] }) : void 0,
120
+ children: /* @__PURE__ */ jsx(Bar, {
121
+ dataKey: "count",
122
+ name: countLabel,
123
+ fill: "var(--chart-1)",
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
+ })
131
+ });
132
+ }
133
+ //#endregion
134
+ export { HistogramChartCard, bucketOf, histogram, markLines, percentile };
@@ -0,0 +1,23 @@
1
+ import { ReactNode } from "react";
2
+ //#region src/components/siyahi/page-hero.d.ts
3
+ export interface PageHeroProps {
4
+ /** A short line above the title in the mono face and the link colour, such as the app or a section name. */
5
+ eyebrow?: ReactNode;
6
+ /** The page's h1. */
7
+ title: ReactNode;
8
+ /** What the page is, in a sentence, in the muted colour. */
9
+ lead?: ReactNode;
10
+ /** Scope, sources or what is left out, run on after the lead in the same muted line. */
11
+ detail?: ReactNode;
12
+ /** The one or two places a reader goes next, usually a primary Button and an outline one, at the right. */
13
+ actions?: ReactNode;
14
+ /** A small mark before the eyebrow, such as the product's logo as an SVG that paints with currentColor. */
15
+ mark?: ReactNode;
16
+ }
17
+ /**
18
+ * The masthead at the top of a page: a small mark and eyebrow, the title, one muted line saying what the page is,
19
+ * the actions at the right, and a hairline under it all. One per page, above everything else; a section uses
20
+ * PageSection.
21
+ */
22
+ export declare function PageHero({ eyebrow, title, lead, detail, actions, mark }: PageHeroProps): import("react").JSX.Element;
23
+ //#endregion
@@ -0,0 +1,43 @@
1
+ import { jsx, jsxs } from "react/jsx-runtime";
2
+ //#region src/components/siyahi/page-hero.tsx
3
+ /**
4
+ * The masthead at the top of a page: a small mark and eyebrow, the title, one muted line saying what the page is,
5
+ * the actions at the right, and a hairline under it all. One per page, above everything else; a section uses
6
+ * PageSection.
7
+ */
8
+ function PageHero({ eyebrow, title, lead, detail, actions, mark }) {
9
+ return /* @__PURE__ */ jsxs("header", {
10
+ "data-slot": "page-hero",
11
+ className: "flex flex-wrap items-end justify-between gap-x-6 gap-y-3 border-b pb-4",
12
+ children: [/* @__PURE__ */ jsxs("div", {
13
+ className: "flex min-w-0 flex-col gap-1",
14
+ children: [
15
+ (mark || eyebrow) && /* @__PURE__ */ jsxs("p", {
16
+ className: "flex items-center gap-1.5 font-mono text-xs text-link",
17
+ children: [mark && /* @__PURE__ */ jsx("span", {
18
+ "aria-hidden": true,
19
+ className: "size-3.5 shrink-0 [&>svg]:size-full",
20
+ children: mark
21
+ }), eyebrow]
22
+ }),
23
+ /* @__PURE__ */ jsx("h1", {
24
+ className: "text-3xl font-semibold tracking-tight text-balance",
25
+ children: title
26
+ }),
27
+ (lead || detail) && /* @__PURE__ */ jsxs("div", {
28
+ className: "text-sm text-pretty text-muted-foreground",
29
+ children: [
30
+ lead,
31
+ lead && detail && " ",
32
+ detail
33
+ ]
34
+ })
35
+ ]
36
+ }), actions && /* @__PURE__ */ jsx("div", {
37
+ className: "flex flex-wrap gap-2",
38
+ children: actions
39
+ })]
40
+ });
41
+ }
42
+ //#endregion
43
+ export { PageHero };
@@ -12,7 +12,10 @@ export interface PageSectionProps {
12
12
  action?: ReactNode;
13
13
  children?: ReactNode;
14
14
  }
15
- /** One part of a long page: a rule above, then the heading and lead on the canvas, then its content. */
15
+ /**
16
+ * One part of a long page: a rule above, then the heading and lead on the canvas, then its content. Spaced for
17
+ * density, 24px under the rule and 16px to the content, so sections stacked 24px apart sit 24px either side of it.
18
+ */
16
19
  export declare function PageSection({ id, eyebrow, title, lead, action, children }: PageSectionProps): import("react").JSX.Element;
17
20
  /** A heading inside a PageSection, one level under its title. */
18
21
  export declare function PageSubheading({ children }: {
@@ -1,11 +1,14 @@
1
1
  import { jsx, jsxs } from "react/jsx-runtime";
2
2
  //#region src/components/siyahi/page-section.tsx
3
- /** One part of a long page: a rule above, then the heading and lead on the canvas, then its content. */
3
+ /**
4
+ * One part of a long page: a rule above, then the heading and lead on the canvas, then its content. Spaced for
5
+ * density, 24px under the rule and 16px to the content, so sections stacked 24px apart sit 24px either side of it.
6
+ */
4
7
  function PageSection({ id, eyebrow, title, lead, action, children }) {
5
8
  return /* @__PURE__ */ jsxs("section", {
6
9
  id,
7
10
  "data-slot": "page-section",
8
- className: "flex scroll-mt-6 flex-col gap-6 border-t pt-10",
11
+ className: "flex scroll-mt-6 flex-col gap-4 border-t pt-6",
9
12
  children: [/* @__PURE__ */ jsxs("div", {
10
13
  className: "flex flex-wrap items-end justify-between gap-4",
11
14
  children: [/* @__PURE__ */ jsxs("div", {
@@ -16,7 +19,7 @@ function PageSection({ id, eyebrow, title, lead, action, children }) {
16
19
  children: eyebrow
17
20
  }),
18
21
  /* @__PURE__ */ jsx("h2", {
19
- className: "text-2xl font-semibold tracking-tight",
22
+ className: "text-xl font-semibold tracking-tight",
20
23
  children: title
21
24
  }),
22
25
  lead && /* @__PURE__ */ jsx("div", {
@@ -0,0 +1,20 @@
1
+ //#region src/components/siyahi/page-toc.d.ts
2
+ export interface PageTocItem {
3
+ /** The `id` of the PageSection it jumps to. */
4
+ id: string;
5
+ title: string;
6
+ }
7
+ export interface PageTocProps {
8
+ items: PageTocItem[];
9
+ /** Names the navigation for screen readers. */
10
+ label?: string;
11
+ /** Layout only, such as `top-14` under a sticky page header. */
12
+ className?: string;
13
+ }
14
+ /**
15
+ * A long page's sections as one sticky "On this page" index. It stays at the top while the reader scrolls,
16
+ * underlines the section in view in the link colour, and scrolls sideways on a phone with the current link kept
17
+ * in sight.
18
+ */
19
+ export declare function PageToc({ items, label, className }: PageTocProps): import("react").JSX.Element;
20
+ //#endregion
@@ -0,0 +1,76 @@
1
+ import { cn } from "cn";
2
+ import { ChevronRightIcon } from "lucide-react";
3
+ import { jsx, jsxs } from "react/jsx-runtime";
4
+ import { useEffect, useRef, useState } from "react";
5
+ //#region src/components/siyahi/page-toc.tsx
6
+ /**
7
+ * A long page's sections as one sticky "On this page" index. It stays at the top while the reader scrolls,
8
+ * underlines the section in view in the link colour, and scrolls sideways on a phone with the current link kept
9
+ * in sight.
10
+ */
11
+ function PageToc({ items, label = "On this page", className }) {
12
+ const nav = useRef(null);
13
+ const active = useActiveSection(items, nav);
14
+ const strip = useRef(null);
15
+ useEffect(() => {
16
+ const link = strip.current?.querySelector(`a[href="#${CSS.escape(active ?? "")}"]`);
17
+ const box = strip.current;
18
+ if (!link || !box) return;
19
+ const left = link.offsetLeft - box.clientWidth / 2 + link.offsetWidth / 2;
20
+ box.scrollTo({
21
+ left,
22
+ behavior: "smooth"
23
+ });
24
+ }, [active]);
25
+ return /* @__PURE__ */ jsx("nav", {
26
+ ref: nav,
27
+ "aria-label": label,
28
+ "data-slot": "page-toc",
29
+ className: cn("sticky top-0 z-10 -mx-4 border-b bg-background px-4 sm:mx-0 sm:px-0", className),
30
+ children: /* @__PURE__ */ jsxs("div", {
31
+ ref: strip,
32
+ className: "flex items-baseline gap-x-4 overflow-x-auto py-2.5 mask-r-from-90% [scrollbar-width:none] sm:mask-none",
33
+ children: [/* @__PURE__ */ jsxs("span", {
34
+ "aria-hidden": true,
35
+ className: "flex shrink-0 cursor-default items-center gap-1 self-center text-[0.6875rem] font-medium tracking-wider text-muted-foreground uppercase select-none",
36
+ children: [label, /* @__PURE__ */ jsx(ChevronRightIcon, { className: "size-3.5" })]
37
+ }), items.map((item) => /* @__PURE__ */ jsx("a", {
38
+ href: `#${item.id}`,
39
+ "aria-current": item.id === active ? "location" : void 0,
40
+ onClick: (event) => {
41
+ event.preventDefault();
42
+ document.getElementById(item.id)?.scrollIntoView({ behavior: "smooth" });
43
+ },
44
+ className: "shrink-0 text-sm whitespace-nowrap text-muted-foreground decoration-2 underline-offset-[6px] transition-colors hover:text-foreground hover:underline hover:decoration-border aria-[current=location]:font-medium aria-[current=location]:text-link aria-[current=location]:underline aria-[current=location]:decoration-current",
45
+ children: item.title
46
+ }, item.id))]
47
+ })
48
+ });
49
+ }
50
+ function useActiveSection(items, strip) {
51
+ const [active, setActive] = useState(items[0]?.id);
52
+ const ids = items.map((item) => item.id).join(" ");
53
+ useEffect(() => {
54
+ const sections = ids.split(" ").flatMap((id) => document.getElementById(id) ?? []);
55
+ if (sections.length === 0) return;
56
+ const update = () => {
57
+ const stripBottom = strip.current?.getBoundingClientRect().bottom ?? 0;
58
+ const line = (section) => Math.max(stripBottom, Number.parseFloat(getComputedStyle(section).scrollMarginTop) || 0, innerHeight * .3);
59
+ const reached = sections.filter((section) => section.getBoundingClientRect().top <= line(section) + 1);
60
+ setActive((reached.at(-1) ?? sections[0]).id);
61
+ };
62
+ update();
63
+ addEventListener("scroll", update, {
64
+ passive: true,
65
+ capture: true
66
+ });
67
+ addEventListener("resize", update);
68
+ return () => {
69
+ removeEventListener("scroll", update, { capture: true });
70
+ removeEventListener("resize", update);
71
+ };
72
+ }, [ids, strip]);
73
+ return active;
74
+ }
75
+ //#endregion
76
+ export { PageToc };
@@ -0,0 +1,24 @@
1
+ import { ReactNode } from "react";
2
+ //#region src/components/siyahi/panel-grid.d.ts
3
+ declare const COLUMNS: {
4
+ readonly 1: "";
5
+ readonly 2: "@2xl/panels:grid-cols-2";
6
+ readonly 3: "@2xl/panels:grid-cols-2 @5xl/panels:grid-cols-3";
7
+ readonly 4: "@2xl/panels:grid-cols-2 @5xl/panels:grid-cols-4";
8
+ };
9
+ export interface PanelGridProps {
10
+ /** Blocks across on a wide page; two on a medium one, and one on a phone. */
11
+ columns?: keyof typeof COLUMNS;
12
+ children: ReactNode;
13
+ className?: string;
14
+ }
15
+ /**
16
+ * Data blocks side by side: chart cards, tables, a MetricStrip. On a ruled surface, the default, the grid is filled
17
+ * with the card colour and a hairline parts the blocks across and down, with a rule above and below the grid and none
18
+ * on its sides; open leaves whitespace between them, and card an 8px gap between boxes. Each block's bottom and end
19
+ * edges carry its rules and the grid clips the ones on its own edges, so an empty cell at the end of a short last row
20
+ * is a blank stretch of the sheet, ruled off like the rest. The theme numbers each block's card title 01, 02.
21
+ */
22
+ export declare function PanelGrid({ columns, children, className }: PanelGridProps): import("react").JSX.Element;
23
+ //#endregion
24
+ export {};
@@ -0,0 +1,37 @@
1
+ import { useSurface } from "./siyahi-provider.js";
2
+ import { cn } from "cn";
3
+ import { jsx } from "react/jsx-runtime";
4
+ import { Children } from "react";
5
+ //#region src/components/siyahi/panel-grid.tsx
6
+ const COLUMNS = {
7
+ 1: "",
8
+ 2: "@2xl/panels:grid-cols-2",
9
+ 3: "@2xl/panels:grid-cols-2 @5xl/panels:grid-cols-3",
10
+ 4: "@2xl/panels:grid-cols-2 @5xl/panels:grid-cols-4"
11
+ };
12
+ /**
13
+ * Data blocks side by side: chart cards, tables, a MetricStrip. On a ruled surface, the default, the grid is filled
14
+ * with the card colour and a hairline parts the blocks across and down, with a rule above and below the grid and none
15
+ * on its sides; open leaves whitespace between them, and card an 8px gap between boxes. Each block's bottom and end
16
+ * edges carry its rules and the grid clips the ones on its own edges, so an empty cell at the end of a short last row
17
+ * is a blank stretch of the sheet, ruled off like the rest. The theme numbers each block's card title 01, 02.
18
+ */
19
+ function PanelGrid({ columns = 2, children, className }) {
20
+ const surface = useSurface();
21
+ const ruled = surface === "ruled";
22
+ return /* @__PURE__ */ jsx("div", {
23
+ "data-slot": "panel-grid",
24
+ "data-surface": surface,
25
+ className: cn("@container/panels", ruled && "overflow-clip border-y", className),
26
+ children: /* @__PURE__ */ jsx("div", {
27
+ className: cn("grid grid-cols-1", COLUMNS[columns], ruled ? "-me-px -mb-px bg-card" : surface === "open" ? "gap-x-8 gap-y-6" : "gap-2"),
28
+ children: Children.map(children, (child) => /* @__PURE__ */ jsx("div", {
29
+ "data-slot": "panel",
30
+ className: cn("min-w-0", ruled && "border-e border-b"),
31
+ children: child
32
+ }))
33
+ })
34
+ });
35
+ }
36
+ //#endregion
37
+ export { PanelGrid };
@@ -0,0 +1,12 @@
1
+ import { ComponentProps, ReactNode } from "react";
2
+ //#region src/components/siyahi/quote.d.ts
3
+ export interface QuoteProps extends ComponentProps<"blockquote"> {
4
+ /** Who said it, or where it is from, set under the quote. */
5
+ source?: ReactNode;
6
+ }
7
+ /**
8
+ * A quotation set apart from the prose on a muted panel, with its source under it. The panel is the whole box, not a
9
+ * stripe down one side.
10
+ */
11
+ export declare function Quote({ source, className, children, ...props }: QuoteProps): import("react").JSX.Element;
12
+ //#endregion