@broberg/bodymap 0.6.0 → 0.7.1

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,34 @@ 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
+
214
242
  ## Fullscreen — fill the viewport with the body
215
243
 
216
244
  ```tsx
@@ -223,7 +251,22 @@ const [big, setBig] = useState(false);
223
251
  <button onClick={() => setBig(true)}>Se stor</button>
224
252
  ```
225
253
 
226
- `Escape` leaves it. The pain report is untouched by entering or leaving.
254
+ `Escape` leaves it **but a phone has no Escape**, so there must always be a
255
+ reachable control. That is why the built-in one exists and why switching it off is
256
+ a commitment to supplying your own.
257
+
258
+ ⚠️ **If you supply your own control, render it as a SIBLING of the fullscreen
259
+ surface — not as a child of a container beneath it.** The surface is
260
+ `position: fixed`, so a button positioned `absolute` inside your original box ends
261
+ up *underneath* it. A consumer hit exactly this: their × disappeared under the
262
+ overlay, and because they had already set `showFullscreenButton={false}` there was
263
+ no way out at all. Give yours `position: fixed` and a z-index above the overlay,
264
+ and assert with `document.elementFromPoint()` that the point you can see is the
265
+ button you think it is.
266
+
267
+ The control sits inside the safe area (`env(safe-area-inset-*)` on all four sides),
268
+ and becomes a compact × in fullscreen — a text pill is wide enough to cover half a
269
+ phone's status bar. The pain report is untouched by entering or leaving.
227
270
 
228
271
  **It is a viewport fill, not the browser Fullscreen API — deliberately.** iOS
229
272
  Safari does not support fullscreen on an arbitrary element at all, and a phone is
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(