@data-profile/fingerprint 0.0.0-stage → 0.3.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.
@@ -0,0 +1,643 @@
1
+ import {
2
+ CALLOUT_GUTTER,
3
+ CALLOUT_HEIGHT,
4
+ DataError,
5
+ describeLayout,
6
+ emptyLayout,
7
+ formFromItems,
8
+ layoutFingerprint,
9
+ placeCallouts
10
+ } from "../chunk-y8jc1j2x.js";
11
+
12
+ // temp/src/react/FingerprintCanvas.tsx
13
+ import {
14
+ memo,
15
+ useCallback,
16
+ useEffect,
17
+ useId,
18
+ useMemo,
19
+ useRef,
20
+ useState
21
+ } from "react";
22
+
23
+ // temp/src/opacity.ts
24
+ function ridgeOpacity(itemCount, style = {}) {
25
+ const { show = "auto", fadeFrom = 60, fadeTo = 160, opacity = 0.22 } = style;
26
+ if (show === "never")
27
+ return 0;
28
+ if (show === "always" || itemCount <= fadeFrom)
29
+ return opacity;
30
+ if (itemCount >= fadeTo)
31
+ return 0;
32
+ return opacity * (1 - (itemCount - fadeFrom) / (fadeTo - fadeFrom));
33
+ }
34
+
35
+ // temp/src/react/renderers.tsx
36
+ import { markOpacity } from "@data-profile/core";
37
+ import { jsx } from "react/jsx-runtime";
38
+ var shapesRenderer = {
39
+ name: "shapes",
40
+ mark: ({ node }) => {
41
+ const r = node.size / 2;
42
+ const paint = {
43
+ fill: node.color,
44
+ fillOpacity: markOpacity(node)
45
+ };
46
+ if (node.shape === null)
47
+ return /* @__PURE__ */ jsx("circle", {
48
+ r: r * 0.8,
49
+ fill: "none",
50
+ stroke: node.color,
51
+ strokeOpacity: markOpacity(node),
52
+ strokeWidth: r * 0.35
53
+ });
54
+ switch (node.shape % 5) {
55
+ case 1:
56
+ return /* @__PURE__ */ jsx("rect", {
57
+ x: -r * 0.85,
58
+ y: -r * 0.85,
59
+ width: r * 1.7,
60
+ height: r * 1.7,
61
+ rx: r * 0.25,
62
+ ...paint
63
+ });
64
+ case 2:
65
+ return /* @__PURE__ */ jsx("polygon", {
66
+ points: `0,${-r} ${r * 0.95},${r * 0.75} ${-r * 0.95},${r * 0.75}`,
67
+ ...paint
68
+ });
69
+ case 3:
70
+ return /* @__PURE__ */ jsx("polygon", {
71
+ points: `0,${-r} ${r},0 0,${r} ${-r},0`,
72
+ ...paint
73
+ });
74
+ case 4:
75
+ return /* @__PURE__ */ jsx("polygon", {
76
+ points: Array.from({ length: 6 }, (_, i) => {
77
+ const a = Math.PI / 3 * i;
78
+ return `${(r * Math.cos(a)).toFixed(2)},${(r * Math.sin(a)).toFixed(2)}`;
79
+ }).join(" "),
80
+ ...paint
81
+ });
82
+ default:
83
+ return /* @__PURE__ */ jsx("circle", {
84
+ r,
85
+ ...paint
86
+ });
87
+ }
88
+ }
89
+ };
90
+ var dotsRenderer = {
91
+ name: "dots",
92
+ mark: ({ node }) => node.shape === null ? /* @__PURE__ */ jsx("circle", {
93
+ r: node.size * 0.4,
94
+ fill: "none",
95
+ stroke: node.color,
96
+ strokeOpacity: markOpacity(node),
97
+ strokeWidth: node.size * 0.18
98
+ }) : /* @__PURE__ */ jsx("circle", {
99
+ r: node.size / 2,
100
+ fill: node.color,
101
+ fillOpacity: markOpacity(node)
102
+ })
103
+ };
104
+
105
+ // temp/src/react/FingerprintCanvas.tsx
106
+ import { jsx as jsx2, jsxs, Fragment } from "react/jsx-runtime";
107
+ var halfTurn = (degrees) => ((degrees + 90) % 180 + 180) % 180 - 90;
108
+ var cssString = (value) => `"${value.replace(/["\\]/g, "\\$&").replace(/\n/g, "\\a ")}"`;
109
+ function useCarriedMarks(layout) {
110
+ const committed = useRef({ keyOf: new Map, order: [] });
111
+ const pending = useRef(committed.current);
112
+ const marks = useMemo(() => {
113
+ const previous = committed.current;
114
+ const keyOf = new Map;
115
+ const nodeOf = new Map;
116
+ layout.nodes.forEach((node, i) => {
117
+ let key;
118
+ for (const id of node.itemIds) {
119
+ const candidate = previous.keyOf.get(id);
120
+ if (candidate !== undefined && !nodeOf.has(candidate)) {
121
+ key = candidate;
122
+ break;
123
+ }
124
+ }
125
+ if (key === undefined)
126
+ key = nodeOf.has(node.id) ? `${node.id}~${i}` : node.id;
127
+ nodeOf.set(key, node);
128
+ for (const id of node.itemIds)
129
+ keyOf.set(id, key);
130
+ });
131
+ const kept = previous.order.filter((key) => nodeOf.has(key));
132
+ const keptSet = new Set(kept);
133
+ const order = [...kept, ...[...nodeOf.keys()].filter((key) => !keptSet.has(key))];
134
+ pending.current = { keyOf, order };
135
+ return order.map((key) => ({ key, node: nodeOf.get(key) }));
136
+ }, [layout]);
137
+ useEffect(() => {
138
+ committed.current = pending.current;
139
+ }, [marks]);
140
+ return marks;
141
+ }
142
+ var DIMMED = 0.22;
143
+ var Mark = memo(function Mark2({
144
+ node,
145
+ dimensions,
146
+ arrange,
147
+ renderer,
148
+ idPrefix,
149
+ onChoose
150
+ }) {
151
+ const attributes = {};
152
+ dimensions.forEach((id, k) => attributes[`data-d${k}`] = node.categories[id] ?? "");
153
+ return /* @__PURE__ */ jsx2("g", {
154
+ className: "fp-mark",
155
+ "data-category": node.categories[arrange],
156
+ ...attributes,
157
+ onClick: onChoose ? () => onChoose(node.categories[arrange]) : undefined,
158
+ style: {
159
+ transform: `translate(${node.x}px, ${node.y}px) rotate(${halfTurn(node.rotation)}deg)`
160
+ },
161
+ children: renderer.mark({ node, idPrefix })
162
+ });
163
+ });
164
+ function useWidth() {
165
+ const ref = useRef(null);
166
+ const [width, setWidth] = useState(null);
167
+ useEffect(() => {
168
+ const element = ref.current;
169
+ if (!element || typeof ResizeObserver !== "function")
170
+ return;
171
+ const observer = new ResizeObserver(([entry]) => setWidth(entry?.contentRect.width ?? null));
172
+ observer.observe(element);
173
+ return () => observer.disconnect();
174
+ }, []);
175
+ return [ref, width];
176
+ }
177
+ var visuallyHidden = {
178
+ position: "absolute",
179
+ width: 1,
180
+ height: 1,
181
+ overflow: "hidden",
182
+ clip: "rect(0 0 0 0)",
183
+ whiteSpace: "nowrap"
184
+ };
185
+ function FingerprintCanvas({
186
+ layout,
187
+ title,
188
+ description,
189
+ renderer = shapesRenderer,
190
+ ridges,
191
+ selected = null,
192
+ onSelect,
193
+ annotations = [],
194
+ callouts,
195
+ unit = "items",
196
+ motion,
197
+ className
198
+ }) {
199
+ const uid = useId().replace(/[^\w-]/g, "");
200
+ const [ref, width] = useWidth();
201
+ const placed = useMemo(() => placeCallouts(layout, annotations), [layout, annotations]);
202
+ const mode = placed.length === 0 ? "none" : (callouts?.placement ?? "auto") === "auto" ? width !== null && width < (callouts?.breakpoint ?? 640) ? "below" : "beside" : callouts.placement;
203
+ const gutter = mode === "beside" ? CALLOUT_GUTTER : 0;
204
+ const opacity = ridgeOpacity(layout.itemCount, ridges);
205
+ const selectedEntry = selected ? layout.legends.find((group) => group.dimensionId === selected.dimensionId)?.entries.find((entry) => entry.id === selected.categoryId) : undefined;
206
+ const latest = useRef({ selected, onSelect, dimensionId: layout.encoding.arrange });
207
+ latest.current = { selected, onSelect, dimensionId: layout.encoding.arrange };
208
+ const choose = useCallback((categoryId) => {
209
+ const { selected: current, onSelect: select, dimensionId } = latest.current;
210
+ select?.(current?.dimensionId === dimensionId && current.categoryId === categoryId ? null : { dimensionId, categoryId });
211
+ }, []);
212
+ const dimensions = useMemo(() => layout.legends.map((g) => g.dimensionId), [layout.legends]);
213
+ const marks = useCarriedMarks(layout);
214
+ const scope = `.fp-${uid} .fp-mark`;
215
+ const selectedIndex = selected ? dimensions.indexOf(selected.dimensionId) : -1;
216
+ const glides = layout.nodes.length <= (motion?.glide ?? 800);
217
+ const fades = layout.nodes.length <= (motion?.fade ?? 400);
218
+ const css = [
219
+ `${scope}{transition:${glides ? "transform 700ms cubic-bezier(.2,.7,.2,1)," : ""}opacity 250ms ease${onSelect ? ";cursor:pointer" : ""}}`,
220
+ fades ? `${scope}>*{transition:fill 600ms ease,fill-opacity 600ms ease,stroke 600ms ease}` : "",
221
+ `@media (prefers-reduced-motion:reduce){${scope},${scope}>*{transition:none}}`,
222
+ selected && selectedIndex >= 0 ? `${scope}:not([data-d${selectedIndex}=${cssString(selected.categoryId)}]){opacity:${DIMMED}}` : ""
223
+ ].join(`
224
+ `);
225
+ const ridgeLines = useMemo(() => opacity > 0 ? /* @__PURE__ */ jsx2("g", {
226
+ className: "fp-ridges",
227
+ fill: "none",
228
+ stroke: ridges?.color ?? "currentColor",
229
+ strokeOpacity: opacity,
230
+ strokeWidth: layout.ridgeSpacing * (ridges?.width ?? 0.12),
231
+ strokeLinecap: "round",
232
+ strokeLinejoin: "round",
233
+ children: layout.ridges.map((ridge, i) => /* @__PURE__ */ jsx2("polyline", {
234
+ points: ridge.map(([x, y]) => `${x.toFixed(1)},${y.toFixed(1)}`).join(" ")
235
+ }, i))
236
+ }) : null, [opacity, ridges?.color, ridges?.width, layout.ridgeSpacing, layout.ridges]);
237
+ return /* @__PURE__ */ jsxs("div", {
238
+ ref,
239
+ className: ["fp-canvas", `fp-${uid}`, className].filter(Boolean).join(" "),
240
+ style: { position: "relative" },
241
+ "data-selection": selected ? `${selected.dimensionId}:${selected.categoryId}` : undefined,
242
+ children: [
243
+ /* @__PURE__ */ jsx2("style", {
244
+ children: css
245
+ }),
246
+ /* @__PURE__ */ jsxs("svg", {
247
+ viewBox: `${-gutter} 0 ${layout.width + gutter * 2} ${layout.height}`,
248
+ role: "img",
249
+ "aria-labelledby": `${uid}-title ${uid}-desc`,
250
+ style: { display: "block", width: "100%", height: "auto", overflow: "visible" },
251
+ children: [
252
+ /* @__PURE__ */ jsx2("title", {
253
+ id: `${uid}-title`,
254
+ children: title
255
+ }),
256
+ /* @__PURE__ */ jsx2("desc", {
257
+ id: `${uid}-desc`,
258
+ children: description
259
+ }),
260
+ renderer.defs && /* @__PURE__ */ jsx2("defs", {
261
+ children: renderer.defs({ idPrefix: uid, legends: layout.legends })
262
+ }),
263
+ ridgeLines,
264
+ /* @__PURE__ */ jsx2("g", {
265
+ className: "fp-marks",
266
+ children: marks.map(({ key, node }) => /* @__PURE__ */ jsx2(Mark, {
267
+ node,
268
+ dimensions,
269
+ arrange: layout.encoding.arrange,
270
+ renderer,
271
+ idPrefix: uid,
272
+ onChoose: onSelect ? choose : undefined
273
+ }, key))
274
+ }),
275
+ mode === "beside" && placed.map((callout) => {
276
+ const left = callout.side === "left";
277
+ const edge = left ? -12 : layout.width + 12;
278
+ return /* @__PURE__ */ jsxs("g", {
279
+ className: "fp-callout",
280
+ "data-side": callout.side,
281
+ children: [
282
+ /* @__PURE__ */ jsx2("polyline", {
283
+ className: "fp-callout__leader",
284
+ points: `${edge},${callout.labelY + 14} ${left ? edge + 10 : edge - 10},${callout.labelY + 14} ${callout.anchor.x},${callout.anchor.y}`,
285
+ fill: "none",
286
+ stroke: "currentColor",
287
+ strokeWidth: 1.2
288
+ }),
289
+ /* @__PURE__ */ jsx2("circle", {
290
+ cx: callout.anchor.x,
291
+ cy: callout.anchor.y,
292
+ r: 3.5,
293
+ fill: "currentColor"
294
+ }),
295
+ /* @__PURE__ */ jsx2("foreignObject", {
296
+ x: left ? -gutter : layout.width + 16,
297
+ y: callout.labelY,
298
+ width: gutter - 16,
299
+ height: CALLOUT_HEIGHT,
300
+ children: /* @__PURE__ */ jsxs("div", {
301
+ className: "fp-callout__label",
302
+ style: { textAlign: left ? "right" : "left", fontSize: 13, lineHeight: 1.35 },
303
+ children: [
304
+ /* @__PURE__ */ jsx2("strong", {
305
+ style: { display: "block", fontSize: 15 },
306
+ children: callout.annotation.title
307
+ }),
308
+ callout.annotation.body && /* @__PURE__ */ jsx2("span", {
309
+ children: callout.annotation.body
310
+ })
311
+ ]
312
+ })
313
+ })
314
+ ]
315
+ }, callout.annotation.id);
316
+ }),
317
+ mode === "below" && placed.map((callout) => /* @__PURE__ */ jsxs("g", {
318
+ className: "fp-callout-marker",
319
+ "aria-hidden": "true",
320
+ children: [
321
+ /* @__PURE__ */ jsx2("circle", {
322
+ cx: callout.anchor.x,
323
+ cy: callout.anchor.y,
324
+ r: 11,
325
+ fill: "currentColor"
326
+ }),
327
+ /* @__PURE__ */ jsx2("text", {
328
+ x: callout.anchor.x,
329
+ y: callout.anchor.y,
330
+ dy: "0.35em",
331
+ textAnchor: "middle",
332
+ fontSize: 13,
333
+ fontWeight: 700,
334
+ style: { fill: "var(--fp-callout-ink, Canvas)" },
335
+ children: callout.number
336
+ })
337
+ ]
338
+ }, callout.annotation.id))
339
+ ]
340
+ }),
341
+ mode === "below" && /* @__PURE__ */ jsx2("ol", {
342
+ className: "fp-callouts",
343
+ children: placed.map((callout) => /* @__PURE__ */ jsxs("li", {
344
+ value: callout.number,
345
+ children: [
346
+ /* @__PURE__ */ jsx2("strong", {
347
+ children: callout.annotation.title
348
+ }),
349
+ callout.annotation.body && /* @__PURE__ */ jsxs(Fragment, {
350
+ children: [
351
+ " ",
352
+ callout.annotation.body
353
+ ]
354
+ })
355
+ ]
356
+ }, callout.annotation.id))
357
+ }),
358
+ mode === "beside" && /* @__PURE__ */ jsx2("ul", {
359
+ style: visuallyHidden,
360
+ children: placed.map((callout) => /* @__PURE__ */ jsxs("li", {
361
+ children: [
362
+ callout.annotation.title,
363
+ ". ",
364
+ callout.annotation.body
365
+ ]
366
+ }, callout.annotation.id))
367
+ }),
368
+ /* @__PURE__ */ jsx2("p", {
369
+ role: "status",
370
+ "aria-live": "polite",
371
+ style: visuallyHidden,
372
+ children: selectedEntry ? `${selectedEntry.label}: ${selectedEntry.count.toLocaleString("en-GB")} ${unit}.` : ""
373
+ })
374
+ ]
375
+ });
376
+ }
377
+ // temp/src/react/FingerprintControls.tsx
378
+ import { useId as useId2 } from "react";
379
+ import { jsx as jsx3, jsxs as jsxs2, Fragment as Fragment2 } from "react/jsx-runtime";
380
+ var join = (...names) => names.filter(Boolean).join(" ");
381
+ function FingerprintViewSwitch({
382
+ views,
383
+ value,
384
+ onChange,
385
+ label = "View",
386
+ className
387
+ }) {
388
+ return /* @__PURE__ */ jsx3("div", {
389
+ role: "group",
390
+ "aria-label": label,
391
+ className: join("fp-views", className),
392
+ children: views.map((view) => /* @__PURE__ */ jsx3("button", {
393
+ type: "button",
394
+ className: "fp-views__view",
395
+ "aria-pressed": view.id === value,
396
+ onClick: () => onChange(view.id),
397
+ children: view.label
398
+ }, view.id))
399
+ });
400
+ }
401
+ var isSelected = (selected, group, id) => selected?.dimensionId === group.dimensionId && selected.categoryId === id;
402
+ function FingerprintLegend({
403
+ group,
404
+ renderer = shapesRenderer,
405
+ selected = null,
406
+ onSelect,
407
+ heading = true,
408
+ counts = true,
409
+ hideEmpty = false,
410
+ className
411
+ }) {
412
+ const uid = useId2().replace(/[^\w-]/g, "");
413
+ const headingId = `${uid}-heading`;
414
+ return /* @__PURE__ */ jsxs2("div", {
415
+ className: join("fp-legend", className),
416
+ "data-channels": group.channels.join(" "),
417
+ children: [
418
+ heading && /* @__PURE__ */ jsx3("p", {
419
+ id: headingId,
420
+ className: "fp-legend__heading",
421
+ children: group.label
422
+ }),
423
+ /* @__PURE__ */ jsx3("ul", {
424
+ "aria-labelledby": heading ? headingId : undefined,
425
+ "aria-label": heading ? undefined : group.label,
426
+ style: { listStyle: "none", padding: 0, margin: 0 },
427
+ children: group.entries.filter((entry) => !hideEmpty || entry.count > 0).map((entry, index) => {
428
+ const prefix = `${uid}-${index}`;
429
+ const swatch = /* @__PURE__ */ jsxs2("svg", {
430
+ viewBox: "-7 -7 14 14",
431
+ width: "1em",
432
+ height: "1em",
433
+ "aria-hidden": "true",
434
+ className: "fp-legend__swatch",
435
+ children: [
436
+ renderer.defs && /* @__PURE__ */ jsx3("defs", {
437
+ children: renderer.defs({ idPrefix: prefix, legends: [group] })
438
+ }),
439
+ renderer.mark({
440
+ node: {
441
+ id: entry.id,
442
+ itemIds: [],
443
+ categories: { [group.dimensionId]: entry.id },
444
+ x: 0,
445
+ y: 0,
446
+ rotation: 0,
447
+ size: 12,
448
+ weight: 1,
449
+ color: entry.color,
450
+ shape: entry.shape,
451
+ lightness: entry.lightness
452
+ },
453
+ idPrefix: prefix
454
+ })
455
+ ]
456
+ });
457
+ const text = /* @__PURE__ */ jsxs2(Fragment2, {
458
+ children: [
459
+ swatch,
460
+ /* @__PURE__ */ jsx3("span", {
461
+ className: "fp-legend__label",
462
+ children: entry.label
463
+ }),
464
+ counts && /* @__PURE__ */ jsx3("span", {
465
+ className: "fp-legend__count",
466
+ children: entry.count.toLocaleString("en-GB")
467
+ })
468
+ ]
469
+ });
470
+ return /* @__PURE__ */ jsx3("li", {
471
+ className: "fp-legend__entry",
472
+ children: onSelect ? /* @__PURE__ */ jsx3("button", {
473
+ type: "button",
474
+ className: "fp-legend__toggle",
475
+ "aria-pressed": isSelected(selected, group, entry.id),
476
+ onClick: () => onSelect(isSelected(selected, group, entry.id) ? null : { dimensionId: group.dimensionId, categoryId: entry.id }),
477
+ children: text
478
+ }) : text
479
+ }, entry.id);
480
+ })
481
+ })
482
+ ]
483
+ });
484
+ }
485
+ function FingerprintList({
486
+ group,
487
+ unit = "items",
488
+ linkLabel = "Explore",
489
+ selected = null,
490
+ onSelect,
491
+ className
492
+ }) {
493
+ const total = group.entries.reduce((sum, entry) => sum + entry.count, 0);
494
+ const share = (count) => total ? `${Math.round(count / total * 100)}%` : "–";
495
+ const links = group.entries.some((entry) => entry.href);
496
+ return /* @__PURE__ */ jsxs2("table", {
497
+ className: join("fp-list", className),
498
+ children: [
499
+ /* @__PURE__ */ jsxs2("caption", {
500
+ children: [
501
+ group.label,
502
+ ": ",
503
+ total.toLocaleString("en-GB"),
504
+ " ",
505
+ unit
506
+ ]
507
+ }),
508
+ /* @__PURE__ */ jsx3("thead", {
509
+ children: /* @__PURE__ */ jsxs2("tr", {
510
+ children: [
511
+ /* @__PURE__ */ jsx3("th", {
512
+ scope: "col",
513
+ children: group.label
514
+ }),
515
+ /* @__PURE__ */ jsx3("th", {
516
+ scope: "col",
517
+ children: unit[0].toUpperCase() + unit.slice(1)
518
+ }),
519
+ /* @__PURE__ */ jsx3("th", {
520
+ scope: "col",
521
+ children: "Share"
522
+ }),
523
+ links && /* @__PURE__ */ jsx3("th", {
524
+ scope: "col",
525
+ children: /* @__PURE__ */ jsx3("span", {
526
+ className: "fp-list__hidden",
527
+ children: "Link"
528
+ })
529
+ })
530
+ ]
531
+ })
532
+ }),
533
+ /* @__PURE__ */ jsx3("tbody", {
534
+ children: group.entries.map((entry) => /* @__PURE__ */ jsxs2("tr", {
535
+ "data-selected": isSelected(selected, group, entry.id) || undefined,
536
+ children: [
537
+ /* @__PURE__ */ jsx3("th", {
538
+ scope: "row",
539
+ children: onSelect ? /* @__PURE__ */ jsx3("button", {
540
+ type: "button",
541
+ className: "fp-list__toggle",
542
+ "aria-pressed": isSelected(selected, group, entry.id),
543
+ onClick: () => onSelect(isSelected(selected, group, entry.id) ? null : { dimensionId: group.dimensionId, categoryId: entry.id }),
544
+ children: entry.label
545
+ }) : entry.label
546
+ }),
547
+ /* @__PURE__ */ jsx3("td", {
548
+ children: entry.count.toLocaleString("en-GB")
549
+ }),
550
+ /* @__PURE__ */ jsx3("td", {
551
+ children: share(entry.count)
552
+ }),
553
+ links && /* @__PURE__ */ jsx3("td", {
554
+ children: entry.href && /* @__PURE__ */ jsxs2("a", {
555
+ href: entry.href,
556
+ children: [
557
+ linkLabel,
558
+ /* @__PURE__ */ jsxs2("span", {
559
+ className: "fp-list__hidden",
560
+ children: [
561
+ " ",
562
+ entry.label
563
+ ]
564
+ })
565
+ ]
566
+ })
567
+ })
568
+ ]
569
+ }, entry.id))
570
+ })
571
+ ]
572
+ });
573
+ }
574
+ // temp/src/react/useFingerprint.ts
575
+ import { useCallback as useCallback2, useMemo as useMemo2, useState as useState2 } from "react";
576
+ function useFingerprint({
577
+ items,
578
+ dimensions,
579
+ views: given,
580
+ initialViewId,
581
+ form,
582
+ seed = "fingerprint",
583
+ maxMarks,
584
+ unit = "items"
585
+ }) {
586
+ const views = useMemo2(() => given ?? dimensions.map((d) => ({ id: d.id, label: d.label, encoding: { arrange: d.id } })), [given, dimensions]);
587
+ const [viewId, setView] = useState2(initialViewId ?? views[0]?.id ?? "");
588
+ const [selected, setSelected] = useState2(null);
589
+ const view = views.find((v) => v.id === viewId) ?? views[0];
590
+ const resolvedForm = useMemo2(() => ({ ...formFromItems(items, dimensions, seed), ...form }), [items, dimensions, seed, form]);
591
+ const { layout, error } = useMemo2(() => {
592
+ try {
593
+ const layout2 = layoutFingerprint({
594
+ items,
595
+ dimensions,
596
+ encoding: view?.encoding,
597
+ seed,
598
+ form: resolvedForm,
599
+ maxMarks
600
+ });
601
+ return { layout: layout2, error: null };
602
+ } catch (caught) {
603
+ if (caught instanceof DataError)
604
+ return { layout: emptyLayout(), error: caught };
605
+ throw caught;
606
+ }
607
+ }, [items, dimensions, view, seed, resolvedForm, maxMarks]);
608
+ const description = useMemo2(() => error ? error.message : describeLayout(layout, unit), [layout, error, unit]);
609
+ const setViewId = useCallback2((id) => {
610
+ setView(id);
611
+ setSelected(null);
612
+ }, []);
613
+ const canvasProps = useMemo2(() => ({ layout, description, selected, onSelect: setSelected, unit }), [layout, description, selected, unit]);
614
+ const legendProps = useCallback2((group) => ({ group, selected, onSelect: setSelected }), [selected]);
615
+ const listProps = useCallback2((group) => ({ group, selected, onSelect: setSelected, unit }), [selected, unit]);
616
+ return {
617
+ layout,
618
+ description,
619
+ error,
620
+ views,
621
+ view,
622
+ viewId: view?.id ?? "",
623
+ setViewId,
624
+ selected,
625
+ setSelected,
626
+ canvasProps,
627
+ legendProps,
628
+ listProps
629
+ };
630
+ }
631
+ export {
632
+ useFingerprint,
633
+ shapesRenderer,
634
+ markOpacity,
635
+ dotsRenderer,
636
+ FingerprintViewSwitch,
637
+ FingerprintList,
638
+ FingerprintLegend,
639
+ FingerprintCanvas
640
+ };
641
+
642
+ //# debugId=2AE2D4ED63A9036664756E2164756E21
643
+ //# sourceMappingURL=index.js.map