@broberg/bodymap 0.11.0 → 0.12.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/dist/three.d.cts CHANGED
@@ -371,6 +371,56 @@ interface BodyMap3DProps {
371
371
  haptics?: boolean;
372
372
  className?: string;
373
373
  }
374
+ /**
375
+ * F052.30 — the controls now READ THE PALETTE.
376
+ *
377
+ * These were module-level constants with the colours written in: #e2e8f0 border,
378
+ * #fff surface, #0e8f8a accent. Three of those already existed as `chrome`
379
+ * fields and were simply not used, so a consumer who set `ui.border` saw it on
380
+ * the card and NOT on the buttons inside the card — in a component whose whole
381
+ * selling point is that the consuming app themes it.
382
+ *
383
+ * They take `chrome` now, which is why they are functions rather than
384
+ * constants: there is no correct value to compute before the palette is known.
385
+ */
386
+ /**
387
+ * The intensity circle — 46px, and every one of the eleven takes it.
388
+ *
389
+ * THE SIZE IS NOT A DESIGN CHOICE, it is the sibling's. Measured while making
390
+ * these round: the 2D renderer's `.bmap__i` is 38px and **46px on touch**; this
391
+ * renderer's were **30px with no touch override at all**. F052.8 set a >=44px
392
+ * floor for this component because a phone is its primary surface, and F052.11
393
+ * made accessibility a legal duty here — so the 3D half had been under the floor
394
+ * since it shipped, and the two halves had quietly diverged.
395
+ *
396
+ * Same shape as F052.32's tap guard: 2D correct, 3D not, nobody looking. Asking
397
+ * for circles is what surfaced it — the second time this week that making
398
+ * something explicit uncovered a defect that was already there (F052.30's accent
399
+ * was the first).
400
+ *
401
+ * 46 rather than 44 so the two renderers carry ONE number instead of two that
402
+ * both happen to pass. The widest label («10») fits at the shipped font size,
403
+ * and all eleven take the same box so none is an ellipse among circles.
404
+ */
405
+ declare const INTENSITY_SIZE = 46;
406
+ /**
407
+ * F052.33 — should the help note be on screen at all?
408
+ *
409
+ * Extracted because the renderer's own answer is unreachable from a unit test:
410
+ * happy-dom has no WebGL, so no region can be selected, and a test written
411
+ * against the component asserts the note is PRESENT twice and never that it
412
+ * disappears. I wrote that test and it was green and vacuous.
413
+ *
414
+ * `openRegion` wins over everything. A note reading «tap a body part» while the
415
+ * picker is open is wrong advice, not a layering problem — and it was covering
416
+ * the panel's close control, the third exit hidden under something this week.
417
+ */
418
+ declare function shouldShowHint(opts: {
419
+ openRegion: string | null;
420
+ hint: false | BodyMap3DHint | undefined;
421
+ anySelectable: boolean;
422
+ hasExplicitHoverHint: boolean;
423
+ }): boolean;
374
424
  declare function BodyMap3D(props: BodyMap3DProps): react.JSX.Element;
375
425
 
376
- export { BodyMap3D, type BodyMap3DHint, type BodyMap3DModels, type BodyMap3DProps, type BodyMap3DSex, type BodyMap3DUiLabels, TAP_MAX_DISTANCE, ensureNormals, isTap, seamBlend, seamWeight, serializeReport };
426
+ export { BodyMap3D, type BodyMap3DHint, type BodyMap3DModels, type BodyMap3DProps, type BodyMap3DSex, type BodyMap3DUiLabels, INTENSITY_SIZE, TAP_MAX_DISTANCE, ensureNormals, isTap, seamBlend, seamWeight, serializeReport, shouldShowHint };
package/dist/three.d.ts CHANGED
@@ -371,6 +371,56 @@ interface BodyMap3DProps {
371
371
  haptics?: boolean;
372
372
  className?: string;
373
373
  }
374
+ /**
375
+ * F052.30 — the controls now READ THE PALETTE.
376
+ *
377
+ * These were module-level constants with the colours written in: #e2e8f0 border,
378
+ * #fff surface, #0e8f8a accent. Three of those already existed as `chrome`
379
+ * fields and were simply not used, so a consumer who set `ui.border` saw it on
380
+ * the card and NOT on the buttons inside the card — in a component whose whole
381
+ * selling point is that the consuming app themes it.
382
+ *
383
+ * They take `chrome` now, which is why they are functions rather than
384
+ * constants: there is no correct value to compute before the palette is known.
385
+ */
386
+ /**
387
+ * The intensity circle — 46px, and every one of the eleven takes it.
388
+ *
389
+ * THE SIZE IS NOT A DESIGN CHOICE, it is the sibling's. Measured while making
390
+ * these round: the 2D renderer's `.bmap__i` is 38px and **46px on touch**; this
391
+ * renderer's were **30px with no touch override at all**. F052.8 set a >=44px
392
+ * floor for this component because a phone is its primary surface, and F052.11
393
+ * made accessibility a legal duty here — so the 3D half had been under the floor
394
+ * since it shipped, and the two halves had quietly diverged.
395
+ *
396
+ * Same shape as F052.32's tap guard: 2D correct, 3D not, nobody looking. Asking
397
+ * for circles is what surfaced it — the second time this week that making
398
+ * something explicit uncovered a defect that was already there (F052.30's accent
399
+ * was the first).
400
+ *
401
+ * 46 rather than 44 so the two renderers carry ONE number instead of two that
402
+ * both happen to pass. The widest label («10») fits at the shipped font size,
403
+ * and all eleven take the same box so none is an ellipse among circles.
404
+ */
405
+ declare const INTENSITY_SIZE = 46;
406
+ /**
407
+ * F052.33 — should the help note be on screen at all?
408
+ *
409
+ * Extracted because the renderer's own answer is unreachable from a unit test:
410
+ * happy-dom has no WebGL, so no region can be selected, and a test written
411
+ * against the component asserts the note is PRESENT twice and never that it
412
+ * disappears. I wrote that test and it was green and vacuous.
413
+ *
414
+ * `openRegion` wins over everything. A note reading «tap a body part» while the
415
+ * picker is open is wrong advice, not a layering problem — and it was covering
416
+ * the panel's close control, the third exit hidden under something this week.
417
+ */
418
+ declare function shouldShowHint(opts: {
419
+ openRegion: string | null;
420
+ hint: false | BodyMap3DHint | undefined;
421
+ anySelectable: boolean;
422
+ hasExplicitHoverHint: boolean;
423
+ }): boolean;
374
424
  declare function BodyMap3D(props: BodyMap3DProps): react.JSX.Element;
375
425
 
376
- export { BodyMap3D, type BodyMap3DHint, type BodyMap3DModels, type BodyMap3DProps, type BodyMap3DSex, type BodyMap3DUiLabels, TAP_MAX_DISTANCE, ensureNormals, isTap, seamBlend, seamWeight, serializeReport };
426
+ export { BodyMap3D, type BodyMap3DHint, type BodyMap3DModels, type BodyMap3DProps, type BodyMap3DSex, type BodyMap3DUiLabels, INTENSITY_SIZE, TAP_MAX_DISTANCE, ensureNormals, isTap, seamBlend, seamWeight, serializeReport, shouldShowHint };
package/dist/three.js CHANGED
@@ -289,6 +289,12 @@ function webglAvailable() {
289
289
  return false;
290
290
  }
291
291
  }
292
+ var INTENSITY_SIZE = 46;
293
+ function shouldShowHint(opts) {
294
+ if (opts.openRegion) return false;
295
+ if (opts.hint === false) return false;
296
+ return opts.anySelectable || opts.hasExplicitHoverHint;
297
+ }
292
298
  var btn = (c) => ({
293
299
  font: "inherit",
294
300
  cursor: "pointer",
@@ -683,7 +689,12 @@ function BodyMap3D(props) {
683
689
  const region = selected ? REGIONS.find((r) => r.key === selected) : null;
684
690
  const current = selected ? pointOf(selected) : void 0;
685
691
  const anySelectable = REGIONS.some((r) => isSelectable(r.key, config ?? {}));
686
- const showEmptyHint = hint === false ? false : anySelectable || ui?.hoverHint !== void 0;
692
+ const showEmptyHint = shouldShowHint({
693
+ openRegion: selected,
694
+ hint,
695
+ anySelectable,
696
+ hasExplicitHoverHint: ui?.hoverHint !== void 0
697
+ });
687
698
  const hintText = (hint ? hint.text : void 0) ?? UI.hoverHint;
688
699
  const hintAtStage = isFullscreen && !!hint && hint.placement === "stage-bottom";
689
700
  return /* @__PURE__ */ jsxs(
@@ -791,13 +802,23 @@ function BodyMap3D(props) {
791
802
  "data-testid": "bodymap3d-close",
792
803
  "aria-label": L.close,
793
804
  onClick: () => setSelected(null),
794
- style: { ...btn(chrome), width: 32, height: 32, padding: 0, fontSize: 20, lineHeight: 1, color: chrome.mutedText, borderColor: chrome.border, background: chrome.panelBg },
805
+ style: { ...btn(chrome), width: 40, height: 40, minWidth: 40, padding: 0, borderRadius: "50%", fontSize: 20, lineHeight: 1, color: chrome.mutedText, borderColor: chrome.border, background: chrome.panelBg },
795
806
  children: "\xD7"
796
807
  }
797
808
  )
798
809
  ] }),
799
810
  /* @__PURE__ */ jsx("div", { style: { fontSize: 11, fontWeight: 700, letterSpacing: ".06em", textTransform: "uppercase", color: chrome.mutedText, marginBottom: 7 }, children: L.intensity }),
800
- /* @__PURE__ */ jsx("div", { style: { display: "flex", flexWrap: "wrap", gap: 4, marginBottom: 12 }, children: Array.from({ length: 11 }, (_, i) => /* @__PURE__ */ 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)) }),
811
+ /* @__PURE__ */ jsx("div", { style: { display: "flex", flexWrap: "wrap", gap: 4, marginBottom: 12 }, children: Array.from({ length: 11 }, (_, i) => (
812
+ /* F052.34 — CIRCLES, and all eleven the same size.
813
+ Dimensioned by «10», the widest label: sizing by the average
814
+ would make the two-digit one an ellipse among circles, which
815
+ is worse than eleven rounded squares because the odd one out
816
+ becomes a different SHAPE rather than slightly wider.
817
+ 46px is the SIBLING's touch size, not a new number: the 2D
818
+ renderer already used it and this one was on 30px, under the
819
+ >=44px floor F052.8 set. See INTENSITY_SIZE. */
820
+ /* @__PURE__ */ jsx("button", { "data-testid": `bodymap3d-intensity-${i}`, onClick: () => setPain(region.key, i, current?.type), style: { ...btn(chrome), display: "inline-flex", alignItems: "center", justifyContent: "center", width: INTENSITY_SIZE, height: INTENSITY_SIZE, minWidth: INTENSITY_SIZE, padding: 0, borderRadius: "50%", background: current?.intensity === i ? chrome.accent : chrome.panelBg, color: current?.intensity === i ? chrome.accentText : chrome.text }, children: i }, i)
821
+ )) }),
801
822
  /* @__PURE__ */ jsx("div", { style: { fontSize: 11, fontWeight: 700, letterSpacing: ".06em", textTransform: "uppercase", color: chrome.mutedText, marginBottom: 7 }, children: L.quality }),
802
823
  /* @__PURE__ */ jsx("div", { style: { display: "flex", flexWrap: "wrap", gap: 6, marginBottom: 14 }, children: PAIN_TYPES.map((t) => /* @__PURE__ */ 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)) }),
803
824
  current && /* @__PURE__ */ jsx("button", { "data-testid": "bodymap3d-remove", onClick: () => removePain(region.key), style: { ...btn(chrome), color: chrome.danger, borderColor: chrome.dangerBorder }, children: L.remove })
@@ -833,6 +854,6 @@ function BodyMap3D(props) {
833
854
  );
834
855
  }
835
856
 
836
- export { BodyMap3D, TAP_MAX_DISTANCE, ensureNormals, isTap, seamBlend, seamWeight, serializeReport };
857
+ export { BodyMap3D, INTENSITY_SIZE, TAP_MAX_DISTANCE, ensureNormals, isTap, seamBlend, seamWeight, serializeReport, shouldShowHint };
837
858
  //# sourceMappingURL=three.js.map
838
859
  //# sourceMappingURL=three.js.map