@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,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.
|