@hex-core/components 1.7.0 → 1.8.1
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/dist/_tsup-dts-rollup.d.ts +1566 -5
- package/dist/arc.d.ts +4 -0
- package/dist/arc.js +147 -0
- package/dist/arc.js.map +1 -0
- package/dist/audio-player.d.ts +2 -0
- package/dist/audio-player.js +119 -0
- package/dist/audio-player.js.map +1 -0
- package/dist/audio-waveform.d.ts +2 -0
- package/dist/audio-waveform.js +72 -0
- package/dist/audio-waveform.js.map +1 -0
- package/dist/canvas.d.ts +2 -0
- package/dist/canvas.js +73 -0
- package/dist/canvas.js.map +1 -0
- package/dist/chord.d.ts +4 -0
- package/dist/chord.js +230 -0
- package/dist/chord.js.map +1 -0
- package/dist/cloze.d.ts +3 -0
- package/dist/cloze.js +98 -0
- package/dist/cloze.js.map +1 -0
- package/dist/color-picker.js.map +1 -1
- package/dist/compare-table.d.ts +4 -0
- package/dist/compare-table.js +109 -0
- package/dist/compare-table.js.map +1 -0
- package/dist/data-table.js.map +1 -1
- package/dist/deck.d.ts +3 -0
- package/dist/deck.js +231 -0
- package/dist/deck.js.map +1 -0
- package/dist/dendrogram.d.ts +3 -0
- package/dist/dendrogram.js +162 -0
- package/dist/dendrogram.js.map +1 -0
- package/dist/diagram.d.ts +2 -0
- package/dist/diagram.js +70 -0
- package/dist/diagram.js.map +1 -0
- package/dist/flashcard.d.ts +2 -0
- package/dist/flashcard.js +107 -0
- package/dist/flashcard.js.map +1 -0
- package/dist/flowchart.d.ts +4 -0
- package/dist/flowchart.js +275 -0
- package/dist/flowchart.js.map +1 -0
- package/dist/funnel.d.ts +3 -0
- package/dist/funnel.js +157 -0
- package/dist/funnel.js.map +1 -0
- package/dist/gantt.d.ts +3 -0
- package/dist/gantt.js +279 -0
- package/dist/gantt.js.map +1 -0
- package/dist/image-occlusion.d.ts +3 -0
- package/dist/image-occlusion.js +106 -0
- package/dist/image-occlusion.js.map +1 -0
- package/dist/index.d.ts +84 -0
- package/dist/index.js +3946 -2
- package/dist/index.js.map +1 -1
- package/dist/matrix.d.ts +3 -0
- package/dist/matrix.js +155 -0
- package/dist/matrix.js.map +1 -0
- package/dist/mind-map.d.ts +3 -0
- package/dist/mind-map.js +167 -0
- package/dist/mind-map.js.map +1 -0
- package/dist/org-chart.d.ts +3 -0
- package/dist/org-chart.js +215 -0
- package/dist/org-chart.js.map +1 -0
- package/dist/pyramid.d.ts +3 -0
- package/dist/pyramid.js +150 -0
- package/dist/pyramid.js.map +1 -0
- package/dist/quiz.d.ts +3 -0
- package/dist/quiz.js +128 -0
- package/dist/quiz.js.map +1 -0
- package/dist/sankey.d.ts +4 -0
- package/dist/sankey.js +190 -0
- package/dist/sankey.js.map +1 -0
- package/dist/schemas.d.ts +23 -0
- package/dist/schemas.js +2210 -3
- package/dist/schemas.js.map +1 -1
- package/dist/sequence.d.ts +4 -0
- package/dist/sequence.js +229 -0
- package/dist/sequence.js.map +1 -0
- package/dist/sonner.js.map +1 -1
- package/dist/spaced-repetition.d.ts +3 -0
- package/dist/spaced-repetition.js +73 -0
- package/dist/spaced-repetition.js.map +1 -0
- package/dist/sunburst.d.ts +3 -0
- package/dist/sunburst.js +205 -0
- package/dist/sunburst.js.map +1 -0
- package/dist/terminal.d.ts +2 -0
- package/dist/terminal.js +153 -0
- package/dist/terminal.js.map +1 -0
- package/dist/textarea.js.map +1 -1
- package/dist/time-axis.d.ts +3 -0
- package/dist/time-axis.js +233 -0
- package/dist/time-axis.js.map +1 -0
- package/dist/tool-call.js +6 -1
- package/dist/tool-call.js.map +1 -1
- package/dist/tree-map.d.ts +3 -0
- package/dist/tree-map.js +171 -0
- package/dist/tree-map.js.map +1 -0
- package/dist/venn.d.ts +3 -0
- package/dist/venn.js +196 -0
- package/dist/venn.js.map +1 -0
- package/package.json +49 -5
package/dist/chord.js
ADDED
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import * as React from 'react';
|
|
3
|
+
import { clsx } from 'clsx';
|
|
4
|
+
import { twMerge } from 'tailwind-merge';
|
|
5
|
+
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
6
|
+
|
|
7
|
+
// src/lib/chart-palette.ts
|
|
8
|
+
var CHART_PALETTE = [
|
|
9
|
+
"hsl(var(--chart-1, var(--primary)))",
|
|
10
|
+
"hsl(var(--chart-2, var(--primary)))",
|
|
11
|
+
"hsl(var(--chart-3, var(--primary)))",
|
|
12
|
+
"hsl(var(--chart-4, var(--primary)))",
|
|
13
|
+
"hsl(var(--chart-5, var(--primary)))",
|
|
14
|
+
"hsl(var(--chart-6, var(--primary)))"
|
|
15
|
+
];
|
|
16
|
+
function pickChartHue(index) {
|
|
17
|
+
const safe = (index % CHART_PALETTE.length + CHART_PALETTE.length) % CHART_PALETTE.length;
|
|
18
|
+
return CHART_PALETTE[safe];
|
|
19
|
+
}
|
|
20
|
+
function cn(...inputs) {
|
|
21
|
+
return twMerge(clsx(inputs));
|
|
22
|
+
}
|
|
23
|
+
function Chord({
|
|
24
|
+
nodes,
|
|
25
|
+
matrix,
|
|
26
|
+
size = 480,
|
|
27
|
+
padAngle = 0.04,
|
|
28
|
+
onChordHover,
|
|
29
|
+
onNodeClick,
|
|
30
|
+
className,
|
|
31
|
+
...rest
|
|
32
|
+
}) {
|
|
33
|
+
const [d3c, setD3c] = React.useState(null);
|
|
34
|
+
const [d3s, setD3s] = React.useState(null);
|
|
35
|
+
const [importError, setImportError] = React.useState(false);
|
|
36
|
+
React.useEffect(() => {
|
|
37
|
+
let cancelled = false;
|
|
38
|
+
void Promise.all([import('d3-chord'), import('d3-shape')]).then(
|
|
39
|
+
([c, s]) => {
|
|
40
|
+
if (cancelled) return;
|
|
41
|
+
setD3c(c);
|
|
42
|
+
setD3s(s);
|
|
43
|
+
},
|
|
44
|
+
() => {
|
|
45
|
+
if (!cancelled) setImportError(true);
|
|
46
|
+
}
|
|
47
|
+
);
|
|
48
|
+
return () => {
|
|
49
|
+
cancelled = true;
|
|
50
|
+
};
|
|
51
|
+
}, []);
|
|
52
|
+
const laidOut = React.useMemo(() => {
|
|
53
|
+
if (!d3c || !d3s) return null;
|
|
54
|
+
return layout(d3c, d3s, nodes, matrix, size, padAngle);
|
|
55
|
+
}, [d3c, d3s, nodes, matrix, size, padAngle]);
|
|
56
|
+
if (importError) {
|
|
57
|
+
return /* @__PURE__ */ jsxs(
|
|
58
|
+
"div",
|
|
59
|
+
{
|
|
60
|
+
"data-hex-chord-error": true,
|
|
61
|
+
role: "alert",
|
|
62
|
+
className: cn(
|
|
63
|
+
"inline-flex items-center justify-center rounded-md border border-destructive/40 bg-destructive/5 p-3 text-sm text-destructive",
|
|
64
|
+
className
|
|
65
|
+
),
|
|
66
|
+
style: { width: size, height: size },
|
|
67
|
+
children: [
|
|
68
|
+
"Install ",
|
|
69
|
+
/* @__PURE__ */ jsx("code", { className: "mx-1", children: "d3-chord" }),
|
|
70
|
+
" to view this chord diagram."
|
|
71
|
+
]
|
|
72
|
+
}
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
if (!laidOut) {
|
|
76
|
+
return /* @__PURE__ */ jsx(
|
|
77
|
+
"div",
|
|
78
|
+
{
|
|
79
|
+
"data-hex-chord-loading": true,
|
|
80
|
+
"aria-busy": "true",
|
|
81
|
+
"aria-label": "Loading chord diagram",
|
|
82
|
+
className: cn("inline-block bg-muted/20", className),
|
|
83
|
+
style: { width: size, height: size }
|
|
84
|
+
}
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
const { arcs, chords } = laidOut;
|
|
88
|
+
const desc = `Chord diagram with ${nodes.length} node${nodes.length === 1 ? "" : "s"} and ${chords.length} ribbon${chords.length === 1 ? "" : "s"}`;
|
|
89
|
+
const radius = size / 2;
|
|
90
|
+
return /* @__PURE__ */ jsxs(
|
|
91
|
+
"svg",
|
|
92
|
+
{
|
|
93
|
+
...rest,
|
|
94
|
+
"data-hex-chord": true,
|
|
95
|
+
role: "img",
|
|
96
|
+
width: size,
|
|
97
|
+
height: size,
|
|
98
|
+
viewBox: `${-radius} ${-radius} ${size} ${size}`,
|
|
99
|
+
className: cn("block", className),
|
|
100
|
+
children: [
|
|
101
|
+
/* @__PURE__ */ jsx("title", { children: "Chord diagram" }),
|
|
102
|
+
/* @__PURE__ */ jsx("desc", { children: desc }),
|
|
103
|
+
/* @__PURE__ */ jsx("g", { "data-hex-chord-ribbons": true, children: chords.map((c, i) => {
|
|
104
|
+
const interactive = Boolean(onChordHover);
|
|
105
|
+
const payload = {
|
|
106
|
+
source: c.source,
|
|
107
|
+
target: c.target,
|
|
108
|
+
sourceValue: c.sourceValue,
|
|
109
|
+
targetValue: c.targetValue
|
|
110
|
+
};
|
|
111
|
+
const fireHover = (chord) => onChordHover?.(chord);
|
|
112
|
+
return /* @__PURE__ */ jsx(
|
|
113
|
+
"path",
|
|
114
|
+
{
|
|
115
|
+
"data-hex-chord-ribbon": true,
|
|
116
|
+
d: c.d,
|
|
117
|
+
fill: pickChartHue(c.sourceIdx),
|
|
118
|
+
fillOpacity: 0.55,
|
|
119
|
+
stroke: "hsl(var(--background))",
|
|
120
|
+
strokeWidth: 0.5,
|
|
121
|
+
role: interactive ? "button" : void 0,
|
|
122
|
+
tabIndex: interactive ? 0 : void 0,
|
|
123
|
+
"aria-label": interactive ? `Flow from ${c.source.label} to ${c.target.label}, value ${c.sourceValue}; reverse value ${c.targetValue}` : void 0,
|
|
124
|
+
style: {
|
|
125
|
+
cursor: interactive ? "pointer" : void 0,
|
|
126
|
+
transition: "fill-opacity 120ms ease"
|
|
127
|
+
},
|
|
128
|
+
onMouseEnter: interactive ? () => fireHover(payload) : void 0,
|
|
129
|
+
onMouseLeave: interactive ? () => fireHover(null) : void 0,
|
|
130
|
+
onFocus: interactive ? () => fireHover(payload) : void 0,
|
|
131
|
+
onBlur: interactive ? () => fireHover(null) : void 0
|
|
132
|
+
},
|
|
133
|
+
`${c.source.id}-${c.target.id}-${i}`
|
|
134
|
+
);
|
|
135
|
+
}) }),
|
|
136
|
+
/* @__PURE__ */ jsx("g", { "data-hex-chord-arcs": true, children: arcs.map((a) => {
|
|
137
|
+
const interactive = Boolean(onNodeClick);
|
|
138
|
+
const handleActivate = () => onNodeClick?.(a.node);
|
|
139
|
+
const flip = a.labelAngle > Math.PI / 2 && a.labelAngle < 3 * Math.PI / 2;
|
|
140
|
+
return /* @__PURE__ */ jsxs(
|
|
141
|
+
"g",
|
|
142
|
+
{
|
|
143
|
+
"data-hex-chord-arc": true,
|
|
144
|
+
"data-depth": a.depth,
|
|
145
|
+
role: interactive ? "button" : void 0,
|
|
146
|
+
tabIndex: interactive ? 0 : void 0,
|
|
147
|
+
"aria-label": interactive ? a.node.label : void 0,
|
|
148
|
+
style: interactive ? { cursor: "pointer" } : void 0,
|
|
149
|
+
onClick: interactive ? handleActivate : void 0,
|
|
150
|
+
onKeyDown: interactive ? (e) => activateOnKey(e, handleActivate) : void 0,
|
|
151
|
+
children: [
|
|
152
|
+
/* @__PURE__ */ jsx(
|
|
153
|
+
"path",
|
|
154
|
+
{
|
|
155
|
+
d: a.d,
|
|
156
|
+
fill: pickChartHue(a.depth),
|
|
157
|
+
stroke: "hsl(var(--background))",
|
|
158
|
+
strokeWidth: 1
|
|
159
|
+
}
|
|
160
|
+
),
|
|
161
|
+
/* @__PURE__ */ jsx(
|
|
162
|
+
"text",
|
|
163
|
+
{
|
|
164
|
+
x: a.labelX,
|
|
165
|
+
y: a.labelY,
|
|
166
|
+
textAnchor: flip ? "end" : "start",
|
|
167
|
+
dy: "0.35em",
|
|
168
|
+
transform: flip ? `rotate(${a.labelAngle * 180 / Math.PI - 270} ${a.labelX} ${a.labelY})` : `rotate(${a.labelAngle * 180 / Math.PI - 90} ${a.labelX} ${a.labelY})`,
|
|
169
|
+
fontSize: 11,
|
|
170
|
+
fill: "hsl(var(--foreground))",
|
|
171
|
+
style: { pointerEvents: "none" },
|
|
172
|
+
children: a.node.label
|
|
173
|
+
}
|
|
174
|
+
)
|
|
175
|
+
]
|
|
176
|
+
},
|
|
177
|
+
a.node.id
|
|
178
|
+
);
|
|
179
|
+
}) })
|
|
180
|
+
]
|
|
181
|
+
}
|
|
182
|
+
);
|
|
183
|
+
}
|
|
184
|
+
function layout(d3c, d3s, nodes, matrix, size, padAngle) {
|
|
185
|
+
const longestLabel = nodes.reduce((m, n) => Math.max(m, n.label.length), 0);
|
|
186
|
+
const labelMargin = Math.max(40, longestLabel * 6 + 16);
|
|
187
|
+
const radius = size / 2 - labelMargin;
|
|
188
|
+
const innerRadius = radius - 12;
|
|
189
|
+
const outerRadius = radius;
|
|
190
|
+
const chordGen = d3c.chord().padAngle(padAngle).sortSubgroups((a, b) => a < b ? 1 : a > b ? -1 : 0);
|
|
191
|
+
const result = chordGen(matrix);
|
|
192
|
+
const arc = d3s.arc().innerRadius(innerRadius).outerRadius(outerRadius);
|
|
193
|
+
const ribbon = d3c.ribbon().radius(innerRadius);
|
|
194
|
+
const arcs = result.groups.map((g) => {
|
|
195
|
+
const node = nodes[g.index] ?? { id: `_${g.index}`, label: `Set ${g.index}` };
|
|
196
|
+
const midAngle = (g.startAngle + g.endAngle) / 2;
|
|
197
|
+
const labelRadius = outerRadius + 6;
|
|
198
|
+
return {
|
|
199
|
+
node,
|
|
200
|
+
depth: g.index,
|
|
201
|
+
d: arc({ startAngle: g.startAngle, endAngle: g.endAngle, innerRadius, outerRadius }) ?? "",
|
|
202
|
+
labelX: labelRadius * Math.sin(midAngle),
|
|
203
|
+
labelY: -labelRadius * Math.cos(midAngle),
|
|
204
|
+
labelAngle: midAngle
|
|
205
|
+
};
|
|
206
|
+
});
|
|
207
|
+
const buildRibbonInput = (c) => ({
|
|
208
|
+
source: { ...c.source, radius: innerRadius },
|
|
209
|
+
target: { ...c.target, radius: innerRadius }
|
|
210
|
+
});
|
|
211
|
+
const chords = result.filter((c) => c.source.value > 0 || c.target.value > 0).map((c) => ({
|
|
212
|
+
source: nodes[c.source.index] ?? { id: `_${c.source.index}`, label: `Set ${c.source.index}` },
|
|
213
|
+
target: nodes[c.target.index] ?? { id: `_${c.target.index}`, label: `Set ${c.target.index}` },
|
|
214
|
+
sourceValue: c.source.value,
|
|
215
|
+
targetValue: c.target.value,
|
|
216
|
+
d: ribbon(buildRibbonInput(c)) ?? "",
|
|
217
|
+
sourceIdx: c.source.index
|
|
218
|
+
}));
|
|
219
|
+
return { arcs, chords };
|
|
220
|
+
}
|
|
221
|
+
function activateOnKey(e, fn) {
|
|
222
|
+
if (e.key === "Enter" || e.key === " ") {
|
|
223
|
+
e.preventDefault();
|
|
224
|
+
fn();
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
export { Chord };
|
|
229
|
+
//# sourceMappingURL=chord.js.map
|
|
230
|
+
//# sourceMappingURL=chord.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/lib/chart-palette.ts","../src/lib/utils.ts","../src/artifacts/chord/chord.tsx"],"names":[],"mappings":"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","file":"chord.js","sourcesContent":["/**\n * Categorical chart palette for diagram primitives that encode categorical\n * data (sunburst, treemap, sankey, chord, funnel, pyramid, venn, matrix).\n * Cycled by an integer key — node index, leaf index, depth-1 ancestor, etc.\n *\n * The values reach into `--chart-1` through `--chart-6`, the dedicated\n * diagram-encoding tokens added in `@hex-core/tokens` 1.4. Each token has a\n * `var(--primary)` fallback so consumers on theme presets that haven't been\n * updated to ship `--chart-N` (or who run a custom theme without the chart\n * family) still see a coherent monochrome rendering instead of black SVG\n * fills.\n *\n * Why a chart palette and not the semantic tokens: `--primary`, `--accent`,\n * `--secondary`, and `--muted` collapse to a single hue family in the\n * default monochrome theme — adjacent segments of a chart end up\n * indistinguishable. Chart tokens are tuned for perceptual differentiation.\n */\nexport const CHART_PALETTE = [\n\t\"hsl(var(--chart-1, var(--primary)))\",\n\t\"hsl(var(--chart-2, var(--primary)))\",\n\t\"hsl(var(--chart-3, var(--primary)))\",\n\t\"hsl(var(--chart-4, var(--primary)))\",\n\t\"hsl(var(--chart-5, var(--primary)))\",\n\t\"hsl(var(--chart-6, var(--primary)))\",\n] as const;\n\n/**\n * Return the chart hue at a stable index. Cycles modulo `CHART_PALETTE.length`\n * so the caller doesn't have to range-check.\n *\n * @param index - Integer key (node index, leaf index, depth-1 ancestor index, …).\n * @returns A `hsl(var(...))` string suitable for SVG `fill` / `stroke`.\n */\nexport function pickChartHue(index: number): string {\n\tconst safe = ((index % CHART_PALETTE.length) + CHART_PALETTE.length) % CHART_PALETTE.length;\n\t// `safe` is provably 0..CHART_PALETTE.length-1 — the assertion documents that.\n\treturn CHART_PALETTE[safe] as string;\n}\n","import { type ClassValue, clsx } from \"clsx\";\nimport { twMerge } from \"tailwind-merge\";\n\n/**\n * Merge class names with Tailwind CSS conflict resolution.\n * @param inputs - Class values (strings, arrays, objects) to merge\n * @returns A single merged class string with Tailwind conflicts resolved\n */\nexport function cn(...inputs: ClassValue[]) {\n\treturn twMerge(clsx(inputs));\n}\n","\"use client\";\n\nimport * as React from \"react\";\nimport { pickChartHue } from \"../../lib/chart-palette.js\";\nimport { cn } from \"../../lib/utils.js\";\n\n/**\n * Chord diagram. Nodes sit on a ring; ribbons inside the ring encode\n * weighted bidirectional relationships between them. Common for\n * trade flows, migration, hyperlink graphs, citation networks —\n * anywhere \"A relates to B with weight w\" matters at scale.\n *\n * Heavy peers: requires `d3-chord` (~3 KB gzip) and `d3-shape` (~6 KB\n * gzip, already in the artifacts/ family). The hex-core CLI's `add`\n * flow prompts before installing.\n *\n * @example\n * <Chord\n * nodes={[\"A\", \"B\", \"C\", \"D\"].map((id) => ({ id, label: id }))}\n * matrix={[\n * [0, 5, 8, 1],\n * [3, 0, 2, 4],\n * [6, 0, 0, 7],\n * [2, 1, 9, 0],\n * ]}\n * />\n */\nexport type ChordNode = {\n\tid: string;\n\tlabel: string;\n};\n\n/**\n * Payload fired to `onChordHover`. `sourceValue` is the i→j flow;\n * `targetValue` is the j→i flow. They differ for asymmetric matrices and\n * are equal for symmetric ones.\n */\nexport type ChordHoverPayload = {\n\tsource: ChordNode;\n\ttarget: ChordNode;\n\tsourceValue: number;\n\ttargetValue: number;\n};\n\nexport interface ChordProps extends Omit<React.SVGAttributes<SVGSVGElement>, \"children\"> {\n\t/** Nodes (as ring segments). Order matches matrix rows/columns. */\n\tnodes: ChordNode[];\n\t/** Square N×N matrix of weights. matrix[i][j] = flow from node i to node j. */\n\tmatrix: number[][];\n\t/** Pixel size of the rendered SVG (it's square). Default 480. */\n\tsize?: number;\n\t/** Pixel padding between adjacent ring segments. Default 0.04 (radians, d3 convention). */\n\tpadAngle?: number;\n\t/** Fired when a ribbon is hovered (or hover ends, with `null`). */\n\tonChordHover?: (chord: ChordHoverPayload | null) => void;\n\t/** Fired when a node arc is clicked. */\n\tonNodeClick?: (node: ChordNode) => void;\n}\n\ninterface LaidOutArc {\n\tnode: ChordNode;\n\tdepth: number;\n\td: string;\n\tlabelX: number;\n\tlabelY: number;\n\tlabelAngle: number;\n}\n\ninterface LaidOutChord {\n\tsource: ChordNode;\n\ttarget: ChordNode;\n\tsourceValue: number;\n\ttargetValue: number;\n\td: string;\n\t/** Index of the source node — ribbons inherit the source arc's hue\n\t * so the eye can trace \"where did this flow originate\". */\n\tsourceIdx: number;\n}\n\ntype D3ChordMod = typeof import(\"d3-chord\");\ntype D3ShapeMod = typeof import(\"d3-shape\");\n\nfunction Chord({\n\tnodes,\n\tmatrix,\n\tsize = 480,\n\tpadAngle = 0.04,\n\tonChordHover,\n\tonNodeClick,\n\tclassName,\n\t...rest\n}: ChordProps) {\n\tconst [d3c, setD3c] = React.useState<D3ChordMod | null>(null);\n\tconst [d3s, setD3s] = React.useState<D3ShapeMod | null>(null);\n\tconst [importError, setImportError] = React.useState(false);\n\n\tReact.useEffect(() => {\n\t\tlet cancelled = false;\n\t\tvoid Promise.all([import(\"d3-chord\"), import(\"d3-shape\")]).then(\n\t\t\t([c, s]) => {\n\t\t\t\tif (cancelled) return;\n\t\t\t\tsetD3c(c);\n\t\t\t\tsetD3s(s);\n\t\t\t},\n\t\t\t() => {\n\t\t\t\tif (!cancelled) setImportError(true);\n\t\t\t},\n\t\t);\n\t\treturn () => {\n\t\t\tcancelled = true;\n\t\t};\n\t}, []);\n\n\tconst laidOut = React.useMemo(() => {\n\t\tif (!d3c || !d3s) return null;\n\t\treturn layout(d3c, d3s, nodes, matrix, size, padAngle);\n\t}, [d3c, d3s, nodes, matrix, size, padAngle]);\n\n\tif (importError) {\n\t\treturn (\n\t\t\t<div\n\t\t\t\tdata-hex-chord-error\n\t\t\t\trole=\"alert\"\n\t\t\t\tclassName={cn(\n\t\t\t\t\t\"inline-flex items-center justify-center rounded-md border border-destructive/40 bg-destructive/5 p-3 text-sm text-destructive\",\n\t\t\t\t\tclassName,\n\t\t\t\t)}\n\t\t\t\tstyle={{ width: size, height: size }}\n\t\t\t>\n\t\t\t\tInstall <code className=\"mx-1\">d3-chord</code> to view this chord diagram.\n\t\t\t</div>\n\t\t);\n\t}\n\n\tif (!laidOut) {\n\t\treturn (\n\t\t\t<div\n\t\t\t\tdata-hex-chord-loading\n\t\t\t\taria-busy=\"true\"\n\t\t\t\taria-label=\"Loading chord diagram\"\n\t\t\t\tclassName={cn(\"inline-block bg-muted/20\", className)}\n\t\t\t\tstyle={{ width: size, height: size }}\n\t\t\t/>\n\t\t);\n\t}\n\n\tconst { arcs, chords } = laidOut;\n\tconst desc = `Chord diagram with ${nodes.length} node${nodes.length === 1 ? \"\" : \"s\"} and ${chords.length} ribbon${chords.length === 1 ? \"\" : \"s\"}`;\n\tconst radius = size / 2;\n\n\treturn (\n\t\t<svg\n\t\t\t{...rest}\n\t\t\tdata-hex-chord\n\t\t\trole=\"img\"\n\t\t\twidth={size}\n\t\t\theight={size}\n\t\t\tviewBox={`${-radius} ${-radius} ${size} ${size}`}\n\t\t\tclassName={cn(\"block\", className)}\n\t\t>\n\t\t\t<title>Chord diagram</title>\n\t\t\t<desc>{desc}</desc>\n\t\t\t<g data-hex-chord-ribbons>\n\t\t\t\t{chords.map((c, i) => {\n\t\t\t\t\tconst interactive = Boolean(onChordHover);\n\t\t\t\t\tconst payload: ChordHoverPayload = {\n\t\t\t\t\t\tsource: c.source,\n\t\t\t\t\t\ttarget: c.target,\n\t\t\t\t\t\tsourceValue: c.sourceValue,\n\t\t\t\t\t\ttargetValue: c.targetValue,\n\t\t\t\t\t};\n\t\t\t\t\tconst fireHover = (chord: ChordHoverPayload | null) => onChordHover?.(chord);\n\t\t\t\t\treturn (\n\t\t\t\t\t\t<path\n\t\t\t\t\t\t\tkey={`${c.source.id}-${c.target.id}-${i}`}\n\t\t\t\t\t\t\tdata-hex-chord-ribbon\n\t\t\t\t\t\t\td={c.d}\n\t\t\t\t\t\t\tfill={pickChartHue(c.sourceIdx)}\n\t\t\t\t\t\t\tfillOpacity={0.55}\n\t\t\t\t\t\t\tstroke=\"hsl(var(--background))\"\n\t\t\t\t\t\t\tstrokeWidth={0.5}\n\t\t\t\t\t\t\trole={interactive ? \"button\" : undefined}\n\t\t\t\t\t\t\ttabIndex={interactive ? 0 : undefined}\n\t\t\t\t\t\t\taria-label={\n\t\t\t\t\t\t\t\tinteractive\n\t\t\t\t\t\t\t\t\t? `Flow from ${c.source.label} to ${c.target.label}, value ${c.sourceValue}; reverse value ${c.targetValue}`\n\t\t\t\t\t\t\t\t\t: undefined\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tstyle={{\n\t\t\t\t\t\t\t\tcursor: interactive ? \"pointer\" : undefined,\n\t\t\t\t\t\t\t\ttransition: \"fill-opacity 120ms ease\",\n\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\tonMouseEnter={interactive ? () => fireHover(payload) : undefined}\n\t\t\t\t\t\t\tonMouseLeave={interactive ? () => fireHover(null) : undefined}\n\t\t\t\t\t\t\tonFocus={interactive ? () => fireHover(payload) : undefined}\n\t\t\t\t\t\t\tonBlur={interactive ? () => fireHover(null) : undefined}\n\t\t\t\t\t\t/>\n\t\t\t\t\t);\n\t\t\t\t})}\n\t\t\t</g>\n\t\t\t<g data-hex-chord-arcs>\n\t\t\t\t{arcs.map((a) => {\n\t\t\t\t\tconst interactive = Boolean(onNodeClick);\n\t\t\t\t\tconst handleActivate = () => onNodeClick?.(a.node);\n\t\t\t\t\tconst flip = a.labelAngle > Math.PI / 2 && a.labelAngle < (3 * Math.PI) / 2;\n\t\t\t\t\treturn (\n\t\t\t\t\t\t<g\n\t\t\t\t\t\t\tkey={a.node.id}\n\t\t\t\t\t\t\tdata-hex-chord-arc\n\t\t\t\t\t\t\tdata-depth={a.depth}\n\t\t\t\t\t\t\trole={interactive ? \"button\" : undefined}\n\t\t\t\t\t\t\ttabIndex={interactive ? 0 : undefined}\n\t\t\t\t\t\t\taria-label={interactive ? a.node.label : undefined}\n\t\t\t\t\t\t\tstyle={interactive ? { cursor: \"pointer\" } : undefined}\n\t\t\t\t\t\t\tonClick={interactive ? handleActivate : undefined}\n\t\t\t\t\t\t\tonKeyDown={interactive ? (e) => activateOnKey(e, handleActivate) : undefined}\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t<path\n\t\t\t\t\t\t\t\td={a.d}\n\t\t\t\t\t\t\t\tfill={pickChartHue(a.depth)}\n\t\t\t\t\t\t\t\tstroke=\"hsl(var(--background))\"\n\t\t\t\t\t\t\t\tstrokeWidth={1}\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t<text\n\t\t\t\t\t\t\t\tx={a.labelX}\n\t\t\t\t\t\t\t\ty={a.labelY}\n\t\t\t\t\t\t\t\ttextAnchor={flip ? \"end\" : \"start\"}\n\t\t\t\t\t\t\t\tdy=\"0.35em\"\n\t\t\t\t\t\t\t\ttransform={\n\t\t\t\t\t\t\t\t\tflip\n\t\t\t\t\t\t\t\t\t\t? `rotate(${(a.labelAngle * 180) / Math.PI - 270} ${a.labelX} ${a.labelY})`\n\t\t\t\t\t\t\t\t\t\t: `rotate(${(a.labelAngle * 180) / Math.PI - 90} ${a.labelX} ${a.labelY})`\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tfontSize={11}\n\t\t\t\t\t\t\t\tfill=\"hsl(var(--foreground))\"\n\t\t\t\t\t\t\t\tstyle={{ pointerEvents: \"none\" }}\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t{a.node.label}\n\t\t\t\t\t\t\t</text>\n\t\t\t\t\t\t</g>\n\t\t\t\t\t);\n\t\t\t\t})}\n\t\t\t</g>\n\t\t</svg>\n\t);\n}\n\nfunction layout(\n\td3c: D3ChordMod,\n\td3s: D3ShapeMod,\n\tnodes: ChordNode[],\n\tmatrix: number[][],\n\tsize: number,\n\tpadAngle: number,\n): { arcs: LaidOutArc[]; chords: LaidOutChord[] } {\n\t// Reserve margin for label text scaled by the longest label, so words\n\t// like \"Americas\" / \"Manufacturing\" don't clip against the SVG edge.\n\t// 6 px/char + 16 px slack — empirically fits 12-char labels at fontSize 11.\n\tconst longestLabel = nodes.reduce((m, n) => Math.max(m, n.label.length), 0);\n\tconst labelMargin = Math.max(40, longestLabel * 6 + 16);\n\tconst radius = size / 2 - labelMargin;\n\tconst innerRadius = radius - 12;\n\tconst outerRadius = radius;\n\n\t// d3.descending lives in d3-array — inline the comparator to avoid pulling\n\t// another peer dep just for one helper.\n\tconst chordGen = d3c\n\t\t.chord()\n\t\t.padAngle(padAngle)\n\t\t.sortSubgroups((a, b) => (a < b ? 1 : a > b ? -1 : 0));\n\tconst result = chordGen(matrix);\n\n\tconst arc = d3s.arc().innerRadius(innerRadius).outerRadius(outerRadius);\n\t// `ribbon()` is exported from d3-chord, not d3-shape.\n\tconst ribbon = d3c.ribbon().radius(innerRadius);\n\n\tconst arcs: LaidOutArc[] = result.groups.map((g) => {\n\t\tconst node = nodes[g.index] ?? { id: `_${g.index}`, label: `Set ${g.index}` };\n\t\tconst midAngle = (g.startAngle + g.endAngle) / 2;\n\t\tconst labelRadius = outerRadius + 6;\n\t\treturn {\n\t\t\tnode,\n\t\t\tdepth: g.index,\n\t\t\td: arc({ startAngle: g.startAngle, endAngle: g.endAngle, innerRadius, outerRadius }) ?? \"\",\n\t\t\tlabelX: labelRadius * Math.sin(midAngle),\n\t\t\tlabelY: -labelRadius * Math.cos(midAngle),\n\t\t\tlabelAngle: midAngle,\n\t\t};\n\t});\n\n\t// Satisfies @types/d3-chord@3.0.6's `RibbonSubgroup.radius` requirement;\n\t// runtime ignores per-subgroup radius when the generator's `.radius()`\n\t// is set, so this spread is purely a typing accommodation.\n\tconst buildRibbonInput = (c: typeof result[number]) => ({\n\t\tsource: { ...c.source, radius: innerRadius },\n\t\ttarget: { ...c.target, radius: innerRadius },\n\t});\n\n\tconst chords: LaidOutChord[] = result\n\t\t// Drop chord pairs where both directions have zero flow — d3-chord\n\t\t// emits placeholder pairs for them which would otherwise render as\n\t\t// invisible-but-event-firing ribbons.\n\t\t.filter((c) => c.source.value > 0 || c.target.value > 0)\n\t\t.map((c) => ({\n\t\t\tsource: nodes[c.source.index] ?? { id: `_${c.source.index}`, label: `Set ${c.source.index}` },\n\t\t\ttarget: nodes[c.target.index] ?? { id: `_${c.target.index}`, label: `Set ${c.target.index}` },\n\t\t\tsourceValue: c.source.value,\n\t\t\ttargetValue: c.target.value,\n\t\t\td: ribbon(buildRibbonInput(c)) ?? \"\",\n\t\t\tsourceIdx: c.source.index,\n\t\t}));\n\n\treturn { arcs, chords };\n}\n\nfunction activateOnKey(e: React.KeyboardEvent, fn: () => void): void {\n\tif (e.key === \"Enter\" || e.key === \" \") {\n\t\te.preventDefault();\n\t\tfn();\n\t}\n}\n\nexport { Chord };\n"]}
|
package/dist/cloze.d.ts
ADDED
package/dist/cloze.js
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import * as React from 'react';
|
|
3
|
+
import { clsx } from 'clsx';
|
|
4
|
+
import { twMerge } from 'tailwind-merge';
|
|
5
|
+
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
6
|
+
|
|
7
|
+
function cn(...inputs) {
|
|
8
|
+
return twMerge(clsx(inputs));
|
|
9
|
+
}
|
|
10
|
+
var AUTO_ID_PREFIX = "__hex_cloze_auto_";
|
|
11
|
+
function normalize(parts) {
|
|
12
|
+
let blankIndex = 0;
|
|
13
|
+
const fragments = parts.map((p) => {
|
|
14
|
+
if (typeof p === "string") return p;
|
|
15
|
+
const id = p.id ?? `${AUTO_ID_PREFIX}${blankIndex}`;
|
|
16
|
+
const out = { hidden: p.hidden, id, blankIndex };
|
|
17
|
+
blankIndex++;
|
|
18
|
+
return out;
|
|
19
|
+
});
|
|
20
|
+
return { fragments, total: blankIndex };
|
|
21
|
+
}
|
|
22
|
+
function Cloze({ parts, revealMode = "click", onReveal, className, ...rest }) {
|
|
23
|
+
const { fragments, total } = React.useMemo(() => normalize(parts), [parts]);
|
|
24
|
+
const [revealed, setRevealed] = React.useState(() => /* @__PURE__ */ new Set());
|
|
25
|
+
const onRevealRef = React.useRef(onReveal);
|
|
26
|
+
onRevealRef.current = onReveal;
|
|
27
|
+
const update = React.useCallback((next) => {
|
|
28
|
+
setRevealed(next);
|
|
29
|
+
onRevealRef.current?.(Array.from(next));
|
|
30
|
+
}, []);
|
|
31
|
+
const toggleBlank = React.useCallback(
|
|
32
|
+
(id) => {
|
|
33
|
+
const next = new Set(revealed);
|
|
34
|
+
if (next.has(id)) next.delete(id);
|
|
35
|
+
else next.add(id);
|
|
36
|
+
update(next);
|
|
37
|
+
},
|
|
38
|
+
[revealed, update]
|
|
39
|
+
);
|
|
40
|
+
const allRevealed = total > 0 && revealed.size === total;
|
|
41
|
+
const toggleAll = React.useCallback(() => {
|
|
42
|
+
if (allRevealed) {
|
|
43
|
+
update(/* @__PURE__ */ new Set());
|
|
44
|
+
} else {
|
|
45
|
+
const next = /* @__PURE__ */ new Set();
|
|
46
|
+
for (const f of fragments) if (typeof f !== "string") next.add(f.id);
|
|
47
|
+
update(next);
|
|
48
|
+
}
|
|
49
|
+
}, [allRevealed, fragments, update]);
|
|
50
|
+
return /* @__PURE__ */ jsxs(
|
|
51
|
+
"div",
|
|
52
|
+
{
|
|
53
|
+
...rest,
|
|
54
|
+
"data-hex-cloze": true,
|
|
55
|
+
"data-all-revealed": allRevealed,
|
|
56
|
+
className: cn("text-base leading-relaxed", className),
|
|
57
|
+
children: [
|
|
58
|
+
/* @__PURE__ */ jsx("p", { "data-hex-cloze-text": true, children: fragments.map((f, i) => {
|
|
59
|
+
if (typeof f === "string") return /* @__PURE__ */ jsx(React.Fragment, { children: f }, `s-${i}`);
|
|
60
|
+
const isRevealed = revealed.has(f.id);
|
|
61
|
+
return /* @__PURE__ */ jsx(
|
|
62
|
+
"button",
|
|
63
|
+
{
|
|
64
|
+
type: "button",
|
|
65
|
+
"data-hex-cloze-blank": true,
|
|
66
|
+
"data-blank-id": f.id,
|
|
67
|
+
"data-revealed": isRevealed,
|
|
68
|
+
"aria-pressed": isRevealed,
|
|
69
|
+
"aria-label": isRevealed ? `Blank ${f.blankIndex + 1} revealed: ${f.hidden}. Activate to hide.` : `Blank ${f.blankIndex + 1} of ${total}. Activate to reveal.`,
|
|
70
|
+
onClick: () => toggleBlank(f.id),
|
|
71
|
+
className: cn(
|
|
72
|
+
"mx-0.5 inline-block rounded px-1.5 py-0 align-baseline transition-colors focus:outline-none focus-visible:ring-2 focus-visible:ring-ring",
|
|
73
|
+
isRevealed ? "bg-primary/10 text-foreground underline decoration-primary decoration-dotted underline-offset-4" : "select-none bg-muted text-transparent"
|
|
74
|
+
),
|
|
75
|
+
children: f.hidden
|
|
76
|
+
},
|
|
77
|
+
f.id
|
|
78
|
+
);
|
|
79
|
+
}) }),
|
|
80
|
+
revealMode === "all" && total > 0 ? /* @__PURE__ */ jsx("div", { className: "mt-3", children: /* @__PURE__ */ jsx(
|
|
81
|
+
"button",
|
|
82
|
+
{
|
|
83
|
+
type: "button",
|
|
84
|
+
"data-hex-cloze-toggle-all": true,
|
|
85
|
+
"aria-label": allRevealed ? "Hide all blanks" : "Reveal all blanks",
|
|
86
|
+
onClick: toggleAll,
|
|
87
|
+
className: "rounded-md border bg-background px-3 py-1 text-xs font-medium text-foreground transition-colors hover:bg-muted focus:outline-none focus-visible:ring-2 focus-visible:ring-ring",
|
|
88
|
+
children: allRevealed ? "Hide all" : "Reveal all"
|
|
89
|
+
}
|
|
90
|
+
) }) : null
|
|
91
|
+
]
|
|
92
|
+
}
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export { Cloze };
|
|
97
|
+
//# sourceMappingURL=cloze.js.map
|
|
98
|
+
//# sourceMappingURL=cloze.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/lib/utils.ts","../src/artifacts/cloze/cloze.tsx"],"names":[],"mappings":"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","file":"cloze.js","sourcesContent":["import { type ClassValue, clsx } from \"clsx\";\nimport { twMerge } from \"tailwind-merge\";\n\n/**\n * Merge class names with Tailwind CSS conflict resolution.\n * @param inputs - Class values (strings, arrays, objects) to merge\n * @returns A single merged class string with Tailwind conflicts resolved\n */\nexport function cn(...inputs: ClassValue[]) {\n\treturn twMerge(clsx(inputs));\n}\n","\"use client\";\n\nimport * as React from \"react\";\nimport { cn } from \"../../lib/utils.js\";\n\n/**\n * Cloze deletion — text with hidden segments the learner reveals one at\n * a time (or all at once). Each `{ hidden }` token in the `parts` array\n * renders as a redacted span; click / Enter / Space reveals just that\n * blank.\n *\n * Pure HTML; no heavy peer. Pair with Deck (artifacts/deck) to flow\n * through a sequence of cloze cards, or with SpacedRepetition for\n * confidence rating.\n *\n * @example\n * <Cloze parts={[\n * \"The mitochondria is the \",\n * { hidden: \"powerhouse\" },\n * \" of the cell.\",\n * ]} />\n */\nexport type ClozePart = string | { hidden: string; id?: string };\n\nexport interface ClozeProps extends Omit<React.HTMLAttributes<HTMLDivElement>, \"children\" | \"onReveal\"> {\n\t/** Mixed array: string fragments + { hidden } cloze tokens. */\n\tparts: ClozePart[];\n\t/** \"click\" reveals one blank at a time; \"all\" additionally surfaces a \"Reveal all\" toggle. Default \"click\". */\n\trevealMode?: \"click\" | \"all\";\n\t/** Fired with the cumulative set of revealed blank ids on every reveal change. */\n\tonReveal?: (revealedIds: string[]) => void;\n}\n\ninterface NormalizedBlank {\n\thidden: string;\n\tid: string;\n\tblankIndex: number;\n}\n\n// Namespace auto-ids with a prefix that's vanishingly unlikely to collide\n// with consumer-supplied explicit ids. Without this, `[{hidden:'a'},\n// {hidden:'b'}]` (auto-numbered \"blank-0\", \"blank-1\") would silently\n// alias with a future `{hidden:'c', id:'blank-1'}`.\nconst AUTO_ID_PREFIX = \"__hex_cloze_auto_\";\n\nfunction normalize(parts: ClozePart[]): { fragments: Array<string | NormalizedBlank>; total: number } {\n\tlet blankIndex = 0;\n\tconst fragments = parts.map((p) => {\n\t\tif (typeof p === \"string\") return p;\n\t\tconst id = p.id ?? `${AUTO_ID_PREFIX}${blankIndex}`;\n\t\tconst out: NormalizedBlank = { hidden: p.hidden, id, blankIndex };\n\t\tblankIndex++;\n\t\treturn out;\n\t});\n\treturn { fragments, total: blankIndex };\n}\n\nfunction Cloze({ parts, revealMode = \"click\", onReveal, className, ...rest }: ClozeProps) {\n\tconst { fragments, total } = React.useMemo(() => normalize(parts), [parts]);\n\tconst [revealed, setRevealed] = React.useState<Set<string>>(() => new Set());\n\tconst onRevealRef = React.useRef(onReveal);\n\tonRevealRef.current = onReveal;\n\n\tconst update = React.useCallback((next: Set<string>) => {\n\t\tsetRevealed(next);\n\t\tonRevealRef.current?.(Array.from(next));\n\t}, []);\n\n\tconst toggleBlank = React.useCallback(\n\t\t(id: string) => {\n\t\t\tconst next = new Set(revealed);\n\t\t\tif (next.has(id)) next.delete(id);\n\t\t\telse next.add(id);\n\t\t\tupdate(next);\n\t\t},\n\t\t[revealed, update],\n\t);\n\n\tconst allRevealed = total > 0 && revealed.size === total;\n\n\tconst toggleAll = React.useCallback(() => {\n\t\tif (allRevealed) {\n\t\t\tupdate(new Set());\n\t\t} else {\n\t\t\tconst next = new Set<string>();\n\t\t\tfor (const f of fragments) if (typeof f !== \"string\") next.add(f.id);\n\t\t\tupdate(next);\n\t\t}\n\t}, [allRevealed, fragments, update]);\n\n\treturn (\n\t\t<div\n\t\t\t{...rest}\n\t\t\tdata-hex-cloze\n\t\t\tdata-all-revealed={allRevealed}\n\t\t\tclassName={cn(\"text-base leading-relaxed\", className)}\n\t\t>\n\t\t\t<p data-hex-cloze-text>\n\t\t\t\t{fragments.map((f, i) => {\n\t\t\t\t\tif (typeof f === \"string\") return <React.Fragment key={`s-${i}`}>{f}</React.Fragment>;\n\t\t\t\t\tconst isRevealed = revealed.has(f.id);\n\t\t\t\t\treturn (\n\t\t\t\t\t\t<button\n\t\t\t\t\t\t\tkey={f.id}\n\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\tdata-hex-cloze-blank\n\t\t\t\t\t\t\tdata-blank-id={f.id}\n\t\t\t\t\t\t\tdata-revealed={isRevealed}\n\t\t\t\t\t\t\taria-pressed={isRevealed}\n\t\t\t\t\t\t\taria-label={\n\t\t\t\t\t\t\t\tisRevealed\n\t\t\t\t\t\t\t\t\t? `Blank ${f.blankIndex + 1} revealed: ${f.hidden}. Activate to hide.`\n\t\t\t\t\t\t\t\t\t: `Blank ${f.blankIndex + 1} of ${total}. Activate to reveal.`\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tonClick={() => toggleBlank(f.id)}\n\t\t\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\t\t\"mx-0.5 inline-block rounded px-1.5 py-0 align-baseline transition-colors focus:outline-none focus-visible:ring-2 focus-visible:ring-ring\",\n\t\t\t\t\t\t\t\tisRevealed\n\t\t\t\t\t\t\t\t\t? \"bg-primary/10 text-foreground underline decoration-primary decoration-dotted underline-offset-4\"\n\t\t\t\t\t\t\t\t\t: \"select-none bg-muted text-transparent\",\n\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t{/* Always render the hidden text so the visual width matches\n\t\t\t\t\t\t\t once revealed; transparent text when hidden. */}\n\t\t\t\t\t\t\t{f.hidden}\n\t\t\t\t\t\t</button>\n\t\t\t\t\t);\n\t\t\t\t})}\n\t\t\t</p>\n\t\t\t{revealMode === \"all\" && total > 0 ? (\n\t\t\t\t<div className=\"mt-3\">\n\t\t\t\t\t<button\n\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\tdata-hex-cloze-toggle-all\n\t\t\t\t\t\taria-label={allRevealed ? \"Hide all blanks\" : \"Reveal all blanks\"}\n\t\t\t\t\t\tonClick={toggleAll}\n\t\t\t\t\t\tclassName=\"rounded-md border bg-background px-3 py-1 text-xs font-medium text-foreground transition-colors hover:bg-muted focus:outline-none focus-visible:ring-2 focus-visible:ring-ring\"\n\t\t\t\t\t>\n\t\t\t\t\t\t{allRevealed ? \"Hide all\" : \"Reveal all\"}\n\t\t\t\t\t</button>\n\t\t\t\t</div>\n\t\t\t) : null}\n\t\t</div>\n\t);\n}\n\nexport { Cloze };\n"]}
|
package/dist/color-picker.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/lib/utils.ts","../src/lib/color.ts","../src/primitives/input/input.tsx","../src/primitives/label/label.tsx","../src/primitives/slider/slider.tsx","../src/components/popover/popover.tsx","../src/components/color-picker/color-picker.tsx"],"names":["React","React2","jsx","React3","React4","jsxs"],"mappings":"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","file":"color-picker.js","sourcesContent":["import { type ClassValue, clsx } from \"clsx\";\nimport { twMerge } from \"tailwind-merge\";\n\n/**\n * Merge class names with Tailwind CSS conflict resolution.\n * @param inputs - Class values (strings, arrays, objects) to merge\n * @returns A single merged class string with Tailwind conflicts resolved\n */\nexport function cn(...inputs: ClassValue[]) {\n\treturn twMerge(clsx(inputs));\n}\n","/**\n * Color conversion utilities for the HSL-triplet token format used across\n * `@hex-core/tokens` themes (`H S% L%`, e.g. `\"240 5.9% 10%\"` — no `hsl()`\n * wrapper, no commas).\n *\n * The triplet is the round-trip-safe serialization for Hex UI: tokens flow\n * triplet → CSS `hsl(var(--token))` → rendered color, and the ColorPicker\n * component edits triplets directly. Hex/RGB conversions are display\n * adapters, not the source of truth.\n */\n\n/** Parsed HSL components. `h` is degrees (0–360); `s` and `l` are percentages (0–100). */\nexport interface HslTriplet {\n\th: number;\n\ts: number;\n\tl: number;\n}\n\n/** Parsed RGB components. Each channel is 0–255. */\nexport interface RgbColor {\n\tr: number;\n\tg: number;\n\tb: number;\n}\n\n/**\n * Parse an HSL triplet string into numeric components.\n *\n * Note: malformed input silently coerces to `{0,0,0}` (pure black) rather than\n * returning an error signal. Callers that need to distinguish \"user typed\n * black\" from \"user typed garbage\" should validate the input format first.\n * `hexToHslTriplet` returns `null` for malformed hex; this asymmetry is\n * intentional — triplets feed CSS variables where any non-color value would\n * already break rendering.\n *\n * @param triplet - String in the form `\"<H> <S>% <L>%\"` (e.g. `\"240 5.9% 10%\"`).\n * @returns Numeric components, or `{0,0,0}` if the input is malformed.\n */\nexport function parseHslTriplet(triplet: string): HslTriplet {\n\tconst parts = triplet.trim().split(/\\s+/);\n\treturn {\n\t\th: Number.parseFloat(parts[0]) || 0,\n\t\ts: Number.parseFloat(parts[1]) || 0,\n\t\tl: Number.parseFloat(parts[2]) || 0,\n\t};\n}\n\n/**\n * Format HSL components into an HSL triplet string (the canonical token format).\n * @param hsl - Numeric components.\n * @returns Triplet in the form `\"<H> <S>% <L>%\"`.\n */\nexport function formatHslTriplet({ h, s, l }: HslTriplet): string {\n\t// Tolerant integer check: rgbToHsl can produce values like 5.0000000001 due\n\t// to float arithmetic; format those as \"5\" rather than \"5.0\".\n\tconst round = (n: number) =>\n\t\tMath.abs(n - Math.round(n)) < 1e-6 ? `${Math.round(n)}` : n.toFixed(1);\n\treturn `${Math.round(h)} ${round(s)}% ${round(l)}%`;\n}\n\n/**\n * Convert HSL components to RGB.\n * @param h - Hue (0–360).\n * @param s - Saturation (0–100).\n * @param l - Lightness (0–100).\n * @returns RGB channels (0–255, rounded).\n */\nexport function hslToRgb(h: number, s: number, l: number): RgbColor {\n\tconst sN = s / 100;\n\tconst lN = l / 100;\n\tconst k = (n: number) => (n + h / 30) % 12;\n\tconst a = sN * Math.min(lN, 1 - lN);\n\tconst f = (n: number) => lN - a * Math.max(-1, Math.min(k(n) - 3, 9 - k(n), 1));\n\treturn {\n\t\tr: Math.round(255 * f(0)),\n\t\tg: Math.round(255 * f(8)),\n\t\tb: Math.round(255 * f(4)),\n\t};\n}\n\n/**\n * Convert RGB components to HSL.\n * @param r - Red (0–255).\n * @param g - Green (0–255).\n * @param b - Blue (0–255).\n * @returns HSL components (h: 0–360, s: 0–100, l: 0–100).\n */\nexport function rgbToHsl(r: number, g: number, b: number): HslTriplet {\n\tconst rN = r / 255;\n\tconst gN = g / 255;\n\tconst bN = b / 255;\n\tconst max = Math.max(rN, gN, bN);\n\tconst min = Math.min(rN, gN, bN);\n\tlet h = 0;\n\tlet s = 0;\n\tconst l = (max + min) / 2;\n\tif (max !== min) {\n\t\tconst d = max - min;\n\t\ts = l > 0.5 ? d / (2 - max - min) : d / (max + min);\n\t\tif (max === rN) h = (gN - bN) / d + (gN < bN ? 6 : 0);\n\t\telse if (max === gN) h = (bN - rN) / d + 2;\n\t\telse h = (rN - gN) / d + 4;\n\t\th /= 6;\n\t}\n\treturn { h: h * 360, s: s * 100, l: l * 100 };\n}\n\n/**\n * Convert an HSL triplet to a 6-digit hex string.\n * @param triplet - HSL triplet (e.g. `\"240 5.9% 10%\"`).\n * @returns Lowercase hex string with leading `#` (e.g. `\"#181a1f\"`).\n */\nexport function hslTripletToHex(triplet: string): string {\n\tconst { h, s, l } = parseHslTriplet(triplet);\n\tconst { r, g, b } = hslToRgb(h, s, l);\n\tconst toHex = (n: number) => n.toString(16).padStart(2, \"0\");\n\treturn `#${toHex(r)}${toHex(g)}${toHex(b)}`;\n}\n\n/**\n * Convert a hex string to an HSL triplet.\n * Accepts 3-digit (`#abc`) or 6-digit (`#aabbcc`) hex with optional `#`.\n * @param hex - Hex color string.\n * @returns HSL triplet, or `null` if the input is malformed.\n */\nexport function hexToHslTriplet(hex: string): string | null {\n\tconst clean = hex.trim().replace(/^#/, \"\");\n\tlet normalized: string;\n\tif (/^[0-9a-fA-F]{3}$/.test(clean)) {\n\t\tnormalized = clean\n\t\t\t.split(\"\")\n\t\t\t.map((c) => c + c)\n\t\t\t.join(\"\");\n\t} else if (/^[0-9a-fA-F]{6}$/.test(clean)) {\n\t\tnormalized = clean;\n\t} else {\n\t\treturn null;\n\t}\n\tconst r = Number.parseInt(normalized.slice(0, 2), 16);\n\tconst g = Number.parseInt(normalized.slice(2, 4), 16);\n\tconst b = Number.parseInt(normalized.slice(4, 6), 16);\n\treturn formatHslTriplet(rgbToHsl(r, g, b));\n}\n","import * as React from \"react\";\nimport { cn } from \"../../lib/utils.js\";\n\nexport type InputProps = React.InputHTMLAttributes<HTMLInputElement>;\n\nconst Input = React.forwardRef<HTMLInputElement, InputProps>(\n\t({ className, type, ...props }, ref) => {\n\t\treturn (\n\t\t\t<input\n\t\t\t\ttype={type}\n\t\t\t\tclassName={cn(\n\t\t\t\t\t\"flex h-[var(--control-height-md,2.5rem)] w-full rounded-md border border-input bg-background px-[var(--space-3,0.75rem)] py-[var(--space-2,0.5rem)] text-sm\",\n\t\t\t\t\t\"transition-all duration-[var(--duration-normal,200ms)] ease-out\",\n\t\t\t\t\t// inset-ring gives a self-borne edge so the input field is visible on flat\n\t\t\t\t\t// surfaces (token border alone is too low-contrast on bg-background=white).\n\t\t\t\t\t\"shadow-sm inset-ring-1 inset-ring-foreground/[0.06]\",\n\t\t\t\t\t\"file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground\",\n\t\t\t\t\t\"placeholder:text-muted-foreground\",\n\t\t\t\t\t\"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2\",\n\t\t\t\t\t\"focus-visible:shadow-md focus-visible:border-ring/50\",\n\t\t\t\t\t\"hover:border-ring/30 hover:shadow-md\",\n\t\t\t\t\t\"disabled:cursor-not-allowed disabled:opacity-50\",\n\t\t\t\t\tclassName,\n\t\t\t\t)}\n\t\t\t\tref={ref}\n\t\t\t\t{...props}\n\t\t\t/>\n\t\t);\n\t},\n);\nInput.displayName = \"Input\";\n\nexport { Input };\n","\"use client\";\n\nimport * as LabelPrimitive from \"@radix-ui/react-label\";\nimport { type VariantProps, cva } from \"class-variance-authority\";\nimport * as React from \"react\";\nimport { cn } from \"../../lib/utils.js\";\n\nconst labelVariants = cva(\n\t\"text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70\",\n);\n\nexport interface LabelProps\n\textends React.ComponentPropsWithoutRef<typeof LabelPrimitive.Root>,\n\t\tVariantProps<typeof labelVariants> {}\n\nconst Label = React.forwardRef<React.ComponentRef<typeof LabelPrimitive.Root>, LabelProps>(\n\t({ className, ...props }, ref) => (\n\t\t<LabelPrimitive.Root ref={ref} className={cn(labelVariants(), className)} {...props} />\n\t),\n);\nLabel.displayName = \"Label\";\n\nexport { Label };\n","\"use client\";\n\nimport * as SliderPrimitive from \"@radix-ui/react-slider\";\nimport * as React from \"react\";\nimport { cn } from \"../../lib/utils.js\";\n\n// Local ambient — components run in browsers where bundlers (Next, Vite, tsup)\n// replace `process.env.NODE_ENV` at build time. Avoids pulling @types/node into\n// the components package just for one dev-mode warning.\ndeclare const process: { env?: { NODE_ENV?: string } } | undefined;\n\ninterface SliderProps\n\textends React.ComponentPropsWithoutRef<typeof SliderPrimitive.Root> {\n\t/**\n\t * Per-thumb accessible labels. When the slider has multiple thumbs, pass\n\t * one entry per thumb (e.g. [\"Minimum\", \"Maximum\"]). For a single-thumb\n\t * slider, the Root's `aria-label` / `aria-labelledby` is mirrored onto\n\t * the thumb automatically — pass `thumbLabels` only when those defaults\n\t * are insufficient.\n\t */\n\tthumbLabels?: string[];\n}\n\n/**\n * A range input with one or more draggable thumbs.\n * Built on Radix UI Slider with keyboard controls (arrows, Home, End, PageUp/Down).\n */\nconst Slider = React.forwardRef<\n\tReact.ComponentRef<typeof SliderPrimitive.Root>,\n\tSliderProps\n>(({ className, thumbLabels, ...props }, ref) => {\n\tconst values = props.value ?? props.defaultValue ?? [0];\n\tconst rootLabel = props[\"aria-label\"];\n\tconst rootLabelledBy = props[\"aria-labelledby\"];\n\n\tif (\n\t\ttypeof process !== \"undefined\" &&\n\t\tprocess.env?.NODE_ENV !== \"production\" &&\n\t\tthumbLabels &&\n\t\tthumbLabels.length !== values.length\n\t) {\n\t\tconsole.warn(\n\t\t\t`Slider: thumbLabels.length (${thumbLabels.length}) does not match value.length (${values.length}). ` +\n\t\t\t\t`Missing labels fall back to indexed names; extra labels are ignored.`,\n\t\t);\n\t}\n\n\treturn (\n\t\t<SliderPrimitive.Root\n\t\t\tref={ref}\n\t\t\tclassName={cn(\"relative flex w-full touch-none select-none items-center\", className)}\n\t\t\t{...props}\n\t\t>\n\t\t\t<SliderPrimitive.Track className=\"relative h-2 w-full grow overflow-hidden rounded-full border border-foreground/[0.08] bg-secondary\">\n\t\t\t\t<SliderPrimitive.Range className=\"absolute h-full bg-primary\" />\n\t\t\t</SliderPrimitive.Track>\n\t\t\t{values.map((_, i) => {\n\t\t\t\tconst explicit = thumbLabels?.[i];\n\t\t\t\tconst fallback =\n\t\t\t\t\tvalues.length === 1\n\t\t\t\t\t\t? rootLabel\n\t\t\t\t\t\t: rootLabel\n\t\t\t\t\t\t\t? `${rootLabel} (${i + 1} of ${values.length})`\n\t\t\t\t\t\t\t: undefined;\n\t\t\t\treturn (\n\t\t\t\t\t<SliderPrimitive.Thumb\n\t\t\t\t\t\t// biome-ignore lint/suspicious/noArrayIndexKey: Radix renders one thumb per value by index\n\t\t\t\t\t\tkey={i}\n\t\t\t\t\t\taria-label={explicit ?? fallback}\n\t\t\t\t\t\taria-labelledby={\n\t\t\t\t\t\t\texplicit || fallback ? undefined : rootLabelledBy\n\t\t\t\t\t\t}\n\t\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\t\"block h-5 w-5 rounded-full border-2 border-primary bg-background\",\n\t\t\t\t\t\t\t\"transition-all duration-[var(--duration-normal,200ms)] ease-out shadow-md\",\n\t\t\t\t\t\t\t\"hover:shadow-lg hover:scale-110\",\n\t\t\t\t\t\t\t\"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2\",\n\t\t\t\t\t\t\t\"disabled:pointer-events-none disabled:opacity-50\",\n\t\t\t\t\t\t)}\n\t\t\t\t\t/>\n\t\t\t\t);\n\t\t\t})}\n\t\t</SliderPrimitive.Root>\n\t);\n});\nSlider.displayName = \"Slider\";\n\nexport type { SliderProps };\n\nexport { Slider };\n","\"use client\";\n\nimport * as PopoverPrimitive from \"@radix-ui/react-popover\";\nimport * as React from \"react\";\nimport { cn } from \"../../lib/utils.js\";\n\n/** Root container for a popover. */\nconst Popover = PopoverPrimitive.Root;\n\n/** The element that anchors and opens the popover. */\nconst PopoverTrigger = PopoverPrimitive.Trigger;\n\n/** Helper to explicitly anchor the popover to a different element. */\nconst PopoverAnchor = PopoverPrimitive.Anchor;\n\n/** The floating popover content panel. */\nconst PopoverContent = React.forwardRef<\n\tReact.ComponentRef<typeof PopoverPrimitive.Content>,\n\tReact.ComponentPropsWithoutRef<typeof PopoverPrimitive.Content>\n>(({ className, align = \"center\", sideOffset = 4, ...props }, ref) => (\n\t<PopoverPrimitive.Portal>\n\t\t<PopoverPrimitive.Content\n\t\t\tref={ref}\n\t\t\talign={align}\n\t\t\tsideOffset={sideOffset}\n\t\t\tclassName={cn(\n\t\t\t\t\"z-50 w-72 rounded-md border border-foreground/[0.08] bg-popover p-[var(--space-4,1rem)] text-popover-foreground shadow-md outline-none\",\n\t\t\t\t\"data-[state=open]:animate-in data-[state=closed]:animate-out\",\n\t\t\t\t\"data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0\",\n\t\t\t\t\"data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95\",\n\t\t\t\t\"data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2\",\n\t\t\t\tclassName,\n\t\t\t)}\n\t\t\t{...props}\n\t\t/>\n\t</PopoverPrimitive.Portal>\n));\nPopoverContent.displayName = \"PopoverContent\";\n\nexport { Popover, PopoverTrigger, PopoverContent, PopoverAnchor };\n","\"use client\";\n\nimport * as React from \"react\";\nimport { cn } from \"../../lib/utils.js\";\nimport {\n\tformatHslTriplet,\n\thexToHslTriplet,\n\thslTripletToHex,\n\tparseHslTriplet,\n} from \"../../lib/color.js\";\nimport { Input } from \"../../primitives/input/input.js\";\nimport { Label } from \"../../primitives/label/label.js\";\nimport { Slider } from \"../../primitives/slider/slider.js\";\nimport { Popover, PopoverContent, PopoverTrigger } from \"../popover/popover.js\";\n\n/** Tolerance for treating a float as integer-valued (handles arithmetic drift in HSL roundtrips). */\nconst INTEGER_EPSILON = 1e-6;\n\nconst looksInteger = (n: number) => Math.abs(n - Math.round(n)) < INTEGER_EPSILON;\n\n/** Props for the ColorPicker component. */\nexport interface ColorPickerProps {\n\t/**\n\t * Current color as an HSL triplet string (`\"<H> <S>% <L>%\"`, e.g. `\"240 5.9% 10%\"`).\n\t * Match the format used by `@hex-core/tokens`; round-trip safe.\n\t */\n\tvalue: string;\n\t/**\n\t * Called with the next HSL triplet whenever the user drags a slider or commits a valid hex value.\n\t * Not called for invalid hex input — the picker keeps the prior value until the input parses cleanly.\n\t */\n\tonChange: (value: string) => void;\n\t/**\n\t * Disable interaction. Native `disabled` attribute is set on the trigger so the\n\t * popover doesn't open via mouse or keyboard activation. Tab focus still lands\n\t * on the trigger per browser defaults; if you want to fully remove it from the\n\t * tab order, wrap in a parent that handles `tabIndex` accordingly.\n\t */\n\tdisabled?: boolean;\n\t/** Accessible name for the trigger button (defaults to \"Pick color\"). */\n\t\"aria-label\"?: string;\n\t/** Additional class names merged onto the trigger. */\n\tclassName?: string;\n}\n\n/**\n * HSL-native color picker. Edits an HSL triplet directly via three sliders\n * (H/S/L), with a hex input as a display adapter.\n *\n * Round-trip safe: triplet → hex → triplet preserves the slider state because\n * sliders are the source of truth and hex updates them only when valid.\n *\n * @param props - Controlled component; `value` and `onChange` are required.\n * @returns A trigger button that opens a popover with H/S/L sliders + hex input.\n * @example\n * ```tsx\n * const [color, setColor] = React.useState(\"240 5.9% 10%\");\n * <ColorPicker value={color} onChange={setColor} aria-label=\"Primary color\" />\n * ```\n */\nfunction ColorPicker({\n\tvalue,\n\tonChange,\n\tdisabled,\n\t\"aria-label\": ariaLabel = \"Pick color\",\n\tclassName,\n}: ColorPickerProps) {\n\t// Memoize so the slider-row callbacks below are stable across renders with\n\t// the same `value`; downstream `Slider`s avoid spurious re-mounts.\n\tconst hsl = React.useMemo(() => parseHslTriplet(value), [value]);\n\tconst hex = React.useMemo(() => hslTripletToHex(value), [value]);\n\n\tconst update = React.useCallback(\n\t\t(patch: Partial<typeof hsl>) => {\n\t\t\tonChange(formatHslTriplet({ ...hsl, ...patch }));\n\t\t},\n\t\t[hsl, onChange],\n\t);\n\n\t// Hex input is locally controlled so the user can type intermediate states\n\t// (e.g. \"#1a\") without committing. Sync the buffer to the canonical hex\n\t// whenever the input is NOT focused — typing into a focused input must not\n\t// be clobbered by a parent re-render reflecting our own `onChange`.\n\tconst hexInputRef = React.useRef<HTMLInputElement>(null);\n\tconst [hexBuffer, setHexBuffer] = React.useState(hex);\n\tReact.useEffect(() => {\n\t\tif (\n\t\t\ttypeof document === \"undefined\" ||\n\t\t\tdocument.activeElement !== hexInputRef.current\n\t\t) {\n\t\t\tsetHexBuffer(hex);\n\t\t}\n\t}, [hex]);\n\n\tconst handleHexChange = (e: React.ChangeEvent<HTMLInputElement>) => {\n\t\tconst next = e.target.value;\n\t\tsetHexBuffer(next);\n\t\tconst triplet = hexToHslTriplet(next);\n\t\tif (triplet !== null) onChange(triplet);\n\t};\n\n\tconst hexId = React.useId();\n\n\treturn (\n\t\t<Popover>\n\t\t\t<PopoverTrigger asChild>\n\t\t\t\t<button\n\t\t\t\t\ttype=\"button\"\n\t\t\t\t\tdisabled={disabled}\n\t\t\t\t\taria-label={ariaLabel}\n\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\"inline-flex h-9 items-center gap-2 rounded-md border border-input bg-background px-3 text-sm shadow-sm\",\n\t\t\t\t\t\t\"transition-all duration-[var(--duration-normal,200ms)] ease-out\",\n\t\t\t\t\t\t\"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2\",\n\t\t\t\t\t\t\"hover:shadow-md disabled:pointer-events-none disabled:opacity-50\",\n\t\t\t\t\t\tclassName,\n\t\t\t\t\t)}\n\t\t\t\t>\n\t\t\t\t\t<span\n\t\t\t\t\t\taria-hidden=\"true\"\n\t\t\t\t\t\tclassName=\"h-5 w-5 rounded-sm border border-border\"\n\t\t\t\t\t\tstyle={{ backgroundColor: `hsl(${value})` }}\n\t\t\t\t\t/>\n\t\t\t\t\t<span className=\"font-mono text-xs uppercase\">{hex}</span>\n\t\t\t\t</button>\n\t\t\t</PopoverTrigger>\n\t\t\t<PopoverContent className=\"w-72 p-4\" align=\"start\">\n\t\t\t\t<div className=\"space-y-4\">\n\t\t\t\t\t<SliderRow\n\t\t\t\t\t\tlabel=\"Hue\"\n\t\t\t\t\t\tsuffix=\"°\"\n\t\t\t\t\t\tvalue={hsl.h}\n\t\t\t\t\t\tmax={360}\n\t\t\t\t\t\tstep={1}\n\t\t\t\t\t\tonChange={(h) => update({ h })}\n\t\t\t\t\t/>\n\t\t\t\t\t<SliderRow\n\t\t\t\t\t\tlabel=\"Saturation\"\n\t\t\t\t\t\tsuffix=\"%\"\n\t\t\t\t\t\tvalue={hsl.s}\n\t\t\t\t\t\tmax={100}\n\t\t\t\t\t\tstep={0.1}\n\t\t\t\t\t\tonChange={(s) => update({ s })}\n\t\t\t\t\t/>\n\t\t\t\t\t<SliderRow\n\t\t\t\t\t\tlabel=\"Lightness\"\n\t\t\t\t\t\tsuffix=\"%\"\n\t\t\t\t\t\tvalue={hsl.l}\n\t\t\t\t\t\tmax={100}\n\t\t\t\t\t\tstep={0.1}\n\t\t\t\t\t\tonChange={(l) => update({ l })}\n\t\t\t\t\t/>\n\t\t\t\t\t<div className=\"flex items-end gap-2\">\n\t\t\t\t\t\t<div className=\"flex-1 space-y-1\">\n\t\t\t\t\t\t\t<Label htmlFor={hexId} className=\"text-xs\">\n\t\t\t\t\t\t\t\tHex\n\t\t\t\t\t\t\t</Label>\n\t\t\t\t\t\t\t<Input\n\t\t\t\t\t\t\t\tid={hexId}\n\t\t\t\t\t\t\t\tref={hexInputRef}\n\t\t\t\t\t\t\t\tvalue={hexBuffer}\n\t\t\t\t\t\t\t\tonChange={handleHexChange}\n\t\t\t\t\t\t\t\tclassName=\"font-mono text-xs uppercase\"\n\t\t\t\t\t\t\t\tspellCheck={false}\n\t\t\t\t\t\t\t\tautoComplete=\"off\"\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t<span\n\t\t\t\t\t\t\taria-hidden=\"true\"\n\t\t\t\t\t\t\tclassName=\"h-9 w-9 shrink-0 rounded-md border border-border\"\n\t\t\t\t\t\t\tstyle={{ backgroundColor: `hsl(${value})` }}\n\t\t\t\t\t\t/>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t</PopoverContent>\n\t\t</Popover>\n\t);\n}\n\ninterface SliderRowProps {\n\tlabel: string;\n\tsuffix: string;\n\tvalue: number;\n\tmax: number;\n\tstep: number;\n\tonChange: (next: number) => void;\n}\n\n/**\n * One labeled slider row inside the ColorPicker popover. Internal helper —\n * not exported.\n * @param props - Slider config + value-change callback.\n */\nfunction SliderRow({ label, suffix, value, max, step, onChange }: SliderRowProps) {\n\tconst display = looksInteger(value) ? `${Math.round(value)}` : value.toFixed(1);\n\treturn (\n\t\t<div className=\"space-y-1.5\">\n\t\t\t<div className=\"flex items-center justify-between\">\n\t\t\t\t<Label className=\"text-xs\">{label}</Label>\n\t\t\t\t<span className=\"font-mono text-xs tabular-nums text-muted-foreground\">\n\t\t\t\t\t{display}\n\t\t\t\t\t{suffix}\n\t\t\t\t</span>\n\t\t\t</div>\n\t\t\t<Slider\n\t\t\t\tvalue={[value]}\n\t\t\t\tmin={0}\n\t\t\t\tmax={max}\n\t\t\t\tstep={step}\n\t\t\t\taria-label={label}\n\t\t\t\tonValueChange={(values) => onChange(values[0] ?? 0)}\n\t\t\t/>\n\t\t</div>\n\t);\n}\n\nexport { ColorPicker };\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/lib/utils.ts","../src/lib/color.ts","../src/primitives/input/input.tsx","../src/primitives/label/label.tsx","../src/primitives/slider/slider.tsx","../src/components/popover/popover.tsx","../src/components/color-picker/color-picker.tsx"],"names":["React","React2","jsx","React3","React4","jsxs"],"mappings":"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","file":"color-picker.js","sourcesContent":["import { type ClassValue, clsx } from \"clsx\";\nimport { twMerge } from \"tailwind-merge\";\n\n/**\n * Merge class names with Tailwind CSS conflict resolution.\n * @param inputs - Class values (strings, arrays, objects) to merge\n * @returns A single merged class string with Tailwind conflicts resolved\n */\nexport function cn(...inputs: ClassValue[]) {\n\treturn twMerge(clsx(inputs));\n}\n","/**\n * Color conversion utilities for the HSL-triplet token format used across\n * `@hex-core/tokens` themes (`H S% L%`, e.g. `\"240 5.9% 10%\"` — no `hsl()`\n * wrapper, no commas).\n *\n * The triplet is the round-trip-safe serialization for Hex Core: tokens flow\n * triplet → CSS `hsl(var(--token))` → rendered color, and the ColorPicker\n * component edits triplets directly. Hex/RGB conversions are display\n * adapters, not the source of truth.\n */\n\n/** Parsed HSL components. `h` is degrees (0–360); `s` and `l` are percentages (0–100). */\nexport interface HslTriplet {\n\th: number;\n\ts: number;\n\tl: number;\n}\n\n/** Parsed RGB components. Each channel is 0–255. */\nexport interface RgbColor {\n\tr: number;\n\tg: number;\n\tb: number;\n}\n\n/**\n * Parse an HSL triplet string into numeric components.\n *\n * Note: malformed input silently coerces to `{0,0,0}` (pure black) rather than\n * returning an error signal. Callers that need to distinguish \"user typed\n * black\" from \"user typed garbage\" should validate the input format first.\n * `hexToHslTriplet` returns `null` for malformed hex; this asymmetry is\n * intentional — triplets feed CSS variables where any non-color value would\n * already break rendering.\n *\n * @param triplet - String in the form `\"<H> <S>% <L>%\"` (e.g. `\"240 5.9% 10%\"`).\n * @returns Numeric components, or `{0,0,0}` if the input is malformed.\n */\nexport function parseHslTriplet(triplet: string): HslTriplet {\n\tconst parts = triplet.trim().split(/\\s+/);\n\treturn {\n\t\th: Number.parseFloat(parts[0]) || 0,\n\t\ts: Number.parseFloat(parts[1]) || 0,\n\t\tl: Number.parseFloat(parts[2]) || 0,\n\t};\n}\n\n/**\n * Format HSL components into an HSL triplet string (the canonical token format).\n * @param hsl - Numeric components.\n * @returns Triplet in the form `\"<H> <S>% <L>%\"`.\n */\nexport function formatHslTriplet({ h, s, l }: HslTriplet): string {\n\t// Tolerant integer check: rgbToHsl can produce values like 5.0000000001 due\n\t// to float arithmetic; format those as \"5\" rather than \"5.0\".\n\tconst round = (n: number) =>\n\t\tMath.abs(n - Math.round(n)) < 1e-6 ? `${Math.round(n)}` : n.toFixed(1);\n\treturn `${Math.round(h)} ${round(s)}% ${round(l)}%`;\n}\n\n/**\n * Convert HSL components to RGB.\n * @param h - Hue (0–360).\n * @param s - Saturation (0–100).\n * @param l - Lightness (0–100).\n * @returns RGB channels (0–255, rounded).\n */\nexport function hslToRgb(h: number, s: number, l: number): RgbColor {\n\tconst sN = s / 100;\n\tconst lN = l / 100;\n\tconst k = (n: number) => (n + h / 30) % 12;\n\tconst a = sN * Math.min(lN, 1 - lN);\n\tconst f = (n: number) => lN - a * Math.max(-1, Math.min(k(n) - 3, 9 - k(n), 1));\n\treturn {\n\t\tr: Math.round(255 * f(0)),\n\t\tg: Math.round(255 * f(8)),\n\t\tb: Math.round(255 * f(4)),\n\t};\n}\n\n/**\n * Convert RGB components to HSL.\n * @param r - Red (0–255).\n * @param g - Green (0–255).\n * @param b - Blue (0–255).\n * @returns HSL components (h: 0–360, s: 0–100, l: 0–100).\n */\nexport function rgbToHsl(r: number, g: number, b: number): HslTriplet {\n\tconst rN = r / 255;\n\tconst gN = g / 255;\n\tconst bN = b / 255;\n\tconst max = Math.max(rN, gN, bN);\n\tconst min = Math.min(rN, gN, bN);\n\tlet h = 0;\n\tlet s = 0;\n\tconst l = (max + min) / 2;\n\tif (max !== min) {\n\t\tconst d = max - min;\n\t\ts = l > 0.5 ? d / (2 - max - min) : d / (max + min);\n\t\tif (max === rN) h = (gN - bN) / d + (gN < bN ? 6 : 0);\n\t\telse if (max === gN) h = (bN - rN) / d + 2;\n\t\telse h = (rN - gN) / d + 4;\n\t\th /= 6;\n\t}\n\treturn { h: h * 360, s: s * 100, l: l * 100 };\n}\n\n/**\n * Convert an HSL triplet to a 6-digit hex string.\n * @param triplet - HSL triplet (e.g. `\"240 5.9% 10%\"`).\n * @returns Lowercase hex string with leading `#` (e.g. `\"#181a1f\"`).\n */\nexport function hslTripletToHex(triplet: string): string {\n\tconst { h, s, l } = parseHslTriplet(triplet);\n\tconst { r, g, b } = hslToRgb(h, s, l);\n\tconst toHex = (n: number) => n.toString(16).padStart(2, \"0\");\n\treturn `#${toHex(r)}${toHex(g)}${toHex(b)}`;\n}\n\n/**\n * Convert a hex string to an HSL triplet.\n * Accepts 3-digit (`#abc`) or 6-digit (`#aabbcc`) hex with optional `#`.\n * @param hex - Hex color string.\n * @returns HSL triplet, or `null` if the input is malformed.\n */\nexport function hexToHslTriplet(hex: string): string | null {\n\tconst clean = hex.trim().replace(/^#/, \"\");\n\tlet normalized: string;\n\tif (/^[0-9a-fA-F]{3}$/.test(clean)) {\n\t\tnormalized = clean\n\t\t\t.split(\"\")\n\t\t\t.map((c) => c + c)\n\t\t\t.join(\"\");\n\t} else if (/^[0-9a-fA-F]{6}$/.test(clean)) {\n\t\tnormalized = clean;\n\t} else {\n\t\treturn null;\n\t}\n\tconst r = Number.parseInt(normalized.slice(0, 2), 16);\n\tconst g = Number.parseInt(normalized.slice(2, 4), 16);\n\tconst b = Number.parseInt(normalized.slice(4, 6), 16);\n\treturn formatHslTriplet(rgbToHsl(r, g, b));\n}\n","import * as React from \"react\";\nimport { cn } from \"../../lib/utils.js\";\n\nexport type InputProps = React.InputHTMLAttributes<HTMLInputElement>;\n\nconst Input = React.forwardRef<HTMLInputElement, InputProps>(\n\t({ className, type, ...props }, ref) => {\n\t\treturn (\n\t\t\t<input\n\t\t\t\ttype={type}\n\t\t\t\tclassName={cn(\n\t\t\t\t\t\"flex h-[var(--control-height-md,2.5rem)] w-full rounded-md border border-input bg-background px-[var(--space-3,0.75rem)] py-[var(--space-2,0.5rem)] text-sm\",\n\t\t\t\t\t\"transition-all duration-[var(--duration-normal,200ms)] ease-out\",\n\t\t\t\t\t// inset-ring gives a self-borne edge so the input field is visible on flat\n\t\t\t\t\t// surfaces (token border alone is too low-contrast on bg-background=white).\n\t\t\t\t\t\"shadow-sm inset-ring-1 inset-ring-foreground/[0.06]\",\n\t\t\t\t\t\"file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground\",\n\t\t\t\t\t\"placeholder:text-muted-foreground\",\n\t\t\t\t\t\"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2\",\n\t\t\t\t\t\"focus-visible:shadow-md focus-visible:border-ring/50\",\n\t\t\t\t\t\"hover:border-ring/30 hover:shadow-md\",\n\t\t\t\t\t\"disabled:cursor-not-allowed disabled:opacity-50\",\n\t\t\t\t\tclassName,\n\t\t\t\t)}\n\t\t\t\tref={ref}\n\t\t\t\t{...props}\n\t\t\t/>\n\t\t);\n\t},\n);\nInput.displayName = \"Input\";\n\nexport { Input };\n","\"use client\";\n\nimport * as LabelPrimitive from \"@radix-ui/react-label\";\nimport { type VariantProps, cva } from \"class-variance-authority\";\nimport * as React from \"react\";\nimport { cn } from \"../../lib/utils.js\";\n\nconst labelVariants = cva(\n\t\"text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70\",\n);\n\nexport interface LabelProps\n\textends React.ComponentPropsWithoutRef<typeof LabelPrimitive.Root>,\n\t\tVariantProps<typeof labelVariants> {}\n\nconst Label = React.forwardRef<React.ComponentRef<typeof LabelPrimitive.Root>, LabelProps>(\n\t({ className, ...props }, ref) => (\n\t\t<LabelPrimitive.Root ref={ref} className={cn(labelVariants(), className)} {...props} />\n\t),\n);\nLabel.displayName = \"Label\";\n\nexport { Label };\n","\"use client\";\n\nimport * as SliderPrimitive from \"@radix-ui/react-slider\";\nimport * as React from \"react\";\nimport { cn } from \"../../lib/utils.js\";\n\n// Local ambient — components run in browsers where bundlers (Next, Vite, tsup)\n// replace `process.env.NODE_ENV` at build time. Avoids pulling @types/node into\n// the components package just for one dev-mode warning.\ndeclare const process: { env?: { NODE_ENV?: string } } | undefined;\n\ninterface SliderProps\n\textends React.ComponentPropsWithoutRef<typeof SliderPrimitive.Root> {\n\t/**\n\t * Per-thumb accessible labels. When the slider has multiple thumbs, pass\n\t * one entry per thumb (e.g. [\"Minimum\", \"Maximum\"]). For a single-thumb\n\t * slider, the Root's `aria-label` / `aria-labelledby` is mirrored onto\n\t * the thumb automatically — pass `thumbLabels` only when those defaults\n\t * are insufficient.\n\t */\n\tthumbLabels?: string[];\n}\n\n/**\n * A range input with one or more draggable thumbs.\n * Built on Radix UI Slider with keyboard controls (arrows, Home, End, PageUp/Down).\n */\nconst Slider = React.forwardRef<\n\tReact.ComponentRef<typeof SliderPrimitive.Root>,\n\tSliderProps\n>(({ className, thumbLabels, ...props }, ref) => {\n\tconst values = props.value ?? props.defaultValue ?? [0];\n\tconst rootLabel = props[\"aria-label\"];\n\tconst rootLabelledBy = props[\"aria-labelledby\"];\n\n\tif (\n\t\ttypeof process !== \"undefined\" &&\n\t\tprocess.env?.NODE_ENV !== \"production\" &&\n\t\tthumbLabels &&\n\t\tthumbLabels.length !== values.length\n\t) {\n\t\tconsole.warn(\n\t\t\t`Slider: thumbLabels.length (${thumbLabels.length}) does not match value.length (${values.length}). ` +\n\t\t\t\t`Missing labels fall back to indexed names; extra labels are ignored.`,\n\t\t);\n\t}\n\n\treturn (\n\t\t<SliderPrimitive.Root\n\t\t\tref={ref}\n\t\t\tclassName={cn(\"relative flex w-full touch-none select-none items-center\", className)}\n\t\t\t{...props}\n\t\t>\n\t\t\t<SliderPrimitive.Track className=\"relative h-2 w-full grow overflow-hidden rounded-full border border-foreground/[0.08] bg-secondary\">\n\t\t\t\t<SliderPrimitive.Range className=\"absolute h-full bg-primary\" />\n\t\t\t</SliderPrimitive.Track>\n\t\t\t{values.map((_, i) => {\n\t\t\t\tconst explicit = thumbLabels?.[i];\n\t\t\t\tconst fallback =\n\t\t\t\t\tvalues.length === 1\n\t\t\t\t\t\t? rootLabel\n\t\t\t\t\t\t: rootLabel\n\t\t\t\t\t\t\t? `${rootLabel} (${i + 1} of ${values.length})`\n\t\t\t\t\t\t\t: undefined;\n\t\t\t\treturn (\n\t\t\t\t\t<SliderPrimitive.Thumb\n\t\t\t\t\t\t// biome-ignore lint/suspicious/noArrayIndexKey: Radix renders one thumb per value by index\n\t\t\t\t\t\tkey={i}\n\t\t\t\t\t\taria-label={explicit ?? fallback}\n\t\t\t\t\t\taria-labelledby={\n\t\t\t\t\t\t\texplicit || fallback ? undefined : rootLabelledBy\n\t\t\t\t\t\t}\n\t\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\t\"block h-5 w-5 rounded-full border-2 border-primary bg-background\",\n\t\t\t\t\t\t\t\"transition-all duration-[var(--duration-normal,200ms)] ease-out shadow-md\",\n\t\t\t\t\t\t\t\"hover:shadow-lg hover:scale-110\",\n\t\t\t\t\t\t\t\"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2\",\n\t\t\t\t\t\t\t\"disabled:pointer-events-none disabled:opacity-50\",\n\t\t\t\t\t\t)}\n\t\t\t\t\t/>\n\t\t\t\t);\n\t\t\t})}\n\t\t</SliderPrimitive.Root>\n\t);\n});\nSlider.displayName = \"Slider\";\n\nexport type { SliderProps };\n\nexport { Slider };\n","\"use client\";\n\nimport * as PopoverPrimitive from \"@radix-ui/react-popover\";\nimport * as React from \"react\";\nimport { cn } from \"../../lib/utils.js\";\n\n/** Root container for a popover. */\nconst Popover = PopoverPrimitive.Root;\n\n/** The element that anchors and opens the popover. */\nconst PopoverTrigger = PopoverPrimitive.Trigger;\n\n/** Helper to explicitly anchor the popover to a different element. */\nconst PopoverAnchor = PopoverPrimitive.Anchor;\n\n/** The floating popover content panel. */\nconst PopoverContent = React.forwardRef<\n\tReact.ComponentRef<typeof PopoverPrimitive.Content>,\n\tReact.ComponentPropsWithoutRef<typeof PopoverPrimitive.Content>\n>(({ className, align = \"center\", sideOffset = 4, ...props }, ref) => (\n\t<PopoverPrimitive.Portal>\n\t\t<PopoverPrimitive.Content\n\t\t\tref={ref}\n\t\t\talign={align}\n\t\t\tsideOffset={sideOffset}\n\t\t\tclassName={cn(\n\t\t\t\t\"z-50 w-72 rounded-md border border-foreground/[0.08] bg-popover p-[var(--space-4,1rem)] text-popover-foreground shadow-md outline-none\",\n\t\t\t\t\"data-[state=open]:animate-in data-[state=closed]:animate-out\",\n\t\t\t\t\"data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0\",\n\t\t\t\t\"data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95\",\n\t\t\t\t\"data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2\",\n\t\t\t\tclassName,\n\t\t\t)}\n\t\t\t{...props}\n\t\t/>\n\t</PopoverPrimitive.Portal>\n));\nPopoverContent.displayName = \"PopoverContent\";\n\nexport { Popover, PopoverTrigger, PopoverContent, PopoverAnchor };\n","\"use client\";\n\nimport * as React from \"react\";\nimport { cn } from \"../../lib/utils.js\";\nimport {\n\tformatHslTriplet,\n\thexToHslTriplet,\n\thslTripletToHex,\n\tparseHslTriplet,\n} from \"../../lib/color.js\";\nimport { Input } from \"../../primitives/input/input.js\";\nimport { Label } from \"../../primitives/label/label.js\";\nimport { Slider } from \"../../primitives/slider/slider.js\";\nimport { Popover, PopoverContent, PopoverTrigger } from \"../popover/popover.js\";\n\n/** Tolerance for treating a float as integer-valued (handles arithmetic drift in HSL roundtrips). */\nconst INTEGER_EPSILON = 1e-6;\n\nconst looksInteger = (n: number) => Math.abs(n - Math.round(n)) < INTEGER_EPSILON;\n\n/** Props for the ColorPicker component. */\nexport interface ColorPickerProps {\n\t/**\n\t * Current color as an HSL triplet string (`\"<H> <S>% <L>%\"`, e.g. `\"240 5.9% 10%\"`).\n\t * Match the format used by `@hex-core/tokens`; round-trip safe.\n\t */\n\tvalue: string;\n\t/**\n\t * Called with the next HSL triplet whenever the user drags a slider or commits a valid hex value.\n\t * Not called for invalid hex input — the picker keeps the prior value until the input parses cleanly.\n\t */\n\tonChange: (value: string) => void;\n\t/**\n\t * Disable interaction. Native `disabled` attribute is set on the trigger so the\n\t * popover doesn't open via mouse or keyboard activation. Tab focus still lands\n\t * on the trigger per browser defaults; if you want to fully remove it from the\n\t * tab order, wrap in a parent that handles `tabIndex` accordingly.\n\t */\n\tdisabled?: boolean;\n\t/** Accessible name for the trigger button (defaults to \"Pick color\"). */\n\t\"aria-label\"?: string;\n\t/** Additional class names merged onto the trigger. */\n\tclassName?: string;\n}\n\n/**\n * HSL-native color picker. Edits an HSL triplet directly via three sliders\n * (H/S/L), with a hex input as a display adapter.\n *\n * Round-trip safe: triplet → hex → triplet preserves the slider state because\n * sliders are the source of truth and hex updates them only when valid.\n *\n * @param props - Controlled component; `value` and `onChange` are required.\n * @returns A trigger button that opens a popover with H/S/L sliders + hex input.\n * @example\n * ```tsx\n * const [color, setColor] = React.useState(\"240 5.9% 10%\");\n * <ColorPicker value={color} onChange={setColor} aria-label=\"Primary color\" />\n * ```\n */\nfunction ColorPicker({\n\tvalue,\n\tonChange,\n\tdisabled,\n\t\"aria-label\": ariaLabel = \"Pick color\",\n\tclassName,\n}: ColorPickerProps) {\n\t// Memoize so the slider-row callbacks below are stable across renders with\n\t// the same `value`; downstream `Slider`s avoid spurious re-mounts.\n\tconst hsl = React.useMemo(() => parseHslTriplet(value), [value]);\n\tconst hex = React.useMemo(() => hslTripletToHex(value), [value]);\n\n\tconst update = React.useCallback(\n\t\t(patch: Partial<typeof hsl>) => {\n\t\t\tonChange(formatHslTriplet({ ...hsl, ...patch }));\n\t\t},\n\t\t[hsl, onChange],\n\t);\n\n\t// Hex input is locally controlled so the user can type intermediate states\n\t// (e.g. \"#1a\") without committing. Sync the buffer to the canonical hex\n\t// whenever the input is NOT focused — typing into a focused input must not\n\t// be clobbered by a parent re-render reflecting our own `onChange`.\n\tconst hexInputRef = React.useRef<HTMLInputElement>(null);\n\tconst [hexBuffer, setHexBuffer] = React.useState(hex);\n\tReact.useEffect(() => {\n\t\tif (\n\t\t\ttypeof document === \"undefined\" ||\n\t\t\tdocument.activeElement !== hexInputRef.current\n\t\t) {\n\t\t\tsetHexBuffer(hex);\n\t\t}\n\t}, [hex]);\n\n\tconst handleHexChange = (e: React.ChangeEvent<HTMLInputElement>) => {\n\t\tconst next = e.target.value;\n\t\tsetHexBuffer(next);\n\t\tconst triplet = hexToHslTriplet(next);\n\t\tif (triplet !== null) onChange(triplet);\n\t};\n\n\tconst hexId = React.useId();\n\n\treturn (\n\t\t<Popover>\n\t\t\t<PopoverTrigger asChild>\n\t\t\t\t<button\n\t\t\t\t\ttype=\"button\"\n\t\t\t\t\tdisabled={disabled}\n\t\t\t\t\taria-label={ariaLabel}\n\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\"inline-flex h-9 items-center gap-2 rounded-md border border-input bg-background px-3 text-sm shadow-sm\",\n\t\t\t\t\t\t\"transition-all duration-[var(--duration-normal,200ms)] ease-out\",\n\t\t\t\t\t\t\"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2\",\n\t\t\t\t\t\t\"hover:shadow-md disabled:pointer-events-none disabled:opacity-50\",\n\t\t\t\t\t\tclassName,\n\t\t\t\t\t)}\n\t\t\t\t>\n\t\t\t\t\t<span\n\t\t\t\t\t\taria-hidden=\"true\"\n\t\t\t\t\t\tclassName=\"h-5 w-5 rounded-sm border border-border\"\n\t\t\t\t\t\tstyle={{ backgroundColor: `hsl(${value})` }}\n\t\t\t\t\t/>\n\t\t\t\t\t<span className=\"font-mono text-xs uppercase\">{hex}</span>\n\t\t\t\t</button>\n\t\t\t</PopoverTrigger>\n\t\t\t<PopoverContent className=\"w-72 p-4\" align=\"start\">\n\t\t\t\t<div className=\"space-y-4\">\n\t\t\t\t\t<SliderRow\n\t\t\t\t\t\tlabel=\"Hue\"\n\t\t\t\t\t\tsuffix=\"°\"\n\t\t\t\t\t\tvalue={hsl.h}\n\t\t\t\t\t\tmax={360}\n\t\t\t\t\t\tstep={1}\n\t\t\t\t\t\tonChange={(h) => update({ h })}\n\t\t\t\t\t/>\n\t\t\t\t\t<SliderRow\n\t\t\t\t\t\tlabel=\"Saturation\"\n\t\t\t\t\t\tsuffix=\"%\"\n\t\t\t\t\t\tvalue={hsl.s}\n\t\t\t\t\t\tmax={100}\n\t\t\t\t\t\tstep={0.1}\n\t\t\t\t\t\tonChange={(s) => update({ s })}\n\t\t\t\t\t/>\n\t\t\t\t\t<SliderRow\n\t\t\t\t\t\tlabel=\"Lightness\"\n\t\t\t\t\t\tsuffix=\"%\"\n\t\t\t\t\t\tvalue={hsl.l}\n\t\t\t\t\t\tmax={100}\n\t\t\t\t\t\tstep={0.1}\n\t\t\t\t\t\tonChange={(l) => update({ l })}\n\t\t\t\t\t/>\n\t\t\t\t\t<div className=\"flex items-end gap-2\">\n\t\t\t\t\t\t<div className=\"flex-1 space-y-1\">\n\t\t\t\t\t\t\t<Label htmlFor={hexId} className=\"text-xs\">\n\t\t\t\t\t\t\t\tHex\n\t\t\t\t\t\t\t</Label>\n\t\t\t\t\t\t\t<Input\n\t\t\t\t\t\t\t\tid={hexId}\n\t\t\t\t\t\t\t\tref={hexInputRef}\n\t\t\t\t\t\t\t\tvalue={hexBuffer}\n\t\t\t\t\t\t\t\tonChange={handleHexChange}\n\t\t\t\t\t\t\t\tclassName=\"font-mono text-xs uppercase\"\n\t\t\t\t\t\t\t\tspellCheck={false}\n\t\t\t\t\t\t\t\tautoComplete=\"off\"\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t<span\n\t\t\t\t\t\t\taria-hidden=\"true\"\n\t\t\t\t\t\t\tclassName=\"h-9 w-9 shrink-0 rounded-md border border-border\"\n\t\t\t\t\t\t\tstyle={{ backgroundColor: `hsl(${value})` }}\n\t\t\t\t\t\t/>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t</PopoverContent>\n\t\t</Popover>\n\t);\n}\n\ninterface SliderRowProps {\n\tlabel: string;\n\tsuffix: string;\n\tvalue: number;\n\tmax: number;\n\tstep: number;\n\tonChange: (next: number) => void;\n}\n\n/**\n * One labeled slider row inside the ColorPicker popover. Internal helper —\n * not exported.\n * @param props - Slider config + value-change callback.\n */\nfunction SliderRow({ label, suffix, value, max, step, onChange }: SliderRowProps) {\n\tconst display = looksInteger(value) ? `${Math.round(value)}` : value.toFixed(1);\n\treturn (\n\t\t<div className=\"space-y-1.5\">\n\t\t\t<div className=\"flex items-center justify-between\">\n\t\t\t\t<Label className=\"text-xs\">{label}</Label>\n\t\t\t\t<span className=\"font-mono text-xs tabular-nums text-muted-foreground\">\n\t\t\t\t\t{display}\n\t\t\t\t\t{suffix}\n\t\t\t\t</span>\n\t\t\t</div>\n\t\t\t<Slider\n\t\t\t\tvalue={[value]}\n\t\t\t\tmin={0}\n\t\t\t\tmax={max}\n\t\t\t\tstep={step}\n\t\t\t\taria-label={label}\n\t\t\t\tonValueChange={(values) => onChange(values[0] ?? 0)}\n\t\t\t/>\n\t\t</div>\n\t);\n}\n\nexport { ColorPicker };\n"]}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export { CompareSubject_alias_1 as CompareSubject } from './_tsup-dts-rollup.js';
|
|
2
|
+
export { CompareAttribute_alias_1 as CompareAttribute } from './_tsup-dts-rollup.js';
|
|
3
|
+
export { CompareTableProps_alias_1 as CompareTableProps } from './_tsup-dts-rollup.js';
|
|
4
|
+
export { CompareTable_alias_1 as CompareTable } from './_tsup-dts-rollup.js';
|