@broberg/bodymap 0.1.2 → 0.1.4
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/react.cjs +228 -32
- package/dist/react.cjs.map +1 -1
- package/dist/react.d.cts +83 -4
- package/dist/react.d.ts +83 -4
- package/dist/react.js +226 -33
- package/dist/react.js.map +1 -1
- package/package.json +1 -1
package/dist/react.js
CHANGED
|
@@ -39,6 +39,9 @@ var REGIONS = [
|
|
|
39
39
|
];
|
|
40
40
|
var REGION_KEY_SET = new Set(REGIONS.map((r) => r.key));
|
|
41
41
|
REGIONS.map((r) => r.key);
|
|
42
|
+
function getRegion(key) {
|
|
43
|
+
return REGIONS.find((r) => r.key === key);
|
|
44
|
+
}
|
|
42
45
|
var PAIN_TYPES = ["stikkende", "dump", "konstant", "jagende"];
|
|
43
46
|
var painPointSchema = z.object({
|
|
44
47
|
region: z.string().refine((k) => REGION_KEY_SET.has(k), { message: "unknown region" }),
|
|
@@ -55,6 +58,18 @@ function isSelectable(key, config = {}) {
|
|
|
55
58
|
if (s?.visible === false) return false;
|
|
56
59
|
return s?.selectable ?? true;
|
|
57
60
|
}
|
|
61
|
+
var defaultPalette = {
|
|
62
|
+
body: "#d2d7de",
|
|
63
|
+
hover: "#8fd0cd",
|
|
64
|
+
selected: "#5cc4b7",
|
|
65
|
+
heat: { low: "#fcd34d", mid: "#fb923c", high: "#ef4444" }
|
|
66
|
+
};
|
|
67
|
+
function heatFor(intensity, palette = defaultPalette) {
|
|
68
|
+
return intensity >= 7 ? palette.heat.high : intensity >= 4 ? palette.heat.mid : palette.heat.low;
|
|
69
|
+
}
|
|
70
|
+
function baseColorFor(regionKey, palette = defaultPalette) {
|
|
71
|
+
return palette.regions?.[regionKey] ?? palette.body;
|
|
72
|
+
}
|
|
58
73
|
z.object({
|
|
59
74
|
schema: z.literal("bodymap/v1"),
|
|
60
75
|
view: z.enum(["front", "back", "left", "right"]),
|
|
@@ -165,6 +180,84 @@ var FILL_BACK = {
|
|
|
165
180
|
hip_left: "#c99bd6",
|
|
166
181
|
hip_right: "#c99bd6"
|
|
167
182
|
};
|
|
183
|
+
var daRegions = Object.fromEntries(REGIONS.map((r) => [r.key, r.label]));
|
|
184
|
+
var EN_CODE = {
|
|
185
|
+
HEAD: "Head",
|
|
186
|
+
NECK: "Neck",
|
|
187
|
+
CHEST: "Chest",
|
|
188
|
+
THORA: "Upper back",
|
|
189
|
+
LUMBAR: "Lower back",
|
|
190
|
+
GROIN: "Groin",
|
|
191
|
+
SHOULDER: "Shoulder",
|
|
192
|
+
UARM: "Upper arm",
|
|
193
|
+
ELBOW: "Elbow",
|
|
194
|
+
FARM: "Forearm",
|
|
195
|
+
WRIST: "Wrist",
|
|
196
|
+
HAND: "Hand",
|
|
197
|
+
HIP: "Hip",
|
|
198
|
+
THIGH: "Thigh",
|
|
199
|
+
KNEE: "Knee",
|
|
200
|
+
LOWLEG: "Lower leg",
|
|
201
|
+
ANKLE: "Ankle",
|
|
202
|
+
FOOT: "Foot"
|
|
203
|
+
};
|
|
204
|
+
var enRegions = Object.fromEntries(
|
|
205
|
+
REGIONS.map((r) => [r.key, EN_CODE[r.code] + (r.side ? r.side === "left" ? ", left" : ", right" : "")])
|
|
206
|
+
);
|
|
207
|
+
var LABELS_DA = {
|
|
208
|
+
regions: daRegions,
|
|
209
|
+
qualities: { stikkende: "stikkende", dump: "dump", konstant: "konstant", jagende: "jagende" },
|
|
210
|
+
intensity: "Intensitet (0-10)",
|
|
211
|
+
quality: "Kvalitet",
|
|
212
|
+
remove: "Fjern punkt",
|
|
213
|
+
front: "Forfra",
|
|
214
|
+
back: "Bagfra",
|
|
215
|
+
empty: "V\xE6lg en kropsdel for at markere smerte.",
|
|
216
|
+
before: "F\xF8r",
|
|
217
|
+
after: "Efter",
|
|
218
|
+
noChange: "Ingen \xE6ndring",
|
|
219
|
+
change: { new: "nyt", resolved: "forsvundet", improved: "bedre", worse: "v\xE6rre" },
|
|
220
|
+
ariaMarked: (n, i, q) => `${n}, smerte ${i} af 10${q ? ", " + q : ""}`,
|
|
221
|
+
ariaUnmarked: (n) => `${n}, ikke markeret. Aktiv\xE9r for at markere smerte.`,
|
|
222
|
+
svgLabel: "Kropskort \u2014 v\xE6lg hvor det g\xF8r ondt",
|
|
223
|
+
viewLabel: "Visning",
|
|
224
|
+
zoomLabel: "Zoom",
|
|
225
|
+
zoomIn: "Zoom ind",
|
|
226
|
+
zoomOut: "Zoom ud",
|
|
227
|
+
zoomReset: "Nulstil zoom"
|
|
228
|
+
};
|
|
229
|
+
var LABELS_EN = {
|
|
230
|
+
regions: enRegions,
|
|
231
|
+
qualities: { stikkende: "stabbing", dump: "dull", konstant: "constant", jagende: "shooting" },
|
|
232
|
+
intensity: "Intensity (0-10)",
|
|
233
|
+
quality: "Quality",
|
|
234
|
+
remove: "Remove point",
|
|
235
|
+
front: "Front",
|
|
236
|
+
back: "Back",
|
|
237
|
+
empty: "Pick a body part to mark pain.",
|
|
238
|
+
before: "Before",
|
|
239
|
+
after: "After",
|
|
240
|
+
noChange: "No change",
|
|
241
|
+
change: { new: "new", resolved: "resolved", improved: "improved", worse: "worse" },
|
|
242
|
+
ariaMarked: (n, i, q) => `${n}, pain ${i} of 10${q ? ", " + q : ""}`,
|
|
243
|
+
ariaUnmarked: (n) => `${n}, not marked. Activate to mark pain.`,
|
|
244
|
+
svgLabel: "Body map \u2014 pick where it hurts",
|
|
245
|
+
viewLabel: "View",
|
|
246
|
+
zoomLabel: "Zoom",
|
|
247
|
+
zoomIn: "Zoom in",
|
|
248
|
+
zoomOut: "Zoom out",
|
|
249
|
+
zoomReset: "Reset zoom"
|
|
250
|
+
};
|
|
251
|
+
function resolveLabels(locale, overrides) {
|
|
252
|
+
const base = locale === "en" ? LABELS_EN : LABELS_DA;
|
|
253
|
+
if (!overrides) return base;
|
|
254
|
+
return {
|
|
255
|
+
...base,
|
|
256
|
+
...overrides,
|
|
257
|
+
regions: { ...base.regions, ...overrides.regions },
|
|
258
|
+
qualities: { ...base.qualities, ...overrides.qualities }
|
|
259
|
+
};
|
|
260
|
+
}
|
|
168
261
|
var VBW = 260;
|
|
169
262
|
var VBH = 470;
|
|
170
263
|
var MIN_HIT = 18;
|
|
@@ -184,6 +277,15 @@ function hitShape(s) {
|
|
|
184
277
|
function heat(v) {
|
|
185
278
|
return v >= 7 ? "#ef4444" : v >= 4 ? "#fb923c" : "#fcd34d";
|
|
186
279
|
}
|
|
280
|
+
function heatTier(v) {
|
|
281
|
+
return v >= 7 ? "high" : v >= 4 ? "mid" : "low";
|
|
282
|
+
}
|
|
283
|
+
function baseFill(key, rainbow, palette) {
|
|
284
|
+
return palette ? baseColorFor(key, palette) : `var(--bmap-region-${key}, var(--bmap-body, ${rainbow}))`;
|
|
285
|
+
}
|
|
286
|
+
function markedFill(v, palette) {
|
|
287
|
+
return palette ? heatFor(v, palette) : `var(--bmap-heat-${heatTier(v)}, ${heat(v)})`;
|
|
288
|
+
}
|
|
187
289
|
function shapeEl(s, props) {
|
|
188
290
|
if (s.el === "circle") return /* @__PURE__ */ jsx("circle", { cx: s.cx, cy: s.cy, r: s.r, ...props });
|
|
189
291
|
if (s.el === "ellipse") return /* @__PURE__ */ jsx("ellipse", { cx: s.cx, cy: s.cy, rx: s.rx, ry: s.ry, ...props });
|
|
@@ -196,7 +298,7 @@ var STYLE = `
|
|
|
196
298
|
.bmap__stage{display:flex;flex-direction:column;gap:10px;flex:0 1 340px;min-width:0;max-width:360px}
|
|
197
299
|
.bmap__bar{display:flex;align-items:center;gap:10px;flex-wrap:wrap}
|
|
198
300
|
.bmap__viewtoggle{display:inline-flex;background:#f1f5f9;border:1px solid var(--bmap-line);border-radius:10px;padding:3px;gap:2px}
|
|
199
|
-
.bmap__vbtn{font:inherit;font-size:13px;font-weight:600;color
|
|
301
|
+
.bmap__vbtn{font:inherit;font-size:13px;font-weight:600;color:#556274;background:none;border:0;border-radius:7px;padding:8px 16px;cursor:pointer;transition:color .12s,background .12s,transform .1s}
|
|
200
302
|
.bmap__vbtn:hover{color:var(--bmap-ink)}
|
|
201
303
|
.bmap__vbtn:active{transform:scale(.97)}
|
|
202
304
|
.bmap__vbtn--on{background:#fff;color:var(--bmap-ink);box-shadow:0 1px 2px rgba(15,23,42,.14)}
|
|
@@ -233,6 +335,23 @@ var STYLE = `
|
|
|
233
335
|
.bmap__rm:hover{background:#fef2f2}
|
|
234
336
|
.bmap__rm:active{transform:scale(.97)}
|
|
235
337
|
.bmap__i:focus-visible,.bmap__chip:focus-visible,.bmap__vbtn:focus-visible,.bmap__rm:focus-visible,.bmap__zoom button:focus-visible{outline:2px solid var(--bmap-accent);outline-offset:2px}
|
|
338
|
+
.bmapc{--bmap-accent:var(--primary,#0e8f8a);--bmap-line:#e2e8f0;--bmap-ink:#1e293b;--bmap-muted:#64748b;font:15px/1.5 ui-sans-serif,system-ui,-apple-system,sans-serif;color:var(--bmap-ink)}
|
|
339
|
+
.bmapc__bodies{display:flex;gap:14px;flex-wrap:wrap}
|
|
340
|
+
.bmapc__fig{flex:1 1 150px;min-width:130px;max-width:220px;margin:0;text-align:center}
|
|
341
|
+
.bmapc__cap{font-size:12px;font-weight:700;letter-spacing:.05em;text-transform:uppercase;color:#556274;margin-bottom:6px}
|
|
342
|
+
.bmapc__svg{width:100%;height:auto;display:block}
|
|
343
|
+
.bmapc__vis{stroke:#fff;stroke-width:2.4}
|
|
344
|
+
.bmapc__n{font:700 12px ui-sans-serif;fill:#fff;pointer-events:none}
|
|
345
|
+
.bmapc__delta{margin-top:16px;display:flex;flex-direction:column;gap:6px;max-width:460px}
|
|
346
|
+
.bmapc__row{display:flex;align-items:center;gap:10px;font-size:13.5px;border:1px solid var(--bmap-line);border-radius:9px;padding:7px 11px}
|
|
347
|
+
.bmapc__rname{font-weight:600;flex:1 1 auto}
|
|
348
|
+
.bmapc__rval{font:12px ui-monospace,monospace;color:#556274;white-space:nowrap}
|
|
349
|
+
.bmapc__badge{font-size:11px;font-weight:700;border-radius:6px;padding:2px 8px;white-space:nowrap}
|
|
350
|
+
.bmapc__badge--improved{background:#dcfce7;color:#166534}
|
|
351
|
+
.bmapc__badge--worse{background:#fee2e2;color:#991b1b}
|
|
352
|
+
.bmapc__badge--new{background:#fef3c7;color:#92400e}
|
|
353
|
+
.bmapc__badge--resolved{background:#e2e8f0;color:#475569}
|
|
354
|
+
.bmapc__empty{color:#556274;font-size:13.5px;margin-top:14px}
|
|
236
355
|
@media (max-width:560px){
|
|
237
356
|
.bmap{flex-direction:column;gap:14px}
|
|
238
357
|
.bmap__stage{max-width:100%;width:100%;flex-basis:auto}
|
|
@@ -264,6 +383,10 @@ function BodyMap({
|
|
|
264
383
|
config = {},
|
|
265
384
|
defaultView = "front",
|
|
266
385
|
onViewChange,
|
|
386
|
+
palette,
|
|
387
|
+
locale = "da",
|
|
388
|
+
labels,
|
|
389
|
+
readOnly = false,
|
|
267
390
|
className
|
|
268
391
|
}) {
|
|
269
392
|
useEffect(ensureStyles, []);
|
|
@@ -275,6 +398,8 @@ function BodyMap({
|
|
|
275
398
|
const report = value ?? internal;
|
|
276
399
|
const shapes = view === "back" ? SHAPES_BACK : SHAPES;
|
|
277
400
|
const fills = view === "back" ? FILL_BACK : FILL;
|
|
401
|
+
const L = resolveLabels(locale, labels);
|
|
402
|
+
const nameOf = (key) => L.regions[key] ?? getRegion(key)?.label ?? key;
|
|
278
403
|
const svgRef = useRef(null);
|
|
279
404
|
const zoomRef = useRef(1);
|
|
280
405
|
const panRef = useRef({ x: 0, y: 0 });
|
|
@@ -376,14 +501,15 @@ function BodyMap({
|
|
|
376
501
|
const region = selected ? REGIONS.find((r) => r.key === selected) : void 0;
|
|
377
502
|
const current = selected ? pointOf(selected) : void 0;
|
|
378
503
|
const rootCls = ["bmap", className].filter(Boolean).join(" ");
|
|
379
|
-
const
|
|
380
|
-
|
|
381
|
-
|
|
504
|
+
const rootStyle = palette ? { "--bmap-accent": palette.selected } : void 0;
|
|
505
|
+
const ariaFor = (r, marked, interactive = true) => {
|
|
506
|
+
if (marked) return L.ariaMarked(nameOf(r.key), marked.intensity, marked.type ? L.qualities[marked.type] : void 0);
|
|
507
|
+
return interactive ? L.ariaUnmarked(nameOf(r.key)) : nameOf(r.key);
|
|
382
508
|
};
|
|
383
|
-
return /* @__PURE__ */ jsxs("div", { className: rootCls, "data-testid": "bodymap-root", children: [
|
|
509
|
+
return /* @__PURE__ */ jsxs("div", { className: rootCls, "data-testid": "bodymap-root", style: rootStyle, children: [
|
|
384
510
|
/* @__PURE__ */ jsxs("div", { className: "bmap__stage", children: [
|
|
385
511
|
/* @__PURE__ */ jsxs("div", { className: "bmap__bar", children: [
|
|
386
|
-
/* @__PURE__ */ jsxs("div", { className: "bmap__viewtoggle", role: "group", "aria-label":
|
|
512
|
+
/* @__PURE__ */ jsxs("div", { className: "bmap__viewtoggle", role: "group", "aria-label": L.viewLabel, children: [
|
|
387
513
|
/* @__PURE__ */ jsx(
|
|
388
514
|
"button",
|
|
389
515
|
{
|
|
@@ -392,7 +518,7 @@ function BodyMap({
|
|
|
392
518
|
"data-testid": "bodymap-view-front",
|
|
393
519
|
"aria-pressed": view === "front",
|
|
394
520
|
onClick: () => changeView("front"),
|
|
395
|
-
children:
|
|
521
|
+
children: L.front
|
|
396
522
|
}
|
|
397
523
|
),
|
|
398
524
|
/* @__PURE__ */ jsx(
|
|
@@ -403,14 +529,14 @@ function BodyMap({
|
|
|
403
529
|
"data-testid": "bodymap-view-back",
|
|
404
530
|
"aria-pressed": view === "back",
|
|
405
531
|
onClick: () => changeView("back"),
|
|
406
|
-
children:
|
|
532
|
+
children: L.back
|
|
407
533
|
}
|
|
408
534
|
)
|
|
409
535
|
] }),
|
|
410
|
-
/* @__PURE__ */ jsxs("div", { className: "bmap__zoom", role: "group", "aria-label":
|
|
411
|
-
/* @__PURE__ */ jsx("button", { type: "button", "data-testid": "bodymap-zoom-out", "aria-label":
|
|
412
|
-
/* @__PURE__ */ jsx("button", { type: "button", "data-testid": "bodymap-zoom-reset", "aria-label":
|
|
413
|
-
/* @__PURE__ */ jsx("button", { type: "button", "data-testid": "bodymap-zoom-in", "aria-label":
|
|
536
|
+
/* @__PURE__ */ jsxs("div", { className: "bmap__zoom", role: "group", "aria-label": L.zoomLabel, children: [
|
|
537
|
+
/* @__PURE__ */ jsx("button", { type: "button", "data-testid": "bodymap-zoom-out", "aria-label": L.zoomOut, disabled: zoom <= ZMIN, onClick: () => zoomBtn(1 / 1.4), children: "\u2212" }),
|
|
538
|
+
/* @__PURE__ */ jsx("button", { type: "button", "data-testid": "bodymap-zoom-reset", "aria-label": L.zoomReset, disabled: zoom === 1 && pan.x === 0 && pan.y === 0, onClick: resetZoom, children: "\u2922" }),
|
|
539
|
+
/* @__PURE__ */ jsx("button", { type: "button", "data-testid": "bodymap-zoom-in", "aria-label": L.zoomIn, disabled: zoom >= ZMAX, onClick: () => zoomBtn(1.4), children: "+" })
|
|
414
540
|
] })
|
|
415
541
|
] }),
|
|
416
542
|
/* @__PURE__ */ jsx(
|
|
@@ -420,7 +546,7 @@ function BodyMap({
|
|
|
420
546
|
className: "bmap__svg",
|
|
421
547
|
viewBox: "0 0 260 470",
|
|
422
548
|
role: "group",
|
|
423
|
-
"aria-label":
|
|
549
|
+
"aria-label": L.svgLabel,
|
|
424
550
|
onPointerDown,
|
|
425
551
|
onPointerMove,
|
|
426
552
|
onPointerUp,
|
|
@@ -431,21 +557,20 @@ function BodyMap({
|
|
|
431
557
|
const s = shapes[r.key];
|
|
432
558
|
if (!s) return null;
|
|
433
559
|
const marked = pointOf(r.key);
|
|
434
|
-
const
|
|
560
|
+
const interactive = isSelectable(r.key, config) && !readOnly;
|
|
435
561
|
const visCls = ["bmap__vis", selected === r.key && "bmap__vis--sel"].filter(Boolean).join(" ");
|
|
436
|
-
const fill = marked ?
|
|
562
|
+
const fill = marked ? markedFill(marked.intensity, palette) : baseFill(r.key, fills[r.key] ?? "#cbd5e1", palette);
|
|
437
563
|
const hs = hitShape(s);
|
|
438
|
-
const act = () => selectable ? pickRegion(r.key) : void 0;
|
|
439
564
|
return /* @__PURE__ */ jsxs("g", { children: [
|
|
440
565
|
shapeEl(hs, {
|
|
441
|
-
className: "bmap__hit" + (
|
|
566
|
+
className: "bmap__hit" + (interactive ? "" : " bmap__hit--locked"),
|
|
442
567
|
"data-testid": `bodymap-region-${r.key}`,
|
|
443
|
-
role:
|
|
444
|
-
tabIndex:
|
|
445
|
-
"aria-label": ariaFor(r, marked,
|
|
446
|
-
"aria-pressed":
|
|
447
|
-
onClick:
|
|
448
|
-
onKeyDown:
|
|
568
|
+
role: interactive ? "button" : "img",
|
|
569
|
+
tabIndex: interactive ? 0 : -1,
|
|
570
|
+
"aria-label": ariaFor(r, marked, interactive),
|
|
571
|
+
"aria-pressed": interactive ? selected === r.key : void 0,
|
|
572
|
+
onClick: interactive ? () => pickRegion(r.key) : void 0,
|
|
573
|
+
onKeyDown: interactive ? (e) => {
|
|
449
574
|
if (e.key === "Enter" || e.key === " ") {
|
|
450
575
|
e.preventDefault();
|
|
451
576
|
suppressClick.current = false;
|
|
@@ -453,7 +578,7 @@ function BodyMap({
|
|
|
453
578
|
}
|
|
454
579
|
} : void 0
|
|
455
580
|
}),
|
|
456
|
-
shapeEl(s, { className: visCls, fill, opacity:
|
|
581
|
+
shapeEl(s, { className: visCls, fill, opacity: !readOnly && !isSelectable(r.key, config) ? 0.4 : 1 })
|
|
457
582
|
] }, r.key);
|
|
458
583
|
}),
|
|
459
584
|
report.map((p) => {
|
|
@@ -466,26 +591,25 @@ function BodyMap({
|
|
|
466
591
|
}
|
|
467
592
|
)
|
|
468
593
|
] }),
|
|
469
|
-
region ? /* @__PURE__ */ jsxs("div", { className: "bmap__panel", "data-testid": "bodymap-panel", children: [
|
|
594
|
+
readOnly ? null : region ? /* @__PURE__ */ jsxs("div", { className: "bmap__panel", "data-testid": "bodymap-panel", children: [
|
|
470
595
|
/* @__PURE__ */ jsxs("div", { className: "bmap__ph", children: [
|
|
471
|
-
/* @__PURE__ */ jsx("b", { children: region.
|
|
596
|
+
/* @__PURE__ */ jsx("b", { children: nameOf(region.key) }),
|
|
472
597
|
/* @__PURE__ */ jsx("span", { className: "bmap__code", children: region.code })
|
|
473
598
|
] }),
|
|
474
|
-
/* @__PURE__ */ jsx("p", { className: "bmap__lbl", id: "bmap-intensity-lbl", children:
|
|
599
|
+
/* @__PURE__ */ jsx("p", { className: "bmap__lbl", id: "bmap-intensity-lbl", children: L.intensity }),
|
|
475
600
|
/* @__PURE__ */ jsx("div", { className: "bmap__scale", role: "group", "aria-labelledby": "bmap-intensity-lbl", children: Array.from({ length: 11 }, (_, n) => /* @__PURE__ */ jsx(
|
|
476
601
|
"button",
|
|
477
602
|
{
|
|
478
603
|
type: "button",
|
|
479
604
|
className: "bmap__i" + (current?.intensity === n ? " bmap__i--on" : ""),
|
|
480
605
|
"data-testid": `bodymap-intensity-${n}`,
|
|
481
|
-
"aria-label": `Intensitet ${n}`,
|
|
482
606
|
"aria-pressed": current?.intensity === n,
|
|
483
607
|
onClick: () => setPain(region.key, n, current?.type),
|
|
484
608
|
children: n
|
|
485
609
|
},
|
|
486
610
|
n
|
|
487
611
|
)) }),
|
|
488
|
-
/* @__PURE__ */ jsx("p", { className: "bmap__lbl", id: "bmap-quality-lbl", children:
|
|
612
|
+
/* @__PURE__ */ jsx("p", { className: "bmap__lbl", id: "bmap-quality-lbl", children: L.quality }),
|
|
489
613
|
/* @__PURE__ */ jsx("div", { className: "bmap__chips", role: "group", "aria-labelledby": "bmap-quality-lbl", children: PAIN_TYPES.map((t) => /* @__PURE__ */ jsx(
|
|
490
614
|
"button",
|
|
491
615
|
{
|
|
@@ -494,15 +618,84 @@ function BodyMap({
|
|
|
494
618
|
"data-testid": `bodymap-type-${t}`,
|
|
495
619
|
"aria-pressed": current?.type === t,
|
|
496
620
|
onClick: () => setPain(region.key, current?.intensity ?? 5, t),
|
|
497
|
-
children: t
|
|
621
|
+
children: L.qualities[t]
|
|
498
622
|
},
|
|
499
623
|
t
|
|
500
624
|
)) }),
|
|
501
|
-
current && /* @__PURE__ */ jsx("button", { type: "button", className: "bmap__rm", "data-testid": "bodymap-remove", onClick: () => removePain(region.key), children:
|
|
502
|
-
] }) : /* @__PURE__ */ jsx("div", { className: "bmap__panel bmap__panel--empty", "data-testid": "bodymap-panel", children:
|
|
625
|
+
current && /* @__PURE__ */ jsx("button", { type: "button", className: "bmap__rm", "data-testid": "bodymap-remove", onClick: () => removePain(region.key), children: L.remove })
|
|
626
|
+
] }) : /* @__PURE__ */ jsx("div", { className: "bmap__panel bmap__panel--empty", "data-testid": "bodymap-panel", children: L.empty })
|
|
627
|
+
] });
|
|
628
|
+
}
|
|
629
|
+
function changeOf(b, a) {
|
|
630
|
+
if (b == null && a != null) return "new";
|
|
631
|
+
if (b != null && a == null) return "resolved";
|
|
632
|
+
if (b == null || a == null) return "same";
|
|
633
|
+
return a < b ? "improved" : a > b ? "worse" : "same";
|
|
634
|
+
}
|
|
635
|
+
function CompareFigure({ report, view, palette, caption, testid }) {
|
|
636
|
+
const shapes = view === "back" ? SHAPES_BACK : SHAPES;
|
|
637
|
+
const fills = view === "back" ? FILL_BACK : FILL;
|
|
638
|
+
return /* @__PURE__ */ jsxs("figure", { className: "bmapc__fig", "data-testid": testid, children: [
|
|
639
|
+
/* @__PURE__ */ jsx("figcaption", { className: "bmapc__cap", children: caption }),
|
|
640
|
+
/* @__PURE__ */ jsxs("svg", { className: "bmapc__svg", viewBox: "0 0 260 470", role: "img", "aria-label": caption, children: [
|
|
641
|
+
REGIONS.map((r) => {
|
|
642
|
+
const s = shapes[r.key];
|
|
643
|
+
if (!s) return null;
|
|
644
|
+
const m = report.find((p) => p.region === r.key);
|
|
645
|
+
const fill = m ? markedFill(m.intensity, palette) : baseFill(r.key, fills[r.key] ?? "#cbd5e1", palette);
|
|
646
|
+
return /* @__PURE__ */ jsx("g", { children: shapeEl(s, { className: "bmapc__vis", fill }) }, r.key);
|
|
647
|
+
}),
|
|
648
|
+
report.map((p) => {
|
|
649
|
+
const s = shapes[p.region];
|
|
650
|
+
if (!s) return null;
|
|
651
|
+
const c = center(s);
|
|
652
|
+
return /* @__PURE__ */ jsx("text", { x: c.x, y: c.y + 4, textAnchor: "middle", className: "bmapc__n", children: p.intensity }, p.region);
|
|
653
|
+
})
|
|
654
|
+
] })
|
|
655
|
+
] });
|
|
656
|
+
}
|
|
657
|
+
function BodyMapCompare({
|
|
658
|
+
before,
|
|
659
|
+
after,
|
|
660
|
+
palette,
|
|
661
|
+
locale = "da",
|
|
662
|
+
labels,
|
|
663
|
+
defaultView = "front",
|
|
664
|
+
className
|
|
665
|
+
}) {
|
|
666
|
+
useEffect(ensureStyles, []);
|
|
667
|
+
const [view, setView] = useState(defaultView);
|
|
668
|
+
const L = resolveLabels(locale, labels);
|
|
669
|
+
const nameOf = (k) => L.regions[k] ?? getRegion(k)?.label ?? k;
|
|
670
|
+
const rootStyle = palette ? { "--bmap-accent": palette.selected } : void 0;
|
|
671
|
+
const keys = Array.from(new Set([...before, ...after].map((p) => p.region)));
|
|
672
|
+
const changes = keys.map((k) => ({
|
|
673
|
+
key: k,
|
|
674
|
+
b: before.find((p) => p.region === k)?.intensity,
|
|
675
|
+
a: after.find((p) => p.region === k)?.intensity,
|
|
676
|
+
status: changeOf(before.find((p) => p.region === k)?.intensity, after.find((p) => p.region === k)?.intensity)
|
|
677
|
+
})).filter((c) => c.status !== "same").sort((x, y) => x.key < y.key ? -1 : 1);
|
|
678
|
+
return /* @__PURE__ */ jsxs("div", { className: ["bmapc", className].filter(Boolean).join(" "), "data-testid": "bodymap-compare", style: rootStyle, children: [
|
|
679
|
+
/* @__PURE__ */ jsxs("div", { className: "bmap__viewtoggle", role: "group", "aria-label": L.viewLabel, style: { marginBottom: 12 }, children: [
|
|
680
|
+
/* @__PURE__ */ jsx("button", { type: "button", className: "bmap__vbtn" + (view === "front" ? " bmap__vbtn--on" : ""), "data-testid": "bodymap-compare-view-front", "aria-pressed": view === "front", onClick: () => setView("front"), children: L.front }),
|
|
681
|
+
/* @__PURE__ */ jsx("button", { type: "button", className: "bmap__vbtn" + (view === "back" ? " bmap__vbtn--on" : ""), "data-testid": "bodymap-compare-view-back", "aria-pressed": view === "back", onClick: () => setView("back"), children: L.back })
|
|
682
|
+
] }),
|
|
683
|
+
/* @__PURE__ */ jsxs("div", { className: "bmapc__bodies", children: [
|
|
684
|
+
/* @__PURE__ */ jsx(CompareFigure, { report: before, view, palette, caption: L.before, testid: "bodymap-compare-before" }),
|
|
685
|
+
/* @__PURE__ */ jsx(CompareFigure, { report: after, view, palette, caption: L.after, testid: "bodymap-compare-after" })
|
|
686
|
+
] }),
|
|
687
|
+
changes.length ? /* @__PURE__ */ jsx("div", { className: "bmapc__delta", "data-testid": "bodymap-compare-delta", children: changes.map((c) => /* @__PURE__ */ jsxs("div", { className: "bmapc__row", "data-testid": `bodymap-compare-delta-${c.key}`, children: [
|
|
688
|
+
/* @__PURE__ */ jsx("span", { className: "bmapc__rname", children: nameOf(c.key) }),
|
|
689
|
+
/* @__PURE__ */ jsxs("span", { className: "bmapc__rval", children: [
|
|
690
|
+
c.b ?? "\u2013",
|
|
691
|
+
" \u2192 ",
|
|
692
|
+
c.a ?? "\u2013"
|
|
693
|
+
] }),
|
|
694
|
+
/* @__PURE__ */ jsx("span", { className: `bmapc__badge bmapc__badge--${c.status}`, children: L.change[c.status] })
|
|
695
|
+
] }, c.key)) }) : /* @__PURE__ */ jsx("div", { className: "bmapc__empty", "data-testid": "bodymap-compare-delta", children: L.noChange })
|
|
503
696
|
] });
|
|
504
697
|
}
|
|
505
698
|
|
|
506
|
-
export { BodyMap };
|
|
699
|
+
export { BodyMap, BodyMapCompare, LABELS_DA, LABELS_EN };
|
|
507
700
|
//# sourceMappingURL=react.js.map
|
|
508
701
|
//# sourceMappingURL=react.js.map
|