@broberg/bodymap 0.5.0 → 0.7.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 CHANGED
@@ -211,6 +211,70 @@ had *arrived* on a device that never *showed* it.)
211
211
  `VIBRATION_PATTERNS.ignore` is empty on purpose: a tap that changed nothing must
212
212
  not feel like it did.
213
213
 
214
+ ## Closing the picker without marking anything
215
+
216
+ Opening a region writes **nothing** to the report — only choosing an intensity
217
+ does. So until 0.7.0 there was no exit from a region you opened by accident: your
218
+ options were to mark it (writing something you did not mean) or to open a
219
+ different region (which you also did not mean).
220
+
221
+ Both renderers now carry a close control (`bodymap-close` / `bodymap3d-close`),
222
+ and in the 3D renderer `Escape` closes the open region — falling through to
223
+ leaving fullscreen only when no region is open, so one press never does two
224
+ things.
225
+
226
+ **Close is not remove, and the distinction is deliberate:**
227
+
228
+ | | |
229
+ |---|---|
230
+ | **Close** | always available. Stops editing this region. **Never touches the report.** |
231
+ | **Remove** | only when something is actually marked. Deletes the mark. |
232
+
233
+ A «Remove» button offered while the report is empty tells the user something was
234
+ stored when nothing was — so it stays gated, and the close control is what fills
235
+ the gap.
236
+
237
+ **Why it went unnoticed for so long:** every test and demo in this package taps a
238
+ region *in order to mark it*. Nobody had ever tested changing their mind. Nothing
239
+ was behaving wrongly — it was a path with no exit, and paths with no exit do not
240
+ fail, they strand people.
241
+
242
+ ## Fullscreen — fill the viewport with the body
243
+
244
+ ```tsx
245
+ // built-in control (default)
246
+ <BodyMap3D models={models} />
247
+
248
+ // your own button — you own the state
249
+ const [big, setBig] = useState(false);
250
+ <BodyMap3D models={models} fullscreen={big} onFullscreenChange={setBig} showFullscreenButton={false} />
251
+ <button onClick={() => setBig(true)}>Se stor</button>
252
+ ```
253
+
254
+ `Escape` leaves it. The pain report is untouched by entering or leaving.
255
+
256
+ **It is a viewport fill, not the browser Fullscreen API — deliberately.** iOS
257
+ Safari does not support fullscreen on an arbitrary element at all, and a phone is
258
+ this component's primary surface. A `position: fixed; inset: 0` overlay behaves
259
+ identically on every platform, needs no user-gesture dance, and cannot leave you
260
+ with a control that silently does nothing on the one device that matters most.
261
+
262
+ ### It ships with a `ResizeObserver`, and that is not incidental
263
+
264
+ Before 0.6.0 the canvas measured the element **once at mount** and only re-measured
265
+ on a **window** resize. Everything below changes the ELEMENT without touching the
266
+ window, and each one left the picture at its old size — a body that renders small,
267
+ off-centre, or clipped, with nothing wrong in the code because the camera maths is
268
+ correct and the numbers it was handed are stale:
269
+
270
+ - entering or leaving fullscreen
271
+ - switching `canvasHeight` at your own breakpoint
272
+ - a container animating open, or a wizard step revealing the panel
273
+ - **a phone's URL bar collapsing, which changes what `vh` means**
274
+
275
+ If you saw a badly framed body on a phone before 0.6.0, this is the first thing to
276
+ retest.
277
+
214
278
  ## `seam` — soft region edges are OPT-IN, and here is why
215
279
 
216
280
  ```tsx
package/dist/react.cjs CHANGED
@@ -235,6 +235,7 @@ var LABELS_DA = {
235
235
  intensity: "Intensitet (0-10)",
236
236
  quality: "Kvalitet",
237
237
  remove: "Fjern punkt",
238
+ close: "Luk",
238
239
  front: "Forfra",
239
240
  back: "Bagfra",
240
241
  empty: "V\xE6lg en kropsdel for at markere smerte.",
@@ -257,6 +258,7 @@ var LABELS_EN = {
257
258
  intensity: "Intensity (0-10)",
258
259
  quality: "Quality",
259
260
  remove: "Remove point",
261
+ close: "Close",
260
262
  front: "Front",
261
263
  back: "Back",
262
264
  empty: "Pick a body part to mark pain.",
@@ -356,6 +358,10 @@ var STYLE = `
356
358
  .bmap__panel--empty{color:var(--bmap-muted);font-size:13.5px}
357
359
  .bmap__ph{display:flex;align-items:center;justify-content:space-between;gap:8px;margin-bottom:12px}
358
360
  .bmap__ph b{font-size:16px}
361
+ .bmap__close{margin-left:auto;font:inherit;font-size:20px;line-height:1;cursor:pointer;background:none;border:1px solid transparent;border-radius:8px;width:32px;height:32px;color:var(--bmap-muted,#475569)}
362
+ .bmap__close:hover{background:var(--bmap-badge-bg,#f1f5f9)}
363
+ .bmap__close:active{transform:scale(.94)}
364
+ .bmap__close:focus-visible{outline:2px solid var(--bmap-accent,#0e8f8a);outline-offset:2px}
359
365
  .bmap__code{font:11px ui-monospace,monospace;color:var(--bmap-muted);background:var(--bmap-badge-bg);border-radius:6px;padding:2px 7px}
360
366
  .bmap__lbl{font-size:11px;font-weight:700;letter-spacing:.06em;text-transform:uppercase;color:var(--bmap-muted);margin:0 0 7px}
361
367
  .bmap__scale{display:flex;gap:6px;flex-wrap:wrap;margin-bottom:14px}
@@ -653,7 +659,18 @@ function BodyMap({
653
659
  readOnly ? null : region ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bmap__panel", "data-testid": "bodymap-panel", children: [
654
660
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bmap__ph", children: [
655
661
  /* @__PURE__ */ jsxRuntime.jsx("b", { children: nameOf(region.key) }),
656
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "bmap__code", children: region.code })
662
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "bmap__code", children: region.code }),
663
+ /* @__PURE__ */ jsxRuntime.jsx(
664
+ "button",
665
+ {
666
+ type: "button",
667
+ className: "bmap__close",
668
+ "data-testid": "bodymap-close",
669
+ "aria-label": L.close,
670
+ onClick: () => setSelected(null),
671
+ children: "\xD7"
672
+ }
673
+ )
657
674
  ] }),
658
675
  /* @__PURE__ */ jsxRuntime.jsx("p", { className: "bmap__lbl", id: "bmap-intensity-lbl", children: L.intensity }),
659
676
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "bmap__scale", role: "group", "aria-labelledby": "bmap-intensity-lbl", children: Array.from({ length: 11 }, (_, n) => /* @__PURE__ */ jsxRuntime.jsx(