@broberg/bodymap 0.7.1 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +68 -0
- package/dist/index.cjs +13 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +38 -2
- package/dist/index.d.ts +38 -2
- package/dist/index.js +13 -2
- package/dist/index.js.map +1 -1
- package/dist/react.cjs +3 -3
- package/dist/react.cjs.map +1 -1
- package/dist/react.d.cts +28 -1
- package/dist/react.d.ts +28 -1
- package/dist/react.js +3 -3
- package/dist/react.js.map +1 -1
- package/dist/three.cjs +65 -17
- package/dist/three.cjs.map +1 -1
- package/dist/three.d.cts +63 -2
- package/dist/three.d.ts +63 -2
- package/dist/three.js +64 -18
- package/dist/three.js.map +1 -1
- package/package.json +1 -1
package/dist/three.cjs
CHANGED
|
@@ -103,13 +103,24 @@ function emitFeedback(outcome, region, opts = {}) {
|
|
|
103
103
|
if (opts.haptics === false) return "skipped";
|
|
104
104
|
return requestVibration(VIBRATION_PATTERNS[outcome], opts.nav);
|
|
105
105
|
}
|
|
106
|
+
var STAGE_BG = "#0e1424";
|
|
106
107
|
var defaultUi = {
|
|
107
108
|
text: "#1e293b",
|
|
108
109
|
mutedText: "#475569",
|
|
109
110
|
panelBg: "#fff",
|
|
111
|
+
stageBg: STAGE_BG,
|
|
110
112
|
border: "#e2e8f0",
|
|
111
113
|
badgeBg: "#f1f5f9",
|
|
112
|
-
danger: "#dc2626"
|
|
114
|
+
danger: "#dc2626",
|
|
115
|
+
dangerBorder: "#f6c9c9",
|
|
116
|
+
// F052.30 — was #0e8f8a, which measured 3.95:1 against white. AA needs 4.5,
|
|
117
|
+
// and this is the SELECTED intensity button on a surface where AA is a legal
|
|
118
|
+
// duty. It shipped from 0.2.0 and no check could see it: the colour was an
|
|
119
|
+
// inline literal, and the F052.19 contrast sweep only read this object.
|
|
120
|
+
// #0c7d77 is the same teal a shade deeper — 4.98:1, with headroom rather than
|
|
121
|
+
// sitting on the 4.5 boundary where any later tweak reopens it.
|
|
122
|
+
accent: "#0c7d77",
|
|
123
|
+
accentText: "#fff"
|
|
113
124
|
};
|
|
114
125
|
function uiColors(palette) {
|
|
115
126
|
return { ...defaultUi, ...palette?.ui ?? {} };
|
|
@@ -268,6 +279,14 @@ function seamBlend(nearestSq, secondSq) {
|
|
|
268
279
|
if (t >= 1) return 0.5;
|
|
269
280
|
return t * t * (3 - 2 * t) * 0.5;
|
|
270
281
|
}
|
|
282
|
+
function seamWeight(seamEnabled, blend) {
|
|
283
|
+
return seamEnabled ? blend : 0;
|
|
284
|
+
}
|
|
285
|
+
function ensureNormals(geo) {
|
|
286
|
+
if (geo.getAttribute("normal")) return false;
|
|
287
|
+
geo.computeVertexNormals();
|
|
288
|
+
return true;
|
|
289
|
+
}
|
|
271
290
|
var ANCHOR_KEYS = Object.keys(ANCHORS);
|
|
272
291
|
for (const k of ANCHOR_KEYS) ANCHORS[k][0] = -ANCHORS[k][0];
|
|
273
292
|
function mergeLabels(locale, overrides) {
|
|
@@ -288,8 +307,20 @@ function webglAvailable() {
|
|
|
288
307
|
return false;
|
|
289
308
|
}
|
|
290
309
|
}
|
|
291
|
-
var btn =
|
|
292
|
-
|
|
310
|
+
var btn = (c) => ({
|
|
311
|
+
font: "inherit",
|
|
312
|
+
cursor: "pointer",
|
|
313
|
+
borderRadius: 8,
|
|
314
|
+
border: `1px solid ${c.border}`,
|
|
315
|
+
background: c.panelBg,
|
|
316
|
+
padding: "6px 9px"
|
|
317
|
+
});
|
|
318
|
+
var seg = (c, on) => ({
|
|
319
|
+
...btn(c),
|
|
320
|
+
background: on ? c.accent : c.panelBg,
|
|
321
|
+
color: on ? c.accentText : c.text,
|
|
322
|
+
fontWeight: 600
|
|
323
|
+
});
|
|
293
324
|
function BodyMap3D(props) {
|
|
294
325
|
const {
|
|
295
326
|
models,
|
|
@@ -353,6 +384,8 @@ function BodyMap3D(props) {
|
|
|
353
384
|
setReadyRef.current = setReady;
|
|
354
385
|
const seamRef = react.useRef(seam);
|
|
355
386
|
seamRef.current = seam;
|
|
387
|
+
const stageRef = react.useRef(chrome.stageBg);
|
|
388
|
+
stageRef.current = chrome.stageBg;
|
|
356
389
|
const [internalFs, setInternalFs] = react.useState(false);
|
|
357
390
|
const isFullscreen = fullscreenProp ?? internalFs;
|
|
358
391
|
const setFullscreen = (v) => {
|
|
@@ -369,7 +402,7 @@ function BodyMap3D(props) {
|
|
|
369
402
|
if (!el) return;
|
|
370
403
|
let W = el.clientWidth || 520, H = el.clientHeight || 600;
|
|
371
404
|
const scene = new THREE__namespace.Scene();
|
|
372
|
-
scene.background = new THREE__namespace.Color(
|
|
405
|
+
scene.background = new THREE__namespace.Color(stageRef.current);
|
|
373
406
|
const camera = new THREE__namespace.PerspectiveCamera(32, W / H, 0.1, 100);
|
|
374
407
|
camera.position.set(0, 1.05, 4.4);
|
|
375
408
|
let renderer;
|
|
@@ -452,7 +485,7 @@ function BodyMap3D(props) {
|
|
|
452
485
|
};
|
|
453
486
|
for (let i = 0; i < vertexRegion.length; i++) {
|
|
454
487
|
tmp.copy(col(vertexRegion[i]));
|
|
455
|
-
const w = seamRef.current
|
|
488
|
+
const w = seamWeight(seamRef.current, vertexBlend[i]);
|
|
456
489
|
if (w > 0) {
|
|
457
490
|
tmp2.copy(col(vertexNeighbour[i]));
|
|
458
491
|
tmp.lerp(tmp2, w);
|
|
@@ -462,6 +495,7 @@ function BodyMap3D(props) {
|
|
|
462
495
|
colorAttr.needsUpdate = true;
|
|
463
496
|
};
|
|
464
497
|
const refresh = () => {
|
|
498
|
+
scene.background = new THREE__namespace.Color(stageRef.current);
|
|
465
499
|
paint();
|
|
466
500
|
renderFrame();
|
|
467
501
|
};
|
|
@@ -486,7 +520,7 @@ function BodyMap3D(props) {
|
|
|
486
520
|
});
|
|
487
521
|
if (bodyMesh) {
|
|
488
522
|
const geo = bodyMesh.geometry;
|
|
489
|
-
|
|
523
|
+
ensureNormals(geo);
|
|
490
524
|
const pos = geo.getAttribute("position");
|
|
491
525
|
const n = pos.count;
|
|
492
526
|
const cols = new Float32Array(n * 3);
|
|
@@ -676,7 +710,7 @@ function BodyMap3D(props) {
|
|
|
676
710
|
"data-fullscreen": isFullscreen ? "true" : void 0,
|
|
677
711
|
style: {
|
|
678
712
|
fontFamily: "system-ui, sans-serif",
|
|
679
|
-
color:
|
|
713
|
+
color: chrome.text,
|
|
680
714
|
// A viewport fill, not the Fullscreen API — see the `fullscreen` prop.
|
|
681
715
|
// `fixed`+`inset:0` behaves the same on iOS Safari, where element
|
|
682
716
|
// fullscreen does not exist at all, as it does everywhere else.
|
|
@@ -684,7 +718,12 @@ function BodyMap3D(props) {
|
|
|
684
718
|
position: "fixed",
|
|
685
719
|
inset: 0,
|
|
686
720
|
zIndex: 2147483e3,
|
|
687
|
-
|
|
721
|
+
// THE STAGE, not the panel. This used to be `chrome.panelBg`,
|
|
722
|
+
// the same field that paints the picker card — so a consumer with
|
|
723
|
+
// a dark canvas got a white void around a dark body in fullscreen,
|
|
724
|
+
// and could not fix it: darkening panelBg hid the 0-10 buttons.
|
|
725
|
+
// One field, two surfaces, opposite needs. (F052.29)
|
|
726
|
+
background: chrome.stageBg,
|
|
688
727
|
// F052.27 — a fixed inset:0 surface in a standalone/Capacitor
|
|
689
728
|
// webview covers the STATUS BAR too, so a control laid out at the
|
|
690
729
|
// top lands on the clock and the battery. Measured by fd-sundhed
|
|
@@ -703,8 +742,8 @@ function BodyMap3D(props) {
|
|
|
703
742
|
},
|
|
704
743
|
children: [
|
|
705
744
|
showSexToggle && /* @__PURE__ */ jsxRuntime.jsx("div", { style: { display: "flex", gap: 16, flexWrap: "wrap", marginBottom: 12, fontSize: 13 }, children: /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", gap: 6, alignItems: "center" }, children: [
|
|
706
|
-
/* @__PURE__ */ jsxRuntime.jsx("button", { "data-testid": "bodymap3d-sex-male", onClick: () => changeSex("male"), style: seg(sex === "male"), children: UI.male }),
|
|
707
|
-
/* @__PURE__ */ jsxRuntime.jsx("button", { "data-testid": "bodymap3d-sex-female", onClick: () => changeSex("female"), style: seg(sex === "female"), children: UI.female })
|
|
745
|
+
/* @__PURE__ */ jsxRuntime.jsx("button", { "data-testid": "bodymap3d-sex-male", onClick: () => changeSex("male"), style: seg(chrome, sex === "male"), children: UI.male }),
|
|
746
|
+
/* @__PURE__ */ jsxRuntime.jsx("button", { "data-testid": "bodymap3d-sex-female", onClick: () => changeSex("female"), style: seg(chrome, sex === "female"), children: UI.female })
|
|
708
747
|
] }) }),
|
|
709
748
|
/* @__PURE__ */ jsxRuntime.jsx("span", { ref: loadedRef, "data-testid": "bodymap3d-loaded", style: { display: "none" } }),
|
|
710
749
|
ready && /* @__PURE__ */ jsxRuntime.jsx("span", { "data-testid": "bodymap3d-ready", style: { position: "absolute", width: 1, height: 1, opacity: 0, pointerEvents: "none" } }),
|
|
@@ -717,7 +756,7 @@ function BodyMap3D(props) {
|
|
|
717
756
|
"aria-label": isFullscreen ? UI.collapse ?? "Close" : UI.expand ?? "Expand",
|
|
718
757
|
onClick: () => setFullscreen(!isFullscreen),
|
|
719
758
|
style: {
|
|
720
|
-
...btn,
|
|
759
|
+
...btn(chrome),
|
|
721
760
|
color: chrome.text,
|
|
722
761
|
borderColor: chrome.border,
|
|
723
762
|
background: chrome.panelBg,
|
|
@@ -727,7 +766,14 @@ function BodyMap3D(props) {
|
|
|
727
766
|
}
|
|
728
767
|
) }),
|
|
729
768
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", gap: 18, alignItems: "flex-start", flexWrap: "wrap", ...isFullscreen ? { flex: "1 1 auto", minHeight: 0 } : null }, children: [
|
|
730
|
-
unsupported ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
769
|
+
unsupported ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
770
|
+
"div",
|
|
771
|
+
{
|
|
772
|
+
"data-testid": "bodymap3d-unsupported",
|
|
773
|
+
style: { flex: "1 1 520px", minWidth: 320, height: canvasH, borderRadius: 16, background: chrome.panelBg, color: chrome.mutedText, border: `1px solid ${chrome.border}`, display: "flex", alignItems: "center", justifyContent: "center", padding: 24, textAlign: "center", fontSize: 14 },
|
|
774
|
+
children: "3D kr\xE6ver WebGL, som ikke er tilg\xE6ngeligt her."
|
|
775
|
+
}
|
|
776
|
+
) : /* @__PURE__ */ jsxRuntime.jsx(
|
|
731
777
|
"div",
|
|
732
778
|
{
|
|
733
779
|
ref: mountRef,
|
|
@@ -742,7 +788,7 @@ function BodyMap3D(props) {
|
|
|
742
788
|
minHeight: isFullscreen ? 0 : void 0,
|
|
743
789
|
borderRadius: 16,
|
|
744
790
|
overflow: "hidden",
|
|
745
|
-
background:
|
|
791
|
+
background: chrome.stageBg,
|
|
746
792
|
touchAction: "none"
|
|
747
793
|
}
|
|
748
794
|
}
|
|
@@ -761,16 +807,16 @@ function BodyMap3D(props) {
|
|
|
761
807
|
"data-testid": "bodymap3d-close",
|
|
762
808
|
"aria-label": L.close,
|
|
763
809
|
onClick: () => setSelected(null),
|
|
764
|
-
style: { ...btn, width: 32, height: 32, padding: 0, fontSize: 20, lineHeight: 1, color: chrome.mutedText, borderColor: chrome.border, background: chrome.panelBg },
|
|
810
|
+
style: { ...btn(chrome), width: 32, height: 32, padding: 0, fontSize: 20, lineHeight: 1, color: chrome.mutedText, borderColor: chrome.border, background: chrome.panelBg },
|
|
765
811
|
children: "\xD7"
|
|
766
812
|
}
|
|
767
813
|
)
|
|
768
814
|
] }),
|
|
769
815
|
/* @__PURE__ */ jsxRuntime.jsx("div", { style: { fontSize: 11, fontWeight: 700, letterSpacing: ".06em", textTransform: "uppercase", color: chrome.mutedText, marginBottom: 7 }, children: L.intensity }),
|
|
770
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { style: { display: "flex", flexWrap: "wrap", gap: 4, marginBottom: 12 }, children: Array.from({ length: 11 }, (_, i) => /* @__PURE__ */ jsxRuntime.jsx("button", { "data-testid": `bodymap3d-intensity-${i}`, onClick: () => setPain(region.key, i, current?.type), style: { ...btn, display: "inline-flex", alignItems: "center", justifyContent: "center", minWidth: 30, height: 30, padding: 0, background: current?.intensity === i ?
|
|
816
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { style: { display: "flex", flexWrap: "wrap", gap: 4, marginBottom: 12 }, children: Array.from({ length: 11 }, (_, i) => /* @__PURE__ */ jsxRuntime.jsx("button", { "data-testid": `bodymap3d-intensity-${i}`, onClick: () => setPain(region.key, i, current?.type), style: { ...btn(chrome), display: "inline-flex", alignItems: "center", justifyContent: "center", minWidth: 30, height: 30, padding: 0, background: current?.intensity === i ? chrome.accent : chrome.panelBg, color: current?.intensity === i ? chrome.accentText : chrome.text }, children: i }, i)) }),
|
|
771
817
|
/* @__PURE__ */ jsxRuntime.jsx("div", { style: { fontSize: 11, fontWeight: 700, letterSpacing: ".06em", textTransform: "uppercase", color: chrome.mutedText, marginBottom: 7 }, children: L.quality }),
|
|
772
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { style: { display: "flex", flexWrap: "wrap", gap: 6, marginBottom: 14 }, children: PAIN_TYPES.map((t) => /* @__PURE__ */ jsxRuntime.jsx("button", { "data-testid": `bodymap3d-type-${t}`, onClick: () => setPain(region.key, current?.intensity ?? 5, t), style: { ...btn, borderRadius: 999, padding: "6px 12px", background: current?.type === t ?
|
|
773
|
-
current && /* @__PURE__ */ jsxRuntime.jsx("button", { "data-testid": "bodymap3d-remove", onClick: () => removePain(region.key), style: { ...btn, color: chrome.danger, borderColor:
|
|
818
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { style: { display: "flex", flexWrap: "wrap", gap: 6, marginBottom: 14 }, children: PAIN_TYPES.map((t) => /* @__PURE__ */ jsxRuntime.jsx("button", { "data-testid": `bodymap3d-type-${t}`, onClick: () => setPain(region.key, current?.intensity ?? 5, t), style: { ...btn(chrome), borderRadius: 999, padding: "6px 12px", background: current?.type === t ? chrome.text : chrome.panelBg, color: current?.type === t ? chrome.panelBg : chrome.mutedText }, children: L.qualities[t] ?? t }, t)) }),
|
|
819
|
+
current && /* @__PURE__ */ jsxRuntime.jsx("button", { "data-testid": "bodymap3d-remove", onClick: () => removePain(region.key), style: { ...btn(chrome), color: chrome.danger, borderColor: chrome.dangerBorder }, children: L.remove })
|
|
774
820
|
] }) : showEmptyHint ? /* @__PURE__ */ jsxRuntime.jsx("div", { "data-testid": "bodymap3d-empty", style: { border: `1px solid ${chrome.border}`, borderRadius: 14, padding: 16, background: chrome.panelBg, color: chrome.mutedText, fontSize: 13.5 }, children: UI.hoverHint }) : null })
|
|
775
821
|
] })
|
|
776
822
|
]
|
|
@@ -779,7 +825,9 @@ function BodyMap3D(props) {
|
|
|
779
825
|
}
|
|
780
826
|
|
|
781
827
|
exports.BodyMap3D = BodyMap3D;
|
|
828
|
+
exports.ensureNormals = ensureNormals;
|
|
782
829
|
exports.seamBlend = seamBlend;
|
|
830
|
+
exports.seamWeight = seamWeight;
|
|
783
831
|
exports.serializeReport = serializeReport;
|
|
784
832
|
//# sourceMappingURL=three.cjs.map
|
|
785
833
|
//# sourceMappingURL=three.cjs.map
|