@plastic-js/tsumiki 0.1.68 → 0.1.70

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.
@@ -130,6 +130,7 @@ var footerClass = css({
130
130
  padding: "var(--tsu-container-padding-lg)",
131
131
  flexShrink: 0
132
132
  });
133
+ var footerSheetClass = css({ paddingBottom: "calc(var(--tsu-container-padding-lg) + env(safe-area-inset-bottom, 0px))" });
133
134
  var footerBtnClass = css({
134
135
  display: "flex",
135
136
  gap: "20px",
@@ -324,7 +325,7 @@ var Dialog$1 = (props) => {
324
325
  () => {
325
326
  const _el0 = _tmpl4.cloneNode(true);
326
327
  insert(_el0, () => footerContent);
327
- setProp(_el0, "className", () => footerClass);
328
+ setProp(_el0, "className", () => isSheet ? `${footerClass} ${footerSheetClass}` : footerClass);
328
329
  return _el0;
329
330
  }
330
331
  ]
@@ -105,6 +105,12 @@ var triggerIndicatorClass = css({
105
105
  }
106
106
  });
107
107
  var listBodyClass = css({ padding: 0 });
108
+ var pauseTrapWrapClass = css({
109
+ display: "flex",
110
+ flexDirection: "column",
111
+ flex: 1,
112
+ minHeight: 0
113
+ });
108
114
  var listClass = css({
109
115
  flex: 1,
110
116
  minHeight: 0,
@@ -434,6 +440,7 @@ var PauseDialogTrap = ({ children }) => {
434
440
  return () => {
435
441
  const _el0 = _tmpl4.cloneNode(true);
436
442
  insert(_el0, () => children);
443
+ setProp(_el0, "className", () => pauseTrapWrapClass);
437
444
  setProp(_el0, "ref", (el) => {
438
445
  node = el;
439
446
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@plastic-js/tsumiki",
3
- "version": "0.1.68",
3
+ "version": "0.1.70",
4
4
  "description": "A UI component library for Plastic JS.",
5
5
  "license": "MIT",
6
6
  "author": "tigre",
@@ -1,61 +0,0 @@
1
- # Select-in-Dialog on iOS
2
-
3
- **Affects:** `Select.jsx` (nested inside `Dialog`)
4
-
5
- ## Problem (original)
6
-
7
- A `Select` with a filter input, nested inside a `Dialog`, could not be focused on
8
- iOS Safari — tapping the search box did nothing and the keyboard never appeared.
9
-
10
- The `Dialog` (ArkDialog `modal`) installs a focus trap that listens on
11
- `document` and forces focus back into the dialog content whenever it escapes.
12
- The Select's bottom sheet was portaled to `document.body`, which is **outside**
13
- the dialog's focus-trap boundary. On iOS Safari the trap's `focusin` handler
14
- stole focus back from the filter input on every tap, so the input never kept
15
- focus long enough for the keyboard to appear.
16
-
17
- The old `SelectMobile` component hit the exact same WebKit focus-lock bug and
18
- fixed it by rendering the sheet **inline** (no `<Portal>`), keeping it inside
19
- the dialog's focus-trap boundary. The current `Select` re-introduced the bug by
20
- routing through `BottomSheet`, which always portaled.
21
-
22
- ## Problem (regression from the inline fix)
23
-
24
- Rendering the sheet inline inside the Dialog fixed focus, but created a
25
- different iOS-only bug: the Dialog body is a touch-scroll container
26
- (`overflow-y: auto` + `-webkit-overflow-scrolling: touch`). iOS WebKit pins any
27
- `position: fixed` descendant to that container, so the sheet's backdrop
28
- (`position: fixed; inset: 0`) only dimmed the dialog area and the sheet stuck to
29
- the dialog content — it looked like plain dialog content with **no independent
30
- backdrop**. Desktop Chrome (Blink) applies `-webkit-overflow-scrolling: touch`
31
- differently, so the same markup rendered as a proper full-screen overlay there,
32
- which is why the bug was only visible on real iOS devices.
33
-
34
- ## Fix
35
-
36
- The sheet is portaled to `<body>` **always**, so it renders as an independent
37
- overlay with its own full-screen backdrop, and the iOS focus-lock is solved
38
- properly instead of worked around:
39
-
40
- - `BottomSheet` keeps its `portal` prop (default `true`).
41
- - `Select.Content` always passes `portal`.
42
- - When a Dialog is present, `Select.Content` wraps the list in
43
- `PauseDialogTrap`, which calls `trapFocus()` (from `@zag-js/focus-trap`) on the
44
- sheet content for the sheet's lifetime. `@zag-js/focus-trap` keeps a **shared
45
- trap stack**, so activating this trap pauses the parent Dialog's focus trap
46
- and deactivating it resumes the Dialog's. The sheet's filter input therefore
47
- keeps focus on iOS even though it lives outside the dialog's DOM.
48
- - `trapFocus` is configured with `initialFocus: false` (never steal focus on
49
- open — the input is deliberately not auto-focused), `allowOutsideClick: true`
50
- (backdrop dismiss + grabber drag still work), and `escapeDeactivates: false`
51
- (the Select closes on Escape itself).
52
-
53
- Both fixes must share the same `@zag-js/focus-trap` module instance for the
54
- stack to be shared, so tsumiki pins the exact version (`1.40.0`) that
55
- `@zag-js/dialog` (via Ark) uses.
56
-
57
- ## e2e
58
-
59
- `e2e/select-in-dialog-*` verifies the sheet portals to `<body>` (independent
60
- overlay, outside the dialog content tree), the filter input keeps focus after a
61
- tap, and closing the sheet via backdrop leaves the dialog open.