@siyahi/ui 0.1.8-next.4 → 0.1.9-next.2

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 (47) hide show
  1. package/README.md +38 -18
  2. package/THIRD_PARTY_NOTICES.md +11 -10
  3. package/dist/components/registry/charts/area-chart-card.d.ts +4 -3
  4. package/dist/components/registry/charts/area-chart-card.js +2 -2
  5. package/dist/components/registry/charts/bar-chart-card.d.ts +7 -1
  6. package/dist/components/registry/charts/bar-chart-card.js +20 -5
  7. package/dist/components/registry/charts/line-chart-card.d.ts +5 -2
  8. package/dist/components/registry/charts/line-chart-card.js +2 -2
  9. package/dist/components/registry/dashboard-01/section-cards.d.ts +9 -2
  10. package/dist/components/registry/dashboard-01/section-cards.js +32 -21
  11. package/dist/components/registry/dashboardcn/bar-list.d.ts +3 -1
  12. package/dist/components/registry/dashboardcn/bar-list.js +2 -2
  13. package/dist/components/registry/dashboardcn/distribution-bar.d.ts +38 -0
  14. package/dist/components/registry/dashboardcn/distribution-bar.js +118 -0
  15. package/dist/components/registry/minidev/copy-button.d.ts +11 -0
  16. package/dist/components/registry/minidev/copy-button.js +34 -0
  17. package/dist/components/registry/niko-table/components/data-table-search-filter.d.ts +35 -0
  18. package/dist/components/registry/niko-table/components/data-table-search-filter.js +44 -0
  19. package/dist/components/registry/niko-table/filters/table-search-filter.d.ts +38 -0
  20. package/dist/components/registry/niko-table/filters/table-search-filter.js +103 -0
  21. package/dist/components/siyahi/data-list.d.ts +14 -0
  22. package/dist/components/siyahi/data-list.js +25 -0
  23. package/dist/components/siyahi/data-table-row-count.d.ts +11 -0
  24. package/dist/components/siyahi/data-table-row-count.js +18 -0
  25. package/dist/components/siyahi/data-table-sort-header.d.ts +15 -0
  26. package/dist/components/siyahi/data-table-sort-header.js +36 -0
  27. package/dist/components/siyahi/inline-code.d.ts +7 -0
  28. package/dist/components/siyahi/inline-code.js +11 -0
  29. package/dist/components/siyahi/page-section.d.ts +21 -0
  30. package/dist/components/siyahi/page-section.js +39 -0
  31. package/dist/components/siyahi/tone-progress.d.ts +15 -0
  32. package/dist/components/siyahi/tone-progress.js +18 -0
  33. package/dist/components/siyahi/treemap.d.ts +7 -0
  34. package/dist/components/siyahi/treemap.js +69 -2
  35. package/dist/components/ui/code-block.d.ts +28 -0
  36. package/dist/components/ui/code-block.js +122 -0
  37. package/dist/index.d.ts +19 -8
  38. package/dist/index.js +14 -3
  39. package/dist/lib/chart-frame.d.ts +10 -3
  40. package/dist/lib/chart-frame.js +161 -55
  41. package/dist/lib/chart-series.d.ts +55 -3
  42. package/dist/lib/format.d.ts +3 -0
  43. package/dist/lib/format.js +6 -1
  44. package/dist/lib/treemap.d.ts +4 -0
  45. package/dist/lib/treemap.js +9 -4
  46. package/package.json +1 -1
  47. package/registry.lock.json +32 -0
@@ -0,0 +1,44 @@
1
+ "use client";
2
+ import { useDataTable } from "../core/data-table-context.js";
3
+ import { TableSearchFilter } from "../filters/table-search-filter.js";
4
+ import { jsx } from "react/jsx-runtime";
5
+ //#region src/components/registry/niko-table/components/data-table-search-filter.tsx
6
+ /**
7
+ * A search filter component for DataTable.
8
+ * Can be used in controlled or uncontrolled mode.
9
+ *
10
+ * @example
11
+ * // Uncontrolled (manages its own state)
12
+ * <DataTableSearchFilter placeholder="Search products..." />
13
+ *
14
+ * @example
15
+ * // Controlled (you manage the state)
16
+ * const [search, setSearch] = useState("")
17
+ * <DataTableSearchFilter
18
+ * value={search}
19
+ * onChange={setSearch}
20
+ * placeholder="Search..."
21
+ * />
22
+ *
23
+ * @example
24
+ * // With nuqs for URL state
25
+ * const [search, setSearch] = useQueryState('search')
26
+ * <DataTableSearchFilter
27
+ * value={search ?? ""}
28
+ * onChange={setSearch}
29
+ * />
30
+ */
31
+ function DataTableSearchFilter(props) {
32
+ const { table } = useDataTable();
33
+ return /* @__PURE__ */ jsx(TableSearchFilter, {
34
+ table,
35
+ ...props
36
+ });
37
+ }
38
+ /**
39
+ * @required displayName is required for auto feature detection
40
+ * @see "feature-detection.ts"
41
+ */
42
+ DataTableSearchFilter.displayName = "DataTableSearchFilter";
43
+ //#endregion
44
+ export { DataTableSearchFilter };
@@ -0,0 +1,38 @@
1
+ import { DataTableInstance } from "../types/index.js";
2
+ import * as React$1 from "react";
3
+ import { RowData } from "@tanstack/react-table";
4
+ //#region src/components/registry/niko-table/filters/table-search-filter.d.ts
5
+ export interface TableSearchFilterProps<TData extends RowData> {
6
+ table: DataTableInstance<TData>;
7
+ className?: string;
8
+ placeholder?: string;
9
+ /** The field's accessible name, such as "Filter holdings". */
10
+ label?: string;
11
+ showClearButton?: boolean;
12
+ onChange?: (value: string) => void;
13
+ value?: string;
14
+ /**
15
+ * Debounce ms before pushing the typed value into table state. The
16
+ * input reflects keystrokes immediately; only the call to
17
+ * `table.setGlobalFilter` (and the supplied `onChange`) is delayed.
18
+ * Useful for client-side filtering of larger fully-loaded datasets
19
+ * (e.g. 1k+ rows) where each keystroke would otherwise re-walk the
20
+ * row model synchronously.
21
+ *
22
+ * Server-driven search (small `data` array, infinite-query-backed)
23
+ * usually wants this OFF because the network request is already
24
+ * the natural rate limiter — keep at the default.
25
+ *
26
+ * Only applies in uncontrolled mode (when neither `value` nor
27
+ * `onChange` is supplied). In controlled mode, debounce in the
28
+ * consumer's `onChange` instead.
29
+ *
30
+ * @default 0
31
+ */
32
+ debounceMs?: number;
33
+ }
34
+ export declare function TableSearchFilter<TData extends RowData>({ table, className, placeholder, label, showClearButton, onChange, value, debounceMs }: TableSearchFilterProps<TData>): React$1.JSX.Element;
35
+ export declare namespace TableSearchFilter {
36
+ var displayName: string;
37
+ }
38
+ //#endregion
@@ -0,0 +1,103 @@
1
+ import { Button } from "../../../ui/button.js";
2
+ import { Input } from "../../../ui/input.js";
3
+ import { cn } from "../../../../lib/utils.js";
4
+ import { Search, X } from "lucide-react";
5
+ import { jsx, jsxs } from "react/jsx-runtime";
6
+ import * as React$1 from "react";
7
+ //#region src/components/registry/niko-table/filters/table-search-filter.tsx
8
+ function TableSearchFilter({ table, className, placeholder = "Search...", label = "Search table", showClearButton = true, onChange, value, debounceMs = 0 }) {
9
+ const isControlled = value !== void 0;
10
+ const tableGlobalFilter = table.state.globalFilter;
11
+ const globalFilterValue = typeof tableGlobalFilter === "string" ? tableGlobalFilter : "";
12
+ const debounceEnabled = !isControlled && debounceMs > 0;
13
+ const [pendingValue, setPendingValue] = React$1.useState(globalFilterValue);
14
+ const debounceTimerRef = React$1.useRef(null);
15
+ React$1.useEffect(() => {
16
+ if (debounceTimerRef.current) {
17
+ clearTimeout(debounceTimerRef.current);
18
+ debounceTimerRef.current = null;
19
+ }
20
+ if (!debounceEnabled) return;
21
+ setPendingValue(globalFilterValue);
22
+ }, [globalFilterValue, debounceEnabled]);
23
+ React$1.useEffect(() => {
24
+ return () => {
25
+ if (debounceTimerRef.current) clearTimeout(debounceTimerRef.current);
26
+ };
27
+ }, []);
28
+ const currentValue = isControlled ? value : debounceEnabled ? pendingValue : globalFilterValue;
29
+ const handleClear = React$1.useCallback(() => {
30
+ const emptyValue = "";
31
+ if (debounceTimerRef.current) {
32
+ clearTimeout(debounceTimerRef.current);
33
+ debounceTimerRef.current = null;
34
+ }
35
+ if (debounceEnabled) setPendingValue(emptyValue);
36
+ table.setGlobalFilter(emptyValue);
37
+ onChange?.(emptyValue);
38
+ }, [
39
+ table,
40
+ onChange,
41
+ debounceEnabled
42
+ ]);
43
+ const handleChange = React$1.useCallback((event) => {
44
+ const newValue = event.target.value;
45
+ if (!debounceEnabled) {
46
+ table.setGlobalFilter(newValue);
47
+ onChange?.(newValue);
48
+ return;
49
+ }
50
+ setPendingValue(newValue);
51
+ if (debounceTimerRef.current) clearTimeout(debounceTimerRef.current);
52
+ debounceTimerRef.current = setTimeout(() => {
53
+ debounceTimerRef.current = null;
54
+ table.setGlobalFilter(newValue);
55
+ onChange?.(newValue);
56
+ }, debounceMs);
57
+ }, [
58
+ table,
59
+ onChange,
60
+ debounceEnabled,
61
+ debounceMs
62
+ ]);
63
+ const hasValue = currentValue.length > 0;
64
+ return /* @__PURE__ */ jsxs("div", {
65
+ className: cn("relative flex flex-1 items-center", className),
66
+ role: "search",
67
+ children: [
68
+ /* @__PURE__ */ jsx(Search, {
69
+ className: "absolute left-3 h-4 w-4 text-muted-foreground",
70
+ "aria-hidden": "true"
71
+ }),
72
+ /* @__PURE__ */ jsx(Input, {
73
+ placeholder,
74
+ value: currentValue,
75
+ onChange: handleChange,
76
+ className: "pr-9 pl-9",
77
+ "aria-label": label
78
+ }),
79
+ hasValue && showClearButton && /* @__PURE__ */ jsxs(Button, {
80
+ variant: "ghost",
81
+ size: "sm",
82
+ onClick: handleClear,
83
+ className: "absolute right-1 h-7 w-7 p-0 hover:bg-muted",
84
+ type: "button",
85
+ "aria-label": "Clear search",
86
+ children: [/* @__PURE__ */ jsx(X, {
87
+ className: "h-3 w-3",
88
+ "aria-hidden": "true"
89
+ }), /* @__PURE__ */ jsx("span", {
90
+ className: "sr-only",
91
+ children: "Clear search"
92
+ })]
93
+ })
94
+ ]
95
+ });
96
+ }
97
+ /**
98
+ * @required displayName is required for auto feature detection
99
+ * @see src/components/niko-table/config/feature-detection.ts
100
+ */
101
+ TableSearchFilter.displayName = "TableSearchFilter";
102
+ //#endregion
103
+ export { TableSearchFilter };
@@ -0,0 +1,14 @@
1
+ import { ReactNode } from "react";
2
+ //#region src/components/siyahi/data-list.d.ts
3
+ /** Label and value pairs, such as a record's fields in a card. */
4
+ export declare function DataList({ className, children }: {
5
+ className?: string;
6
+ children: ReactNode;
7
+ }): import("react").JSX.Element;
8
+ export interface DataListItemProps {
9
+ /** A short muted label; may hold a badge beside the words. */
10
+ label: ReactNode;
11
+ children: ReactNode;
12
+ }
13
+ export declare function DataListItem({ label, children }: DataListItemProps): import("react").JSX.Element;
14
+ //#endregion
@@ -0,0 +1,25 @@
1
+ import { cn } from "cn";
2
+ import { jsx, jsxs } from "react/jsx-runtime";
3
+ //#region src/components/siyahi/data-list.tsx
4
+ /** Label and value pairs, such as a record's fields in a card. */
5
+ function DataList({ className, children }) {
6
+ return /* @__PURE__ */ jsx("dl", {
7
+ className: cn("flex flex-col gap-4", className),
8
+ children
9
+ });
10
+ }
11
+ function DataListItem({ label, children }) {
12
+ return /* @__PURE__ */ jsxs("div", {
13
+ "data-slot": "data-list-item",
14
+ className: "flex flex-col gap-1",
15
+ children: [/* @__PURE__ */ jsx("dt", {
16
+ className: "flex items-center gap-2 text-xs font-medium text-muted-foreground",
17
+ children: label
18
+ }), /* @__PURE__ */ jsx("dd", {
19
+ className: "min-w-0",
20
+ children
21
+ })]
22
+ });
23
+ }
24
+ //#endregion
25
+ export { DataList, DataListItem };
@@ -0,0 +1,11 @@
1
+ import { RowData } from "@tanstack/react-table";
2
+ //#region src/components/siyahi/data-table-row-count.d.ts
3
+ export interface DataTableRowCountProps {
4
+ /** What a row is, plural: "positions" prints "48 positions" while nothing is filtered. */
5
+ unit: string;
6
+ /** Rows before any filter. Defaults to the table's data; pass it when the page narrowed the data before the table. */
7
+ total?: number;
8
+ }
9
+ /** How many rows the filters leave: "12 of 48", or "48 positions" when they leave all. For a table toolbar, at its end. */
10
+ export declare function DataTableRowCount<TData extends RowData>({ unit, total }: DataTableRowCountProps): import("react").JSX.Element;
11
+ //#endregion
@@ -0,0 +1,18 @@
1
+ import { useFormat } from "../../lib/format.js";
2
+ import { useDataTable } from "../registry/niko-table/core/data-table-context.js";
3
+ import { jsx } from "react/jsx-runtime";
4
+ //#region src/components/siyahi/data-table-row-count.tsx
5
+ /** How many rows the filters leave: "12 of 48", or "48 positions" when they leave all. For a table toolbar, at its end. */
6
+ function DataTableRowCount({ unit, total }) {
7
+ const { table } = useDataTable();
8
+ const { count } = useFormat();
9
+ const all = total ?? table.getCoreRowModel().rows.length;
10
+ const shown = table.getFilteredRowModel().rows.length;
11
+ return /* @__PURE__ */ jsx("span", {
12
+ "data-slot": "data-table-row-count",
13
+ className: "ml-auto text-sm text-muted-foreground tabular-nums",
14
+ children: shown === all ? `${count(all)} ${unit}` : `${count(shown)} of ${count(all)}`
15
+ });
16
+ }
17
+ //#endregion
18
+ export { DataTableRowCount };
@@ -0,0 +1,15 @@
1
+ import { RowData } from "@tanstack/react-table";
2
+ //#region src/components/siyahi/data-table-sort-header.d.ts
3
+ /**
4
+ * A column header that sorts on one click, largest (or Z) first, as shadcn's
5
+ * data table guide draws it: a ghost button with the direction's arrow. The
6
+ * column menu takes two clicks for the same. Use it as a column's `header`:
7
+ * `header: () => <DataTableSortHeader />`; the title defaults to `meta.label`.
8
+ */
9
+ export declare function DataTableSortHeader<TData extends RowData>({ title }: {
10
+ title?: string;
11
+ }): import("react").JSX.Element;
12
+ export declare namespace DataTableSortHeader {
13
+ var displayName: string;
14
+ }
15
+ //#endregion
@@ -0,0 +1,36 @@
1
+ import { Button } from "../ui/button.js";
2
+ import { DataTableColumnHeader, useColumnHeaderContext } from "../registry/niko-table/components/data-table-column-header.js";
3
+ import { useDerivedColumnTitle } from "../registry/niko-table/hooks/use-derived-column-title.js";
4
+ import { registerComponentFeatures } from "../registry/niko-table/config/feature-detection.js";
5
+ import { ArrowDownIcon, ArrowUpDownIcon, ArrowUpIcon } from "lucide-react";
6
+ import { jsx, jsxs } from "react/jsx-runtime";
7
+ //#region src/components/siyahi/data-table-sort-header.tsx
8
+ /**
9
+ * A column header that sorts on one click, largest (or Z) first, as shadcn's
10
+ * data table guide draws it: a ghost button with the direction's arrow. The
11
+ * column menu takes two clicks for the same. Use it as a column's `header`:
12
+ * `header: () => <DataTableSortHeader />`; the title defaults to `meta.label`.
13
+ */
14
+ function DataTableSortHeader({ title }) {
15
+ const { column } = useColumnHeaderContext(true);
16
+ const label = useDerivedColumnTitle(column, column.id, title);
17
+ const sorted = column.getIsSorted();
18
+ return /* @__PURE__ */ jsx(DataTableColumnHeader, { children: /* @__PURE__ */ jsxs(Button, {
19
+ variant: "ghost",
20
+ size: "sm",
21
+ className: "-ml-2",
22
+ onClick: () => column.toggleSorting(sorted !== "desc"),
23
+ children: [
24
+ label,
25
+ /* @__PURE__ */ jsx(sorted === "asc" ? ArrowUpIcon : sorted === "desc" ? ArrowDownIcon : ArrowUpDownIcon, { "aria-hidden": "true" }),
26
+ sorted && /* @__PURE__ */ jsxs("span", {
27
+ className: "sr-only",
28
+ children: [", sorted ", sorted === "asc" ? "ascending" : "descending"]
29
+ })
30
+ ]
31
+ }) });
32
+ }
33
+ DataTableSortHeader.displayName = "DataTableSortHeader";
34
+ registerComponentFeatures(DataTableSortHeader.displayName, { enableSorting: true });
35
+ //#endregion
36
+ export { DataTableSortHeader };
@@ -0,0 +1,7 @@
1
+ import { ReactNode } from "react";
2
+ //#region src/components/siyahi/inline-code.d.ts
3
+ /** A command, file name or identifier inside prose, in Geist Mono on a muted chip, at the size of the text around it. */
4
+ export declare function InlineCode({ children }: {
5
+ children: ReactNode;
6
+ }): import("react").JSX.Element;
7
+ //#endregion
@@ -0,0 +1,11 @@
1
+ import { jsx } from "react/jsx-runtime";
2
+ //#region src/components/siyahi/inline-code.tsx
3
+ /** A command, file name or identifier inside prose, in Geist Mono on a muted chip, at the size of the text around it. */
4
+ function InlineCode({ children }) {
5
+ return /* @__PURE__ */ jsx("code", {
6
+ className: "rounded-sm bg-muted px-1 font-mono",
7
+ children
8
+ });
9
+ }
10
+ //#endregion
11
+ export { InlineCode };
@@ -0,0 +1,21 @@
1
+ import { ReactNode } from "react";
2
+ //#region src/components/siyahi/page-section.d.ts
3
+ export interface PageSectionProps {
4
+ /** Anchor for a table of contents; the section scrolls in with room above it. */
5
+ id?: string;
6
+ /** Small muted line above the title, such as "Part 2". */
7
+ eyebrow?: ReactNode;
8
+ title: ReactNode;
9
+ /** Muted prose under the title: a sentence, or several paragraphs as elements. */
10
+ lead?: ReactNode;
11
+ /** A control for the whole section, such as a copy or mode toggle; it sits right, beside the heading. */
12
+ action?: ReactNode;
13
+ children?: ReactNode;
14
+ }
15
+ /** One part of a long page: a rule above, then the heading and lead on the canvas, then its content. */
16
+ export declare function PageSection({ id, eyebrow, title, lead, action, children }: PageSectionProps): import("react").JSX.Element;
17
+ /** A heading inside a PageSection, one level under its title. */
18
+ export declare function PageSubheading({ children }: {
19
+ children: ReactNode;
20
+ }): import("react").JSX.Element;
21
+ //#endregion
@@ -0,0 +1,39 @@
1
+ import { jsx, jsxs } from "react/jsx-runtime";
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. */
4
+ function PageSection({ id, eyebrow, title, lead, action, children }) {
5
+ return /* @__PURE__ */ jsxs("section", {
6
+ id,
7
+ "data-slot": "page-section",
8
+ className: "flex scroll-mt-6 flex-col gap-6 border-t pt-10",
9
+ children: [/* @__PURE__ */ jsxs("div", {
10
+ className: "flex flex-wrap items-end justify-between gap-4",
11
+ children: [/* @__PURE__ */ jsxs("div", {
12
+ className: "flex flex-col gap-2",
13
+ children: [
14
+ eyebrow && /* @__PURE__ */ jsx("p", {
15
+ className: "text-sm text-muted-foreground",
16
+ children: eyebrow
17
+ }),
18
+ /* @__PURE__ */ jsx("h2", {
19
+ className: "text-2xl font-semibold tracking-tight",
20
+ children: title
21
+ }),
22
+ lead && /* @__PURE__ */ jsx("div", {
23
+ className: "flex max-w-prose flex-col gap-2 text-muted-foreground",
24
+ children: lead
25
+ })
26
+ ]
27
+ }), action]
28
+ }), children]
29
+ });
30
+ }
31
+ /** A heading inside a PageSection, one level under its title. */
32
+ function PageSubheading({ children }) {
33
+ return /* @__PURE__ */ jsx("h3", {
34
+ className: "text-lg font-semibold tracking-tight",
35
+ children
36
+ });
37
+ }
38
+ //#endregion
39
+ export { PageSection, PageSubheading };
@@ -0,0 +1,15 @@
1
+ import { Progress } from "../ui/progress.js";
2
+ import { ComponentProps } from "react";
3
+ //#region src/components/siyahi/tone-progress.d.ts
4
+ declare const TONE: {
5
+ readonly positive: "**:data-[slot=progress-indicator]:bg-positive";
6
+ readonly negative: "**:data-[slot=progress-indicator]:bg-negative";
7
+ };
8
+ export interface ToneProgressProps extends ComponentProps<typeof Progress> {
9
+ /** The meaning colour of the bar: a gain's share in --positive, a loss's in --negative. */
10
+ tone: keyof typeof TONE;
11
+ }
12
+ /** shadcn's Progress with its bar in a meaning colour, such as each position's share of the booked P&L. */
13
+ export declare function ToneProgress({ tone, className, ...props }: ToneProgressProps): import("react").JSX.Element;
14
+ //#endregion
15
+ export {};
@@ -0,0 +1,18 @@
1
+ import { Progress } from "../ui/progress.js";
2
+ import { cn } from "cn";
3
+ import { jsx } from "react/jsx-runtime";
4
+ //#region src/components/siyahi/tone-progress.tsx
5
+ const TONE = {
6
+ positive: "**:data-[slot=progress-indicator]:bg-positive",
7
+ negative: "**:data-[slot=progress-indicator]:bg-negative"
8
+ };
9
+ /** shadcn's Progress with its bar in a meaning colour, such as each position's share of the booked P&L. */
10
+ function ToneProgress({ tone, className, ...props }) {
11
+ return /* @__PURE__ */ jsx(Progress, {
12
+ "data-tone": tone,
13
+ className: cn(TONE[tone], className),
14
+ ...props
15
+ });
16
+ }
17
+ //#endregion
18
+ export { ToneProgress };
@@ -28,4 +28,11 @@ export interface TreemapProps<T extends TreemapItem> {
28
28
  }
29
29
  /** Holdings heat map: area by weight, colour by signed change, optionally grouped; hand-drawn SVG. */
30
30
  export declare function Treemap<T extends TreemapItem>({ items, height, range, onHover, selected, onSelect }: TreemapProps<T>): import("react").JSX.Element;
31
+ export interface TreemapLegendProps {
32
+ /** The Treemap's own range, so each swatch matches its tiles. */
33
+ range?: number;
34
+ className?: string;
35
+ }
36
+ /** The Treemap's colour scale under the map: five steps of loss, no change, five steps of gain, deepest at the ends. */
37
+ export declare function TreemapLegend({ range, className }: TreemapLegendProps): import("react").JSX.Element;
31
38
  //#endregion
@@ -1,5 +1,6 @@
1
1
  import { useFormat } from "../../lib/format.js";
2
- import { heat, layout } from "../../lib/treemap.js";
2
+ import { heat, heatFill, heatRange, layout } from "../../lib/treemap.js";
3
+ import { cn } from "cn";
3
4
  import { jsx, jsxs } from "react/jsx-runtime";
4
5
  import { useEffect, useEffectEvent, useLayoutEffect, useRef, useState } from "react";
5
6
  //#region src/components/siyahi/treemap.tsx
@@ -96,5 +97,71 @@ function Treemap({ items, height, range, onHover, selected, onSelect }) {
96
97
  })
97
98
  });
98
99
  }
100
+ function Swatch({ label, fill }) {
101
+ return /* @__PURE__ */ jsxs("svg", {
102
+ "data-slot": "treemap-legend-swatch",
103
+ className: "h-3 flex-1",
104
+ children: [/* @__PURE__ */ jsx("title", { children: label }), /* @__PURE__ */ jsx("rect", {
105
+ width: "100%",
106
+ height: "100%",
107
+ rx: 2,
108
+ style: { fill }
109
+ })]
110
+ });
111
+ }
112
+ /** The Treemap's colour scale under the map: five steps of loss, no change, five steps of gain, deepest at the ends. */
113
+ function TreemapLegend({ range, className }) {
114
+ const { pct } = useFormat();
115
+ const step = heatRange(range) / 5;
116
+ const digits = Number.isInteger(Math.round(step * 1e6) / 1e4) ? 0 : 1;
117
+ const over = pct(step * 4, digits);
118
+ const swatch = (side, n) => {
119
+ const word = side === "positive" ? "up" : "down";
120
+ const label = n === 5 ? `${word} over ${over}` : `${word} ${pct(step * (n - 1), digits)}–${pct(step * n, digits)}`;
121
+ return /* @__PURE__ */ jsx(Swatch, {
122
+ label,
123
+ fill: heatFill(side, n)
124
+ }, `${side}-${n}`);
125
+ };
126
+ const steps = Array.from({ length: 5 }, (_, i) => i + 1);
127
+ return /* @__PURE__ */ jsxs("div", {
128
+ "data-slot": "treemap-legend",
129
+ className: cn("flex w-full max-w-xs flex-col gap-1 text-xs text-muted-foreground", className),
130
+ children: [
131
+ /* @__PURE__ */ jsxs("div", {
132
+ className: "flex gap-0.5",
133
+ "aria-hidden": "true",
134
+ children: [
135
+ steps.toReversed().map((n) => swatch("negative", n)),
136
+ /* @__PURE__ */ jsx(Swatch, {
137
+ label: "no change, or unknown",
138
+ fill: heat(null)
139
+ }),
140
+ steps.map((n) => swatch("positive", n))
141
+ ]
142
+ }),
143
+ /* @__PURE__ */ jsxs("div", {
144
+ className: "flex justify-between gap-2 tabular-nums",
145
+ children: [
146
+ /* @__PURE__ */ jsxs("span", { children: ["▼ over ", over] }),
147
+ /* @__PURE__ */ jsxs("span", { children: [pct(step, digits), " a step"] }),
148
+ /* @__PURE__ */ jsxs("span", { children: ["▲ over ", over] })
149
+ ]
150
+ }),
151
+ /* @__PURE__ */ jsxs("p", {
152
+ className: "sr-only",
153
+ children: [
154
+ "Tile colours run in ",
155
+ 5,
156
+ " steps of ",
157
+ pct(step, digits),
158
+ " each way, deepest over ",
159
+ over,
160
+ "; a muted tile is no change or unknown."
161
+ ]
162
+ })
163
+ ]
164
+ });
165
+ }
99
166
  //#endregion
100
- export { Treemap };
167
+ export { Treemap, TreemapLegend };
@@ -0,0 +1,28 @@
1
+ import { Button } from "./button.js";
2
+ import * as React$1 from "react";
3
+ import { VariantProps } from "class-variance-authority";
4
+ //#region src/components/ui/code-block.d.ts
5
+ declare const codeBlockVariants: (props?: ({
6
+ variant?: "default" | "destructive" | null | undefined;
7
+ } & import("class-variance-authority/types").ClassProp) | undefined) => string;
8
+ declare function CodeBlock({ className, variant, size, ...props }: React$1.ComponentProps<"div"> & VariantProps<typeof codeBlockVariants> & {
9
+ size?: "default" | "sm";
10
+ }): React$1.JSX.Element;
11
+ declare function CodeBlockHeader({ className, ...props }: React$1.ComponentProps<"div">): React$1.JSX.Element;
12
+ declare function CodeBlockTitle({ className, ...props }: React$1.ComponentProps<"div">): React$1.JSX.Element;
13
+ declare function CodeBlockActions({ className, ...props }: React$1.ComponentProps<"div">): React$1.JSX.Element;
14
+ declare function CodeBlockCopyButton({ value, variant, size, className, onClick, ...props }: React$1.ComponentProps<typeof Button> & {
15
+ value: string;
16
+ }): React$1.JSX.Element;
17
+ declare function CodeBlockContent({ className, children, style, wrap, lineNumbers, startLine, ...props }: React$1.ComponentProps<"pre"> & {
18
+ wrap?: boolean;
19
+ lineNumbers?: boolean;
20
+ startLine?: number;
21
+ }): React$1.JSX.Element;
22
+ declare const codeBlockLineVariants: (props?: ({
23
+ variant?: "added" | "default" | "highlighted" | "removed" | null | undefined;
24
+ } & import("class-variance-authority/types").ClassProp) | undefined) => string;
25
+ declare function CodeBlockLine({ className, variant, children, ...props }: React$1.ComponentProps<"span"> & VariantProps<typeof codeBlockLineVariants>): React$1.JSX.Element;
26
+ declare function CodeBlockFooter({ className, ...props }: React$1.ComponentProps<"div">): React$1.JSX.Element;
27
+ //#endregion
28
+ export { CodeBlock, CodeBlockActions, CodeBlockContent, CodeBlockCopyButton, CodeBlockFooter, CodeBlockHeader, CodeBlockLine, CodeBlockTitle };