@nocoo/basalt 3.1.0 → 3.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ai/DESIGN.md +108 -46
- package/ai/USAGE.md +1 -1
- package/ai/registry.json +115 -73
- package/dist/charts/config.d.ts +1 -1
- package/dist/charts/config.js +1 -1
- package/dist/charts/config.js.map +1 -1
- package/dist/charts/heatmap-matrix.js +2 -2
- package/dist/charts/heatmap-matrix.js.map +1 -1
- package/dist/charts/sparkline.js +1 -1
- package/dist/charts/sparkline.js.map +1 -1
- package/dist/charts/stat-card.js +1 -1
- package/dist/charts/stat-card.js.map +1 -1
- package/dist/components/alert-dialog.js +4 -4
- package/dist/components/alert-dialog.js.map +1 -1
- package/dist/components/approval-card.js +5 -6
- package/dist/components/approval-card.js.map +1 -1
- package/dist/components/avatar.js +1 -1
- package/dist/components/avatar.js.map +1 -1
- package/dist/components/chat-bubble.js +1 -1
- package/dist/components/chat-bubble.js.map +1 -1
- package/dist/components/chat-header.js +1 -1
- package/dist/components/chat-header.js.map +1 -1
- package/dist/components/chat-inbox.js +1 -1
- package/dist/components/chat-inbox.js.map +1 -1
- package/dist/components/chat-markdown.js +1 -1
- package/dist/components/chat-markdown.js.map +1 -1
- package/dist/components/chat-message.js +1 -1
- package/dist/components/chat-message.js.map +1 -1
- package/dist/components/code.js +4 -3
- package/dist/components/code.js.map +1 -1
- package/dist/components/context-cards.js +5 -5
- package/dist/components/context-cards.js.map +1 -1
- package/dist/components/dialog.js +2 -2
- package/dist/components/dialog.js.map +1 -1
- package/dist/components/diff-table.js +2 -2
- package/dist/components/diff-table.js.map +1 -1
- package/dist/components/input-area.js +1 -1
- package/dist/components/input-area.js.map +1 -1
- package/dist/components/layer-card.d.ts +6 -4
- package/dist/components/layer-card.js +60 -71
- package/dist/components/layer-card.js.map +1 -1
- package/dist/components/loader.js +1 -1
- package/dist/components/loader.js.map +1 -1
- package/dist/components/page-header.d.ts +2 -2
- package/dist/components/page-header.js +7 -7
- package/dist/components/page-header.js.map +1 -1
- package/dist/components/recommendation-card.js +2 -2
- package/dist/components/recommendation-card.js.map +1 -1
- package/dist/components/section-rule.js +1 -1
- package/dist/components/section-rule.js.map +1 -1
- package/dist/components/select.js +1 -1
- package/dist/components/select.js.map +1 -1
- package/dist/components/sheet.js +3 -3
- package/dist/components/sheet.js.map +1 -1
- package/dist/components/sidebar.js +2 -2
- package/dist/components/sidebar.js.map +1 -1
- package/dist/components/skeleton-line.js +1 -1
- package/dist/components/skeleton-line.js.map +1 -1
- package/dist/components/stat-strip.js +1 -1
- package/dist/components/stat-strip.js.map +1 -1
- package/dist/components/table.d.ts +2 -0
- package/dist/components/table.js +10 -12
- package/dist/components/table.js.map +1 -1
- package/dist/components/tabs.js +1 -1
- package/dist/components/tabs.js.map +1 -1
- package/dist/components/tag-badge.js +1 -1
- package/dist/components/tag-badge.js.map +1 -1
- package/dist/components/text.js +2 -2
- package/dist/components/text.js.map +1 -1
- package/dist/components/thinking.js +1 -1
- package/dist/components/thinking.js.map +1 -1
- package/dist/components/tool-chips.js +2 -2
- package/dist/components/tool-chips.js.map +1 -1
- package/dist/styles/standalone.css +119 -85
- package/dist/styles/tailwind.css +1 -0
- package/dist/styles/tokens.css +82 -39
- package/package.json +1 -1
package/dist/charts/config.d.ts
CHANGED
|
@@ -4,7 +4,7 @@ import { type ChartTooltipItem } from "./tooltip.js";
|
|
|
4
4
|
export type { ChartSeriesDescriptor } from "./series.js";
|
|
5
5
|
export type ChartTypeFace = "axis" | "legend" | "tooltipTitle" | "tooltipBody";
|
|
6
6
|
export declare const CHART_TYPE: {
|
|
7
|
-
readonly axisFontSize: "var(--basalt-text-
|
|
7
|
+
readonly axisFontSize: "var(--basalt-text-sm)";
|
|
8
8
|
readonly legendFontSize: "var(--basalt-text-sm)";
|
|
9
9
|
readonly tooltipTitleSize: "var(--basalt-text-sm)";
|
|
10
10
|
readonly tooltipBodySize: "var(--basalt-text-sm)";
|
package/dist/charts/config.js
CHANGED
|
@@ -4,7 +4,7 @@ import { ChartTooltipContent as r } from "./tooltip.js";
|
|
|
4
4
|
import { createElement as i } from "react";
|
|
5
5
|
//#region src/charts/config.ts
|
|
6
6
|
var a = {
|
|
7
|
-
axisFontSize: "var(--basalt-text-
|
|
7
|
+
axisFontSize: "var(--basalt-text-sm)",
|
|
8
8
|
legendFontSize: "var(--basalt-text-sm)",
|
|
9
9
|
tooltipTitleSize: "var(--basalt-text-sm)",
|
|
10
10
|
tooltipBodySize: "var(--basalt-text-sm)",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.js","names":["CHART_TYPE","chartFontSize","face","chartTextStyle","chartTickStyle","chartAxis","getChartColor","CHART_COLORS","index","AXIS_CONFIG","cartesianAxisProps","hidden","GRID_PROPS","BAR_RADIUS","ANIMATION_PROPS","CHART_TOOLTIP_CURSOR_LINE","CHART_TOOLTIP_CURSOR_BAR","withAlpha","chartTooltipContentStyle","chartTooltipProps","options","props","createElement","ChartTooltipContent","seriesColor","series","RESPONSIVE_CONTAINER_PROPS","CHART_PLOT_MARGIN","CHART_PLOT_MARGIN_BARE"],"sources":["../../src/charts/config.ts"],"sourcesContent":["import { createElement, type ReactNode } from \"react\";\nimport { CHART_COLORS, chartAxis, withAlpha } from \"./palette\";\nimport type { ChartSeriesDescriptor } from \"./series\";\nimport { ChartTooltipContent, type ChartTooltipItem } from \"./tooltip\";\n\nexport type { ChartSeriesDescriptor } from \"./series\";\n\nexport type ChartTypeFace = \"axis\" | \"legend\" | \"tooltipTitle\" | \"tooltipBody\";\n\nexport const CHART_TYPE = {\n\taxisFontSize: \"var(--basalt-text-
|
|
1
|
+
{"version":3,"file":"config.js","names":["CHART_TYPE","chartFontSize","face","chartTextStyle","chartTickStyle","chartAxis","getChartColor","CHART_COLORS","index","AXIS_CONFIG","cartesianAxisProps","hidden","GRID_PROPS","BAR_RADIUS","ANIMATION_PROPS","CHART_TOOLTIP_CURSOR_LINE","CHART_TOOLTIP_CURSOR_BAR","withAlpha","chartTooltipContentStyle","chartTooltipProps","options","props","createElement","ChartTooltipContent","seriesColor","series","RESPONSIVE_CONTAINER_PROPS","CHART_PLOT_MARGIN","CHART_PLOT_MARGIN_BARE"],"sources":["../../src/charts/config.ts"],"sourcesContent":["import { createElement, type ReactNode } from \"react\";\nimport { CHART_COLORS, chartAxis, withAlpha } from \"./palette\";\nimport type { ChartSeriesDescriptor } from \"./series\";\nimport { ChartTooltipContent, type ChartTooltipItem } from \"./tooltip\";\n\nexport type { ChartSeriesDescriptor } from \"./series\";\n\nexport type ChartTypeFace = \"axis\" | \"legend\" | \"tooltipTitle\" | \"tooltipBody\";\n\nexport const CHART_TYPE = {\n\taxisFontSize: \"var(--basalt-text-sm)\",\n\tlegendFontSize: \"var(--basalt-text-sm)\",\n\ttooltipTitleSize: \"var(--basalt-text-sm)\",\n\ttooltipBodySize: \"var(--basalt-text-sm)\",\n\ttooltipDot: 8,\n\tstrokeWidth: 2,\n\tgridDash: \"3 3\",\n\tgridOpacity: 0.15,\n\tareaFillAlpha: 0.2,\n} as const;\n\nexport function chartFontSize(face: ChartTypeFace): string {\n\tif (face === \"legend\") {\n\t\treturn CHART_TYPE.legendFontSize;\n\t}\n\tif (face === \"tooltipTitle\") {\n\t\treturn CHART_TYPE.tooltipTitleSize;\n\t}\n\tif (face === \"tooltipBody\") {\n\t\treturn CHART_TYPE.tooltipBodySize;\n\t}\n\treturn CHART_TYPE.axisFontSize;\n}\n\nexport function chartTextStyle(face: ChartTypeFace): { fontSize: string } {\n\treturn { fontSize: chartFontSize(face) };\n}\n\nexport function chartTickStyle(face: ChartTypeFace = \"axis\"): {\n\tfontSize: string;\n\tfill: string;\n} {\n\treturn { fontSize: chartFontSize(face), fill: chartAxis };\n}\n\nexport function getChartColor(index: number): string {\n\treturn CHART_COLORS[index % CHART_COLORS.length] ?? CHART_COLORS[0];\n}\n\nexport const AXIS_CONFIG = {\n\tget tick() {\n\t\treturn chartTickStyle(\"axis\");\n\t},\n\taxisLine: false as const,\n\ttickLine: false as const,\n};\n\nexport function cartesianAxisProps(hidden = false) {\n\treturn { hide: hidden, ...AXIS_CONFIG };\n}\n\nexport const GRID_PROPS = {\n\tvertical: false as const,\n\tstroke: chartAxis,\n\tstrokeOpacity: CHART_TYPE.gridOpacity,\n\tstrokeDasharray: CHART_TYPE.gridDash,\n};\n\nexport const BAR_RADIUS = {\n\thorizontal: [0, 4, 4, 0] as [number, number, number, number],\n\tvertical: [4, 4, 0, 0] as [number, number, number, number],\n};\n\nexport const ANIMATION_PROPS = {\n\tisAnimationActive: false,\n};\n\nexport const CHART_TOOLTIP_CURSOR_LINE = {\n\tstroke: chartAxis,\n\tstrokeOpacity: 0.35,\n\tstrokeWidth: 1,\n} as const;\n\nexport const CHART_TOOLTIP_CURSOR_BAR = {\n\tfill: withAlpha(\"foreground\", 0.06),\n\tstroke: \"none\",\n} as const;\n\nexport function chartTooltipContentStyle(): {\n\tbackground: string;\n\tborder: string;\n\tborderRadius: string;\n\tboxShadow: string;\n\tfontSize: string;\n\tcolor: string;\n\tpadding: string;\n} {\n\treturn {\n\t\tbackground: \"transparent\",\n\t\tborder: \"none\",\n\t\tborderRadius: \"0\",\n\t\tboxShadow: \"none\",\n\t\tfontSize: chartFontSize(\"tooltipBody\"),\n\t\tcolor: \"hsl(var(--basalt-foreground))\",\n\t\tpadding: \"0\",\n\t};\n}\n\nexport function chartTooltipProps(options?: {\n\tformatter?: (value: number) => string;\n\tcursor?: \"bar\" | \"line\" | false;\n\tcustomTooltip?: (props: {\n\t\tactive?: boolean;\n\t\tpayload?: readonly ChartTooltipItem[];\n\t\tlabel?: string | number;\n\t}) => ReactNode;\n}) {\n\tconst cursor =\n\t\toptions?.cursor === false\n\t\t\t? false\n\t\t\t: options?.cursor === \"line\"\n\t\t\t\t? CHART_TOOLTIP_CURSOR_LINE\n\t\t\t\t: CHART_TOOLTIP_CURSOR_BAR;\n\treturn {\n\t\tisAnimationActive: false,\n\t\tanimationDuration: 0,\n\t\toffset: 12,\n\t\tallowEscapeViewBox: { x: false, y: false },\n\t\tcursor,\n\t\twrapperStyle: {\n\t\t\toutline: \"none\",\n\t\t\tzIndex: 40,\n\t\t\tpointerEvents: \"none\" as const,\n\t\t\ttransition: \"none\",\n\t\t\tanimation: \"none\",\n\t\t},\n\t\tcontentStyle: chartTooltipContentStyle(),\n\t\tcontent: ((props: {\n\t\t\tactive?: boolean;\n\t\t\tpayload?: readonly ChartTooltipItem[];\n\t\t\tlabel?: unknown;\n\t\t}) => {\n\t\t\tif (options?.customTooltip) {\n\t\t\t\treturn options.customTooltip({\n\t\t\t\t\tactive: props.active,\n\t\t\t\t\tpayload: props.payload,\n\t\t\t\t\tlabel: props.label as string | number | undefined,\n\t\t\t\t});\n\t\t\t}\n\t\t\treturn createElement(ChartTooltipContent, {\n\t\t\t\tactive: props.active,\n\t\t\t\tpayload: props.payload,\n\t\t\t\tlabel: props.label as string | number | undefined,\n\t\t\t\tformatter: options?.formatter,\n\t\t\t});\n\t\t}) as never,\n\t};\n}\n\nexport function seriesColor(\n\tseries: ChartSeriesDescriptor | undefined,\n\tindex: number,\n): string | undefined {\n\treturn series?.color ?? CHART_COLORS[index % CHART_COLORS.length];\n}\n\nexport const RESPONSIVE_CONTAINER_PROPS = {\n\twidth: \"100%\" as const,\n\theight: \"100%\" as const,\n\tminWidth: 0,\n\tminHeight: 0,\n\tdebounce: 150,\n};\n\nexport const CHART_PLOT_MARGIN = {\n\ttop: 4,\n\tright: 4,\n\tbottom: 0,\n\tleft: 0,\n} as const;\n\nexport const CHART_PLOT_MARGIN_BARE = {\n\ttop: 2,\n\tright: 2,\n\tbottom: 2,\n\tleft: 2,\n} as const;\n"],"mappings":";;;;;AASA,IAAaA,IAAa;CACzB,cAAc;CACd,gBAAgB;CAChB,kBAAkB;CAClB,iBAAiB;CACjB,YAAY;CACZ,aAAa;CACb,UAAU;CACV,aAAa;CACb,eAAe;AAChB;AAEA,SAAgBC,EAAc,GAA6B;CAU1D,OATIC,MAAS,WACLF,EAAW,iBAEfE,MAAS,iBACLF,EAAW,mBAEfE,MAAS,gBACLF,EAAW,kBAEZA,EAAW;AACnB;AAEA,SAAgBG,EAAe,GAA2C;CACzE,OAAO,EAAE,UAAUF,EAAcC,CAAI,EAAE;AACxC;AAEA,SAAgBE,EAAe,IAAsB,QAGnD;CACD,OAAO;EAAE,UAAUH,EAAcC,CAAI;EAAG,MAAMG;CAAU;AACzD;AAEA,SAAgBC,EAAc,GAAuB;CACpD,OAAOC,EAAaC,IAAQD,EAAa,WAAWA,EAAa;AAClE;AAEA,IAAaE,IAAc;CAC1B,IAAI,OAAO;EACV,OAAOL,EAAe,MAAM;CAC7B;CACA,UAAU;CACV,UAAU;AACX;AAEA,SAAgBM,EAAmB,IAAS,IAAO;CAClD,OAAO;EAAE,MAAMC;EAAQ,GAAGF;CAAY;AACvC;AAEA,IAAaG,IAAa;CACzB,UAAU;CACV,QAAQP;CACR,eAAeL,EAAW;CAC1B,iBAAiBA,EAAW;AAC7B,GAEaa,IAAa;CACzB,YAAY;EAAC;EAAG;EAAG;EAAG;CAAC;CACvB,UAAU;EAAC;EAAG;EAAG;EAAG;CAAC;AACtB,GAEaC,IAAkB,EAC9B,mBAAmB,GACpB,GAEaC,IAA4B;CACxC,QAAQV;CACR,eAAe;CACf,aAAa;AACd,GAEaW,IAA2B;CACvC,MAAMC,EAAU,cAAc,GAAI;CAClC,QAAQ;AACT;AAEA,SAAgBC,IAQd;CACD,OAAO;EACN,YAAY;EACZ,QAAQ;EACR,cAAc;EACd,WAAW;EACX,UAAUjB,EAAc,aAAa;EACrC,OAAO;EACP,SAAS;CACV;AACD;AAEA,SAAgBkB,EAAkB,GAQ/B;CAOF,OAAO;EACN,mBAAmB;EACnB,mBAAmB;EACnB,QAAQ;EACR,oBAAoB;GAAE,GAAG;GAAO,GAAG;EAAM;EACzC,QAVAC,GAAS,WAAW,KACjB,KACAA,GAAS,WAAW,SACnBL,IACAC;EAOJ,cAAc;GACb,SAAS;GACT,QAAQ;GACR,eAAe;GACf,YAAY;GACZ,WAAW;EACZ;EACA,cAAcE,EAAyB;EACvC,WAAW,MAKNE,GAAS,gBACLA,EAAQ,cAAc;GAC5B,QAAQC,EAAM;GACd,SAASA,EAAM;GACf,OAAOA,EAAM;EACd,CAAC,IAEKC,EAAcC,GAAqB;GACzC,QAAQF,EAAM;GACd,SAASA,EAAM;GACf,OAAOA,EAAM;GACb,WAAWD,GAAS;EACrB,CAAC;CAEH;AACD;AAEA,SAAgBI,EACf,GACA,GACqB;CACrB,OAAOC,GAAQ,SAASlB,EAAaC,IAAQD,EAAa;AAC3D;AAEA,IAAamB,IAA6B;CACzC,OAAO;CACP,QAAQ;CACR,UAAU;CACV,WAAW;CACX,UAAU;AACX,GAEaC,IAAoB;CAChC,KAAK;CACL,OAAO;CACP,QAAQ;CACR,MAAM;AACP,GAEaC,IAAyB;CACrC,KAAK;CACL,OAAO;CACP,QAAQ;CACR,MAAM;AACP"}
|
|
@@ -123,7 +123,7 @@ var _ = o(function({ rowLabels: o, columnLabels: _, values: v, domain: y, colorS
|
|
|
123
123
|
})
|
|
124
124
|
}), _.map((e, t) => /* @__PURE__ */ m("th", {
|
|
125
125
|
scope: "col",
|
|
126
|
-
className: "p-0 text-center font-mono text-basalt-
|
|
126
|
+
className: "p-0 text-center font-mono text-basalt-sm text-basalt-muted-foreground select-none",
|
|
127
127
|
style: {
|
|
128
128
|
width: B,
|
|
129
129
|
maxWidth: B,
|
|
@@ -216,7 +216,7 @@ var _ = o(function({ rowLabels: o, columnLabels: _, values: v, domain: y, colorS
|
|
|
216
216
|
})] }, `${L}-row-${r}`)) })]
|
|
217
217
|
})
|
|
218
218
|
}), te ? /* @__PURE__ */ h("div", {
|
|
219
|
-
className: "mt-basalt-space-sm flex items-center justify-end gap-basalt-space-sm text-basalt-
|
|
219
|
+
className: "mt-basalt-space-sm flex items-center justify-end gap-basalt-space-sm text-basalt-sm text-basalt-muted-foreground select-none",
|
|
220
220
|
"aria-hidden": "true",
|
|
221
221
|
children: [
|
|
222
222
|
/* @__PURE__ */ m("span", { children: O }),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"heatmap-matrix.js","names":["_Fragment","Fragment$1","resolveColorIndex","value","colorScaleLength","levels","max","min","normalized","HeatmapMatrix","forwardRef","heatmapColorScales","val","rest","matrixId","useId","numRows","rowLabels","numCols","columnLabels","effectiveColWidth","columnWidth","cellSize","minVal","maxVal","useMemo","domain","d0","d1","lowest","highest","r","row","values","c","v","activePos","setActivePos","useState","openTooltipPos","setOpenTooltipPos","dismissedByEscapeRef","useRef","activePosRef","containerRef","cellRefs","isFocusedInsideRef","hadFocus","curR","curC","nextR","nextC","prev","key","target","handleContainerKeyDown","onKeyDown","currentRow","currentCol","nextKey","targetButton","scrollContainer","rowHeader","containerRect","btnRect","stickyWidth","visibleLeft","visibleRight","cellGap","handleContainerFocus","onFocus","handleContainerBlur","onBlur","callerCleanupRef","setRootRef","useCallback","node","ref","cleanup","isEmpty","TooltipProvider","ariaLabel","cn","className","missingLabel","col","colIdx","rowName","cellKey","rowIdx","rawVal","isMissing","colorIdx","colorScale","formattedVal","valueFormatter","cellBg","accessibleCellLabel","colName","metricLabel","isSelected","isOpen","cellContext","Tooltip","next","curr","TooltipTrigger","el","TooltipContent","renderTooltip","showLegend","lessLabel","color","idx","moreLabel"],"sources":["../../src/charts/heatmap-matrix.tsx"],"sourcesContent":["import {\n\ttype ComponentProps,\n\ttype FocusEvent,\n\tforwardRef,\n\ttype KeyboardEvent,\n\ttype ReactNode,\n\tuseCallback,\n\tuseEffect,\n\tuseId,\n\tuseMemo,\n\tuseRef,\n\tuseState,\n} from \"react\";\nimport { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from \"../components/tooltip\";\nimport { cn } from \"../utils/cn\";\nimport { heatmapColorScales } from \"./heatmap-calendar\";\n\nexport type HeatmapMatrixValue = number | null | undefined;\n\nexport interface HeatmapMatrixCellContext {\n\trowIndex: number;\n\tcolIndex: number;\n\trowLabel: string;\n\tcolumnLabel: string;\n\tvalue: HeatmapMatrixValue;\n\tformattedValue: string;\n\tisMissing: boolean;\n\tcolorIndex: number;\n}\n\nexport interface HeatmapMatrixProps extends Omit<ComponentProps<\"div\">, \"children\"> {\n\t/**\n\t * Ordered row labels (e.g. weekdays [\"Mon\", \"Tue\", ...], service tiers, or regions).\n\t */\n\trowLabels: readonly string[];\n\t/**\n\t * Ordered column labels (e.g. 24 hours [\"00:00\", ...], days, or status buckets).\n\t */\n\tcolumnLabels: readonly string[];\n\t/**\n\t * 2D matrix of values indexed by [rowIndex][colIndex].\n\t * Supports ragged rows, null, undefined, and non-finite values (treated as missing).\n\t * Numeric `0` is a valid reading and distinct from missing data.\n\t */\n\tvalues: readonly (readonly HeatmapMatrixValue[])[];\n\t/**\n\t * Explicit [min, max] domain for color intensity scaling.\n\t * If reversed [max, min], bounds are automatically normalized to [min, max].\n\t * If equal [v, v], values <= v receive level 0 while values > v clamp to the highest level.\n\t * If non-finite or omitted, dynamically derived solely from visible finite numeric cells in the dataset (or [0, 0] if no finite values exist).\n\t * Values at or below min receive level 0; values >= max clamp to the highest level.\n\t */\n\tdomain?: readonly [min: number, max: number];\n\t/**\n\t * Sequential color palette array from lowest to highest intensity.\n\t * If empty or single-color, gracefully resolves to base level.\n\t * @default heatmapColorScales.green\n\t */\n\tcolorScale?: readonly string[];\n\t/**\n\t * Formatter for numeric values displayed in tooltips and accessible labels.\n\t * Defaults to `value.toLocaleString()`.\n\t */\n\tvalueFormatter?: (value: number) => string;\n\t/**\n\t * Placeholder string used for missing/null/undefined readings.\n\t * @default \"—\"\n\t */\n\tmissingLabel?: string;\n\t/**\n\t * Accessible label describing the matrix purpose or metric name.\n\t * @default \"Heatmap matrix\"\n\t */\n\tmetricLabel?: string;\n\t/**\n\t * Accessible name for the entire grid region.\n\t * @default \"Heatmap matrix\"\n\t */\n\tariaLabel?: string;\n\t/**\n\t * Pixel height (and default width) of each matrix data cell.\n\t * @default 16\n\t */\n\tcellSize?: number;\n\t/**\n\t * Pixel width of each matrix column / data cell.\n\t * When omitted, falls back to `cellSize` for square cells.\n\t * Allows wider rectangular cells to fit readable column headers (e.g. timestamps or service names).\n\t */\n\tcolumnWidth?: number;\n\t/**\n\t * Pixel gap between adjacent cells.\n\t * @default 2\n\t */\n\tcellGap?: number;\n\t/**\n\t * Text for lowest bound legend.\n\t * @default \"Less\"\n\t */\n\tlessLabel?: string;\n\t/**\n\t * Text for highest bound legend.\n\t * @default \"More\"\n\t */\n\tmoreLabel?: string;\n\t/**\n\t * Whether to show the bottom intensity scale legend.\n\t * @default true\n\t */\n\tshowLegend?: boolean;\n\t/**\n\t * Custom cell tooltip content renderer.\n\t * If provided, replaces the default tooltip contents.\n\t */\n\trenderTooltip?: (cell: HeatmapMatrixCellContext) => ReactNode;\n}\n\nfunction resolveColorIndex(\n\tvalue: HeatmapMatrixValue,\n\tmin: number,\n\tmax: number,\n\tcolorScaleLength: number,\n): number {\n\tif (value == null || typeof value !== \"number\" || !Number.isFinite(value)) {\n\t\treturn -1; // missing indicator\n\t}\n\tif (colorScaleLength <= 1) {\n\t\treturn 0;\n\t}\n\tconst levels = colorScaleLength - 1;\n\tif (max <= min) {\n\t\treturn value > min ? levels : 0;\n\t}\n\tconst clamped = Math.max(min, Math.min(max, value));\n\tconst normalized = (clamped - min) / (max - min);\n\tif (normalized <= 0) {\n\t\treturn 0;\n\t}\n\treturn Math.min(levels, Math.ceil(normalized * levels));\n}\n\nexport const HeatmapMatrix = forwardRef<HTMLDivElement, HeatmapMatrixProps>(function HeatmapMatrix(\n\t{\n\t\trowLabels,\n\t\tcolumnLabels,\n\t\tvalues,\n\t\tdomain,\n\t\tcolorScale = heatmapColorScales.green,\n\t\tvalueFormatter = (val) => val.toLocaleString(),\n\t\tmissingLabel = \"—\",\n\t\tmetricLabel = \"Heatmap matrix\",\n\t\tariaLabel = \"Heatmap matrix\",\n\t\tcellSize = 16,\n\t\tcolumnWidth,\n\t\tcellGap = 2,\n\t\tlessLabel = \"Less\",\n\t\tmoreLabel = \"More\",\n\t\tshowLegend = true,\n\t\trenderTooltip,\n\t\tclassName,\n\t\tstyle,\n\t\tonFocus,\n\t\tonBlur,\n\t\tonKeyDown,\n\t\t...rest\n\t},\n\tref,\n) {\n\tconst matrixId = useId();\n\tconst numRows = rowLabels.length;\n\tconst numCols = columnLabels.length;\n\tconst effectiveColWidth = columnWidth ?? cellSize;\n\n\t// Derive domain restricted to visible row/col matrix bounds\n\tconst [minVal, maxVal] = useMemo(() => {\n\t\tif (domain) {\n\t\t\tconst [d0, d1] = domain;\n\t\t\tif (Number.isFinite(d0) && Number.isFinite(d1)) {\n\t\t\t\treturn [Math.min(d0, d1), Math.max(d0, d1)];\n\t\t\t}\n\t\t}\n\t\tlet lowest = Number.POSITIVE_INFINITY;\n\t\tlet highest = Number.NEGATIVE_INFINITY;\n\t\tfor (let r = 0; r < numRows; r++) {\n\t\t\tconst row = values[r];\n\t\t\tif (!row) continue;\n\t\t\tfor (let c = 0; c < numCols; c++) {\n\t\t\t\tconst v = row[c];\n\t\t\t\tif (typeof v === \"number\" && Number.isFinite(v)) {\n\t\t\t\t\tif (v < lowest) lowest = v;\n\t\t\t\t\tif (v > highest) highest = v;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif (!Number.isFinite(lowest) || !Number.isFinite(highest)) {\n\t\t\treturn [0, 0];\n\t\t}\n\t\treturn [lowest, highest];\n\t}, [domain, values, numRows, numCols]);\n\n\t// Focus state: active cell position [rowIndex, colIndex]\n\tconst [activePos, setActivePos] = useState<[number, number]>([0, 0]);\n\tconst [openTooltipPos, setOpenTooltipPos] = useState<string | null>(null);\n\tconst dismissedByEscapeRef = useRef(false);\n\n\tconst activePosRef = useRef<[number, number]>([0, 0]);\n\tactivePosRef.current = activePos;\n\n\tconst containerRef = useRef<HTMLDivElement | null>(null);\n\tconst cellRefs = useRef<Map<string, HTMLButtonElement>>(new Map());\n\tconst isFocusedInsideRef = useRef(false);\n\n\t// Clamp active position when dimensions change\n\tuseEffect(() => {\n\t\tconst hadFocus = isFocusedInsideRef.current;\n\t\tif (numRows === 0 || numCols === 0) {\n\t\t\tsetActivePos(([curR, curC]) => {\n\t\t\t\tif (curR === 0 && curC === 0) return [curR, curC];\n\t\t\t\treturn [0, 0];\n\t\t\t});\n\t\t\tsetOpenTooltipPos(null);\n\t\t\tif (hadFocus) {\n\t\t\t\tcontainerRef.current?.focus();\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\tconst [curR, curC] = activePosRef.current;\n\t\tconst nextR = Math.min(numRows - 1, Math.max(0, curR));\n\t\tconst nextC = Math.min(numCols - 1, Math.max(0, curC));\n\n\t\tsetActivePos((prev) => {\n\t\t\tif (prev[0] === nextR && prev[1] === nextC) {\n\t\t\t\treturn prev;\n\t\t\t}\n\t\t\treturn [nextR, nextC];\n\t\t});\n\n\t\tif (hadFocus) {\n\t\t\tconst key = `${nextR}-${nextC}`;\n\t\t\tconst target = cellRefs.current.get(key);\n\t\t\tif (target && document.activeElement !== target) {\n\t\t\t\ttarget.focus();\n\t\t\t}\n\t\t}\n\t}, [numRows, numCols]);\n\n\tconst handleContainerKeyDown = (e: KeyboardEvent<HTMLDivElement>) => {\n\t\tif (e.key === \"Escape\") {\n\t\t\tdismissedByEscapeRef.current = true;\n\t\t\tsetOpenTooltipPos(null);\n\t\t}\n\n\t\t// Call caller onKeyDown first so caller can inspect or preventDefault\n\t\tonKeyDown?.(e);\n\t\tif (e.defaultPrevented) {\n\t\t\treturn;\n\t\t}\n\n\t\tif (numRows === 0 || numCols === 0) return;\n\t\tif (e.key === \"Escape\") return;\n\n\t\tconst [currentRow, currentCol] = activePosRef.current;\n\t\tlet nextR = currentRow;\n\t\tlet nextC = currentCol;\n\n\t\tif (e.key === \"ArrowRight\") {\n\t\t\tnextC = Math.min(numCols - 1, currentCol + 1);\n\t\t} else if (e.key === \"ArrowLeft\") {\n\t\t\tnextC = Math.max(0, currentCol - 1);\n\t\t} else if (e.key === \"ArrowDown\") {\n\t\t\tnextR = Math.min(numRows - 1, currentRow + 1);\n\t\t} else if (e.key === \"ArrowUp\") {\n\t\t\tnextR = Math.max(0, currentRow - 1);\n\t\t} else if (e.key === \"Home\") {\n\t\t\tnextC = 0;\n\t\t} else if (e.key === \"End\") {\n\t\t\tnextC = numCols - 1;\n\t\t} else if (e.key === \"PageUp\") {\n\t\t\tnextR = 0;\n\t\t} else if (e.key === \"PageDown\") {\n\t\t\tnextR = numRows - 1;\n\t\t} else {\n\t\t\treturn;\n\t\t}\n\n\t\te.preventDefault();\n\t\tdismissedByEscapeRef.current = false;\n\t\tsetActivePos([nextR, nextC]);\n\t\tconst nextKey = `${nextR}-${nextC}`;\n\t\tsetOpenTooltipPos(nextKey);\n\t\tconst targetButton = cellRefs.current.get(nextKey);\n\t\tif (targetButton) {\n\t\t\ttargetButton.focus();\n\t\t\tconst scrollContainer = targetButton.closest<HTMLElement>(\n\t\t\t\t'[role=\"region\"][aria-label$=\"scrollable table\"]',\n\t\t\t);\n\t\t\tconst rowHeader = targetButton.closest(\"tr\")?.querySelector<HTMLElement>('th[scope=\"row\"]');\n\n\t\t\tif (scrollContainer) {\n\t\t\t\tconst containerRect = scrollContainer.getBoundingClientRect();\n\t\t\t\tconst btnRect = targetButton.getBoundingClientRect();\n\t\t\t\tconst stickyWidth = rowHeader ? rowHeader.getBoundingClientRect().width : 0;\n\n\t\t\t\tconst visibleLeft = containerRect.left + stickyWidth;\n\t\t\t\tconst visibleRight = containerRect.right;\n\n\t\t\t\tif (btnRect.left < visibleLeft) {\n\t\t\t\t\tscrollContainer.scrollLeft -= visibleLeft - btnRect.left + cellGap;\n\t\t\t\t} else if (btnRect.right > visibleRight) {\n\t\t\t\t\tscrollContainer.scrollLeft += btnRect.right - visibleRight + cellGap;\n\t\t\t\t}\n\t\t\t} else if (typeof targetButton.scrollIntoView === \"function\") {\n\t\t\t\ttargetButton.scrollIntoView({ block: \"nearest\", inline: \"nearest\" });\n\t\t\t}\n\t\t}\n\t};\n\n\tconst handleContainerFocus = (e: FocusEvent<HTMLDivElement>) => {\n\t\tisFocusedInsideRef.current = true;\n\t\tonFocus?.(e);\n\t};\n\n\tconst handleContainerBlur = (e: FocusEvent<HTMLDivElement>) => {\n\t\tif (!e.currentTarget.contains(e.relatedTarget)) {\n\t\t\tisFocusedInsideRef.current = false;\n\t\t\tdismissedByEscapeRef.current = false;\n\t\t}\n\t\tonBlur?.(e);\n\t};\n\n\tconst callerCleanupRef = useRef<(() => void) | undefined>(undefined);\n\n\tconst setRootRef = useCallback(\n\t\t(node: HTMLDivElement | null) => {\n\t\t\tcontainerRef.current = node;\n\n\t\t\tif (callerCleanupRef.current) {\n\t\t\t\tcallerCleanupRef.current();\n\t\t\t\tcallerCleanupRef.current = undefined;\n\t\t\t}\n\n\t\t\tif (typeof ref === \"function\") {\n\t\t\t\tconst cleanup = ref(node);\n\t\t\t\tif (typeof cleanup === \"function\") {\n\t\t\t\t\tcallerCleanupRef.current = cleanup;\n\t\t\t\t}\n\t\t\t} else if (ref) {\n\t\t\t\tref.current = node;\n\t\t\t}\n\n\t\t\treturn () => {\n\t\t\t\tcontainerRef.current = null;\n\t\t\t\tif (callerCleanupRef.current) {\n\t\t\t\t\tcallerCleanupRef.current();\n\t\t\t\t\tcallerCleanupRef.current = undefined;\n\t\t\t\t} else if (typeof ref === \"function\") {\n\t\t\t\t\tref(null);\n\t\t\t\t} else if (ref) {\n\t\t\t\t\tref.current = null;\n\t\t\t\t}\n\t\t\t};\n\t\t},\n\t\t[ref],\n\t);\n\n\tconst isEmpty = numRows === 0 || numCols === 0;\n\n\treturn (\n\t\t<TooltipProvider>\n\t\t\t<div\n\t\t\t\tref={setRootRef}\n\t\t\t\ttabIndex={isEmpty ? -1 : undefined}\n\t\t\t\trole=\"region\"\n\t\t\t\taria-label={ariaLabel}\n\t\t\t\tdata-testid=\"heatmap-matrix\"\n\t\t\t\tclassName={cn(\n\t\t\t\t\t\"flex flex-col gap-basalt-space-lg rounded-basalt-md border border-basalt-border/60 bg-basalt-card p-basalt-space-lg text-basalt-sm outline-none\",\n\t\t\t\t\tisEmpty ? \"text-basalt-muted-foreground\" : \"text-basalt-foreground\",\n\t\t\t\t\tclassName,\n\t\t\t\t)}\n\t\t\t\tstyle={style}\n\t\t\t\tonFocus={handleContainerFocus}\n\t\t\t\tonBlur={handleContainerBlur}\n\t\t\t\tonKeyDown={handleContainerKeyDown}\n\t\t\t\t{...rest}\n\t\t\t>\n\t\t\t\t{isEmpty ? (\n\t\t\t\t\t<span className=\"italic\">{missingLabel}</span>\n\t\t\t\t) : (\n\t\t\t\t\t<>\n\t\t\t\t\t\t{/* Scrollable table container */}\n\t\t\t\t\t\t<div\n\t\t\t\t\t\t\ttabIndex={-1}\n\t\t\t\t\t\t\trole=\"region\"\n\t\t\t\t\t\t\taria-label={`${ariaLabel} scrollable table`}\n\t\t\t\t\t\t\tclassName=\"w-full overflow-x-auto outline-none\"\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t<table\n\t\t\t\t\t\t\t\t// biome-ignore lint/a11y/noNoninteractiveElementToInteractiveRole: W3C ARIA interactive 2D grid table with descendant roving tab buttons\n\t\t\t\t\t\t\t\trole=\"grid\"\n\t\t\t\t\t\t\t\taria-label={ariaLabel}\n\t\t\t\t\t\t\t\tclassName=\"border-separate\"\n\t\t\t\t\t\t\t\tstyle={{ borderSpacing: cellGap }}\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t<thead>\n\t\t\t\t\t\t\t\t\t<tr>\n\t\t\t\t\t\t\t\t\t\t{/* Top-left empty header */}\n\t\t\t\t\t\t\t\t\t\t<th\n\t\t\t\t\t\t\t\t\t\t\tscope=\"col\"\n\t\t\t\t\t\t\t\t\t\t\tclassName=\"sticky left-0 z-10 bg-basalt-card p-0 text-left font-normal\"\n\t\t\t\t\t\t\t\t\t\t\tstyle={{ minWidth: 64 }}\n\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t<span className=\"sr-only\">Row \\ Column</span>\n\t\t\t\t\t\t\t\t\t\t</th>\n\t\t\t\t\t\t\t\t\t\t{columnLabels.map((col, colIdx) => (\n\t\t\t\t\t\t\t\t\t\t\t<th\n\t\t\t\t\t\t\t\t\t\t\t\tkey={`${matrixId}-col-${colIdx}`}\n\t\t\t\t\t\t\t\t\t\t\t\tscope=\"col\"\n\t\t\t\t\t\t\t\t\t\t\t\tclassName=\"p-0 text-center font-mono text-basalt-xs text-basalt-muted-foreground select-none\"\n\t\t\t\t\t\t\t\t\t\t\t\tstyle={{\n\t\t\t\t\t\t\t\t\t\t\t\t\twidth: effectiveColWidth,\n\t\t\t\t\t\t\t\t\t\t\t\t\tmaxWidth: effectiveColWidth,\n\t\t\t\t\t\t\t\t\t\t\t\t\toverflow: \"hidden\",\n\t\t\t\t\t\t\t\t\t\t\t\t\ttextOverflow: \"ellipsis\",\n\t\t\t\t\t\t\t\t\t\t\t\t\twhiteSpace: \"nowrap\",\n\t\t\t\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t\t\t\t\ttitle={col}\n\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t{col}\n\t\t\t\t\t\t\t\t\t\t\t</th>\n\t\t\t\t\t\t\t\t\t\t))}\n\t\t\t\t\t\t\t\t\t</tr>\n\t\t\t\t\t\t\t\t</thead>\n\t\t\t\t\t\t\t\t<tbody>\n\t\t\t\t\t\t\t\t\t{rowLabels.map((rowName, rowIdx) => (\n\t\t\t\t\t\t\t\t\t\t<tr key={`${matrixId}-row-${rowIdx}`}>\n\t\t\t\t\t\t\t\t\t\t\t<th\n\t\t\t\t\t\t\t\t\t\t\t\tscope=\"row\"\n\t\t\t\t\t\t\t\t\t\t\t\tclassName=\"sticky left-0 z-10 bg-basalt-card pr-basalt-space-lg text-left font-medium text-basalt-foreground select-none\"\n\t\t\t\t\t\t\t\t\t\t\t\tstyle={{\n\t\t\t\t\t\t\t\t\t\t\t\t\tlineHeight: `${cellSize}px`,\n\t\t\t\t\t\t\t\t\t\t\t\t\tminWidth: 64,\n\t\t\t\t\t\t\t\t\t\t\t\t\twhiteSpace: \"nowrap\",\n\t\t\t\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t{rowName}\n\t\t\t\t\t\t\t\t\t\t\t</th>\n\t\t\t\t\t\t\t\t\t\t\t{columnLabels.map((colName, colIdx) => {\n\t\t\t\t\t\t\t\t\t\t\t\tconst cellKey = `${rowIdx}-${colIdx}`;\n\t\t\t\t\t\t\t\t\t\t\t\tconst rawVal = values[rowIdx]?.[colIdx];\n\t\t\t\t\t\t\t\t\t\t\t\tconst isMissing =\n\t\t\t\t\t\t\t\t\t\t\t\t\trawVal == null || typeof rawVal !== \"number\" || !Number.isFinite(rawVal);\n\t\t\t\t\t\t\t\t\t\t\t\tconst colorIdx = resolveColorIndex(\n\t\t\t\t\t\t\t\t\t\t\t\t\trawVal,\n\t\t\t\t\t\t\t\t\t\t\t\t\tminVal,\n\t\t\t\t\t\t\t\t\t\t\t\t\tmaxVal,\n\t\t\t\t\t\t\t\t\t\t\t\t\tcolorScale.length,\n\t\t\t\t\t\t\t\t\t\t\t\t);\n\n\t\t\t\t\t\t\t\t\t\t\t\tconst formattedVal = isMissing\n\t\t\t\t\t\t\t\t\t\t\t\t\t? missingLabel\n\t\t\t\t\t\t\t\t\t\t\t\t\t: valueFormatter(rawVal as number);\n\n\t\t\t\t\t\t\t\t\t\t\t\tconst cellBg =\n\t\t\t\t\t\t\t\t\t\t\t\t\tcolorIdx < 0\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t? \"hsl(var(--basalt-muted) / 0.4)\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t: (colorScale[colorIdx] ?? \"hsl(var(--basalt-muted))\");\n\n\t\t\t\t\t\t\t\t\t\t\t\tconst accessibleCellLabel = `${rowName}, ${colName}: ${formattedVal} (${metricLabel})`;\n\t\t\t\t\t\t\t\t\t\t\t\tconst isSelected = activePos[0] === rowIdx && activePos[1] === colIdx;\n\t\t\t\t\t\t\t\t\t\t\t\tconst isOpen = openTooltipPos === cellKey;\n\n\t\t\t\t\t\t\t\t\t\t\t\tconst cellContext: HeatmapMatrixCellContext = {\n\t\t\t\t\t\t\t\t\t\t\t\t\trowIndex: rowIdx,\n\t\t\t\t\t\t\t\t\t\t\t\t\tcolIndex: colIdx,\n\t\t\t\t\t\t\t\t\t\t\t\t\trowLabel: rowName,\n\t\t\t\t\t\t\t\t\t\t\t\t\tcolumnLabel: colName,\n\t\t\t\t\t\t\t\t\t\t\t\t\tvalue: rawVal,\n\t\t\t\t\t\t\t\t\t\t\t\t\tformattedValue: formattedVal,\n\t\t\t\t\t\t\t\t\t\t\t\t\tisMissing,\n\t\t\t\t\t\t\t\t\t\t\t\t\tcolorIndex: colorIdx,\n\t\t\t\t\t\t\t\t\t\t\t\t};\n\n\t\t\t\t\t\t\t\t\t\t\t\treturn (\n\t\t\t\t\t\t\t\t\t\t\t\t\t// biome-ignore lint/a11y/noNoninteractiveElementToInteractiveRole: ARIA gridcell container wrapping roving interactive button\n\t\t\t\t\t\t\t\t\t\t\t\t\t// biome-ignore lint/a11y/useFocusableInteractive: Focus is managed on child button via roving tabindex\n\t\t\t\t\t\t\t\t\t\t\t\t\t<td key={cellKey} role=\"gridcell\" className=\"p-0 text-center\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t<Tooltip\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\topen={isOpen}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tonOpenChange={(next) => {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tif (next) {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tif (!dismissedByEscapeRef.current) {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tsetOpenTooltipPos(cellKey);\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tsetOpenTooltipPos((curr) => {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tif (curr !== cellKey) return curr;\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tif (dismissedByEscapeRef.current) return null;\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tconst isCellFocused =\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tdocument.activeElement === cellRefs.current.get(cellKey);\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\treturn isCellFocused ? curr : null;\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<TooltipTrigger asChild>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tref={(el) => {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tif (el) cellRefs.current.set(cellKey, el);\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\telse cellRefs.current.delete(cellKey);\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\taria-label={accessibleCellLabel}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\ttabIndex={isSelected ? 0 : -1}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tonFocus={() => {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tsetActivePos([rowIdx, colIdx]);\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tdismissedByEscapeRef.current = false;\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tsetOpenTooltipPos(cellKey);\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tonBlur={() => {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tsetOpenTooltipPos((curr) => (curr === cellKey ? null : curr));\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tclassName=\"box-border block cursor-pointer rounded-basalt-sm border-0 p-0 transition-colors basalt-motion hover:ring-1 hover:ring-basalt-foreground focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-basalt-ring\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tstyle={{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\twidth: effectiveColWidth,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\theight: cellSize,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tbackgroundColor: cellBg,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t</TooltipTrigger>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<TooltipContent\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tid={`${matrixId}-${cellKey}`}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tonEscapeKeyDown={() => {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tdismissedByEscapeRef.current = true;\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tsetOpenTooltipPos(null);\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t{renderTooltip ? (\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\trenderTooltip(cellContext)\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t) : (\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<div className=\"text-basalt-sm\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<div className=\"font-semibold text-basalt-popover-foreground\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t{rowName} · {colName}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<div className=\"text-basalt-muted-foreground\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t{metricLabel}:{\" \"}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<span className=\"font-medium text-basalt-popover-foreground\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t{formattedVal}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t</TooltipContent>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t</Tooltip>\n\t\t\t\t\t\t\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\t\t\t\t})}\n\t\t\t\t\t\t\t\t\t\t</tr>\n\t\t\t\t\t\t\t\t\t))}\n\t\t\t\t\t\t\t\t</tbody>\n\t\t\t\t\t\t\t</table>\n\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t{/* Legend footer */}\n\t\t\t\t\t\t{showLegend ? (\n\t\t\t\t\t\t\t<div\n\t\t\t\t\t\t\t\tclassName=\"mt-basalt-space-sm flex items-center justify-end gap-basalt-space-sm text-basalt-xs text-basalt-muted-foreground select-none\"\n\t\t\t\t\t\t\t\taria-hidden=\"true\"\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t<span>{lessLabel}</span>\n\t\t\t\t\t\t\t\t<div\n\t\t\t\t\t\t\t\t\tclassName=\"rounded-basalt-sm\"\n\t\t\t\t\t\t\t\t\ttitle=\"Missing data\"\n\t\t\t\t\t\t\t\t\tstyle={{\n\t\t\t\t\t\t\t\t\t\twidth: cellSize,\n\t\t\t\t\t\t\t\t\t\theight: cellSize,\n\t\t\t\t\t\t\t\t\t\tbackgroundColor: \"hsl(var(--basalt-muted) / 0.4)\",\n\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t{colorScale.map((color, idx) => (\n\t\t\t\t\t\t\t\t\t<div\n\t\t\t\t\t\t\t\t\t\tkey={`${color}-${idx}`}\n\t\t\t\t\t\t\t\t\t\tclassName=\"rounded-basalt-sm\"\n\t\t\t\t\t\t\t\t\t\tstyle={{\n\t\t\t\t\t\t\t\t\t\t\twidth: cellSize,\n\t\t\t\t\t\t\t\t\t\t\theight: cellSize,\n\t\t\t\t\t\t\t\t\t\t\tbackgroundColor: color,\n\t\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t))}\n\t\t\t\t\t\t\t\t<span>{moreLabel}</span>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t) : null}\n\t\t\t\t\t</>\n\t\t\t\t)}\n\t\t\t</div>\n\t\t</TooltipProvider>\n\t);\n});\n\nHeatmapMatrix.displayName = \"HeatmapMatrix\";\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"heatmap-matrix.js","names":["_Fragment","Fragment$1","resolveColorIndex","value","colorScaleLength","levels","max","min","normalized","HeatmapMatrix","forwardRef","heatmapColorScales","val","rest","matrixId","useId","numRows","rowLabels","numCols","columnLabels","effectiveColWidth","columnWidth","cellSize","minVal","maxVal","useMemo","domain","d0","d1","lowest","highest","r","row","values","c","v","activePos","setActivePos","useState","openTooltipPos","setOpenTooltipPos","dismissedByEscapeRef","useRef","activePosRef","containerRef","cellRefs","isFocusedInsideRef","hadFocus","curR","curC","nextR","nextC","prev","key","target","handleContainerKeyDown","onKeyDown","currentRow","currentCol","nextKey","targetButton","scrollContainer","rowHeader","containerRect","btnRect","stickyWidth","visibleLeft","visibleRight","cellGap","handleContainerFocus","onFocus","handleContainerBlur","onBlur","callerCleanupRef","setRootRef","useCallback","node","ref","cleanup","isEmpty","TooltipProvider","ariaLabel","cn","className","missingLabel","col","colIdx","rowName","cellKey","rowIdx","rawVal","isMissing","colorIdx","colorScale","formattedVal","valueFormatter","cellBg","accessibleCellLabel","colName","metricLabel","isSelected","isOpen","cellContext","Tooltip","next","curr","TooltipTrigger","el","TooltipContent","renderTooltip","showLegend","lessLabel","color","idx","moreLabel"],"sources":["../../src/charts/heatmap-matrix.tsx"],"sourcesContent":["import {\n\ttype ComponentProps,\n\ttype FocusEvent,\n\tforwardRef,\n\ttype KeyboardEvent,\n\ttype ReactNode,\n\tuseCallback,\n\tuseEffect,\n\tuseId,\n\tuseMemo,\n\tuseRef,\n\tuseState,\n} from \"react\";\nimport { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from \"../components/tooltip\";\nimport { cn } from \"../utils/cn\";\nimport { heatmapColorScales } from \"./heatmap-calendar\";\n\nexport type HeatmapMatrixValue = number | null | undefined;\n\nexport interface HeatmapMatrixCellContext {\n\trowIndex: number;\n\tcolIndex: number;\n\trowLabel: string;\n\tcolumnLabel: string;\n\tvalue: HeatmapMatrixValue;\n\tformattedValue: string;\n\tisMissing: boolean;\n\tcolorIndex: number;\n}\n\nexport interface HeatmapMatrixProps extends Omit<ComponentProps<\"div\">, \"children\"> {\n\t/**\n\t * Ordered row labels (e.g. weekdays [\"Mon\", \"Tue\", ...], service tiers, or regions).\n\t */\n\trowLabels: readonly string[];\n\t/**\n\t * Ordered column labels (e.g. 24 hours [\"00:00\", ...], days, or status buckets).\n\t */\n\tcolumnLabels: readonly string[];\n\t/**\n\t * 2D matrix of values indexed by [rowIndex][colIndex].\n\t * Supports ragged rows, null, undefined, and non-finite values (treated as missing).\n\t * Numeric `0` is a valid reading and distinct from missing data.\n\t */\n\tvalues: readonly (readonly HeatmapMatrixValue[])[];\n\t/**\n\t * Explicit [min, max] domain for color intensity scaling.\n\t * If reversed [max, min], bounds are automatically normalized to [min, max].\n\t * If equal [v, v], values <= v receive level 0 while values > v clamp to the highest level.\n\t * If non-finite or omitted, dynamically derived solely from visible finite numeric cells in the dataset (or [0, 0] if no finite values exist).\n\t * Values at or below min receive level 0; values >= max clamp to the highest level.\n\t */\n\tdomain?: readonly [min: number, max: number];\n\t/**\n\t * Sequential color palette array from lowest to highest intensity.\n\t * If empty or single-color, gracefully resolves to base level.\n\t * @default heatmapColorScales.green\n\t */\n\tcolorScale?: readonly string[];\n\t/**\n\t * Formatter for numeric values displayed in tooltips and accessible labels.\n\t * Defaults to `value.toLocaleString()`.\n\t */\n\tvalueFormatter?: (value: number) => string;\n\t/**\n\t * Placeholder string used for missing/null/undefined readings.\n\t * @default \"—\"\n\t */\n\tmissingLabel?: string;\n\t/**\n\t * Accessible label describing the matrix purpose or metric name.\n\t * @default \"Heatmap matrix\"\n\t */\n\tmetricLabel?: string;\n\t/**\n\t * Accessible name for the entire grid region.\n\t * @default \"Heatmap matrix\"\n\t */\n\tariaLabel?: string;\n\t/**\n\t * Pixel height (and default width) of each matrix data cell.\n\t * @default 16\n\t */\n\tcellSize?: number;\n\t/**\n\t * Pixel width of each matrix column / data cell.\n\t * When omitted, falls back to `cellSize` for square cells.\n\t * Allows wider rectangular cells to fit readable column headers (e.g. timestamps or service names).\n\t */\n\tcolumnWidth?: number;\n\t/**\n\t * Pixel gap between adjacent cells.\n\t * @default 2\n\t */\n\tcellGap?: number;\n\t/**\n\t * Text for lowest bound legend.\n\t * @default \"Less\"\n\t */\n\tlessLabel?: string;\n\t/**\n\t * Text for highest bound legend.\n\t * @default \"More\"\n\t */\n\tmoreLabel?: string;\n\t/**\n\t * Whether to show the bottom intensity scale legend.\n\t * @default true\n\t */\n\tshowLegend?: boolean;\n\t/**\n\t * Custom cell tooltip content renderer.\n\t * If provided, replaces the default tooltip contents.\n\t */\n\trenderTooltip?: (cell: HeatmapMatrixCellContext) => ReactNode;\n}\n\nfunction resolveColorIndex(\n\tvalue: HeatmapMatrixValue,\n\tmin: number,\n\tmax: number,\n\tcolorScaleLength: number,\n): number {\n\tif (value == null || typeof value !== \"number\" || !Number.isFinite(value)) {\n\t\treturn -1; // missing indicator\n\t}\n\tif (colorScaleLength <= 1) {\n\t\treturn 0;\n\t}\n\tconst levels = colorScaleLength - 1;\n\tif (max <= min) {\n\t\treturn value > min ? levels : 0;\n\t}\n\tconst clamped = Math.max(min, Math.min(max, value));\n\tconst normalized = (clamped - min) / (max - min);\n\tif (normalized <= 0) {\n\t\treturn 0;\n\t}\n\treturn Math.min(levels, Math.ceil(normalized * levels));\n}\n\nexport const HeatmapMatrix = forwardRef<HTMLDivElement, HeatmapMatrixProps>(function HeatmapMatrix(\n\t{\n\t\trowLabels,\n\t\tcolumnLabels,\n\t\tvalues,\n\t\tdomain,\n\t\tcolorScale = heatmapColorScales.green,\n\t\tvalueFormatter = (val) => val.toLocaleString(),\n\t\tmissingLabel = \"—\",\n\t\tmetricLabel = \"Heatmap matrix\",\n\t\tariaLabel = \"Heatmap matrix\",\n\t\tcellSize = 16,\n\t\tcolumnWidth,\n\t\tcellGap = 2,\n\t\tlessLabel = \"Less\",\n\t\tmoreLabel = \"More\",\n\t\tshowLegend = true,\n\t\trenderTooltip,\n\t\tclassName,\n\t\tstyle,\n\t\tonFocus,\n\t\tonBlur,\n\t\tonKeyDown,\n\t\t...rest\n\t},\n\tref,\n) {\n\tconst matrixId = useId();\n\tconst numRows = rowLabels.length;\n\tconst numCols = columnLabels.length;\n\tconst effectiveColWidth = columnWidth ?? cellSize;\n\n\t// Derive domain restricted to visible row/col matrix bounds\n\tconst [minVal, maxVal] = useMemo(() => {\n\t\tif (domain) {\n\t\t\tconst [d0, d1] = domain;\n\t\t\tif (Number.isFinite(d0) && Number.isFinite(d1)) {\n\t\t\t\treturn [Math.min(d0, d1), Math.max(d0, d1)];\n\t\t\t}\n\t\t}\n\t\tlet lowest = Number.POSITIVE_INFINITY;\n\t\tlet highest = Number.NEGATIVE_INFINITY;\n\t\tfor (let r = 0; r < numRows; r++) {\n\t\t\tconst row = values[r];\n\t\t\tif (!row) continue;\n\t\t\tfor (let c = 0; c < numCols; c++) {\n\t\t\t\tconst v = row[c];\n\t\t\t\tif (typeof v === \"number\" && Number.isFinite(v)) {\n\t\t\t\t\tif (v < lowest) lowest = v;\n\t\t\t\t\tif (v > highest) highest = v;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif (!Number.isFinite(lowest) || !Number.isFinite(highest)) {\n\t\t\treturn [0, 0];\n\t\t}\n\t\treturn [lowest, highest];\n\t}, [domain, values, numRows, numCols]);\n\n\t// Focus state: active cell position [rowIndex, colIndex]\n\tconst [activePos, setActivePos] = useState<[number, number]>([0, 0]);\n\tconst [openTooltipPos, setOpenTooltipPos] = useState<string | null>(null);\n\tconst dismissedByEscapeRef = useRef(false);\n\n\tconst activePosRef = useRef<[number, number]>([0, 0]);\n\tactivePosRef.current = activePos;\n\n\tconst containerRef = useRef<HTMLDivElement | null>(null);\n\tconst cellRefs = useRef<Map<string, HTMLButtonElement>>(new Map());\n\tconst isFocusedInsideRef = useRef(false);\n\n\t// Clamp active position when dimensions change\n\tuseEffect(() => {\n\t\tconst hadFocus = isFocusedInsideRef.current;\n\t\tif (numRows === 0 || numCols === 0) {\n\t\t\tsetActivePos(([curR, curC]) => {\n\t\t\t\tif (curR === 0 && curC === 0) return [curR, curC];\n\t\t\t\treturn [0, 0];\n\t\t\t});\n\t\t\tsetOpenTooltipPos(null);\n\t\t\tif (hadFocus) {\n\t\t\t\tcontainerRef.current?.focus();\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\tconst [curR, curC] = activePosRef.current;\n\t\tconst nextR = Math.min(numRows - 1, Math.max(0, curR));\n\t\tconst nextC = Math.min(numCols - 1, Math.max(0, curC));\n\n\t\tsetActivePos((prev) => {\n\t\t\tif (prev[0] === nextR && prev[1] === nextC) {\n\t\t\t\treturn prev;\n\t\t\t}\n\t\t\treturn [nextR, nextC];\n\t\t});\n\n\t\tif (hadFocus) {\n\t\t\tconst key = `${nextR}-${nextC}`;\n\t\t\tconst target = cellRefs.current.get(key);\n\t\t\tif (target && document.activeElement !== target) {\n\t\t\t\ttarget.focus();\n\t\t\t}\n\t\t}\n\t}, [numRows, numCols]);\n\n\tconst handleContainerKeyDown = (e: KeyboardEvent<HTMLDivElement>) => {\n\t\tif (e.key === \"Escape\") {\n\t\t\tdismissedByEscapeRef.current = true;\n\t\t\tsetOpenTooltipPos(null);\n\t\t}\n\n\t\t// Call caller onKeyDown first so caller can inspect or preventDefault\n\t\tonKeyDown?.(e);\n\t\tif (e.defaultPrevented) {\n\t\t\treturn;\n\t\t}\n\n\t\tif (numRows === 0 || numCols === 0) return;\n\t\tif (e.key === \"Escape\") return;\n\n\t\tconst [currentRow, currentCol] = activePosRef.current;\n\t\tlet nextR = currentRow;\n\t\tlet nextC = currentCol;\n\n\t\tif (e.key === \"ArrowRight\") {\n\t\t\tnextC = Math.min(numCols - 1, currentCol + 1);\n\t\t} else if (e.key === \"ArrowLeft\") {\n\t\t\tnextC = Math.max(0, currentCol - 1);\n\t\t} else if (e.key === \"ArrowDown\") {\n\t\t\tnextR = Math.min(numRows - 1, currentRow + 1);\n\t\t} else if (e.key === \"ArrowUp\") {\n\t\t\tnextR = Math.max(0, currentRow - 1);\n\t\t} else if (e.key === \"Home\") {\n\t\t\tnextC = 0;\n\t\t} else if (e.key === \"End\") {\n\t\t\tnextC = numCols - 1;\n\t\t} else if (e.key === \"PageUp\") {\n\t\t\tnextR = 0;\n\t\t} else if (e.key === \"PageDown\") {\n\t\t\tnextR = numRows - 1;\n\t\t} else {\n\t\t\treturn;\n\t\t}\n\n\t\te.preventDefault();\n\t\tdismissedByEscapeRef.current = false;\n\t\tsetActivePos([nextR, nextC]);\n\t\tconst nextKey = `${nextR}-${nextC}`;\n\t\tsetOpenTooltipPos(nextKey);\n\t\tconst targetButton = cellRefs.current.get(nextKey);\n\t\tif (targetButton) {\n\t\t\ttargetButton.focus();\n\t\t\tconst scrollContainer = targetButton.closest<HTMLElement>(\n\t\t\t\t'[role=\"region\"][aria-label$=\"scrollable table\"]',\n\t\t\t);\n\t\t\tconst rowHeader = targetButton.closest(\"tr\")?.querySelector<HTMLElement>('th[scope=\"row\"]');\n\n\t\t\tif (scrollContainer) {\n\t\t\t\tconst containerRect = scrollContainer.getBoundingClientRect();\n\t\t\t\tconst btnRect = targetButton.getBoundingClientRect();\n\t\t\t\tconst stickyWidth = rowHeader ? rowHeader.getBoundingClientRect().width : 0;\n\n\t\t\t\tconst visibleLeft = containerRect.left + stickyWidth;\n\t\t\t\tconst visibleRight = containerRect.right;\n\n\t\t\t\tif (btnRect.left < visibleLeft) {\n\t\t\t\t\tscrollContainer.scrollLeft -= visibleLeft - btnRect.left + cellGap;\n\t\t\t\t} else if (btnRect.right > visibleRight) {\n\t\t\t\t\tscrollContainer.scrollLeft += btnRect.right - visibleRight + cellGap;\n\t\t\t\t}\n\t\t\t} else if (typeof targetButton.scrollIntoView === \"function\") {\n\t\t\t\ttargetButton.scrollIntoView({ block: \"nearest\", inline: \"nearest\" });\n\t\t\t}\n\t\t}\n\t};\n\n\tconst handleContainerFocus = (e: FocusEvent<HTMLDivElement>) => {\n\t\tisFocusedInsideRef.current = true;\n\t\tonFocus?.(e);\n\t};\n\n\tconst handleContainerBlur = (e: FocusEvent<HTMLDivElement>) => {\n\t\tif (!e.currentTarget.contains(e.relatedTarget)) {\n\t\t\tisFocusedInsideRef.current = false;\n\t\t\tdismissedByEscapeRef.current = false;\n\t\t}\n\t\tonBlur?.(e);\n\t};\n\n\tconst callerCleanupRef = useRef<(() => void) | undefined>(undefined);\n\n\tconst setRootRef = useCallback(\n\t\t(node: HTMLDivElement | null) => {\n\t\t\tcontainerRef.current = node;\n\n\t\t\tif (callerCleanupRef.current) {\n\t\t\t\tcallerCleanupRef.current();\n\t\t\t\tcallerCleanupRef.current = undefined;\n\t\t\t}\n\n\t\t\tif (typeof ref === \"function\") {\n\t\t\t\tconst cleanup = ref(node);\n\t\t\t\tif (typeof cleanup === \"function\") {\n\t\t\t\t\tcallerCleanupRef.current = cleanup;\n\t\t\t\t}\n\t\t\t} else if (ref) {\n\t\t\t\tref.current = node;\n\t\t\t}\n\n\t\t\treturn () => {\n\t\t\t\tcontainerRef.current = null;\n\t\t\t\tif (callerCleanupRef.current) {\n\t\t\t\t\tcallerCleanupRef.current();\n\t\t\t\t\tcallerCleanupRef.current = undefined;\n\t\t\t\t} else if (typeof ref === \"function\") {\n\t\t\t\t\tref(null);\n\t\t\t\t} else if (ref) {\n\t\t\t\t\tref.current = null;\n\t\t\t\t}\n\t\t\t};\n\t\t},\n\t\t[ref],\n\t);\n\n\tconst isEmpty = numRows === 0 || numCols === 0;\n\n\treturn (\n\t\t<TooltipProvider>\n\t\t\t<div\n\t\t\t\tref={setRootRef}\n\t\t\t\ttabIndex={isEmpty ? -1 : undefined}\n\t\t\t\trole=\"region\"\n\t\t\t\taria-label={ariaLabel}\n\t\t\t\tdata-testid=\"heatmap-matrix\"\n\t\t\t\tclassName={cn(\n\t\t\t\t\t\"flex flex-col gap-basalt-space-lg rounded-basalt-md border border-basalt-border/60 bg-basalt-card p-basalt-space-lg text-basalt-sm outline-none\",\n\t\t\t\t\tisEmpty ? \"text-basalt-muted-foreground\" : \"text-basalt-foreground\",\n\t\t\t\t\tclassName,\n\t\t\t\t)}\n\t\t\t\tstyle={style}\n\t\t\t\tonFocus={handleContainerFocus}\n\t\t\t\tonBlur={handleContainerBlur}\n\t\t\t\tonKeyDown={handleContainerKeyDown}\n\t\t\t\t{...rest}\n\t\t\t>\n\t\t\t\t{isEmpty ? (\n\t\t\t\t\t<span className=\"italic\">{missingLabel}</span>\n\t\t\t\t) : (\n\t\t\t\t\t<>\n\t\t\t\t\t\t{/* Scrollable table container */}\n\t\t\t\t\t\t<div\n\t\t\t\t\t\t\ttabIndex={-1}\n\t\t\t\t\t\t\trole=\"region\"\n\t\t\t\t\t\t\taria-label={`${ariaLabel} scrollable table`}\n\t\t\t\t\t\t\tclassName=\"w-full overflow-x-auto outline-none\"\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t<table\n\t\t\t\t\t\t\t\t// biome-ignore lint/a11y/noNoninteractiveElementToInteractiveRole: W3C ARIA interactive 2D grid table with descendant roving tab buttons\n\t\t\t\t\t\t\t\trole=\"grid\"\n\t\t\t\t\t\t\t\taria-label={ariaLabel}\n\t\t\t\t\t\t\t\tclassName=\"border-separate\"\n\t\t\t\t\t\t\t\tstyle={{ borderSpacing: cellGap }}\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t<thead>\n\t\t\t\t\t\t\t\t\t<tr>\n\t\t\t\t\t\t\t\t\t\t{/* Top-left empty header */}\n\t\t\t\t\t\t\t\t\t\t<th\n\t\t\t\t\t\t\t\t\t\t\tscope=\"col\"\n\t\t\t\t\t\t\t\t\t\t\tclassName=\"sticky left-0 z-10 bg-basalt-card p-0 text-left font-normal\"\n\t\t\t\t\t\t\t\t\t\t\tstyle={{ minWidth: 64 }}\n\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t<span className=\"sr-only\">Row \\ Column</span>\n\t\t\t\t\t\t\t\t\t\t</th>\n\t\t\t\t\t\t\t\t\t\t{columnLabels.map((col, colIdx) => (\n\t\t\t\t\t\t\t\t\t\t\t<th\n\t\t\t\t\t\t\t\t\t\t\t\tkey={`${matrixId}-col-${colIdx}`}\n\t\t\t\t\t\t\t\t\t\t\t\tscope=\"col\"\n\t\t\t\t\t\t\t\t\t\t\t\tclassName=\"p-0 text-center font-mono text-basalt-sm text-basalt-muted-foreground select-none\"\n\t\t\t\t\t\t\t\t\t\t\t\tstyle={{\n\t\t\t\t\t\t\t\t\t\t\t\t\twidth: effectiveColWidth,\n\t\t\t\t\t\t\t\t\t\t\t\t\tmaxWidth: effectiveColWidth,\n\t\t\t\t\t\t\t\t\t\t\t\t\toverflow: \"hidden\",\n\t\t\t\t\t\t\t\t\t\t\t\t\ttextOverflow: \"ellipsis\",\n\t\t\t\t\t\t\t\t\t\t\t\t\twhiteSpace: \"nowrap\",\n\t\t\t\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t\t\t\t\ttitle={col}\n\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t{col}\n\t\t\t\t\t\t\t\t\t\t\t</th>\n\t\t\t\t\t\t\t\t\t\t))}\n\t\t\t\t\t\t\t\t\t</tr>\n\t\t\t\t\t\t\t\t</thead>\n\t\t\t\t\t\t\t\t<tbody>\n\t\t\t\t\t\t\t\t\t{rowLabels.map((rowName, rowIdx) => (\n\t\t\t\t\t\t\t\t\t\t<tr key={`${matrixId}-row-${rowIdx}`}>\n\t\t\t\t\t\t\t\t\t\t\t<th\n\t\t\t\t\t\t\t\t\t\t\t\tscope=\"row\"\n\t\t\t\t\t\t\t\t\t\t\t\tclassName=\"sticky left-0 z-10 bg-basalt-card pr-basalt-space-lg text-left font-medium text-basalt-foreground select-none\"\n\t\t\t\t\t\t\t\t\t\t\t\tstyle={{\n\t\t\t\t\t\t\t\t\t\t\t\t\tlineHeight: `${cellSize}px`,\n\t\t\t\t\t\t\t\t\t\t\t\t\tminWidth: 64,\n\t\t\t\t\t\t\t\t\t\t\t\t\twhiteSpace: \"nowrap\",\n\t\t\t\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t{rowName}\n\t\t\t\t\t\t\t\t\t\t\t</th>\n\t\t\t\t\t\t\t\t\t\t\t{columnLabels.map((colName, colIdx) => {\n\t\t\t\t\t\t\t\t\t\t\t\tconst cellKey = `${rowIdx}-${colIdx}`;\n\t\t\t\t\t\t\t\t\t\t\t\tconst rawVal = values[rowIdx]?.[colIdx];\n\t\t\t\t\t\t\t\t\t\t\t\tconst isMissing =\n\t\t\t\t\t\t\t\t\t\t\t\t\trawVal == null || typeof rawVal !== \"number\" || !Number.isFinite(rawVal);\n\t\t\t\t\t\t\t\t\t\t\t\tconst colorIdx = resolveColorIndex(\n\t\t\t\t\t\t\t\t\t\t\t\t\trawVal,\n\t\t\t\t\t\t\t\t\t\t\t\t\tminVal,\n\t\t\t\t\t\t\t\t\t\t\t\t\tmaxVal,\n\t\t\t\t\t\t\t\t\t\t\t\t\tcolorScale.length,\n\t\t\t\t\t\t\t\t\t\t\t\t);\n\n\t\t\t\t\t\t\t\t\t\t\t\tconst formattedVal = isMissing\n\t\t\t\t\t\t\t\t\t\t\t\t\t? missingLabel\n\t\t\t\t\t\t\t\t\t\t\t\t\t: valueFormatter(rawVal as number);\n\n\t\t\t\t\t\t\t\t\t\t\t\tconst cellBg =\n\t\t\t\t\t\t\t\t\t\t\t\t\tcolorIdx < 0\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t? \"hsl(var(--basalt-muted) / 0.4)\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t: (colorScale[colorIdx] ?? \"hsl(var(--basalt-muted))\");\n\n\t\t\t\t\t\t\t\t\t\t\t\tconst accessibleCellLabel = `${rowName}, ${colName}: ${formattedVal} (${metricLabel})`;\n\t\t\t\t\t\t\t\t\t\t\t\tconst isSelected = activePos[0] === rowIdx && activePos[1] === colIdx;\n\t\t\t\t\t\t\t\t\t\t\t\tconst isOpen = openTooltipPos === cellKey;\n\n\t\t\t\t\t\t\t\t\t\t\t\tconst cellContext: HeatmapMatrixCellContext = {\n\t\t\t\t\t\t\t\t\t\t\t\t\trowIndex: rowIdx,\n\t\t\t\t\t\t\t\t\t\t\t\t\tcolIndex: colIdx,\n\t\t\t\t\t\t\t\t\t\t\t\t\trowLabel: rowName,\n\t\t\t\t\t\t\t\t\t\t\t\t\tcolumnLabel: colName,\n\t\t\t\t\t\t\t\t\t\t\t\t\tvalue: rawVal,\n\t\t\t\t\t\t\t\t\t\t\t\t\tformattedValue: formattedVal,\n\t\t\t\t\t\t\t\t\t\t\t\t\tisMissing,\n\t\t\t\t\t\t\t\t\t\t\t\t\tcolorIndex: colorIdx,\n\t\t\t\t\t\t\t\t\t\t\t\t};\n\n\t\t\t\t\t\t\t\t\t\t\t\treturn (\n\t\t\t\t\t\t\t\t\t\t\t\t\t// biome-ignore lint/a11y/noNoninteractiveElementToInteractiveRole: ARIA gridcell container wrapping roving interactive button\n\t\t\t\t\t\t\t\t\t\t\t\t\t// biome-ignore lint/a11y/useFocusableInteractive: Focus is managed on child button via roving tabindex\n\t\t\t\t\t\t\t\t\t\t\t\t\t<td key={cellKey} role=\"gridcell\" className=\"p-0 text-center\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t<Tooltip\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\topen={isOpen}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tonOpenChange={(next) => {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tif (next) {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tif (!dismissedByEscapeRef.current) {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tsetOpenTooltipPos(cellKey);\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tsetOpenTooltipPos((curr) => {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tif (curr !== cellKey) return curr;\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tif (dismissedByEscapeRef.current) return null;\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tconst isCellFocused =\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tdocument.activeElement === cellRefs.current.get(cellKey);\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\treturn isCellFocused ? curr : null;\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<TooltipTrigger asChild>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tref={(el) => {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tif (el) cellRefs.current.set(cellKey, el);\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\telse cellRefs.current.delete(cellKey);\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\taria-label={accessibleCellLabel}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\ttabIndex={isSelected ? 0 : -1}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tonFocus={() => {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tsetActivePos([rowIdx, colIdx]);\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tdismissedByEscapeRef.current = false;\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tsetOpenTooltipPos(cellKey);\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tonBlur={() => {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tsetOpenTooltipPos((curr) => (curr === cellKey ? null : curr));\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tclassName=\"box-border block cursor-pointer rounded-basalt-sm border-0 p-0 transition-colors basalt-motion hover:ring-1 hover:ring-basalt-foreground focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-basalt-ring\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tstyle={{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\twidth: effectiveColWidth,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\theight: cellSize,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tbackgroundColor: cellBg,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t</TooltipTrigger>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<TooltipContent\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tid={`${matrixId}-${cellKey}`}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tonEscapeKeyDown={() => {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tdismissedByEscapeRef.current = true;\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tsetOpenTooltipPos(null);\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t{renderTooltip ? (\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\trenderTooltip(cellContext)\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t) : (\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<div className=\"text-basalt-sm\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<div className=\"font-semibold text-basalt-popover-foreground\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t{rowName} · {colName}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<div className=\"text-basalt-muted-foreground\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t{metricLabel}:{\" \"}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<span className=\"font-medium text-basalt-popover-foreground\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t{formattedVal}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t</TooltipContent>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t</Tooltip>\n\t\t\t\t\t\t\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\t\t\t\t})}\n\t\t\t\t\t\t\t\t\t\t</tr>\n\t\t\t\t\t\t\t\t\t))}\n\t\t\t\t\t\t\t\t</tbody>\n\t\t\t\t\t\t\t</table>\n\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t{/* Legend footer */}\n\t\t\t\t\t\t{showLegend ? (\n\t\t\t\t\t\t\t<div\n\t\t\t\t\t\t\t\tclassName=\"mt-basalt-space-sm flex items-center justify-end gap-basalt-space-sm text-basalt-sm text-basalt-muted-foreground select-none\"\n\t\t\t\t\t\t\t\taria-hidden=\"true\"\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t<span>{lessLabel}</span>\n\t\t\t\t\t\t\t\t<div\n\t\t\t\t\t\t\t\t\tclassName=\"rounded-basalt-sm\"\n\t\t\t\t\t\t\t\t\ttitle=\"Missing data\"\n\t\t\t\t\t\t\t\t\tstyle={{\n\t\t\t\t\t\t\t\t\t\twidth: cellSize,\n\t\t\t\t\t\t\t\t\t\theight: cellSize,\n\t\t\t\t\t\t\t\t\t\tbackgroundColor: \"hsl(var(--basalt-muted) / 0.4)\",\n\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t{colorScale.map((color, idx) => (\n\t\t\t\t\t\t\t\t\t<div\n\t\t\t\t\t\t\t\t\t\tkey={`${color}-${idx}`}\n\t\t\t\t\t\t\t\t\t\tclassName=\"rounded-basalt-sm\"\n\t\t\t\t\t\t\t\t\t\tstyle={{\n\t\t\t\t\t\t\t\t\t\t\twidth: cellSize,\n\t\t\t\t\t\t\t\t\t\t\theight: cellSize,\n\t\t\t\t\t\t\t\t\t\t\tbackgroundColor: color,\n\t\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t))}\n\t\t\t\t\t\t\t\t<span>{moreLabel}</span>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t) : null}\n\t\t\t\t\t</>\n\t\t\t\t)}\n\t\t\t</div>\n\t\t</TooltipProvider>\n\t);\n});\n\nHeatmapMatrix.displayName = \"HeatmapMatrix\";\n"],"mappings":"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"}
|
package/dist/charts/sparkline.js
CHANGED
|
@@ -37,7 +37,7 @@ function f({ data: f, series: p, ariaLabel: m = "Sparkline", className: h, summa
|
|
|
37
37
|
fill: r.color ?? (y.length === 1 ? e.green : n(r, i)),
|
|
38
38
|
radius: 1,
|
|
39
39
|
...t,
|
|
40
|
-
children: f.map((e, t) => /* @__PURE__ */ a(l, { fillOpacity:
|
|
40
|
+
children: f.map((e, t) => /* @__PURE__ */ a(l, { fillOpacity: 1 }, `${e.x}-${t}`))
|
|
41
41
|
}, r.key))
|
|
42
42
|
]
|
|
43
43
|
})
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sparkline.js","names":["RechartsBar","Sparkline","bars","resolveChartSeries","series","ChartFrame","BarChart","XAxis","YAxis","Bar","item","chart","seriesColor","index","ANIMATION_PROPS","data","Cell","
|
|
1
|
+
{"version":3,"file":"sparkline.js","names":["RechartsBar","Sparkline","bars","resolveChartSeries","series","ChartFrame","BarChart","XAxis","YAxis","Bar","item","chart","seriesColor","index","ANIMATION_PROPS","data","Cell","point","pointIndex"],"sources":["../../src/charts/sparkline.tsx"],"sourcesContent":["import type { ReactNode } from \"react\";\nimport { Bar, Cell, BarChart as RechartsBar, XAxis, YAxis } from \"recharts\";\nimport { ANIMATION_PROPS, seriesColor } from \"./config\";\nimport { ChartFrame } from \"./frame\";\nimport type { LineChartNumericKeys } from \"./line\";\nimport { chart } from \"./palette\";\nimport { type ChartSeriesDescriptor, resolveChartSeries, type XYPoint } from \"./series\";\n\nexport type SparklineProps<\n\tTData extends { x: string | number } = XYPoint,\n\tK extends LineChartNumericKeys<TData> & string = LineChartNumericKeys<TData> & string,\n> = {\n\t/**\n\t * Dataset array where each record requires an `x` category or time coordinate.\n\t * Any remaining fields with numeric, nullable, or optional number values are inferred as valid series keys.\n\t */\n\tdata: TData[];\n\t/**\n\t * Series descriptors identifying which numeric keys to plot.\n\t * Inferred strictly from numeric keys of `TData` (rejects non-numeric fields, `x`, and typos).\n\t */\n\tseries?: Array<ChartSeriesDescriptor<NoInfer<K>>>;\n\tariaLabel?: string;\n\tclassName?: string;\n\t/**\n\t * Textual summary describing key insights, highs, lows, and keyboard exploration instructions.\n\t * Associated with the chart via useId and aria-describedby.\n\t */\n\tsummary?: ReactNode;\n\t/**\n\t * Accessible tabular or structured data alternative rendered outside the plot area.\n\t */\n\tdataAlternative?: ReactNode;\n\t/**\n\t * Whether the interactive accessibility layer is enabled on the underlying Recharts graphic.\n\t * Enables keyboard exploration with Tab and arrow keys where supported by the underlying chart type.\n\t * Non-interactive compact or decorative charts without tooltip navigation should provide summary or dataAlternative.\n\t * @default true\n\t */\n\taccessibilityLayer?: boolean;\n};\n\nexport function Sparkline<\n\tTData extends { x: string | number } = XYPoint,\n\tK extends LineChartNumericKeys<TData> & string = LineChartNumericKeys<TData> & string,\n>({\n\tdata,\n\tseries,\n\tariaLabel = \"Sparkline\",\n\tclassName,\n\tsummary,\n\tdataAlternative,\n\taccessibilityLayer,\n}: SparklineProps<TData, K>) {\n\tconst bars = resolveChartSeries(series, [\"y\" as K]);\n\treturn (\n\t\t<ChartFrame\n\t\t\tariaLabel={ariaLabel}\n\t\t\tclassName={className}\n\t\t\tsize=\"h-basalt-5 w-basalt-28\"\n\t\t\tsummary={summary}\n\t\t\tdataAlternative={dataAlternative}\n\t\t\taccessibilityLayer={accessibilityLayer}\n\t\t>\n\t\t\t<RechartsBar\n\t\t\t\tdata={data}\n\t\t\t\tmargin={{ top: 0, right: 0, bottom: 0, left: 0 }}\n\t\t\t\tbarCategoryGap=\"12%\"\n\t\t\t\tbarGap={1}\n\t\t\t>\n\t\t\t\t<XAxis dataKey=\"x\" hide />\n\t\t\t\t<YAxis hide />\n\t\t\t\t{bars.map((item, index) => (\n\t\t\t\t\t<Bar\n\t\t\t\t\t\tkey={item.key}\n\t\t\t\t\t\tdataKey={item.key}\n\t\t\t\t\t\tname={item.label ?? item.key}\n\t\t\t\t\t\tfill={item.color ?? (bars.length === 1 ? chart.green : seriesColor(item, index))}\n\t\t\t\t\t\tradius={1}\n\t\t\t\t\t\t{...ANIMATION_PROPS}\n\t\t\t\t\t>\n\t\t\t\t\t\t{data.map((point, pointIndex) => (\n\t\t\t\t\t\t\t<Cell key={`${point.x}-${pointIndex}`} fillOpacity={1} />\n\t\t\t\t\t\t))}\n\t\t\t\t\t</Bar>\n\t\t\t\t))}\n\t\t\t</RechartsBar>\n\t\t</ChartFrame>\n\t);\n}\n"],"mappings":";;;;;;;;AA0CA,SAAgBC,EAGd,EACD,SACA,WACA,eAAY,aACZ,cACA,YACA,oBACA,yBAC4B;CAC5B,IAAMC,IAAOC,EAAmBC,GAAQ,CAAC,GAAQ,CAAC;CAClD,OACC,kBAACC,GAAD;EACY;EACA;EACX,MAAK;EACI;EACQ;EACG;EAEpB,UAAA,kBAACL,GAAD;GACO;GACN,QAAQ;IAAE,KAAK;IAAG,OAAO;IAAG,QAAQ;IAAG,MAAM;GAAE;GAC/C,gBAAe;GACf,QAAQ;GAJT,UAAA;IAMC,kBAACO,GAAD;KAAO,SAAQ;KAAI,MAAA;IAAM,CAAA;IACzB,kBAACC,GAAD,EAAO,MAAA,GAAM,CAAA;IACZN,EAAK,KAAK,GAAM,MAChB,kBAACO,GAAD;KAEC,SAASC,EAAK;KACd,MAAMA,EAAK,SAASA,EAAK;KACzB,MAAMA,EAAK,UAAUR,EAAK,WAAW,IAAIS,EAAM,QAAQC,EAAYF,GAAMG,CAAK;KAC9E,QAAQ;KACR,GAAIC;KAEH,UAAAC,EAAK,KAAK,GAAO,MACjB,kBAACC,GAAD,EAAuC,aAAa,EAAI,GAA7C,GAAGC,EAAM,EAAE,GAAGC,GAA+B,CACxD;IACG,GAVCR,EAAK,GAUN,CACL;GACW;;CACF,CAAA;AAEd"}
|
package/dist/charts/stat-card.js
CHANGED
|
@@ -43,7 +43,7 @@ function a({ label: a, title: o, value: s, subtitle: c, icon: l, iconColor: u =
|
|
|
43
43
|
children: [
|
|
44
44
|
y ? /* @__PURE__ */ n("div", { children: p }) : /* @__PURE__ */ n("p", {
|
|
45
45
|
"data-slot": "stat-card-value",
|
|
46
|
-
className: "font-basalt-display text-basalt-
|
|
46
|
+
className: "font-basalt-display text-basalt-3xl leading-basalt-tight font-semibold tracking-tight tabular-nums text-basalt-foreground",
|
|
47
47
|
children: w
|
|
48
48
|
}),
|
|
49
49
|
c ? /* @__PURE__ */ n("p", {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"stat-card.js","names":["hasSlotContent","slot","StatCard","Icon","heading","title","label","hasStatus","status","hasTrendContent","trendContent","hasAction","action","hasChildren","children","isInteractive","display","value","isPositiveTrend","trend","isNegativeTrend","named","ariaLabel","subtitle","LayerCard","cn","className","iconColor","StatGrid","columns"],"sources":["../../src/charts/stat-card.tsx"],"sourcesContent":["import type { LucideIcon } from \"lucide-react\";\nimport type { ReactNode } from \"react\";\nimport { LayerCard } from \"../components/layer-card\";\nimport { cn } from \"../utils/cn\";\n\nexport type StatCardProps = {\n\t/**\n\t * Secondary heading label for the metric card (overridden by title).\n\t */\n\tlabel?: string;\n\t/**\n\t * Primary heading title for the metric card (takes precedence over label).\n\t */\n\ttitle?: string;\n\t/**\n\t * Metric value, formatted via toLocaleString() if a number is provided.\n\t * Overridden by the status slot if provided.\n\t */\n\tvalue: string | number;\n\t/**\n\t * Supporting descriptive subtitle text rendered below value.\n\t */\n\tsubtitle?: string;\n\t/**\n\t * Optional decorative Lucide icon component.\n\t */\n\ticon?: LucideIcon;\n\t/**\n\t * Color class applied to the decorative icon.\n\t * @default \"text-basalt-muted-foreground\"\n\t */\n\ticonColor?: string;\n\t/**\n\t * Structured percentage trend value and optional label.\n\t * Overridden by the trendContent slot if provided.\n\t */\n\ttrend?: { value: number; label?: string };\n\t/**\n\t * Optional header action or interactive element (e.g. tooltip trigger or button).\n\t * Renders alongside the title/icon. Switches card role to group for accessible descendants.\n\t */\n\taction?: ReactNode;\n\t/**\n\t * Optional custom status replacement slot (e.g. loading skeleton, error state, or empty state).\n\t * Takes precedence over the default value display and excludes value from accessible name.\n\t * Subtitle and trend remain caller-managed; when error/empty state is shown, callers should omit trend.\n\t */\n\tstatus?: ReactNode;\n\t/**\n\t * Optional custom trend replacement slot (e.g. custom badge, sparkline, or localized trend indicator).\n\t * Takes precedence over the default trend calculation and excludes default trend from accessible name.\n\t */\n\ttrendContent?: ReactNode;\n\t/**\n\t * Optional supporting content rendered below the value, subtitle, and trend in the same surface.\n\t */\n\tchildren?: ReactNode;\n\t/**\n\t * Explicit accessible label overriding automatic accessible name calculation.\n\t */\n\tariaLabel?: string;\n\t/**\n\t * Additional CSS class names for styling the card root.\n\t */\n\tclassName?: string;\n};\n\nfunction hasSlotContent(slot: ReactNode | undefined): boolean {\n\treturn slot !== undefined && slot !== null && slot !== false;\n}\n\nexport function StatCard({\n\tlabel,\n\ttitle,\n\tvalue,\n\tsubtitle,\n\ticon: Icon,\n\ticonColor = \"text-basalt-muted-foreground\",\n\ttrend,\n\taction,\n\tstatus,\n\ttrendContent,\n\tchildren,\n\tariaLabel,\n\tclassName,\n}: StatCardProps) {\n\tconst heading = title ?? label;\n\tconst hasStatus = hasSlotContent(status);\n\tconst hasTrendContent = hasSlotContent(trendContent);\n\tconst hasAction = hasSlotContent(action);\n\tconst hasChildren = hasSlotContent(children);\n\tconst isInteractive = hasAction || hasStatus || hasTrendContent || hasChildren;\n\n\tconst display = typeof value === \"number\" ? value.toLocaleString() : value;\n\tconst isPositiveTrend = trend && trend.value > 0;\n\tconst isNegativeTrend = trend && trend.value < 0;\n\n\t// Accessible name: If custom status or custom trendContent is provided, do not report\n\t// the overridden hidden default value or default trend to assistive technology.\n\tconst named =\n\t\tariaLabel ??\n\t\t[\n\t\t\theading,\n\t\t\t!hasStatus ? display : null,\n\t\t\tsubtitle,\n\t\t\t!hasTrendContent && trend\n\t\t\t\t? `${isPositiveTrend ? \"+\" : \"\"}${trend.value}% ${trend.label ?? \"\"}`\n\t\t\t\t: null,\n\t\t]\n\t\t\t.filter(Boolean)\n\t\t\t.join(\" \")\n\t\t\t.trim();\n\n\treturn (\n\t\t<LayerCard\n\t\t\tdata-slot=\"stat-card\"\n\t\t\tclassName={cn(\n\t\t\t\t\"flex min-w-0 flex-col gap-basalt-card-sm overflow-visible [overflow-wrap:anywhere]\",\n\t\t\t\tclassName,\n\t\t\t)}\n\t\t\trole={isInteractive ? \"group\" : \"img\"}\n\t\t\taria-label={named}\n\t\t>\n\t\t\t{heading || Icon || hasAction ? (\n\t\t\t\t<div\n\t\t\t\t\tdata-slot=\"stat-card-heading\"\n\t\t\t\t\tclassName=\"flex items-center justify-between gap-basalt-space-lg\"\n\t\t\t\t>\n\t\t\t\t\t<div className=\"flex min-w-0 items-start gap-basalt-space-lg\">\n\t\t\t\t\t\t{Icon ? (\n\t\t\t\t\t\t\t<Icon\n\t\t\t\t\t\t\t\taria-hidden=\"true\"\n\t\t\t\t\t\t\t\tclassName={cn(\"mt-basalt-space-xs size-basalt-icon-lg shrink-0\", iconColor)}\n\t\t\t\t\t\t\t\tstrokeWidth={1.5}\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t) : null}\n\t\t\t\t\t\t<p className=\"min-w-0 text-basalt-base font-medium text-basalt-muted-foreground\">\n\t\t\t\t\t\t\t{heading}\n\t\t\t\t\t\t</p>\n\t\t\t\t\t</div>\n\t\t\t\t\t{hasAction ? <div className=\"flex shrink-0 items-center\">{action}</div> : null}\n\t\t\t\t</div>\n\t\t\t) : null}\n\t\t\t<div data-slot=\"stat-card-metric\" className=\"min-w-0 space-y-basalt-space-sm\">\n\t\t\t\t{hasStatus ? (\n\t\t\t\t\t<div>{status}</div>\n\t\t\t\t) : (\n\t\t\t\t\t<p\n\t\t\t\t\t\tdata-slot=\"stat-card-value\"\n\t\t\t\t\t\tclassName=\"font-basalt-display text-basalt-
|
|
1
|
+
{"version":3,"file":"stat-card.js","names":["hasSlotContent","slot","StatCard","Icon","heading","title","label","hasStatus","status","hasTrendContent","trendContent","hasAction","action","hasChildren","children","isInteractive","display","value","isPositiveTrend","trend","isNegativeTrend","named","ariaLabel","subtitle","LayerCard","cn","className","iconColor","StatGrid","columns"],"sources":["../../src/charts/stat-card.tsx"],"sourcesContent":["import type { LucideIcon } from \"lucide-react\";\nimport type { ReactNode } from \"react\";\nimport { LayerCard } from \"../components/layer-card\";\nimport { cn } from \"../utils/cn\";\n\nexport type StatCardProps = {\n\t/**\n\t * Secondary heading label for the metric card (overridden by title).\n\t */\n\tlabel?: string;\n\t/**\n\t * Primary heading title for the metric card (takes precedence over label).\n\t */\n\ttitle?: string;\n\t/**\n\t * Metric value, formatted via toLocaleString() if a number is provided.\n\t * Overridden by the status slot if provided.\n\t */\n\tvalue: string | number;\n\t/**\n\t * Supporting descriptive subtitle text rendered below value.\n\t */\n\tsubtitle?: string;\n\t/**\n\t * Optional decorative Lucide icon component.\n\t */\n\ticon?: LucideIcon;\n\t/**\n\t * Color class applied to the decorative icon.\n\t * @default \"text-basalt-muted-foreground\"\n\t */\n\ticonColor?: string;\n\t/**\n\t * Structured percentage trend value and optional label.\n\t * Overridden by the trendContent slot if provided.\n\t */\n\ttrend?: { value: number; label?: string };\n\t/**\n\t * Optional header action or interactive element (e.g. tooltip trigger or button).\n\t * Renders alongside the title/icon. Switches card role to group for accessible descendants.\n\t */\n\taction?: ReactNode;\n\t/**\n\t * Optional custom status replacement slot (e.g. loading skeleton, error state, or empty state).\n\t * Takes precedence over the default value display and excludes value from accessible name.\n\t * Subtitle and trend remain caller-managed; when error/empty state is shown, callers should omit trend.\n\t */\n\tstatus?: ReactNode;\n\t/**\n\t * Optional custom trend replacement slot (e.g. custom badge, sparkline, or localized trend indicator).\n\t * Takes precedence over the default trend calculation and excludes default trend from accessible name.\n\t */\n\ttrendContent?: ReactNode;\n\t/**\n\t * Optional supporting content rendered below the value, subtitle, and trend in the same surface.\n\t */\n\tchildren?: ReactNode;\n\t/**\n\t * Explicit accessible label overriding automatic accessible name calculation.\n\t */\n\tariaLabel?: string;\n\t/**\n\t * Additional CSS class names for styling the card root.\n\t */\n\tclassName?: string;\n};\n\nfunction hasSlotContent(slot: ReactNode | undefined): boolean {\n\treturn slot !== undefined && slot !== null && slot !== false;\n}\n\nexport function StatCard({\n\tlabel,\n\ttitle,\n\tvalue,\n\tsubtitle,\n\ticon: Icon,\n\ticonColor = \"text-basalt-muted-foreground\",\n\ttrend,\n\taction,\n\tstatus,\n\ttrendContent,\n\tchildren,\n\tariaLabel,\n\tclassName,\n}: StatCardProps) {\n\tconst heading = title ?? label;\n\tconst hasStatus = hasSlotContent(status);\n\tconst hasTrendContent = hasSlotContent(trendContent);\n\tconst hasAction = hasSlotContent(action);\n\tconst hasChildren = hasSlotContent(children);\n\tconst isInteractive = hasAction || hasStatus || hasTrendContent || hasChildren;\n\n\tconst display = typeof value === \"number\" ? value.toLocaleString() : value;\n\tconst isPositiveTrend = trend && trend.value > 0;\n\tconst isNegativeTrend = trend && trend.value < 0;\n\n\t// Accessible name: If custom status or custom trendContent is provided, do not report\n\t// the overridden hidden default value or default trend to assistive technology.\n\tconst named =\n\t\tariaLabel ??\n\t\t[\n\t\t\theading,\n\t\t\t!hasStatus ? display : null,\n\t\t\tsubtitle,\n\t\t\t!hasTrendContent && trend\n\t\t\t\t? `${isPositiveTrend ? \"+\" : \"\"}${trend.value}% ${trend.label ?? \"\"}`\n\t\t\t\t: null,\n\t\t]\n\t\t\t.filter(Boolean)\n\t\t\t.join(\" \")\n\t\t\t.trim();\n\n\treturn (\n\t\t<LayerCard\n\t\t\tdata-slot=\"stat-card\"\n\t\t\tclassName={cn(\n\t\t\t\t\"flex min-w-0 flex-col gap-basalt-card-sm overflow-visible [overflow-wrap:anywhere]\",\n\t\t\t\tclassName,\n\t\t\t)}\n\t\t\trole={isInteractive ? \"group\" : \"img\"}\n\t\t\taria-label={named}\n\t\t>\n\t\t\t{heading || Icon || hasAction ? (\n\t\t\t\t<div\n\t\t\t\t\tdata-slot=\"stat-card-heading\"\n\t\t\t\t\tclassName=\"flex items-center justify-between gap-basalt-space-lg\"\n\t\t\t\t>\n\t\t\t\t\t<div className=\"flex min-w-0 items-start gap-basalt-space-lg\">\n\t\t\t\t\t\t{Icon ? (\n\t\t\t\t\t\t\t<Icon\n\t\t\t\t\t\t\t\taria-hidden=\"true\"\n\t\t\t\t\t\t\t\tclassName={cn(\"mt-basalt-space-xs size-basalt-icon-lg shrink-0\", iconColor)}\n\t\t\t\t\t\t\t\tstrokeWidth={1.5}\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t) : null}\n\t\t\t\t\t\t<p className=\"min-w-0 text-basalt-base font-medium text-basalt-muted-foreground\">\n\t\t\t\t\t\t\t{heading}\n\t\t\t\t\t\t</p>\n\t\t\t\t\t</div>\n\t\t\t\t\t{hasAction ? <div className=\"flex shrink-0 items-center\">{action}</div> : null}\n\t\t\t\t</div>\n\t\t\t) : null}\n\t\t\t<div data-slot=\"stat-card-metric\" className=\"min-w-0 space-y-basalt-space-sm\">\n\t\t\t\t{hasStatus ? (\n\t\t\t\t\t<div>{status}</div>\n\t\t\t\t) : (\n\t\t\t\t\t<p\n\t\t\t\t\t\tdata-slot=\"stat-card-value\"\n\t\t\t\t\t\tclassName=\"font-basalt-display text-basalt-3xl leading-basalt-tight font-semibold tracking-tight tabular-nums text-basalt-foreground\"\n\t\t\t\t\t>\n\t\t\t\t\t\t{display}\n\t\t\t\t\t</p>\n\t\t\t\t)}\n\t\t\t\t{subtitle ? (\n\t\t\t\t\t<p className=\"text-basalt-sm text-basalt-muted-foreground\">{subtitle}</p>\n\t\t\t\t) : null}\n\t\t\t\t{hasTrendContent ? (\n\t\t\t\t\t<div\n\t\t\t\t\t\tdata-slot=\"stat-card-trend\"\n\t\t\t\t\t\tclassName=\"flex flex-wrap items-baseline gap-x-basalt-space-md gap-y-basalt-space-xs text-basalt-sm\"\n\t\t\t\t\t>\n\t\t\t\t\t\t{trendContent}\n\t\t\t\t\t</div>\n\t\t\t\t) : trend ? (\n\t\t\t\t\t<div\n\t\t\t\t\t\tdata-slot=\"stat-card-trend\"\n\t\t\t\t\t\tclassName=\"flex flex-wrap items-baseline gap-x-basalt-space-md gap-y-basalt-space-xs text-basalt-sm\"\n\t\t\t\t\t>\n\t\t\t\t\t\t<span\n\t\t\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\t\t\"font-medium tabular-nums\",\n\t\t\t\t\t\t\t\tisPositiveTrend && \"text-basalt-tag-success-foreground\",\n\t\t\t\t\t\t\t\tisNegativeTrend && \"text-basalt-destructive\",\n\t\t\t\t\t\t\t\t!isPositiveTrend && !isNegativeTrend && \"text-basalt-muted-foreground\",\n\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t{`${isPositiveTrend ? \"+\" : \"\"}${trend.value}%`}\n\t\t\t\t\t\t</span>\n\t\t\t\t\t\t{trend.label ? (\n\t\t\t\t\t\t\t<span className=\"text-basalt-muted-foreground\">{trend.label}</span>\n\t\t\t\t\t\t) : null}\n\t\t\t\t\t</div>\n\t\t\t\t) : null}\n\t\t\t</div>\n\t\t\t{hasChildren ? (\n\t\t\t\t<div data-slot=\"stat-card-content\" className=\"mt-auto min-w-0\">\n\t\t\t\t\t{children}\n\t\t\t\t</div>\n\t\t\t) : null}\n\t\t</LayerCard>\n\t);\n}\n\nexport type StatGridProps = {\n\tchildren: ReactNode;\n\tcolumns?: 2 | 3 | 4;\n\tclassName?: string;\n};\n\nexport function StatGrid({ children, columns = 4, className }: StatGridProps) {\n\tconst gridCols = {\n\t\t2: \"grid-cols-1 sm:grid-cols-2\",\n\t\t3: \"grid-cols-1 sm:grid-cols-2 lg:grid-cols-3\",\n\t\t4: \"grid-cols-1 sm:grid-cols-2 lg:grid-cols-4\",\n\t};\n\treturn (\n\t\t<div className={cn(\"grid gap-basalt-layout\", gridCols[columns], className)}>{children}</div>\n\t);\n}\n"],"mappings":";;;;;AAmEA,SAASA,EAAe,GAAsC;CAC7D,OAAOC,KAA+B,QAAQA,MAAS;AACxD;AAEA,SAAgBC,EAAS,EACxB,UACA,UACA,UACA,aACA,MAAMC,GACN,eAAY,gCACZ,UACA,WACA,WACA,iBACA,aACA,cACA,gBACiB;CACjB,IAAMC,IAAUC,KAASC,GACnBC,IAAYP,EAAeQ,CAAM,GACjCC,IAAkBT,EAAeU,CAAY,GAC7CC,IAAYX,EAAeY,CAAM,GACjCC,IAAcb,EAAec,CAAQ,GACrCC,IAAgBJ,KAAaJ,KAAaE,KAAmBI,GAE7DG,IAAU,OAAOC,KAAU,WAAWA,EAAM,eAAe,IAAIA,GAC/DC,IAAkBC,KAASA,EAAM,QAAQ,GACzCC,IAAkBD,KAASA,EAAM,QAAQ,GAIzCE,IACLC,KACA;EACClB;EACCG,IAAsB,OAAVS;EACbO;EACA,CAACd,KAAmBU,IACjB,GAAGD,IAAkB,MAAM,KAAKC,EAAM,MAAM,IAAIA,EAAM,SAAS,OAC/D;CACJ,CAAC,CACC,OAAO,OAAO,CAAC,CACf,KAAK,GAAG,CAAC,CACT,KAAK;CAER,OACC,kBAACK,GAAD;EACC,aAAU;EACV,WAAWC,EACV,sFACAC,CACD;EACA,MAAMX,IAAgB,UAAU;EAChC,cAAYM;EAPb,UAAA;GASEjB,KAAWD,KAAQQ,IACnB,kBAAC,OAAD;IACC,aAAU;IACV,WAAU;IAFX,UAAA,CAIC,kBAAC,OAAD;KAAK,WAAU;KAAf,UAAA,CACER,IACA,kBAACA,GAAD;MACC,eAAY;MACZ,WAAWsB,EAAG,mDAAmDE,CAAS;MAC1E,aAAa;KACb,CAAA,IACE,MACJ,kBAAC,KAAD;MAAG,WAAU;MACX,UAAAvB;KACC,CAAA,CACC;IACJ,CAAA,GAAAO,IAAY,kBAAC,OAAD;KAAK,WAAU;KAA8B,UAAAC;IAAY,CAAA,IAAI,IACtE;GACF,CAAA,IAAA;GACJ,kBAAC,OAAD;IAAK,aAAU;IAAmB,WAAU;IAA5C,UAAA;KACEL,IACA,kBAAC,OAAD,EAAA,UAAMC,EAAY,CAAA,IAElB,kBAAC,KAAD;MACC,aAAU;MACV,WAAU;MAET,UAAAQ;KACC,CAAA;KAEHO,IACA,kBAAC,KAAD;MAAG,WAAU;MAA+C,UAAAA;KAAY,CAAA,IACrE;KACHd,IACA,kBAAC,OAAD;MACC,aAAU;MACV,WAAU;MAET,UAAAC;KACG,CAAA,IACFS,IACH,kBAAC,OAAD;MACC,aAAU;MACV,WAAU;MAFX,UAAA,CAIC,kBAAC,QAAD;OACC,WAAWM,EACV,4BACAP,KAAmB,sCACnBE,KAAmB,2BACnB,CAACF,KAAmB,CAACE,KAAmB,8BACzC;OAEC,UAAA,GAAGF,IAAkB,MAAM,KAAKC,EAAM,MAAM;MACxC,CAAA,GACLA,EAAM,QACN,kBAAC,QAAD;OAAM,WAAU;OAAgC,UAAAA,EAAM;MAAY,CAAA,IAC/D,IACA;KACF,CAAA,IAAA;IACA;;GACJN,IACA,kBAAC,OAAD;IAAK,aAAU;IAAoB,WAAU;IAC3C;GACG,CAAA,IACF;EACM;;AAEb;AAQA,SAAgBe,EAAS,EAAE,aAAU,aAAU,GAAG,gBAA4B;CAM7E,OACC,kBAAC,OAAD;EAAK,WAAWH,EAAG,0BAA0B;GAL7C,GAAG;GACH,GAAG;GACH,GAAG;EAG0C,EAASI,IAAUH,CAAS;EAAI;CAAc,CAAA;AAE7F"}
|
|
@@ -27,13 +27,13 @@ var f = a.forwardRef(({ className: e, size: t = "base", ...n }, a) => /* @__PURE
|
|
|
27
27
|
f.displayName = i.Content.displayName;
|
|
28
28
|
var p = a.forwardRef(({ className: t, ...n }, r) => /* @__PURE__ */ o(i.Title, {
|
|
29
29
|
ref: r,
|
|
30
|
-
className: e("min-w-0 text-basalt-
|
|
30
|
+
className: e("min-w-0 text-basalt-xl font-semibold leading-basalt-tight tracking-tight wrap-anywhere", t),
|
|
31
31
|
...n
|
|
32
32
|
}));
|
|
33
33
|
p.displayName = i.Title.displayName;
|
|
34
34
|
var m = a.forwardRef(({ className: t, ...n }, r) => /* @__PURE__ */ o(i.Description, {
|
|
35
35
|
ref: r,
|
|
36
|
-
className: e("text-basalt-
|
|
36
|
+
className: e("text-basalt-base leading-[var(--basalt-line-relaxed)] text-basalt-muted-foreground", t),
|
|
37
37
|
...n
|
|
38
38
|
}));
|
|
39
39
|
m.displayName = i.Description.displayName;
|
|
@@ -50,12 +50,12 @@ var g = a.forwardRef(({ className: n, ...r }, a) => /* @__PURE__ */ o(i.Action,
|
|
|
50
50
|
}));
|
|
51
51
|
g.displayName = i.Action.displayName;
|
|
52
52
|
var _ = ({ className: t, ...n }) => /* @__PURE__ */ o("div", {
|
|
53
|
-
className: e("flex flex-col space-y-basalt-space-
|
|
53
|
+
className: e("flex flex-col space-y-basalt-space-md text-left", t),
|
|
54
54
|
...n
|
|
55
55
|
});
|
|
56
56
|
_.displayName = "AlertDialogHeader";
|
|
57
57
|
var v = ({ className: t, ...n }) => /* @__PURE__ */ o("div", {
|
|
58
|
-
className: e("mt-basalt-
|
|
58
|
+
className: e("mt-basalt-layout flex flex-col-reverse gap-basalt-space-lg sm:flex-row sm:justify-end", t),
|
|
59
59
|
...n
|
|
60
60
|
});
|
|
61
61
|
v.displayName = "AlertDialogFooter";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"alert-dialog.js","names":["AlertDialogPrimitive","React","AlertDialog","AlertDialogTrigger","AlertDialogPortal","AlertDialogOverlay","props","dialogOverlayClass","className","AlertDialogContent","dialogPanelClass","AlertDialogTitle","cn","AlertDialogDescription","AlertDialogCancel","buttonVariants","AlertDialogAction","AlertDialogHeader","AlertDialogFooter"],"sources":["../../src/components/alert-dialog.tsx"],"sourcesContent":["import * as AlertDialogPrimitive from \"@radix-ui/react-alert-dialog\";\nimport * as React from \"react\";\nimport { cn } from \"../utils/cn\";\nimport { buttonVariants } from \"./button\";\nimport { type DialogSize, dialogOverlayClass, dialogPanelClass } from \"./dialog\";\n\ntype RadixAlertDialogProps = React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Root>;\ntype RadixAlertDialogTriggerProps = React.ComponentPropsWithoutRef<\n\ttypeof AlertDialogPrimitive.Trigger\n>;\ntype RadixAlertDialogPortalProps = React.ComponentPropsWithoutRef<\n\ttypeof AlertDialogPrimitive.Portal\n>;\ntype RadixAlertDialogOverlayProps = React.ComponentPropsWithoutRef<\n\ttypeof AlertDialogPrimitive.Overlay\n>;\ntype RadixAlertDialogContentProps = React.ComponentPropsWithoutRef<\n\ttypeof AlertDialogPrimitive.Content\n>;\ntype RadixAlertDialogTitleProps = React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Title>;\ntype RadixAlertDialogDescriptionProps = React.ComponentPropsWithoutRef<\n\ttypeof AlertDialogPrimitive.Description\n>;\ntype RadixAlertDialogActionProps = React.ComponentPropsWithoutRef<\n\ttypeof AlertDialogPrimitive.Action\n>;\ntype RadixAlertDialogCancelProps = React.ComponentPropsWithoutRef<\n\ttypeof AlertDialogPrimitive.Cancel\n>;\n\nexport interface AlertDialogProps\n\textends Omit<RadixAlertDialogProps, \"open\" | \"defaultOpen\" | \"onOpenChange\"> {\n\t/**\n\t * Controlled open state of the alert dialog.\n\t */\n\topen?: RadixAlertDialogProps[\"open\"];\n\t/**\n\t * Uncontrolled default open state of the alert dialog on initial render.\n\t * @default false\n\t */\n\tdefaultOpen?: RadixAlertDialogProps[\"defaultOpen\"];\n\t/**\n\t * Callback invoked when the open state changes.\n\t */\n\tonOpenChange?: RadixAlertDialogProps[\"onOpenChange\"];\n}\n\nexport const AlertDialog: React.FC<AlertDialogProps> = AlertDialogPrimitive.Root;\n\nexport interface AlertDialogTriggerProps extends Omit<RadixAlertDialogTriggerProps, \"asChild\"> {\n\t/**\n\t * Change the default rendered button element to the child element, merging props and behavior.\n\t * Trigger forwards ref to HTMLButtonElement and inherits native button attributes.\n\t * @default false\n\t */\n\tasChild?: RadixAlertDialogTriggerProps[\"asChild\"];\n}\n\nexport const AlertDialogTrigger = AlertDialogPrimitive.Trigger;\n\nexport interface AlertDialogPortalProps\n\textends Omit<RadixAlertDialogPortalProps, \"container\" | \"forceMount\"> {\n\t/**\n\t * Target container element where the alert dialog overlay and content portal should mount.\n\t */\n\tcontainer?: RadixAlertDialogPortalProps[\"container\"];\n\t/**\n\t * Used to force mounting when controlling transition animations externally.\n\t */\n\tforceMount?: RadixAlertDialogPortalProps[\"forceMount\"];\n}\n\nexport const AlertDialogPortal = AlertDialogPrimitive.Portal;\n\nexport interface AlertDialogOverlayProps\n\textends Omit<RadixAlertDialogOverlayProps, \"asChild\" | \"forceMount\"> {\n\t/**\n\t * Change the default rendered div element to the child element, merging props and behavior.\n\t * Overlay forwards ref to HTMLDivElement and inherits native div attributes.\n\t * @default false\n\t */\n\tasChild?: RadixAlertDialogOverlayProps[\"asChild\"];\n\t/**\n\t * Used to force mounting when controlling transition animations externally.\n\t */\n\tforceMount?: RadixAlertDialogOverlayProps[\"forceMount\"];\n}\n\nexport const AlertDialogOverlay = React.forwardRef<\n\tReact.ElementRef<typeof AlertDialogPrimitive.Overlay>,\n\tAlertDialogOverlayProps\n>(({ className, ...props }, ref) => (\n\t<AlertDialogPrimitive.Overlay ref={ref} className={dialogOverlayClass(className)} {...props} />\n));\nAlertDialogOverlay.displayName = AlertDialogPrimitive.Overlay.displayName;\n\nexport interface AlertDialogContentProps\n\textends Omit<\n\t\tRadixAlertDialogContentProps,\n\t\t| \"size\"\n\t\t| \"asChild\"\n\t\t| \"forceMount\"\n\t\t| \"trapFocus\"\n\t\t| \"onOpenAutoFocus\"\n\t\t| \"onCloseAutoFocus\"\n\t\t| \"onEscapeKeyDown\"\n\t> {\n\t/**\n\t * Fixed desktop width preset, shared with Dialog. Content scrolls vertically when overflowing available height.\n\t * @default \"base\"\n\t */\n\tsize?: DialogSize;\n\t/**\n\t * Change the default rendered div element to the child element, merging props and behavior.\n\t * Content forwards ref to HTMLDivElement and inherits native div attributes.\n\t * @default false\n\t */\n\tasChild?: RadixAlertDialogContentProps[\"asChild\"];\n\t/**\n\t * Force mounting content in DOM for external animation controls.\n\t * When forceMount is true, the content remains mounted even when closed.\n\t * The caller is responsible for visibility transitions and unmounting after animation completes.\n\t * Note: Keeping modal content forceMounted may isolate background interactions until unmounted.\n\t */\n\tforceMount?: RadixAlertDialogContentProps[\"forceMount\"];\n\t/**\n\t * Event handler called when auto-focusing on open. Defaults to focusing the cancel button. Can be prevented with event.preventDefault().\n\t */\n\tonOpenAutoFocus?: RadixAlertDialogContentProps[\"onOpenAutoFocus\"];\n\t/**\n\t * Event handler called when restoring focus on close. Can be prevented with event.preventDefault().\n\t */\n\tonCloseAutoFocus?: RadixAlertDialogContentProps[\"onCloseAutoFocus\"];\n\t/**\n\t * Event handler called when the Escape key is pressed. Can be prevented with event.preventDefault().\n\t */\n\tonEscapeKeyDown?: RadixAlertDialogContentProps[\"onEscapeKeyDown\"];\n}\n\nexport const AlertDialogContent = React.forwardRef<\n\tReact.ElementRef<typeof AlertDialogPrimitive.Content>,\n\tAlertDialogContentProps\n>(({ className, size = \"base\", ...props }, ref) => (\n\t<AlertDialogPortal forceMount={props.forceMount}>\n\t\t<AlertDialogOverlay forceMount={props.forceMount} />\n\t\t<AlertDialogPrimitive.Content\n\t\t\tref={ref}\n\t\t\tdata-basalt-surface-root=\"\"\n\t\t\tclassName={dialogPanelClass({ size, className })}\n\t\t\t{...props}\n\t\t/>\n\t</AlertDialogPortal>\n));\nAlertDialogContent.displayName = AlertDialogPrimitive.Content.displayName;\n\nexport interface AlertDialogTitleProps extends Omit<RadixAlertDialogTitleProps, \"asChild\"> {\n\t/**\n\t * Change the default rendered heading element to the child element, merging props and behavior.\n\t * Title forwards ref to HTMLHeadingElement and inherits native h2 attributes.\n\t * @default false\n\t */\n\tasChild?: RadixAlertDialogTitleProps[\"asChild\"];\n}\n\nexport const AlertDialogTitle = React.forwardRef<\n\tReact.ElementRef<typeof AlertDialogPrimitive.Title>,\n\tAlertDialogTitleProps\n>(({ className, ...props }, ref) => (\n\t<AlertDialogPrimitive.Title\n\t\tref={ref}\n\t\tclassName={cn(\"min-w-0 text-basalt-3xl font-semibold tracking-tight wrap-anywhere\", className)}\n\t\t{...props}\n\t/>\n));\nAlertDialogTitle.displayName = AlertDialogPrimitive.Title.displayName;\n\nexport interface AlertDialogDescriptionProps\n\textends Omit<RadixAlertDialogDescriptionProps, \"asChild\"> {\n\t/**\n\t * Change the default rendered paragraph element to the child element, merging props and behavior.\n\t * Description forwards ref to HTMLParagraphElement and inherits native p attributes.\n\t * @default false\n\t */\n\tasChild?: RadixAlertDialogDescriptionProps[\"asChild\"];\n}\n\nexport const AlertDialogDescription = React.forwardRef<\n\tReact.ElementRef<typeof AlertDialogPrimitive.Description>,\n\tAlertDialogDescriptionProps\n>(({ className, ...props }, ref) => (\n\t<AlertDialogPrimitive.Description\n\t\tref={ref}\n\t\tclassName={cn(\"text-basalt-lg text-basalt-muted-foreground\", className)}\n\t\t{...props}\n\t/>\n));\nAlertDialogDescription.displayName = AlertDialogPrimitive.Description.displayName;\n\nexport interface AlertDialogCancelProps extends Omit<RadixAlertDialogCancelProps, \"asChild\"> {\n\t/**\n\t * Change the default rendered button element to the child element, merging props and behavior.\n\t * Cancel forwards ref to HTMLButtonElement and inherits native button attributes.\n\t * @default false\n\t */\n\tasChild?: RadixAlertDialogCancelProps[\"asChild\"];\n}\n\nexport const AlertDialogCancel = React.forwardRef<\n\tReact.ElementRef<typeof AlertDialogPrimitive.Cancel>,\n\tAlertDialogCancelProps\n>(({ className, ...props }, ref) => (\n\t<AlertDialogPrimitive.Cancel\n\t\tref={ref}\n\t\tclassName={cn(buttonVariants({ variant: \"outline\" }), className)}\n\t\t{...props}\n\t/>\n));\nAlertDialogCancel.displayName = AlertDialogPrimitive.Cancel.displayName;\n\nexport interface AlertDialogActionProps extends Omit<RadixAlertDialogActionProps, \"asChild\"> {\n\t/**\n\t * Change the default rendered button element to the child element, merging props and behavior.\n\t * Action forwards ref to HTMLButtonElement and inherits native button attributes.\n\t * @default false\n\t */\n\tasChild?: RadixAlertDialogActionProps[\"asChild\"];\n}\n\nexport const AlertDialogAction = React.forwardRef<\n\tReact.ElementRef<typeof AlertDialogPrimitive.Action>,\n\tAlertDialogActionProps\n>(({ className, ...props }, ref) => (\n\t<AlertDialogPrimitive.Action\n\t\tref={ref}\n\t\tclassName={cn(buttonVariants({ variant: \"destructive\" }), className)}\n\t\t{...props}\n\t/>\n));\nAlertDialogAction.displayName = AlertDialogPrimitive.Action.displayName;\n\nexport interface AlertDialogHeaderProps extends React.HTMLAttributes<HTMLDivElement> {}\n\nexport const AlertDialogHeader = ({ className, ...props }: AlertDialogHeaderProps) => (\n\t<div className={cn(\"flex flex-col space-y-basalt-space-lg text-left\", className)} {...props} />\n);\nAlertDialogHeader.displayName = \"AlertDialogHeader\";\n\nexport interface AlertDialogFooterProps extends React.HTMLAttributes<HTMLDivElement> {}\n\nexport const AlertDialogFooter = ({ className, ...props }: AlertDialogFooterProps) => (\n\t<div\n\t\tclassName={cn(\n\t\t\t\"mt-basalt-space-lg flex flex-col-reverse gap-basalt-space-lg sm:flex-row sm:justify-end\",\n\t\t\tclassName,\n\t\t)}\n\t\t{...props}\n\t/>\n);\nAlertDialogFooter.displayName = \"AlertDialogFooter\";\n"],"mappings":";;;;;;;;AA+CA,IAAaE,IAA0CF,EAAqB,MAW/DG,IAAqBH,EAAqB,SAc1CI,IAAoBJ,EAAqB,QAgBzCK,IAAqBJ,EAAM,YAGrC,EAAE,cAAW,GAAGK,KAAS,MAC3B,kBAACN,EAAqB,SAAtB;CAAmC;CAAK,WAAWO,EAAmBC,CAAS;CAAG,GAAIF;AAAQ,CAAA,CAC9F;AACD,EAAmB,cAAcN,EAAqB,QAAQ;AA6C9D,IAAaS,IAAqBR,EAAM,YAGrC,EAAE,cAAW,UAAO,QAAQ,GAAGK,KAAS,MAC1C,kBAACF,GAAD;CAAmB,YAAYE,EAAM;CAArC,UAAA,CACC,kBAACD,GAAD,EAAoB,YAAYC,EAAM,WAAa,CAAA,GACnD,kBAACN,EAAqB,SAAtB;EACM;EACL,4BAAyB;EACzB,WAAWU,EAAiB;GAAE;GAAM;EAAU,CAAC;EAC/C,GAAIJ;CACJ,CAAA,CACiB;AACnB,CAAA,CAAA;AACD,EAAmB,cAAcN,EAAqB,QAAQ;AAW9D,IAAaW,IAAmBV,EAAM,YAGnC,EAAE,cAAW,GAAGK,KAAS,MAC3B,kBAACN,EAAqB,OAAtB;CACM;CACL,WAAWY,EAAG,sEAAsEJ,CAAS;CAC7F,GAAIF;AACJ,CAAA,CACD;AACD,EAAiB,cAAcN,EAAqB,MAAM;AAY1D,IAAaa,IAAyBZ,EAAM,YAGzC,EAAE,cAAW,GAAGK,KAAS,MAC3B,kBAACN,EAAqB,aAAtB;CACM;CACL,WAAWY,EAAG,+CAA+CJ,CAAS;CACtE,GAAIF;AACJ,CAAA,CACD;AACD,EAAuB,cAAcN,EAAqB,YAAY;AAWtE,IAAac,IAAoBb,EAAM,YAGpC,EAAE,cAAW,GAAGK,KAAS,MAC3B,kBAACN,EAAqB,QAAtB;CACM;CACL,WAAWY,EAAGG,EAAe,EAAE,SAAS,UAAU,CAAC,GAAGP,CAAS;CAC/D,GAAIF;AACJ,CAAA,CACD;AACD,EAAkB,cAAcN,EAAqB,OAAO;AAW5D,IAAagB,IAAoBf,EAAM,YAGpC,EAAE,cAAW,GAAGK,KAAS,MAC3B,kBAACN,EAAqB,QAAtB;CACM;CACL,WAAWY,EAAGG,EAAe,EAAE,SAAS,cAAc,CAAC,GAAGP,CAAS;CACnE,GAAIF;AACJ,CAAA,CACD;AACD,EAAkB,cAAcN,EAAqB,OAAO;AAI5D,IAAaiB,KAAqB,EAAE,cAAW,GAAGX,QACjD,kBAAC,OAAD;CAAK,WAAWM,EAAG,mDAAmDJ,CAAS;CAAG,GAAIF;AAAQ,CAAA;AAE/F,EAAkB,cAAc;AAIhC,IAAaY,KAAqB,EAAE,cAAW,GAAGZ,QACjD,kBAAC,OAAD;CACC,WAAWM,EACV,2FACAJ,CACD;CACA,GAAIF;AACJ,CAAA;AAEF,EAAkB,cAAc"}
|
|
1
|
+
{"version":3,"file":"alert-dialog.js","names":["AlertDialogPrimitive","React","AlertDialog","AlertDialogTrigger","AlertDialogPortal","AlertDialogOverlay","props","dialogOverlayClass","className","AlertDialogContent","dialogPanelClass","AlertDialogTitle","cn","AlertDialogDescription","AlertDialogCancel","buttonVariants","AlertDialogAction","AlertDialogHeader","AlertDialogFooter"],"sources":["../../src/components/alert-dialog.tsx"],"sourcesContent":["import * as AlertDialogPrimitive from \"@radix-ui/react-alert-dialog\";\nimport * as React from \"react\";\nimport { cn } from \"../utils/cn\";\nimport { buttonVariants } from \"./button\";\nimport { type DialogSize, dialogOverlayClass, dialogPanelClass } from \"./dialog\";\n\ntype RadixAlertDialogProps = React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Root>;\ntype RadixAlertDialogTriggerProps = React.ComponentPropsWithoutRef<\n\ttypeof AlertDialogPrimitive.Trigger\n>;\ntype RadixAlertDialogPortalProps = React.ComponentPropsWithoutRef<\n\ttypeof AlertDialogPrimitive.Portal\n>;\ntype RadixAlertDialogOverlayProps = React.ComponentPropsWithoutRef<\n\ttypeof AlertDialogPrimitive.Overlay\n>;\ntype RadixAlertDialogContentProps = React.ComponentPropsWithoutRef<\n\ttypeof AlertDialogPrimitive.Content\n>;\ntype RadixAlertDialogTitleProps = React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Title>;\ntype RadixAlertDialogDescriptionProps = React.ComponentPropsWithoutRef<\n\ttypeof AlertDialogPrimitive.Description\n>;\ntype RadixAlertDialogActionProps = React.ComponentPropsWithoutRef<\n\ttypeof AlertDialogPrimitive.Action\n>;\ntype RadixAlertDialogCancelProps = React.ComponentPropsWithoutRef<\n\ttypeof AlertDialogPrimitive.Cancel\n>;\n\nexport interface AlertDialogProps\n\textends Omit<RadixAlertDialogProps, \"open\" | \"defaultOpen\" | \"onOpenChange\"> {\n\t/**\n\t * Controlled open state of the alert dialog.\n\t */\n\topen?: RadixAlertDialogProps[\"open\"];\n\t/**\n\t * Uncontrolled default open state of the alert dialog on initial render.\n\t * @default false\n\t */\n\tdefaultOpen?: RadixAlertDialogProps[\"defaultOpen\"];\n\t/**\n\t * Callback invoked when the open state changes.\n\t */\n\tonOpenChange?: RadixAlertDialogProps[\"onOpenChange\"];\n}\n\nexport const AlertDialog: React.FC<AlertDialogProps> = AlertDialogPrimitive.Root;\n\nexport interface AlertDialogTriggerProps extends Omit<RadixAlertDialogTriggerProps, \"asChild\"> {\n\t/**\n\t * Change the default rendered button element to the child element, merging props and behavior.\n\t * Trigger forwards ref to HTMLButtonElement and inherits native button attributes.\n\t * @default false\n\t */\n\tasChild?: RadixAlertDialogTriggerProps[\"asChild\"];\n}\n\nexport const AlertDialogTrigger = AlertDialogPrimitive.Trigger;\n\nexport interface AlertDialogPortalProps\n\textends Omit<RadixAlertDialogPortalProps, \"container\" | \"forceMount\"> {\n\t/**\n\t * Target container element where the alert dialog overlay and content portal should mount.\n\t */\n\tcontainer?: RadixAlertDialogPortalProps[\"container\"];\n\t/**\n\t * Used to force mounting when controlling transition animations externally.\n\t */\n\tforceMount?: RadixAlertDialogPortalProps[\"forceMount\"];\n}\n\nexport const AlertDialogPortal = AlertDialogPrimitive.Portal;\n\nexport interface AlertDialogOverlayProps\n\textends Omit<RadixAlertDialogOverlayProps, \"asChild\" | \"forceMount\"> {\n\t/**\n\t * Change the default rendered div element to the child element, merging props and behavior.\n\t * Overlay forwards ref to HTMLDivElement and inherits native div attributes.\n\t * @default false\n\t */\n\tasChild?: RadixAlertDialogOverlayProps[\"asChild\"];\n\t/**\n\t * Used to force mounting when controlling transition animations externally.\n\t */\n\tforceMount?: RadixAlertDialogOverlayProps[\"forceMount\"];\n}\n\nexport const AlertDialogOverlay = React.forwardRef<\n\tReact.ElementRef<typeof AlertDialogPrimitive.Overlay>,\n\tAlertDialogOverlayProps\n>(({ className, ...props }, ref) => (\n\t<AlertDialogPrimitive.Overlay ref={ref} className={dialogOverlayClass(className)} {...props} />\n));\nAlertDialogOverlay.displayName = AlertDialogPrimitive.Overlay.displayName;\n\nexport interface AlertDialogContentProps\n\textends Omit<\n\t\tRadixAlertDialogContentProps,\n\t\t| \"size\"\n\t\t| \"asChild\"\n\t\t| \"forceMount\"\n\t\t| \"trapFocus\"\n\t\t| \"onOpenAutoFocus\"\n\t\t| \"onCloseAutoFocus\"\n\t\t| \"onEscapeKeyDown\"\n\t> {\n\t/**\n\t * Fixed desktop width preset, shared with Dialog. Content scrolls vertically when overflowing available height.\n\t * @default \"base\"\n\t */\n\tsize?: DialogSize;\n\t/**\n\t * Change the default rendered div element to the child element, merging props and behavior.\n\t * Content forwards ref to HTMLDivElement and inherits native div attributes.\n\t * @default false\n\t */\n\tasChild?: RadixAlertDialogContentProps[\"asChild\"];\n\t/**\n\t * Force mounting content in DOM for external animation controls.\n\t * When forceMount is true, the content remains mounted even when closed.\n\t * The caller is responsible for visibility transitions and unmounting after animation completes.\n\t * Note: Keeping modal content forceMounted may isolate background interactions until unmounted.\n\t */\n\tforceMount?: RadixAlertDialogContentProps[\"forceMount\"];\n\t/**\n\t * Event handler called when auto-focusing on open. Defaults to focusing the cancel button. Can be prevented with event.preventDefault().\n\t */\n\tonOpenAutoFocus?: RadixAlertDialogContentProps[\"onOpenAutoFocus\"];\n\t/**\n\t * Event handler called when restoring focus on close. Can be prevented with event.preventDefault().\n\t */\n\tonCloseAutoFocus?: RadixAlertDialogContentProps[\"onCloseAutoFocus\"];\n\t/**\n\t * Event handler called when the Escape key is pressed. Can be prevented with event.preventDefault().\n\t */\n\tonEscapeKeyDown?: RadixAlertDialogContentProps[\"onEscapeKeyDown\"];\n}\n\nexport const AlertDialogContent = React.forwardRef<\n\tReact.ElementRef<typeof AlertDialogPrimitive.Content>,\n\tAlertDialogContentProps\n>(({ className, size = \"base\", ...props }, ref) => (\n\t<AlertDialogPortal forceMount={props.forceMount}>\n\t\t<AlertDialogOverlay forceMount={props.forceMount} />\n\t\t<AlertDialogPrimitive.Content\n\t\t\tref={ref}\n\t\t\tdata-basalt-surface-root=\"\"\n\t\t\tclassName={dialogPanelClass({ size, className })}\n\t\t\t{...props}\n\t\t/>\n\t</AlertDialogPortal>\n));\nAlertDialogContent.displayName = AlertDialogPrimitive.Content.displayName;\n\nexport interface AlertDialogTitleProps extends Omit<RadixAlertDialogTitleProps, \"asChild\"> {\n\t/**\n\t * Change the default rendered heading element to the child element, merging props and behavior.\n\t * Title forwards ref to HTMLHeadingElement and inherits native h2 attributes.\n\t * @default false\n\t */\n\tasChild?: RadixAlertDialogTitleProps[\"asChild\"];\n}\n\nexport const AlertDialogTitle = React.forwardRef<\n\tReact.ElementRef<typeof AlertDialogPrimitive.Title>,\n\tAlertDialogTitleProps\n>(({ className, ...props }, ref) => (\n\t<AlertDialogPrimitive.Title\n\t\tref={ref}\n\t\tclassName={cn(\n\t\t\t\"min-w-0 text-basalt-xl font-semibold leading-basalt-tight tracking-tight wrap-anywhere\",\n\t\t\tclassName,\n\t\t)}\n\t\t{...props}\n\t/>\n));\nAlertDialogTitle.displayName = AlertDialogPrimitive.Title.displayName;\n\nexport interface AlertDialogDescriptionProps\n\textends Omit<RadixAlertDialogDescriptionProps, \"asChild\"> {\n\t/**\n\t * Change the default rendered paragraph element to the child element, merging props and behavior.\n\t * Description forwards ref to HTMLParagraphElement and inherits native p attributes.\n\t * @default false\n\t */\n\tasChild?: RadixAlertDialogDescriptionProps[\"asChild\"];\n}\n\nexport const AlertDialogDescription = React.forwardRef<\n\tReact.ElementRef<typeof AlertDialogPrimitive.Description>,\n\tAlertDialogDescriptionProps\n>(({ className, ...props }, ref) => (\n\t<AlertDialogPrimitive.Description\n\t\tref={ref}\n\t\tclassName={cn(\n\t\t\t\"text-basalt-base leading-[var(--basalt-line-relaxed)] text-basalt-muted-foreground\",\n\t\t\tclassName,\n\t\t)}\n\t\t{...props}\n\t/>\n));\nAlertDialogDescription.displayName = AlertDialogPrimitive.Description.displayName;\n\nexport interface AlertDialogCancelProps extends Omit<RadixAlertDialogCancelProps, \"asChild\"> {\n\t/**\n\t * Change the default rendered button element to the child element, merging props and behavior.\n\t * Cancel forwards ref to HTMLButtonElement and inherits native button attributes.\n\t * @default false\n\t */\n\tasChild?: RadixAlertDialogCancelProps[\"asChild\"];\n}\n\nexport const AlertDialogCancel = React.forwardRef<\n\tReact.ElementRef<typeof AlertDialogPrimitive.Cancel>,\n\tAlertDialogCancelProps\n>(({ className, ...props }, ref) => (\n\t<AlertDialogPrimitive.Cancel\n\t\tref={ref}\n\t\tclassName={cn(buttonVariants({ variant: \"outline\" }), className)}\n\t\t{...props}\n\t/>\n));\nAlertDialogCancel.displayName = AlertDialogPrimitive.Cancel.displayName;\n\nexport interface AlertDialogActionProps extends Omit<RadixAlertDialogActionProps, \"asChild\"> {\n\t/**\n\t * Change the default rendered button element to the child element, merging props and behavior.\n\t * Action forwards ref to HTMLButtonElement and inherits native button attributes.\n\t * @default false\n\t */\n\tasChild?: RadixAlertDialogActionProps[\"asChild\"];\n}\n\nexport const AlertDialogAction = React.forwardRef<\n\tReact.ElementRef<typeof AlertDialogPrimitive.Action>,\n\tAlertDialogActionProps\n>(({ className, ...props }, ref) => (\n\t<AlertDialogPrimitive.Action\n\t\tref={ref}\n\t\tclassName={cn(buttonVariants({ variant: \"destructive\" }), className)}\n\t\t{...props}\n\t/>\n));\nAlertDialogAction.displayName = AlertDialogPrimitive.Action.displayName;\n\nexport interface AlertDialogHeaderProps extends React.HTMLAttributes<HTMLDivElement> {}\n\nexport const AlertDialogHeader = ({ className, ...props }: AlertDialogHeaderProps) => (\n\t<div className={cn(\"flex flex-col space-y-basalt-space-md text-left\", className)} {...props} />\n);\nAlertDialogHeader.displayName = \"AlertDialogHeader\";\n\nexport interface AlertDialogFooterProps extends React.HTMLAttributes<HTMLDivElement> {}\n\nexport const AlertDialogFooter = ({ className, ...props }: AlertDialogFooterProps) => (\n\t<div\n\t\tclassName={cn(\n\t\t\t\"mt-basalt-layout flex flex-col-reverse gap-basalt-space-lg sm:flex-row sm:justify-end\",\n\t\t\tclassName,\n\t\t)}\n\t\t{...props}\n\t/>\n);\nAlertDialogFooter.displayName = \"AlertDialogFooter\";\n"],"mappings":";;;;;;;;AA+CA,IAAaE,IAA0CF,EAAqB,MAW/DG,IAAqBH,EAAqB,SAc1CI,IAAoBJ,EAAqB,QAgBzCK,IAAqBJ,EAAM,YAGrC,EAAE,cAAW,GAAGK,KAAS,MAC3B,kBAACN,EAAqB,SAAtB;CAAmC;CAAK,WAAWO,EAAmBC,CAAS;CAAG,GAAIF;AAAQ,CAAA,CAC9F;AACD,EAAmB,cAAcN,EAAqB,QAAQ;AA6C9D,IAAaS,IAAqBR,EAAM,YAGrC,EAAE,cAAW,UAAO,QAAQ,GAAGK,KAAS,MAC1C,kBAACF,GAAD;CAAmB,YAAYE,EAAM;CAArC,UAAA,CACC,kBAACD,GAAD,EAAoB,YAAYC,EAAM,WAAa,CAAA,GACnD,kBAACN,EAAqB,SAAtB;EACM;EACL,4BAAyB;EACzB,WAAWU,EAAiB;GAAE;GAAM;EAAU,CAAC;EAC/C,GAAIJ;CACJ,CAAA,CACiB;AACnB,CAAA,CAAA;AACD,EAAmB,cAAcN,EAAqB,QAAQ;AAW9D,IAAaW,IAAmBV,EAAM,YAGnC,EAAE,cAAW,GAAGK,KAAS,MAC3B,kBAACN,EAAqB,OAAtB;CACM;CACL,WAAWY,EACV,0FACAJ,CACD;CACA,GAAIF;AACJ,CAAA,CACD;AACD,EAAiB,cAAcN,EAAqB,MAAM;AAY1D,IAAaa,IAAyBZ,EAAM,YAGzC,EAAE,cAAW,GAAGK,KAAS,MAC3B,kBAACN,EAAqB,aAAtB;CACM;CACL,WAAWY,EACV,sFACAJ,CACD;CACA,GAAIF;AACJ,CAAA,CACD;AACD,EAAuB,cAAcN,EAAqB,YAAY;AAWtE,IAAac,IAAoBb,EAAM,YAGpC,EAAE,cAAW,GAAGK,KAAS,MAC3B,kBAACN,EAAqB,QAAtB;CACM;CACL,WAAWY,EAAGG,EAAe,EAAE,SAAS,UAAU,CAAC,GAAGP,CAAS;CAC/D,GAAIF;AACJ,CAAA,CACD;AACD,EAAkB,cAAcN,EAAqB,OAAO;AAW5D,IAAagB,IAAoBf,EAAM,YAGpC,EAAE,cAAW,GAAGK,KAAS,MAC3B,kBAACN,EAAqB,QAAtB;CACM;CACL,WAAWY,EAAGG,EAAe,EAAE,SAAS,cAAc,CAAC,GAAGP,CAAS;CACnE,GAAIF;AACJ,CAAA,CACD;AACD,EAAkB,cAAcN,EAAqB,OAAO;AAI5D,IAAaiB,KAAqB,EAAE,cAAW,GAAGX,QACjD,kBAAC,OAAD;CAAK,WAAWM,EAAG,mDAAmDJ,CAAS;CAAG,GAAIF;AAAQ,CAAA;AAE/F,EAAkB,cAAc;AAIhC,IAAaY,KAAqB,EAAE,cAAW,GAAGZ,QACjD,kBAAC,OAAD;CACC,WAAWM,EACV,yFACAJ,CACD;CACA,GAAIF;AACJ,CAAA;AAEF,EAAkB,cAAc"}
|
|
@@ -43,7 +43,7 @@ function h({ className: h, onDismiss: g, ..._ }) {
|
|
|
43
43
|
let T = v.status === "submitting";
|
|
44
44
|
return /* @__PURE__ */ m(a, {
|
|
45
45
|
padding: "sm",
|
|
46
|
-
className: e("w-full space-y-basalt-
|
|
46
|
+
className: e("w-full space-y-basalt-card-sm px-basalt-panel-x py-basalt-panel-y leading-[var(--basalt-line-body)]", h),
|
|
47
47
|
children: [
|
|
48
48
|
/* @__PURE__ */ m("div", {
|
|
49
49
|
className: "flex items-center justify-between gap-basalt-content-gap",
|
|
@@ -57,6 +57,7 @@ function h({ className: h, onDismiss: g, ..._ }) {
|
|
|
57
57
|
}), g && /* @__PURE__ */ p(t, {
|
|
58
58
|
variant: "ghost",
|
|
59
59
|
size: "icon",
|
|
60
|
+
className: "-my-basalt-space-sm -mr-basalt-space-sm",
|
|
60
61
|
"aria-label": "Dismiss approval",
|
|
61
62
|
disabled: T,
|
|
62
63
|
onClick: g,
|
|
@@ -77,7 +78,7 @@ function h({ className: h, onDismiss: g, ..._ }) {
|
|
|
77
78
|
"data-hover-selected": v.answer?.selected.includes(e.id),
|
|
78
79
|
"data-disabled": e.disabled || T ? "" : void 0,
|
|
79
80
|
htmlFor: `${y}-${e.id}`,
|
|
80
|
-
className: "flex cursor-pointer items-center gap-basalt-row-gap rounded-basalt-sm px-basalt-row-x py-basalt-row-y text-basalt-
|
|
81
|
+
className: "flex cursor-pointer items-center gap-basalt-row-gap rounded-basalt-sm px-basalt-row-x py-basalt-row-y text-basalt-base leading-basalt-row",
|
|
81
82
|
children: [/* @__PURE__ */ p(s.Item, {
|
|
82
83
|
id: `${y}-${e.id}`,
|
|
83
84
|
value: e.id,
|
|
@@ -93,7 +94,7 @@ function h({ className: h, onDismiss: g, ..._ }) {
|
|
|
93
94
|
"data-hover-selected": v.answer?.selected.includes(e.id),
|
|
94
95
|
"data-disabled": e.disabled || T ? "" : void 0,
|
|
95
96
|
htmlFor: `${y}-${e.id}`,
|
|
96
|
-
className: "flex cursor-pointer items-center gap-basalt-row-gap rounded-basalt-sm px-basalt-row-x py-basalt-row-y text-basalt-
|
|
97
|
+
className: "flex cursor-pointer items-center gap-basalt-row-gap rounded-basalt-sm px-basalt-row-x py-basalt-row-y text-basalt-base leading-basalt-row",
|
|
97
98
|
children: [/* @__PURE__ */ p(n, {
|
|
98
99
|
id: `${y}-${e.id}`,
|
|
99
100
|
checked: v.answer?.selected.includes(e.id) ?? !1,
|
|
@@ -116,7 +117,7 @@ function h({ className: h, onDismiss: g, ..._ }) {
|
|
|
116
117
|
children: v.error
|
|
117
118
|
}),
|
|
118
119
|
/* @__PURE__ */ m("div", {
|
|
119
|
-
className: "flex items-center justify-between gap-basalt-content-gap border-t border-basalt-border pt-basalt-
|
|
120
|
+
className: "flex items-center justify-between gap-basalt-content-gap border-t border-basalt-border pt-basalt-card-sm",
|
|
120
121
|
children: [/* @__PURE__ */ m("div", {
|
|
121
122
|
className: "flex items-center gap-basalt-content-gap",
|
|
122
123
|
children: [/* @__PURE__ */ p(t, {
|
|
@@ -139,12 +140,10 @@ function h({ className: h, onDismiss: g, ..._ }) {
|
|
|
139
140
|
className: "flex gap-basalt-content-gap",
|
|
140
141
|
children: [w.required === !1 && /* @__PURE__ */ p(t, {
|
|
141
142
|
variant: "ghost",
|
|
142
|
-
size: "sm",
|
|
143
143
|
disabled: T,
|
|
144
144
|
onClick: v.skip,
|
|
145
145
|
children: "Skip"
|
|
146
146
|
}), /* @__PURE__ */ p(t, {
|
|
147
|
-
size: "sm",
|
|
148
147
|
loading: T,
|
|
149
148
|
disabled: !v.canContinue || T,
|
|
150
149
|
onClick: v.continue,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"approval-card.js","names":["ApprovalCard","props","vm","useApprovalCardViewModel","id","useId","options","useRef","heading","submitted","highlightRef","useHoverHighlight","useEffect","active","question","LayerCard","busy","cn","className","onDismiss","Button","X","Radio","option","Checkbox","Input","event","ChevronLeft"],"sources":["../../src/components/approval-card.tsx"],"sourcesContent":["import { ChevronLeft, X } from \"lucide-react\";\nimport { useEffect, useId, useRef } from \"react\";\nimport type { ApprovalAnswers, ApprovalQuestion } from \"../models/agent-feedback\";\nimport { cn } from \"../utils/cn\";\nimport { useHoverHighlight } from \"../utils/use-hover-highlight\";\nimport { useApprovalCardViewModel } from \"../viewmodels/use-approval-card\";\nimport { Button } from \"./button\";\nimport { Checkbox } from \"./checkbox\";\nimport { Input } from \"./input\";\nimport { LayerCard } from \"./layer-card\";\nimport { Radio } from \"./radio\";\n\nexport type { ApprovalAnswer, ApprovalAnswers, ApprovalQuestion } from \"../models/agent-feedback\";\nexport interface ApprovalCardProps {\n\t/** Stable question and option IDs. Remount with a new key for a new request. */\n\tquestions: readonly ApprovalQuestion[];\n\t/** Submit answers; a rejected promise keeps answers and shows retry feedback. */\n\tonSubmit: (answers: ApprovalAnswers) => void | Promise<void>;\n\t/** Enable automatic progression after a single selection. Never auto-submits. @default true */\n\tautoAdvance?: boolean;\n\tonDismiss?: () => void;\n\tclassName?: string;\n}\n\nexport function ApprovalCard({ className, onDismiss, ...props }: ApprovalCardProps) {\n\tconst vm = useApprovalCardViewModel(props);\n\tconst id = useId();\n\tconst options = useRef<HTMLDivElement | null>(null);\n\tconst heading = useRef<HTMLHeadingElement | null>(null);\n\tconst submitted = useRef<HTMLParagraphElement | null>(null);\n\tconst highlightRef = useHoverHighlight(options);\n\tuseEffect(() => {\n\t\tif (!vm.focusToken) return;\n\t\tconst active = document.activeElement;\n\t\t// A control the user is still holding stays put; a removed or disabled one hands off to the question.\n\t\tif (\n\t\t\tactive instanceof HTMLElement &&\n\t\t\tactive !== document.body &&\n\t\t\tactive.isConnected &&\n\t\t\t!active.matches(\":disabled, [aria-disabled='true']\")\n\t\t)\n\t\t\treturn;\n\t\tconst answer = options.current?.querySelector<HTMLElement>(\n\t\t\t':is([role=\"radio\"], [role=\"checkbox\"], input):not(:disabled):not([aria-disabled=\"true\"])',\n\t\t);\n\t\t(answer ?? heading.current)?.focus();\n\t}, [vm.focusToken]);\n\tuseEffect(() => {\n\t\tif (vm.status === \"submitted\") submitted.current?.focus();\n\t}, [vm.status]);\n\tconst question = vm.question;\n\tif (!question)\n\t\treturn (\n\t\t\t<LayerCard padding=\"sm\" className={className}>\n\t\t\t\t<p className=\"text-basalt-base text-basalt-muted-foreground\">No approval questions</p>\n\t\t\t</LayerCard>\n\t\t);\n\tif (vm.status === \"submitted\")\n\t\treturn (\n\t\t\t<LayerCard padding=\"sm\" className={className}>\n\t\t\t\t<p ref={submitted} tabIndex={-1} role=\"status\" className=\"text-basalt-base\">\n\t\t\t\t\tAnswers submitted\n\t\t\t\t</p>\n\t\t\t</LayerCard>\n\t\t);\n\tconst busy = vm.status === \"submitting\";\n\treturn (\n\t\t<LayerCard\n\t\t\tpadding=\"sm\"\n\t\t\tclassName={cn(\n\t\t\t\t\"w-full space-y-basalt-content-gap px-basalt-panel-x py-basalt-panel-y leading-[var(--basalt-line-body)]\",\n\t\t\t\tclassName,\n\t\t\t)}\n\t\t>\n\t\t\t<div className=\"flex items-center justify-between gap-basalt-content-gap\">\n\t\t\t\t<h3\n\t\t\t\t\tref={heading}\n\t\t\t\t\tid={id}\n\t\t\t\t\ttabIndex={-1}\n\t\t\t\t\tclassName=\"text-basalt-base font-medium\"\n\t\t\t\t\taria-live=\"polite\"\n\t\t\t\t>\n\t\t\t\t\t{question.label}\n\t\t\t\t</h3>\n\t\t\t\t{onDismiss && (\n\t\t\t\t\t<Button\n\t\t\t\t\t\tvariant=\"ghost\"\n\t\t\t\t\t\tsize=\"icon\"\n\t\t\t\t\t\taria-label=\"Dismiss approval\"\n\t\t\t\t\t\tdisabled={busy}\n\t\t\t\t\t\tonClick={onDismiss}\n\t\t\t\t\t>\n\t\t\t\t\t\t<X />\n\t\t\t\t\t</Button>\n\t\t\t\t)}\n\t\t\t</div>\n\t\t\t<div\n\t\t\t\tkey={question.id}\n\t\t\t\tref={highlightRef}\n\t\t\t\tclassName=\"basalt-agent-reveal basalt-hover-list -mx-basalt-row-x space-y-basalt-content-gap\"\n\t\t\t>\n\t\t\t\t{question.type === \"single\" ? (\n\t\t\t\t\t<Radio.Group\n\t\t\t\t\t\tvalue={vm.answer?.selected[0] ?? \"\"}\n\t\t\t\t\t\tonValueChange={vm.choose}\n\t\t\t\t\t\tdisabled={busy}\n\t\t\t\t\t\taria-labelledby={id}\n\t\t\t\t\t\tclassName=\"gap-basalt-space-xs\"\n\t\t\t\t\t>\n\t\t\t\t\t\t{question.options.map((option) => (\n\t\t\t\t\t\t\t<label\n\t\t\t\t\t\t\t\tkey={option.id}\n\t\t\t\t\t\t\t\tdata-basalt-hover-item=\"\"\n\t\t\t\t\t\t\t\tdata-hover-selected={vm.answer?.selected.includes(option.id)}\n\t\t\t\t\t\t\t\tdata-disabled={option.disabled || busy ? \"\" : undefined}\n\t\t\t\t\t\t\t\thtmlFor={`${id}-${option.id}`}\n\t\t\t\t\t\t\t\tclassName=\"flex cursor-pointer items-center gap-basalt-row-gap rounded-basalt-sm px-basalt-row-x py-basalt-row-y text-basalt-code leading-basalt-row\"\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t<Radio.Item\n\t\t\t\t\t\t\t\t\tid={`${id}-${option.id}`}\n\t\t\t\t\t\t\t\t\tvalue={option.id}\n\t\t\t\t\t\t\t\t\tdisabled={option.disabled}\n\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t{option.label}\n\t\t\t\t\t\t\t</label>\n\t\t\t\t\t\t))}\n\t\t\t\t\t</Radio.Group>\n\t\t\t\t) : (\n\t\t\t\t\t<fieldset disabled={busy} aria-labelledby={id} className=\"space-y-basalt-space-xs\">\n\t\t\t\t\t\t{question.options.map((option) => (\n\t\t\t\t\t\t\t<label\n\t\t\t\t\t\t\t\tkey={option.id}\n\t\t\t\t\t\t\t\tdata-basalt-hover-item=\"\"\n\t\t\t\t\t\t\t\tdata-hover-selected={vm.answer?.selected.includes(option.id)}\n\t\t\t\t\t\t\t\tdata-disabled={option.disabled || busy ? \"\" : undefined}\n\t\t\t\t\t\t\t\thtmlFor={`${id}-${option.id}`}\n\t\t\t\t\t\t\t\tclassName=\"flex cursor-pointer items-center gap-basalt-row-gap rounded-basalt-sm px-basalt-row-x py-basalt-row-y text-basalt-code leading-basalt-row\"\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t<Checkbox\n\t\t\t\t\t\t\t\t\tid={`${id}-${option.id}`}\n\t\t\t\t\t\t\t\t\tchecked={vm.answer?.selected.includes(option.id) ?? false}\n\t\t\t\t\t\t\t\t\tonCheckedChange={() => vm.choose(option.id)}\n\t\t\t\t\t\t\t\t\tdisabled={option.disabled}\n\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t{option.label}\n\t\t\t\t\t\t\t</label>\n\t\t\t\t\t\t))}\n\t\t\t\t\t</fieldset>\n\t\t\t\t)}\n\t\t\t\t{question.allowCustom && (\n\t\t\t\t\t<Input\n\t\t\t\t\t\taria-label=\"Custom answer\"\n\t\t\t\t\t\tplaceholder=\"Something else...\"\n\t\t\t\t\t\tclassName=\"ml-basalt-row-x w-[calc(100%-2*var(--basalt-space-row-x))]\"\n\t\t\t\t\t\tvalue={vm.answer?.custom ?? \"\"}\n\t\t\t\t\t\tdisabled={busy}\n\t\t\t\t\t\tonChange={(event) => vm.setCustom(event.target.value)}\n\t\t\t\t\t/>\n\t\t\t\t)}\n\t\t\t</div>\n\t\t\t{vm.error && (\n\t\t\t\t<p role=\"alert\" className=\"text-basalt-sm text-basalt-danger\">\n\t\t\t\t\t{vm.error}\n\t\t\t\t</p>\n\t\t\t)}\n\t\t\t<div className=\"flex items-center justify-between gap-basalt-content-gap border-t border-basalt-border pt-basalt-space-lg\">\n\t\t\t\t<div className=\"flex items-center gap-basalt-content-gap\">\n\t\t\t\t\t<Button\n\t\t\t\t\t\tvariant=\"ghost\"\n\t\t\t\t\t\tsize=\"icon\"\n\t\t\t\t\t\tclassName=\"-ml-[calc((var(--basalt-size-action)-var(--basalt-size-icon))/2)]\"\n\t\t\t\t\t\taria-label=\"Previous question\"\n\t\t\t\t\t\tdisabled={busy || vm.position === 0}\n\t\t\t\t\t\tonClick={() => vm.move(vm.position - 1)}\n\t\t\t\t\t>\n\t\t\t\t\t\t<ChevronLeft />\n\t\t\t\t\t</Button>\n\t\t\t\t\t<span className=\"text-basalt-sm tabular-nums text-basalt-muted-foreground\">\n\t\t\t\t\t\t{vm.position + 1} / {props.questions.length}\n\t\t\t\t\t</span>\n\t\t\t\t</div>\n\t\t\t\t<div className=\"flex gap-basalt-content-gap\">\n\t\t\t\t\t{question.required === false && (\n\t\t\t\t\t\t<Button variant=\"ghost\" size=\"sm\" disabled={busy} onClick={vm.skip}>\n\t\t\t\t\t\t\tSkip\n\t\t\t\t\t\t</Button>\n\t\t\t\t\t)}\n\t\t\t\t\t<Button size=\"sm\" loading={busy} disabled={!vm.canContinue || busy} onClick={vm.continue}>\n\t\t\t\t\t\t{vm.last ? \"Submit\" : \"Continue\"}\n\t\t\t\t\t</Button>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t</LayerCard>\n\t);\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"approval-card.js","names":["ApprovalCard","props","vm","useApprovalCardViewModel","id","useId","options","useRef","heading","submitted","highlightRef","useHoverHighlight","useEffect","active","question","LayerCard","busy","cn","className","onDismiss","Button","X","Radio","option","Checkbox","Input","event","ChevronLeft"],"sources":["../../src/components/approval-card.tsx"],"sourcesContent":["import { ChevronLeft, X } from \"lucide-react\";\nimport { useEffect, useId, useRef } from \"react\";\nimport type { ApprovalAnswers, ApprovalQuestion } from \"../models/agent-feedback\";\nimport { cn } from \"../utils/cn\";\nimport { useHoverHighlight } from \"../utils/use-hover-highlight\";\nimport { useApprovalCardViewModel } from \"../viewmodels/use-approval-card\";\nimport { Button } from \"./button\";\nimport { Checkbox } from \"./checkbox\";\nimport { Input } from \"./input\";\nimport { LayerCard } from \"./layer-card\";\nimport { Radio } from \"./radio\";\n\nexport type { ApprovalAnswer, ApprovalAnswers, ApprovalQuestion } from \"../models/agent-feedback\";\nexport interface ApprovalCardProps {\n\t/** Stable question and option IDs. Remount with a new key for a new request. */\n\tquestions: readonly ApprovalQuestion[];\n\t/** Submit answers; a rejected promise keeps answers and shows retry feedback. */\n\tonSubmit: (answers: ApprovalAnswers) => void | Promise<void>;\n\t/** Enable automatic progression after a single selection. Never auto-submits. @default true */\n\tautoAdvance?: boolean;\n\tonDismiss?: () => void;\n\tclassName?: string;\n}\n\nexport function ApprovalCard({ className, onDismiss, ...props }: ApprovalCardProps) {\n\tconst vm = useApprovalCardViewModel(props);\n\tconst id = useId();\n\tconst options = useRef<HTMLDivElement | null>(null);\n\tconst heading = useRef<HTMLHeadingElement | null>(null);\n\tconst submitted = useRef<HTMLParagraphElement | null>(null);\n\tconst highlightRef = useHoverHighlight(options);\n\tuseEffect(() => {\n\t\tif (!vm.focusToken) return;\n\t\tconst active = document.activeElement;\n\t\t// A control the user is still holding stays put; a removed or disabled one hands off to the question.\n\t\tif (\n\t\t\tactive instanceof HTMLElement &&\n\t\t\tactive !== document.body &&\n\t\t\tactive.isConnected &&\n\t\t\t!active.matches(\":disabled, [aria-disabled='true']\")\n\t\t)\n\t\t\treturn;\n\t\tconst answer = options.current?.querySelector<HTMLElement>(\n\t\t\t':is([role=\"radio\"], [role=\"checkbox\"], input):not(:disabled):not([aria-disabled=\"true\"])',\n\t\t);\n\t\t(answer ?? heading.current)?.focus();\n\t}, [vm.focusToken]);\n\tuseEffect(() => {\n\t\tif (vm.status === \"submitted\") submitted.current?.focus();\n\t}, [vm.status]);\n\tconst question = vm.question;\n\tif (!question)\n\t\treturn (\n\t\t\t<LayerCard padding=\"sm\" className={className}>\n\t\t\t\t<p className=\"text-basalt-base text-basalt-muted-foreground\">No approval questions</p>\n\t\t\t</LayerCard>\n\t\t);\n\tif (vm.status === \"submitted\")\n\t\treturn (\n\t\t\t<LayerCard padding=\"sm\" className={className}>\n\t\t\t\t<p ref={submitted} tabIndex={-1} role=\"status\" className=\"text-basalt-base\">\n\t\t\t\t\tAnswers submitted\n\t\t\t\t</p>\n\t\t\t</LayerCard>\n\t\t);\n\tconst busy = vm.status === \"submitting\";\n\treturn (\n\t\t<LayerCard\n\t\t\tpadding=\"sm\"\n\t\t\tclassName={cn(\n\t\t\t\t\"w-full space-y-basalt-card-sm px-basalt-panel-x py-basalt-panel-y leading-[var(--basalt-line-body)]\",\n\t\t\t\tclassName,\n\t\t\t)}\n\t\t>\n\t\t\t<div className=\"flex items-center justify-between gap-basalt-content-gap\">\n\t\t\t\t<h3\n\t\t\t\t\tref={heading}\n\t\t\t\t\tid={id}\n\t\t\t\t\ttabIndex={-1}\n\t\t\t\t\tclassName=\"text-basalt-base font-medium\"\n\t\t\t\t\taria-live=\"polite\"\n\t\t\t\t>\n\t\t\t\t\t{question.label}\n\t\t\t\t</h3>\n\t\t\t\t{onDismiss && (\n\t\t\t\t\t<Button\n\t\t\t\t\t\tvariant=\"ghost\"\n\t\t\t\t\t\tsize=\"icon\"\n\t\t\t\t\t\tclassName=\"-my-basalt-space-sm -mr-basalt-space-sm\"\n\t\t\t\t\t\taria-label=\"Dismiss approval\"\n\t\t\t\t\t\tdisabled={busy}\n\t\t\t\t\t\tonClick={onDismiss}\n\t\t\t\t\t>\n\t\t\t\t\t\t<X />\n\t\t\t\t\t</Button>\n\t\t\t\t)}\n\t\t\t</div>\n\t\t\t<div\n\t\t\t\tkey={question.id}\n\t\t\t\tref={highlightRef}\n\t\t\t\tclassName=\"basalt-agent-reveal basalt-hover-list -mx-basalt-row-x space-y-basalt-content-gap\"\n\t\t\t>\n\t\t\t\t{question.type === \"single\" ? (\n\t\t\t\t\t<Radio.Group\n\t\t\t\t\t\tvalue={vm.answer?.selected[0] ?? \"\"}\n\t\t\t\t\t\tonValueChange={vm.choose}\n\t\t\t\t\t\tdisabled={busy}\n\t\t\t\t\t\taria-labelledby={id}\n\t\t\t\t\t\tclassName=\"gap-basalt-space-xs\"\n\t\t\t\t\t>\n\t\t\t\t\t\t{question.options.map((option) => (\n\t\t\t\t\t\t\t<label\n\t\t\t\t\t\t\t\tkey={option.id}\n\t\t\t\t\t\t\t\tdata-basalt-hover-item=\"\"\n\t\t\t\t\t\t\t\tdata-hover-selected={vm.answer?.selected.includes(option.id)}\n\t\t\t\t\t\t\t\tdata-disabled={option.disabled || busy ? \"\" : undefined}\n\t\t\t\t\t\t\t\thtmlFor={`${id}-${option.id}`}\n\t\t\t\t\t\t\t\tclassName=\"flex cursor-pointer items-center gap-basalt-row-gap rounded-basalt-sm px-basalt-row-x py-basalt-row-y text-basalt-base leading-basalt-row\"\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t<Radio.Item\n\t\t\t\t\t\t\t\t\tid={`${id}-${option.id}`}\n\t\t\t\t\t\t\t\t\tvalue={option.id}\n\t\t\t\t\t\t\t\t\tdisabled={option.disabled}\n\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t{option.label}\n\t\t\t\t\t\t\t</label>\n\t\t\t\t\t\t))}\n\t\t\t\t\t</Radio.Group>\n\t\t\t\t) : (\n\t\t\t\t\t<fieldset disabled={busy} aria-labelledby={id} className=\"space-y-basalt-space-xs\">\n\t\t\t\t\t\t{question.options.map((option) => (\n\t\t\t\t\t\t\t<label\n\t\t\t\t\t\t\t\tkey={option.id}\n\t\t\t\t\t\t\t\tdata-basalt-hover-item=\"\"\n\t\t\t\t\t\t\t\tdata-hover-selected={vm.answer?.selected.includes(option.id)}\n\t\t\t\t\t\t\t\tdata-disabled={option.disabled || busy ? \"\" : undefined}\n\t\t\t\t\t\t\t\thtmlFor={`${id}-${option.id}`}\n\t\t\t\t\t\t\t\tclassName=\"flex cursor-pointer items-center gap-basalt-row-gap rounded-basalt-sm px-basalt-row-x py-basalt-row-y text-basalt-base leading-basalt-row\"\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t<Checkbox\n\t\t\t\t\t\t\t\t\tid={`${id}-${option.id}`}\n\t\t\t\t\t\t\t\t\tchecked={vm.answer?.selected.includes(option.id) ?? false}\n\t\t\t\t\t\t\t\t\tonCheckedChange={() => vm.choose(option.id)}\n\t\t\t\t\t\t\t\t\tdisabled={option.disabled}\n\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t{option.label}\n\t\t\t\t\t\t\t</label>\n\t\t\t\t\t\t))}\n\t\t\t\t\t</fieldset>\n\t\t\t\t)}\n\t\t\t\t{question.allowCustom && (\n\t\t\t\t\t<Input\n\t\t\t\t\t\taria-label=\"Custom answer\"\n\t\t\t\t\t\tplaceholder=\"Something else...\"\n\t\t\t\t\t\tclassName=\"ml-basalt-row-x w-[calc(100%-2*var(--basalt-space-row-x))]\"\n\t\t\t\t\t\tvalue={vm.answer?.custom ?? \"\"}\n\t\t\t\t\t\tdisabled={busy}\n\t\t\t\t\t\tonChange={(event) => vm.setCustom(event.target.value)}\n\t\t\t\t\t/>\n\t\t\t\t)}\n\t\t\t</div>\n\t\t\t{vm.error && (\n\t\t\t\t<p role=\"alert\" className=\"text-basalt-sm text-basalt-danger\">\n\t\t\t\t\t{vm.error}\n\t\t\t\t</p>\n\t\t\t)}\n\t\t\t<div className=\"flex items-center justify-between gap-basalt-content-gap border-t border-basalt-border pt-basalt-card-sm\">\n\t\t\t\t<div className=\"flex items-center gap-basalt-content-gap\">\n\t\t\t\t\t<Button\n\t\t\t\t\t\tvariant=\"ghost\"\n\t\t\t\t\t\tsize=\"icon\"\n\t\t\t\t\t\tclassName=\"-ml-[calc((var(--basalt-size-action)-var(--basalt-size-icon))/2)]\"\n\t\t\t\t\t\taria-label=\"Previous question\"\n\t\t\t\t\t\tdisabled={busy || vm.position === 0}\n\t\t\t\t\t\tonClick={() => vm.move(vm.position - 1)}\n\t\t\t\t\t>\n\t\t\t\t\t\t<ChevronLeft />\n\t\t\t\t\t</Button>\n\t\t\t\t\t<span className=\"text-basalt-sm tabular-nums text-basalt-muted-foreground\">\n\t\t\t\t\t\t{vm.position + 1} / {props.questions.length}\n\t\t\t\t\t</span>\n\t\t\t\t</div>\n\t\t\t\t<div className=\"flex gap-basalt-content-gap\">\n\t\t\t\t\t{question.required === false && (\n\t\t\t\t\t\t<Button variant=\"ghost\" disabled={busy} onClick={vm.skip}>\n\t\t\t\t\t\t\tSkip\n\t\t\t\t\t\t</Button>\n\t\t\t\t\t)}\n\t\t\t\t\t<Button loading={busy} disabled={!vm.canContinue || busy} onClick={vm.continue}>\n\t\t\t\t\t\t{vm.last ? \"Submit\" : \"Continue\"}\n\t\t\t\t\t</Button>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t</LayerCard>\n\t);\n}\n"],"mappings":"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"}
|
|
@@ -38,7 +38,7 @@ function d({ name: r, colorKey: i, initials: o, size: c = "default", className:
|
|
|
38
38
|
"aria-label": r,
|
|
39
39
|
className: e(c === "sm" && "size-basalt-6", d),
|
|
40
40
|
children: /* @__PURE__ */ a(l, {
|
|
41
|
-
className: e("font-medium
|
|
41
|
+
className: e("font-medium text-basalt-sm", u[t(i ?? r)]),
|
|
42
42
|
children: o?.trim() ? Array.from(o.trim().toLocaleUpperCase("en-US")).slice(0, 2).join("") : n(r)
|
|
43
43
|
})
|
|
44
44
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"avatar.js","names":["React","AvatarPrimitive","Avatar","props","cn","className","AvatarImage","AvatarFallback","AVATAR_TONES","TAG_COLORS","AvatarInitials","name","size","avatarColorIndex","colorKey","initials","avatarInitials"],"sources":["../../src/components/avatar.tsx"],"sourcesContent":["import * as AvatarPrimitive from \"@radix-ui/react-avatar\";\nimport * as React from \"react\";\nimport { avatarColorIndex, avatarInitials } from \"../models/avatar\";\nimport { cn } from \"../utils/cn\";\nimport { TAG_COLORS } from \"./tag-badge\";\n\nexport type AvatarProps = Omit<\n\tReact.ComponentPropsWithoutRef<typeof AvatarPrimitive.Root>,\n\t\"asChild\"\n> & {\n\t/**\n\t * Change the default rendered element for the one passed as a child, merging their props and behavior.\n\t * @default false\n\t */\n\tasChild?: boolean;\n};\n\nexport const Avatar = React.forwardRef<React.ElementRef<typeof AvatarPrimitive.Root>, AvatarProps>(\n\t({ className, ...props }, ref) => (\n\t\t<AvatarPrimitive.Root\n\t\t\tref={ref}\n\t\t\tclassName={cn(\n\t\t\t\t\"relative flex h-basalt-9 w-basalt-9 shrink-0 overflow-hidden rounded-basalt-full\",\n\t\t\t\tclassName,\n\t\t\t)}\n\t\t\t{...props}\n\t\t/>\n\t),\n);\nAvatar.displayName = AvatarPrimitive.Root.displayName;\n\nexport type AvatarImageProps = Omit<\n\tReact.ComponentPropsWithoutRef<typeof AvatarPrimitive.Image>,\n\t\"asChild\" | \"onLoadingStatusChange\" | \"src\" | \"alt\"\n> & {\n\t/**\n\t * Change the default rendered element for the one passed as a child, merging their props and behavior.\n\t * @default false\n\t */\n\tasChild?: boolean;\n\t/**\n\t * Image source URL for the avatar image.\n\t */\n\tsrc?: React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Image>[\"src\"];\n\t/**\n\t * Text description of the image for accessibility and screen readers.\n\t */\n\talt?: React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Image>[\"alt\"];\n\t/**\n\t * A callback providing the current loading status of the image ('idle' | 'loading' | 'loaded' | 'error').\n\t */\n\tonLoadingStatusChange?: React.ComponentPropsWithoutRef<\n\t\ttypeof AvatarPrimitive.Image\n\t>[\"onLoadingStatusChange\"];\n};\n\nexport const AvatarImage = React.forwardRef<\n\tReact.ElementRef<typeof AvatarPrimitive.Image>,\n\tAvatarImageProps\n>(({ className, ...props }, ref) => (\n\t<AvatarPrimitive.Image\n\t\tref={ref}\n\t\tclassName={cn(\"aspect-square h-full w-full\", className)}\n\t\t{...props}\n\t/>\n));\nAvatarImage.displayName = AvatarPrimitive.Image.displayName;\n\nexport type AvatarFallbackProps = Omit<\n\tReact.ComponentPropsWithoutRef<typeof AvatarPrimitive.Fallback>,\n\t\"asChild\" | \"delayMs\"\n> & {\n\t/**\n\t * Change the default rendered element for the one passed as a child, merging their props and behavior.\n\t * @default false\n\t */\n\tasChild?: boolean;\n\t/**\n\t * Useful for delaying rendering so it only appears for those with slower connections.\n\t * Unset by default, rendering immediately without delay.\n\t */\n\tdelayMs?: number;\n};\n\nexport const AvatarFallback = React.forwardRef<\n\tReact.ElementRef<typeof AvatarPrimitive.Fallback>,\n\tAvatarFallbackProps\n>(({ className, ...props }, ref) => (\n\t<AvatarPrimitive.Fallback\n\t\tref={ref}\n\t\tclassName={cn(\n\t\t\t\"flex h-full w-full items-center justify-center rounded-basalt-full bg-basalt-muted text-basalt-sm\",\n\t\t\tclassName,\n\t\t)}\n\t\t{...props}\n\t/>\n));\nAvatarFallback.displayName = AvatarPrimitive.Fallback.displayName;\n\nconst AVATAR_TONES = [\n\tTAG_COLORS.blue.className,\n\tTAG_COLORS.amber.className,\n\tTAG_COLORS.rose.className,\n\tTAG_COLORS.teal.className,\n\tTAG_COLORS.violet.className,\n\tTAG_COLORS.success.className,\n] as const;\n\nexport interface AvatarInitialsProps {\n\t/** Person or organization name; initials are derived from its first two words. */\n\tname: string;\n\t/** Stable color identity, independent of display name changes. */\n\tcolorKey?: string;\n\t/** Two-letter override for application-specific abbreviations. */\n\tinitials?: string;\n\t/** Avatar diameter. @default \"default\" */\n\tsize?: \"sm\" | \"default\";\n\tclassName?: string;\n}\n\nexport function AvatarInitials({\n\tname,\n\tcolorKey,\n\tinitials,\n\tsize = \"default\",\n\tclassName,\n}: AvatarInitialsProps) {\n\treturn (\n\t\t<Avatar\n\t\t\trole=\"img\"\n\t\t\taria-label={name}\n\t\t\tclassName={cn(size === \"sm\" && \"size-basalt-6\", className)}\n\t\t>\n\t\t\t<AvatarFallback\n\t\t\t\tclassName={cn(\n\t\t\t\t\t\"font-medium
|
|
1
|
+
{"version":3,"file":"avatar.js","names":["React","AvatarPrimitive","Avatar","props","cn","className","AvatarImage","AvatarFallback","AVATAR_TONES","TAG_COLORS","AvatarInitials","name","size","avatarColorIndex","colorKey","initials","avatarInitials"],"sources":["../../src/components/avatar.tsx"],"sourcesContent":["import * as AvatarPrimitive from \"@radix-ui/react-avatar\";\nimport * as React from \"react\";\nimport { avatarColorIndex, avatarInitials } from \"../models/avatar\";\nimport { cn } from \"../utils/cn\";\nimport { TAG_COLORS } from \"./tag-badge\";\n\nexport type AvatarProps = Omit<\n\tReact.ComponentPropsWithoutRef<typeof AvatarPrimitive.Root>,\n\t\"asChild\"\n> & {\n\t/**\n\t * Change the default rendered element for the one passed as a child, merging their props and behavior.\n\t * @default false\n\t */\n\tasChild?: boolean;\n};\n\nexport const Avatar = React.forwardRef<React.ElementRef<typeof AvatarPrimitive.Root>, AvatarProps>(\n\t({ className, ...props }, ref) => (\n\t\t<AvatarPrimitive.Root\n\t\t\tref={ref}\n\t\t\tclassName={cn(\n\t\t\t\t\"relative flex h-basalt-9 w-basalt-9 shrink-0 overflow-hidden rounded-basalt-full\",\n\t\t\t\tclassName,\n\t\t\t)}\n\t\t\t{...props}\n\t\t/>\n\t),\n);\nAvatar.displayName = AvatarPrimitive.Root.displayName;\n\nexport type AvatarImageProps = Omit<\n\tReact.ComponentPropsWithoutRef<typeof AvatarPrimitive.Image>,\n\t\"asChild\" | \"onLoadingStatusChange\" | \"src\" | \"alt\"\n> & {\n\t/**\n\t * Change the default rendered element for the one passed as a child, merging their props and behavior.\n\t * @default false\n\t */\n\tasChild?: boolean;\n\t/**\n\t * Image source URL for the avatar image.\n\t */\n\tsrc?: React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Image>[\"src\"];\n\t/**\n\t * Text description of the image for accessibility and screen readers.\n\t */\n\talt?: React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Image>[\"alt\"];\n\t/**\n\t * A callback providing the current loading status of the image ('idle' | 'loading' | 'loaded' | 'error').\n\t */\n\tonLoadingStatusChange?: React.ComponentPropsWithoutRef<\n\t\ttypeof AvatarPrimitive.Image\n\t>[\"onLoadingStatusChange\"];\n};\n\nexport const AvatarImage = React.forwardRef<\n\tReact.ElementRef<typeof AvatarPrimitive.Image>,\n\tAvatarImageProps\n>(({ className, ...props }, ref) => (\n\t<AvatarPrimitive.Image\n\t\tref={ref}\n\t\tclassName={cn(\"aspect-square h-full w-full\", className)}\n\t\t{...props}\n\t/>\n));\nAvatarImage.displayName = AvatarPrimitive.Image.displayName;\n\nexport type AvatarFallbackProps = Omit<\n\tReact.ComponentPropsWithoutRef<typeof AvatarPrimitive.Fallback>,\n\t\"asChild\" | \"delayMs\"\n> & {\n\t/**\n\t * Change the default rendered element for the one passed as a child, merging their props and behavior.\n\t * @default false\n\t */\n\tasChild?: boolean;\n\t/**\n\t * Useful for delaying rendering so it only appears for those with slower connections.\n\t * Unset by default, rendering immediately without delay.\n\t */\n\tdelayMs?: number;\n};\n\nexport const AvatarFallback = React.forwardRef<\n\tReact.ElementRef<typeof AvatarPrimitive.Fallback>,\n\tAvatarFallbackProps\n>(({ className, ...props }, ref) => (\n\t<AvatarPrimitive.Fallback\n\t\tref={ref}\n\t\tclassName={cn(\n\t\t\t\"flex h-full w-full items-center justify-center rounded-basalt-full bg-basalt-muted text-basalt-sm\",\n\t\t\tclassName,\n\t\t)}\n\t\t{...props}\n\t/>\n));\nAvatarFallback.displayName = AvatarPrimitive.Fallback.displayName;\n\nconst AVATAR_TONES = [\n\tTAG_COLORS.blue.className,\n\tTAG_COLORS.amber.className,\n\tTAG_COLORS.rose.className,\n\tTAG_COLORS.teal.className,\n\tTAG_COLORS.violet.className,\n\tTAG_COLORS.success.className,\n] as const;\n\nexport interface AvatarInitialsProps {\n\t/** Person or organization name; initials are derived from its first two words. */\n\tname: string;\n\t/** Stable color identity, independent of display name changes. */\n\tcolorKey?: string;\n\t/** Two-letter override for application-specific abbreviations. */\n\tinitials?: string;\n\t/** Avatar diameter. @default \"default\" */\n\tsize?: \"sm\" | \"default\";\n\tclassName?: string;\n}\n\nexport function AvatarInitials({\n\tname,\n\tcolorKey,\n\tinitials,\n\tsize = \"default\",\n\tclassName,\n}: AvatarInitialsProps) {\n\treturn (\n\t\t<Avatar\n\t\t\trole=\"img\"\n\t\t\taria-label={name}\n\t\t\tclassName={cn(size === \"sm\" && \"size-basalt-6\", className)}\n\t\t>\n\t\t\t<AvatarFallback\n\t\t\t\tclassName={cn(\n\t\t\t\t\t\"font-medium text-basalt-sm\",\n\t\t\t\t\tAVATAR_TONES[avatarColorIndex(colorKey ?? name)],\n\t\t\t\t)}\n\t\t\t>\n\t\t\t\t{initials?.trim()\n\t\t\t\t\t? Array.from(initials.trim().toLocaleUpperCase(\"en-US\")).slice(0, 2).join(\"\")\n\t\t\t\t\t: avatarInitials(name)}\n\t\t\t</AvatarFallback>\n\t\t</Avatar>\n\t);\n}\n"],"mappings":";;;;;;;;AAiBA,IAAaE,IAASF,EAAM,YAC1B,EAAE,cAAW,GAAGG,KAAS,MACzB,kBAACF,EAAgB,MAAjB;CACM;CACL,WAAWG,EACV,oFACAC,CACD;CACA,GAAIF;AACJ,CAAA,CAEH;AACA,EAAO,cAAcF,EAAgB,KAAK;AA2B1C,IAAaK,IAAcN,EAAM,YAG9B,EAAE,cAAW,GAAGG,KAAS,MAC3B,kBAACF,EAAgB,OAAjB;CACM;CACL,WAAWG,EAAG,+BAA+BC,CAAS;CACtD,GAAIF;AACJ,CAAA,CACD;AACD,EAAY,cAAcF,EAAgB,MAAM;AAkBhD,IAAaM,IAAiBP,EAAM,YAGjC,EAAE,cAAW,GAAGG,KAAS,MAC3B,kBAACF,EAAgB,UAAjB;CACM;CACL,WAAWG,EACV,qGACAC,CACD;CACA,GAAIF;AACJ,CAAA,CACD;AACD,EAAe,cAAcF,EAAgB,SAAS;AAEtD,IAAMO,IAAe;CACpBC,EAAW,KAAK;CAChBA,EAAW,MAAM;CACjBA,EAAW,KAAK;CAChBA,EAAW,KAAK;CAChBA,EAAW,OAAO;CAClBA,EAAW,QAAQ;AACpB;AAcA,SAAgBC,EAAe,EAC9B,SACA,aACA,aACA,UAAO,WACP,gBACuB;CACvB,OACC,kBAACR,GAAD;EACC,MAAK;EACL,cAAYS;EACZ,WAAWP,EAAGQ,MAAS,QAAQ,iBAAiBP,CAAS;EAEzD,UAAA,kBAACE,GAAD;GACC,WAAWH,EACV,8BACAI,EAAaK,EAAiBC,KAAYH,CAAI,EAC/C;GAEC,UAAAI,GAAU,KAAK,IACb,MAAM,KAAKA,EAAS,KAAK,CAAC,CAAC,kBAAkB,OAAO,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,KAAK,EAAE,IAC1EC,EAAeL,CAAI;EACP,CAAA;CACT,CAAA;AAEV"}
|