@rdlabo/ionic-theme-ios27 1.2.0 → 1.2.1-beta.pr273.shae866fb1dca9f
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/css/components/ion-button.css +1 -1
- package/dist/css/components/ion-toolbar.css +1 -1
- package/dist/css/ionic-theme-ios27.css +1 -1
- package/dist/css/vertical-bars.css +1 -1
- package/dist/native/components/index.js +2 -2
- package/dist/native/components/index.js.map +1 -1
- package/dist/native/definitions.d.ts +32 -0
- package/dist/native/definitions.d.ts.map +1 -1
- package/dist/native/index.d.ts.map +1 -1
- package/dist/native/index.js +23 -6
- package/dist/native/index.js.map +1 -1
- package/dist/native/overlays/content.d.ts +4 -0
- package/dist/native/overlays/content.d.ts.map +1 -0
- package/dist/native/overlays/content.js +51 -0
- package/dist/native/overlays/content.js.map +1 -0
- package/dist/native/overlays/controller.d.ts +8 -0
- package/dist/native/overlays/controller.d.ts.map +1 -0
- package/dist/native/overlays/controller.js +266 -0
- package/dist/native/overlays/controller.js.map +1 -0
- package/dist/native/overlays/dialog-animation.d.ts +8 -0
- package/dist/native/overlays/dialog-animation.d.ts.map +1 -0
- package/dist/native/overlays/dialog-animation.js +31 -0
- package/dist/native/overlays/dialog-animation.js.map +1 -0
- package/dist/native/overlays/dismiss-animation.d.ts +4 -0
- package/dist/native/overlays/dismiss-animation.d.ts.map +1 -0
- package/dist/native/overlays/dismiss-animation.js +18 -0
- package/dist/native/overlays/dismiss-animation.js.map +1 -0
- package/dist/native/overlays/focus.d.ts +5 -0
- package/dist/native/overlays/focus.d.ts.map +1 -0
- package/dist/native/overlays/focus.js +59 -0
- package/dist/native/overlays/focus.js.map +1 -0
- package/dist/native/overlays/popover.d.ts +11 -0
- package/dist/native/overlays/popover.d.ts.map +1 -0
- package/dist/native/overlays/popover.js +78 -0
- package/dist/native/overlays/popover.js.map +1 -0
- package/dist/native/overlays/styles.d.ts +9 -0
- package/dist/native/overlays/styles.d.ts.map +1 -0
- package/dist/native/overlays/styles.js +82 -0
- package/dist/native/overlays/styles.js.map +1 -0
- package/dist/native/prehide.d.ts.map +1 -1
- package/dist/native/prehide.js +6 -2
- package/dist/native/prehide.js.map +1 -1
- package/dist/native/runtime.d.ts +4 -1
- package/dist/native/runtime.d.ts.map +1 -1
- package/dist/native/runtime.js +29 -8
- package/dist/native/runtime.js.map +1 -1
- package/dist/native/shared/dom.d.ts +2 -0
- package/dist/native/shared/dom.d.ts.map +1 -1
- package/dist/native/shared/dom.js +5 -1
- package/dist/native/shared/dom.js.map +1 -1
- package/dist/native/shared/projection-id.d.ts +4 -0
- package/dist/native/shared/projection-id.d.ts.map +1 -0
- package/dist/native/shared/projection-id.js +7 -0
- package/dist/native/shared/projection-id.js.map +1 -0
- package/dist/native/vertical-bars-web.d.ts.map +1 -1
- package/dist/native/vertical-bars-web.js +4 -2
- package/dist/native/vertical-bars-web.js.map +1 -1
- package/dist/native-integration/index.d.ts +1 -1
- package/dist/native-integration/index.js.map +1 -1
- package/dist/native-integration/transition.d.ts +1 -1
- package/dist/native-integration/transition.js.map +1 -1
- package/dist/sheets-of-glass/animations.d.ts +1 -1
- package/dist/sheets-of-glass/animations.js +1 -1
- package/dist/sheets-of-glass/animations.js.map +1 -1
- package/dist/sheets-of-glass/index.js +1 -1
- package/dist/sheets-of-glass/index.js.map +1 -1
- package/dist/tab-bar-searchable/animations/enter.d.ts +1 -1
- package/dist/tab-bar-searchable/animations/enter.d.ts.map +1 -1
- package/dist/tab-bar-searchable/animations/enter.js +1 -1
- package/dist/tab-bar-searchable/animations/enter.js.map +1 -1
- package/dist/tab-bar-searchable/animations/leave.d.ts +1 -1
- package/dist/tab-bar-searchable/animations/leave.d.ts.map +1 -1
- package/dist/tab-bar-searchable/animations/leave.js +1 -1
- package/dist/tab-bar-searchable/animations/leave.js.map +1 -1
- package/dist/tab-bar-searchable/index.js +1 -1
- package/dist/tab-bar-searchable/index.js.map +1 -1
- package/dist/transition/index.d.ts +1 -1
- package/dist/transition/index.js +1 -1
- package/dist/transition/index.js.map +1 -1
- package/dist/transition/ios.transition.d.ts +1 -1
- package/dist/transition/ios.transition.js.map +1 -1
- package/dist/utils.d.ts +1 -1
- package/dist/utils.js.map +1 -1
- package/docs/native-ui-shell.md +20 -4
- package/docs/vertical-bars.md +3 -1
- package/ios/Sources/IonicNativeUIShellPlugin/Components/ShellSearchController.swift +65 -91
- package/ios/Sources/IonicNativeUIShellPlugin/Components/ShellTabBar.swift +9 -3
- package/ios/Sources/IonicNativeUIShellPlugin/Components/ShellVerticalBars.swift +79 -3
- package/ios/Sources/IonicNativeUIShellPlugin/IonicNativeUIShellPlugin.swift +57 -1
- package/ios/Sources/IonicNativeUIShellPlugin/Overlays/ShellOverlayController.swift +116 -0
- package/ios/Sources/IonicNativeUIShellPlugin/Overlays/ShellOverlayHost.swift +36 -0
- package/ios/Tests/IonicNativeUIShellPluginTests/ShellSnapshotTests.swift +27 -0
- package/package.json +2 -2
- package/src/native/components/index.ts +3 -2
- package/src/native/definitions.ts +27 -1
- package/src/native/index.ts +23 -6
- package/src/native/overlays/content.ts +51 -0
- package/src/native/overlays/controller.ts +322 -0
- package/src/native/overlays/dialog-animation.ts +30 -0
- package/src/native/overlays/dismiss-animation.ts +15 -0
- package/src/native/overlays/focus.ts +64 -0
- package/src/native/overlays/popover.ts +80 -0
- package/src/native/overlays/styles.ts +80 -0
- package/src/native/prehide.ts +6 -1
- package/src/native/runtime.ts +39 -15
- package/src/native/shared/dom.ts +14 -0
- package/src/native/shared/projection-id.ts +2 -0
- package/src/native/vertical-bars-web.ts +11 -1
- package/src/native-integration/index.ts +1 -1
- package/src/native-integration/transition.ts +1 -1
- package/src/sheets-of-glass/animations.ts +2 -2
- package/src/sheets-of-glass/index.ts +2 -2
- package/src/styles/components/ion-button.scss +47 -0
- package/src/styles/components/ion-toolbar.scss +62 -7
- package/src/styles/vertical-bars.scss +23 -0
- package/src/tab-bar-searchable/animations/enter.ts +2 -1
- package/src/tab-bar-searchable/animations/leave.ts +2 -1
- package/src/tab-bar-searchable/index.ts +1 -1
- package/src/transition/index.ts +3 -3
- package/src/transition/ios.transition.ts +1 -1
- package/src/utils.ts +1 -1
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
import { isExcluded, isPermanentlyExcluded, isShellDisabled } from "../shared/dom.js";
|
|
2
|
+
import { bounded } from "../runtime.js";
|
|
3
|
+
import { activeElement, trapFocus } from "./focus.js";
|
|
4
|
+
import { relayStyles } from "./styles.js";
|
|
5
|
+
import { popoverPresentation, relayPopover, verticalBarsPopoverAnchor } from "./popover.js";
|
|
6
|
+
import { nativeDialogAnimation } from "./dialog-animation.js";
|
|
7
|
+
//#region src/native/overlays/controller.ts
|
|
8
|
+
const lifecycle = (overlay, phase) => `ion${overlay.localName.slice(4, 5).toUpperCase()}${overlay.localName.slice(5)}${phase}`;
|
|
9
|
+
/** Ionic owns lifecycle and dismissal; only one overlay is relayed at a time. */
|
|
10
|
+
const createOverlayController = async (doc, plugin, options, retainProjection, nativeVerticalBars) => {
|
|
11
|
+
let current;
|
|
12
|
+
let opening = Promise.resolve();
|
|
13
|
+
let sequence = 0;
|
|
14
|
+
let stopped = false;
|
|
15
|
+
const suspensions = /* @__PURE__ */ new Set();
|
|
16
|
+
const events = [
|
|
17
|
+
"Modal",
|
|
18
|
+
"Popover",
|
|
19
|
+
"Alert",
|
|
20
|
+
"ActionSheet",
|
|
21
|
+
"Loading",
|
|
22
|
+
"Picker",
|
|
23
|
+
"Toast"
|
|
24
|
+
].map((name) => `ion${name}WillPresent`);
|
|
25
|
+
const cleanup = async (connection) => {
|
|
26
|
+
const stage = async (label, run) => {
|
|
27
|
+
try {
|
|
28
|
+
await bounded(Promise.resolve(run?.()));
|
|
29
|
+
} catch (error) {
|
|
30
|
+
console.error(`Native overlay teardown failed at ${label}; continuing cleanup anyway.`, error);
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
try {
|
|
34
|
+
await stage("events", connection.stopEvents);
|
|
35
|
+
await stage("lifecycle", connection.stopLifecycle);
|
|
36
|
+
await stage("childEvents", connection.stopChildEvents);
|
|
37
|
+
await stage("exclusions", connection.stopExclusions);
|
|
38
|
+
await stage("focus", connection.stopFocus);
|
|
39
|
+
await connection.nativeClosing?.catch((error) => {
|
|
40
|
+
console.error("Native overlay dismissal did not complete; restoring the Web content anyway.", error);
|
|
41
|
+
});
|
|
42
|
+
await stage("content", connection.stopContent);
|
|
43
|
+
await stage("animation", connection.stopAnimation);
|
|
44
|
+
await stage("styles", connection.stopStyles);
|
|
45
|
+
await stage("close", () => bounded(plugin.closeOverlay({ id: connection.id })));
|
|
46
|
+
} finally {
|
|
47
|
+
connection.releaseProjection?.();
|
|
48
|
+
connection.releaseProjection = void 0;
|
|
49
|
+
try {
|
|
50
|
+
connection.win?.close();
|
|
51
|
+
} catch (error) {
|
|
52
|
+
console.error("Native overlay window was already gone.", error);
|
|
53
|
+
}
|
|
54
|
+
if (current === connection) current = void 0;
|
|
55
|
+
const focus = connection.focus;
|
|
56
|
+
if (focus?.isConnected && focus.tabIndex >= 0) focus.focus({ preventScroll: true });
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
const release = (connection) => connection.closing ?? (connection.closing = cleanup(connection));
|
|
60
|
+
const restoreWeb = async () => {
|
|
61
|
+
if (current) await release(current);
|
|
62
|
+
};
|
|
63
|
+
const excluded = (overlay) => isPermanentlyExcluded(overlay) || isExcluded(overlay) || isShellDisabled(overlay);
|
|
64
|
+
const connect = async (connection, ready, presentation) => {
|
|
65
|
+
const { overlay } = connection;
|
|
66
|
+
const gone = () => current !== connection || stopped || suspensions.size || connection.dismissed || !overlay.isConnected || !nativeVerticalBars() || excluded(overlay);
|
|
67
|
+
connection.hideAnimation?.();
|
|
68
|
+
try {
|
|
69
|
+
const staging = (async () => {
|
|
70
|
+
await bounded(plugin.prepareOverlay({ id: connection.id }));
|
|
71
|
+
if (gone()) return void 0;
|
|
72
|
+
const win = doc.defaultView.open(`about:blank#${connection.id}`, "_blank");
|
|
73
|
+
if (!win) throw new Error("Native overlay window unavailable");
|
|
74
|
+
connection.win = win;
|
|
75
|
+
if (decodeURIComponent(win.document.URL) !== `about:blank#${connection.id}` || win.document.readyState !== "complete") await bounded(new Promise((resolve) => win.addEventListener("load", () => resolve(), { once: true })));
|
|
76
|
+
if (gone()) return void 0;
|
|
77
|
+
win.document.open();
|
|
78
|
+
win.document.write("<!doctype html><html><head></head><body></body></html>");
|
|
79
|
+
win.document.close();
|
|
80
|
+
connection.stopChildEvents = observe(win.document);
|
|
81
|
+
const environment = relayStyles(doc, win.document, overlay);
|
|
82
|
+
connection.stopStyles = environment.stop;
|
|
83
|
+
await bounded(environment.ready);
|
|
84
|
+
if (gone()) return void 0;
|
|
85
|
+
return {
|
|
86
|
+
destination: environment.destination,
|
|
87
|
+
win
|
|
88
|
+
};
|
|
89
|
+
})();
|
|
90
|
+
staging.catch(() => {});
|
|
91
|
+
await bounded(ready);
|
|
92
|
+
await bounded(new Promise((resolve) => doc.defaultView.requestAnimationFrame(() => resolve())));
|
|
93
|
+
const staged = await staging;
|
|
94
|
+
if (gone() || !staged) {
|
|
95
|
+
await release(connection);
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
const focused = activeElement(doc);
|
|
99
|
+
const nativePresentation = popoverPresentation(overlay);
|
|
100
|
+
const content = relayPopover(overlay, staged.destination);
|
|
101
|
+
connection.stopContent = content.stop;
|
|
102
|
+
connection.hideAnimation?.();
|
|
103
|
+
const exclusions = new MutationObserver(() => {
|
|
104
|
+
if (!overlay.isConnected || !nativeVerticalBars() || excluded(overlay) || isShellDisabled(content.root)) release(connection).catch(console.error);
|
|
105
|
+
});
|
|
106
|
+
const attributes = {
|
|
107
|
+
attributes: true,
|
|
108
|
+
attributeFilter: [
|
|
109
|
+
"class",
|
|
110
|
+
"data-shell",
|
|
111
|
+
"hidden",
|
|
112
|
+
"inert"
|
|
113
|
+
]
|
|
114
|
+
};
|
|
115
|
+
for (let node = overlay; node; node = node.parentElement) {
|
|
116
|
+
exclusions.observe(node, attributes);
|
|
117
|
+
exclusions.observe(node, { childList: true });
|
|
118
|
+
}
|
|
119
|
+
exclusions.observe(content.root, {
|
|
120
|
+
...attributes,
|
|
121
|
+
subtree: true
|
|
122
|
+
});
|
|
123
|
+
connection.stopExclusions = () => exclusions.disconnect();
|
|
124
|
+
connection.stopFocus = trapFocus(content.root, () => !connection.dismissed && (!("focusTrap" in overlay) || overlay.focusTrap !== false) && !overlay.classList.contains("ion-disable-focus-trap"));
|
|
125
|
+
const close = () => {
|
|
126
|
+
release(connection).catch(console.error);
|
|
127
|
+
};
|
|
128
|
+
const escape = (event) => {
|
|
129
|
+
if (event.key === "Escape" && overlay.backdropDismiss) overlay.dismiss(void 0, "backdrop");
|
|
130
|
+
};
|
|
131
|
+
staged.win.document.addEventListener("keydown", escape);
|
|
132
|
+
overlay.addEventListener(lifecycle(overlay, "DidDismiss"), close, { once: true });
|
|
133
|
+
connection.stopEvents = () => {
|
|
134
|
+
staged.win.document.removeEventListener("keydown", escape);
|
|
135
|
+
overlay.removeEventListener(lifecycle(overlay, "DidDismiss"), close);
|
|
136
|
+
};
|
|
137
|
+
await bounded(plugin.presentOverlay({
|
|
138
|
+
id: connection.id,
|
|
139
|
+
presentation: {
|
|
140
|
+
...nativePresentation,
|
|
141
|
+
animated: presentation.animated
|
|
142
|
+
}
|
|
143
|
+
}));
|
|
144
|
+
(focused?.isConnected && focused.ownerDocument === staged.win.document ? focused : content.root).focus({ preventScroll: true });
|
|
145
|
+
} catch (error) {
|
|
146
|
+
await release(connection);
|
|
147
|
+
console.error("Native overlay projection failed; keeping Ionic content in the source WebView.", error);
|
|
148
|
+
}
|
|
149
|
+
};
|
|
150
|
+
const present = (event) => {
|
|
151
|
+
const overlay = event.target;
|
|
152
|
+
if (current) {
|
|
153
|
+
const connection = current;
|
|
154
|
+
current = void 0;
|
|
155
|
+
opening = opening.then(() => release(connection)).catch(console.error);
|
|
156
|
+
if (!connection.dismissed) return;
|
|
157
|
+
connection.stopAnimation?.();
|
|
158
|
+
connection.stopAnimation = void 0;
|
|
159
|
+
}
|
|
160
|
+
const enabled = overlay.localName === "ion-popover" && options.controls?.popover === true && nativeVerticalBars() && verticalBarsPopoverAnchor(overlay) !== void 0;
|
|
161
|
+
if (stopped || suspensions.size || !enabled || excluded(overlay)) {
|
|
162
|
+
opening = opening.then(restoreWeb).catch(console.error);
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
const connection = {
|
|
166
|
+
id: `ios-theme-overlay-${++sequence}`,
|
|
167
|
+
overlay,
|
|
168
|
+
dismissed: false,
|
|
169
|
+
focus: activeElement(doc)
|
|
170
|
+
};
|
|
171
|
+
const presentation = {
|
|
172
|
+
kind: "popover",
|
|
173
|
+
animated: overlay.animated
|
|
174
|
+
};
|
|
175
|
+
current = connection;
|
|
176
|
+
const anchorId = verticalBarsPopoverAnchor(overlay);
|
|
177
|
+
connection.releaseProjection = retainProjection((id) => id === anchorId && current === connection && !!connection.nativeClosing && !stopped && !suspensions.size && nativeVerticalBars() && !excluded(overlay));
|
|
178
|
+
const closed = () => {
|
|
179
|
+
connection.dismissed = true;
|
|
180
|
+
};
|
|
181
|
+
overlay.addEventListener(lifecycle(overlay, "DidDismiss"), closed, { once: true });
|
|
182
|
+
let resolveReady;
|
|
183
|
+
const readyPromise = new Promise((resolve) => {
|
|
184
|
+
resolveReady = resolve;
|
|
185
|
+
});
|
|
186
|
+
const ready = () => {
|
|
187
|
+
resolveReady();
|
|
188
|
+
};
|
|
189
|
+
const closeNative = () => connection.nativeClosing ?? (connection.nativeClosing = bounded(plugin.dismissOverlay({
|
|
190
|
+
id: connection.id,
|
|
191
|
+
animated: presentation.animated
|
|
192
|
+
})));
|
|
193
|
+
const animation = nativeDialogAnimation(overlay, closeNative);
|
|
194
|
+
connection.stopAnimation = animation.stop;
|
|
195
|
+
connection.hideAnimation = animation.hide;
|
|
196
|
+
animation.hide();
|
|
197
|
+
overlay.addEventListener(lifecycle(overlay, "DidPresent"), ready, { once: true });
|
|
198
|
+
connection.stopLifecycle = () => overlay.removeEventListener(lifecycle(overlay, "DidDismiss"), closed);
|
|
199
|
+
opening = opening.then(() => connect(connection, readyPromise, presentation)).catch(console.error).finally(() => {
|
|
200
|
+
overlay.removeEventListener(lifecycle(overlay, "DidPresent"), ready);
|
|
201
|
+
});
|
|
202
|
+
};
|
|
203
|
+
const menuOpen = (event) => {
|
|
204
|
+
if (!event.composedPath().some((node) => node instanceof HTMLElement && node.matches("ion-menu"))) return;
|
|
205
|
+
const connection = current;
|
|
206
|
+
current = void 0;
|
|
207
|
+
if (connection) opening = opening.then(() => release(connection)).catch(console.error);
|
|
208
|
+
};
|
|
209
|
+
const observe = (target) => {
|
|
210
|
+
for (const name of events) target.addEventListener(name, present);
|
|
211
|
+
target.addEventListener("ionWillOpen", menuOpen);
|
|
212
|
+
return () => {
|
|
213
|
+
for (const name of events) target.removeEventListener(name, present);
|
|
214
|
+
target.removeEventListener("ionWillOpen", menuOpen);
|
|
215
|
+
};
|
|
216
|
+
};
|
|
217
|
+
const invalidate = () => {
|
|
218
|
+
const connection = current;
|
|
219
|
+
current = void 0;
|
|
220
|
+
if (connection) opening = opening.then(() => release(connection)).catch(console.error);
|
|
221
|
+
};
|
|
222
|
+
const win = doc.defaultView;
|
|
223
|
+
win.addEventListener("resize", invalidate);
|
|
224
|
+
doc.addEventListener("ionViewWillLeave", invalidate);
|
|
225
|
+
const visibility = () => {
|
|
226
|
+
if (doc.hidden) invalidate();
|
|
227
|
+
};
|
|
228
|
+
doc.addEventListener("visibilitychange", visibility);
|
|
229
|
+
const listener = await bounded(plugin.addListener("overlay", (event) => {
|
|
230
|
+
if (event.id !== current?.id) return;
|
|
231
|
+
if (event.action === "dismiss" && current.overlay.backdropDismiss) current.overlay.dismiss(void 0, "backdrop").catch(console.error);
|
|
232
|
+
}));
|
|
233
|
+
const stopEvents = observe(doc);
|
|
234
|
+
return {
|
|
235
|
+
async suspend() {
|
|
236
|
+
const lease = Symbol();
|
|
237
|
+
suspensions.add(lease);
|
|
238
|
+
await opening;
|
|
239
|
+
await restoreWeb();
|
|
240
|
+
return () => {
|
|
241
|
+
suspensions.delete(lease);
|
|
242
|
+
};
|
|
243
|
+
},
|
|
244
|
+
async destroy() {
|
|
245
|
+
stopped = true;
|
|
246
|
+
stopEvents();
|
|
247
|
+
win.removeEventListener("resize", invalidate);
|
|
248
|
+
doc.removeEventListener("ionViewWillLeave", invalidate);
|
|
249
|
+
doc.removeEventListener("visibilitychange", visibility);
|
|
250
|
+
try {
|
|
251
|
+
await opening;
|
|
252
|
+
await restoreWeb();
|
|
253
|
+
} finally {
|
|
254
|
+
try {
|
|
255
|
+
await bounded(plugin.stopOverlays());
|
|
256
|
+
} finally {
|
|
257
|
+
await listener.remove();
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
};
|
|
262
|
+
};
|
|
263
|
+
//#endregion
|
|
264
|
+
export { createOverlayController };
|
|
265
|
+
|
|
266
|
+
//# sourceMappingURL=controller.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"controller.js","names":[],"sources":["../../../src/native/overlays/controller.ts"],"sourcesContent":["import type { NativeUIShellOptions, NativeUIShellPlugin } from '../definitions';\nimport { bounded } from '../runtime';\nimport { isExcluded, isPermanentlyExcluded, isShellDisabled } from '../shared/dom';\nimport { activeElement, trapFocus } from './focus';\nimport { relayPopover, popoverPresentation, verticalBarsPopoverAnchor } from './popover';\nimport { nativeDialogAnimation } from './dialog-animation';\nimport { relayStyles } from './styles';\n\ntype Presentation = { kind: 'popover'; animated: boolean };\ntype Overlay = HTMLIonPopoverElement;\nconst lifecycle = (overlay: Overlay, phase: string) =>\n `ion${overlay.localName.slice(4, 5).toUpperCase()}${overlay.localName.slice(5)}${phase}`;\n\ninterface Connection {\n id: string;\n overlay: Overlay;\n dismissed: boolean;\n win?: Window;\n focus: Element | null;\n stopStyles?: () => void;\n releaseProjection?: () => void;\n stopContent?: () => void;\n stopFocus?: () => void;\n stopEvents?: () => void;\n stopChildEvents?: () => void;\n stopExclusions?: () => void;\n closing?: Promise<void>;\n nativeClosing?: Promise<void>;\n stopAnimation?: () => void;\n hideAnimation?: () => void;\n stopLifecycle?: () => void;\n}\n\n/** Ionic owns lifecycle and dismissal; only one overlay is relayed at a time. */\nexport const createOverlayController = async (\n doc: Document,\n plugin: NativeUIShellPlugin,\n options: NativeUIShellOptions,\n retainProjection: (deferActivation?: (id: string) => boolean) => () => void,\n nativeVerticalBars: () => boolean,\n) => {\n let current: Connection | undefined;\n let opening = Promise.resolve();\n let sequence = 0;\n let stopped = false;\n const suspensions = new Set<symbol>();\n const events = ['Modal', 'Popover', 'Alert', 'ActionSheet', 'Loading', 'Picker', 'Toast'].map((name) => `ion${name}WillPresent`);\n const cleanup = async (connection: Connection) => {\n // Every teardown step is isolated and bounded: a throwing or hanging step\n // must not strand the retained page projections or the child window.\n const stage = async (label: string, run?: () => void | Promise<void>) => {\n try {\n await bounded(Promise.resolve(run?.()));\n } catch (error) {\n console.error(`Native overlay teardown failed at ${label}; continuing cleanup anyway.`, error);\n }\n };\n try {\n await stage('events', connection.stopEvents);\n await stage('lifecycle', connection.stopLifecycle);\n await stage('childEvents', connection.stopChildEvents);\n await stage('exclusions', connection.stopExclusions);\n await stage('focus', connection.stopFocus);\n // A timed-out dismissal must not strand the relayed content in the child window.\n await connection.nativeClosing?.catch((error) => {\n console.error('Native overlay dismissal did not complete; restoring the Web content anyway.', error);\n });\n await stage('content', connection.stopContent);\n await stage('animation', connection.stopAnimation);\n await stage('styles', connection.stopStyles);\n await stage('close', () => bounded(plugin.closeOverlay({ id: connection.id })));\n } finally {\n // The page projections must be released even if teardown throws above.\n connection.releaseProjection?.();\n connection.releaseProjection = undefined;\n try {\n connection.win?.close();\n } catch (error) {\n console.error('Native overlay window was already gone.', error);\n }\n if (current === connection) current = undefined;\n const focus = connection.focus as HTMLElement | null;\n if (focus?.isConnected && focus.tabIndex >= 0) focus.focus({ preventScroll: true });\n }\n };\n const release = (connection: Connection): Promise<void> => (connection.closing ??= cleanup(connection));\n const restoreWeb = async () => {\n if (current) await release(current);\n };\n const excluded = (overlay: HTMLElement) => isPermanentlyExcluded(overlay) || isExcluded(overlay) || isShellDisabled(overlay);\n const connect = async (connection: Connection, ready: Promise<void>, presentation: Presentation) => {\n const { overlay } = connection;\n const gone = () =>\n current !== connection ||\n stopped ||\n suspensions.size ||\n connection.dismissed ||\n !overlay.isConnected ||\n !nativeVerticalBars() ||\n excluded(overlay);\n // Keep the source invisible while the native toolbar prepares its popover.\n connection.hideAnimation?.();\n try {\n // Prepare the child window concurrently with Ionic's content initialization.\n const staging = (async () => {\n await bounded(\n plugin.prepareOverlay({\n id: connection.id,\n }),\n );\n if (gone()) return undefined;\n const win = doc.defaultView!.open(`about:blank#${connection.id}`, '_blank');\n if (!win) throw new Error('Native overlay window unavailable');\n connection.win = win;\n // The requested blank page replaces the initial document created by window.open.\n // WebKit percent-encodes the '#' marker in the popup URL; compare decoded.\n // Adopt nodes only after that navigation; otherwise WebKit discards the relay.\n if (decodeURIComponent(win.document.URL) !== `about:blank#${connection.id}` || win.document.readyState !== 'complete')\n await bounded(new Promise<void>((resolve) => win.addEventListener('load', () => resolve(), { once: true })));\n if (gone()) return undefined;\n // about:blank is a quirks-mode document in WebKit. Initialize standards\n // mode before adopting live content: otherwise percentage button heights\n // can resolve against the entire popover viewport.\n win.document.open();\n win.document.write('<!doctype html><html><head></head><body></body></html>');\n win.document.close();\n connection.stopChildEvents = observe(win.document);\n const environment = relayStyles(doc, win.document, overlay);\n connection.stopStyles = environment.stop;\n await bounded(environment.ready);\n if (gone()) return undefined;\n return { destination: environment.destination, win };\n })();\n // The staging join is awaited below; settle early rejections quietly.\n staging.catch(() => {});\n // Ionic initializes its gestures after didPresent. Let that turn finish before adopting their content.\n // A stalled presentation must not park the opening queue forever.\n await bounded(ready);\n // Placement is captured after Ionic has laid out the overlay.\n await bounded(new Promise<void>((resolve) => doc.defaultView!.requestAnimationFrame(() => resolve())));\n const staged = await staging;\n if (gone() || !staged) {\n await release(connection);\n return;\n }\n const focused = activeElement(doc) as HTMLElement | null;\n const nativePresentation = popoverPresentation(overlay);\n const content = relayPopover(overlay, staged.destination);\n connection.stopContent = content.stop;\n connection.hideAnimation?.();\n // Ionic does not dismiss a presented overlay when its page is hidden or\n // destroyed; without this a popover could retain the page projections\n // forever and freeze every later control update.\n const exclusions = new MutationObserver(() => {\n if (!overlay.isConnected || !nativeVerticalBars() || excluded(overlay) || isShellDisabled(content.root))\n void release(connection).catch(console.error);\n });\n const attributes = { attributes: true, attributeFilter: ['class', 'data-shell', 'hidden', 'inert'] };\n for (let node: HTMLElement | null = overlay; node; node = node.parentElement) {\n exclusions.observe(node, attributes);\n exclusions.observe(node, { childList: true });\n }\n exclusions.observe(content.root, { ...attributes, subtree: true });\n connection.stopExclusions = () => exclusions.disconnect();\n connection.stopFocus = trapFocus(\n content.root,\n () =>\n !connection.dismissed &&\n (!('focusTrap' in overlay) || overlay.focusTrap !== false) &&\n !overlay.classList.contains('ion-disable-focus-trap'),\n );\n const close = () => {\n void release(connection).catch(console.error);\n };\n const escape = (event: KeyboardEvent) => {\n if (event.key === 'Escape' && overlay.backdropDismiss) void overlay.dismiss(undefined, 'backdrop');\n };\n staged.win.document.addEventListener('keydown', escape);\n overlay.addEventListener(lifecycle(overlay, 'DidDismiss'), close, { once: true });\n connection.stopEvents = () => {\n staged.win.document.removeEventListener('keydown', escape);\n overlay.removeEventListener(lifecycle(overlay, 'DidDismiss'), close);\n };\n // SwiftUI owns the only visible presentation animation.\n await bounded(\n plugin.presentOverlay({\n id: connection.id,\n presentation: { ...nativePresentation, animated: presentation.animated },\n }),\n );\n (focused?.isConnected && focused.ownerDocument === staged.win.document ? focused : content.root).focus({ preventScroll: true });\n } catch (error) {\n await release(connection);\n console.error('Native overlay projection failed; keeping Ionic content in the source WebView.', error);\n }\n };\n const present = (event: Event) => {\n const overlay = event.target as Overlay;\n if (current) {\n const connection = current;\n current = undefined;\n opening = opening.then(() => release(connection)).catch(console.error);\n if (!connection.dismissed) return;\n // A didDismiss handler may immediately open the next popover while SwiftUI is still closing this one.\n connection.stopAnimation?.();\n connection.stopAnimation = undefined;\n }\n const enabled =\n overlay.localName === 'ion-popover' &&\n options.controls?.popover === true &&\n nativeVerticalBars() &&\n verticalBarsPopoverAnchor(overlay) !== undefined;\n if (stopped || suspensions.size || !enabled || excluded(overlay)) {\n opening = opening.then(restoreWeb).catch(console.error);\n return;\n }\n const connection: Connection = { id: `ios-theme-overlay-${++sequence}`, overlay, dismissed: false, focus: activeElement(doc) };\n const presentation: Presentation = { kind: 'popover', animated: overlay.animated };\n current = connection;\n // Retain synchronously, before the scheduled projection update can retire the rail.\n const anchorId = verticalBarsPopoverAnchor(overlay);\n connection.releaseProjection = retainProjection(\n (id) =>\n id === anchorId &&\n current === connection &&\n !!connection.nativeClosing &&\n !stopped &&\n !suspensions.size &&\n nativeVerticalBars() &&\n !excluded(overlay),\n );\n const closed = () => {\n connection.dismissed = true;\n };\n overlay.addEventListener(lifecycle(overlay, 'DidDismiss'), closed, { once: true });\n let resolveReady!: () => void;\n const readyPromise = new Promise<void>((resolve) => {\n resolveReady = resolve;\n });\n const ready = () => {\n resolveReady();\n };\n const closeNative = () =>\n (connection.nativeClosing ??= bounded(plugin.dismissOverlay({ id: connection.id, animated: presentation.animated })));\n const animation = nativeDialogAnimation(overlay, closeNative);\n connection.stopAnimation = animation.stop;\n connection.hideAnimation = animation.hide;\n animation.hide();\n overlay.addEventListener(lifecycle(overlay, 'DidPresent'), ready, { once: true });\n connection.stopLifecycle = () => overlay.removeEventListener(lifecycle(overlay, 'DidDismiss'), closed);\n opening = opening\n .then(() => connect(connection, readyPromise, presentation))\n .catch(console.error)\n .finally(() => {\n overlay.removeEventListener(lifecycle(overlay, 'DidPresent'), ready);\n });\n };\n const menuOpen = (event: Event) => {\n if (!event.composedPath().some((node): node is HTMLElement => node instanceof HTMLElement && node.matches('ion-menu'))) return;\n // Menus render in the source document below the native overlay; release the relay.\n const connection = current;\n current = undefined;\n if (connection) opening = opening.then(() => release(connection)).catch(console.error);\n };\n const observe = (target: Document) => {\n for (const name of events) target.addEventListener(name, present);\n target.addEventListener('ionWillOpen', menuOpen);\n return () => {\n for (const name of events) target.removeEventListener(name, present);\n target.removeEventListener('ionWillOpen', menuOpen);\n };\n };\n const invalidate = () => {\n const connection = current;\n current = undefined;\n if (connection) opening = opening.then(() => release(connection)).catch(console.error);\n };\n // Retention must not freeze an obsolete layout or a departed page.\n const win = doc.defaultView!;\n win.addEventListener('resize', invalidate);\n doc.addEventListener('ionViewWillLeave', invalidate);\n const visibility = () => {\n if (doc.hidden) invalidate();\n };\n doc.addEventListener('visibilitychange', visibility);\n const listener = await bounded(\n plugin.addListener('overlay', (event) => {\n if (event.id !== current?.id) return;\n if (event.action === 'dismiss' && current.overlay.backdropDismiss)\n void current.overlay.dismiss(undefined, 'backdrop').catch(console.error);\n }),\n );\n const stopEvents = observe(doc);\n return {\n async suspend() {\n const lease = Symbol();\n suspensions.add(lease);\n await opening;\n await restoreWeb();\n return () => {\n suspensions.delete(lease);\n };\n },\n async destroy() {\n stopped = true;\n stopEvents();\n win.removeEventListener('resize', invalidate);\n doc.removeEventListener('ionViewWillLeave', invalidate);\n doc.removeEventListener('visibilitychange', visibility);\n try {\n await opening;\n await restoreWeb();\n } finally {\n try {\n await bounded(plugin.stopOverlays());\n } finally {\n await listener.remove();\n }\n }\n },\n };\n};\n"],"mappings":"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"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
//#region src/native/overlays/dialog-animation.d.ts
|
|
2
|
+
export type Dialog = HTMLIonPopoverElement;
|
|
3
|
+
export declare const nativeDialogAnimation: (overlay: Dialog, close: () => Promise<void>) => {
|
|
4
|
+
hide(): void;
|
|
5
|
+
stop(): void;
|
|
6
|
+
};
|
|
7
|
+
//#endregion
|
|
8
|
+
//# sourceMappingURL=dialog-animation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dialog-animation.d.ts","names":[],"sources":["../../../src/native/overlays/dialog-animation.ts"],"mappings":";YAGY,SAAS;qBAGR,wBAAyB,SAAS,QAAQ,aAAa"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { dismissAnimation } from "./dismiss-animation.js";
|
|
2
|
+
import { createAnimation } from "@ionic/core/components/index.js";
|
|
3
|
+
//#region src/native/overlays/dialog-animation.ts
|
|
4
|
+
/** Hide the emptied source surface once the relay hosts the same live content. */
|
|
5
|
+
const nativeDialogAnimation = (overlay, close) => {
|
|
6
|
+
const enterAnimation = overlay.enterAnimation;
|
|
7
|
+
overlay.enterAnimation = () => createAnimation();
|
|
8
|
+
const leaveAnimation = overlay.leaveAnimation;
|
|
9
|
+
const opacity = overlay.style.opacity;
|
|
10
|
+
const event = "ionPopoverWillDismiss";
|
|
11
|
+
const dismiss = () => {
|
|
12
|
+
const closing = close();
|
|
13
|
+
overlay.leaveAnimation = () => dismissAnimation(closing);
|
|
14
|
+
};
|
|
15
|
+
overlay.addEventListener(event, dismiss);
|
|
16
|
+
return {
|
|
17
|
+
hide() {
|
|
18
|
+
overlay.style.opacity = "0";
|
|
19
|
+
},
|
|
20
|
+
stop() {
|
|
21
|
+
overlay.removeEventListener(event, dismiss);
|
|
22
|
+
overlay.enterAnimation = enterAnimation;
|
|
23
|
+
overlay.leaveAnimation = leaveAnimation;
|
|
24
|
+
overlay.style.opacity = opacity;
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
};
|
|
28
|
+
//#endregion
|
|
29
|
+
export { nativeDialogAnimation };
|
|
30
|
+
|
|
31
|
+
//# sourceMappingURL=dialog-animation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dialog-animation.js","names":[],"sources":["../../../src/native/overlays/dialog-animation.ts"],"sourcesContent":["import { createAnimation } from '@ionic/core/components/index.js';\nimport { dismissAnimation } from './dismiss-animation';\n\nexport type Dialog = HTMLIonPopoverElement;\n\n/** Hide the emptied source surface once the relay hosts the same live content. */\nexport const nativeDialogAnimation = (overlay: Dialog, close: () => Promise<void>) => {\n const enterAnimation = overlay.enterAnimation;\n overlay.enterAnimation = () => createAnimation();\n const leaveAnimation = overlay.leaveAnimation;\n const opacity = overlay.style.opacity;\n const event = 'ionPopoverWillDismiss';\n const dismiss = () => {\n const closing = close();\n overlay.leaveAnimation = () => dismissAnimation(closing);\n };\n overlay.addEventListener(event, dismiss);\n return {\n // Hide the source surface while the native presentation prepares.\n hide() {\n overlay.style.opacity = '0';\n },\n stop() {\n overlay.removeEventListener(event, dismiss);\n overlay.enterAnimation = enterAnimation;\n overlay.leaveAnimation = leaveAnimation;\n overlay.style.opacity = opacity;\n },\n };\n};\n"],"mappings":";;;;AAMA,MAAa,yBAAyB,SAAiB,UAA+B;CACpF,MAAM,iBAAiB,QAAQ;CAC/B,QAAQ,uBAAuB,gBAAgB;CAC/C,MAAM,iBAAiB,QAAQ;CAC/B,MAAM,UAAU,QAAQ,MAAM;CAC9B,MAAM,QAAQ;CACd,MAAM,gBAAgB;EACpB,MAAM,UAAU,MAAM;EACtB,QAAQ,uBAAuB,iBAAiB,OAAO;CACzD;CACA,QAAQ,iBAAiB,OAAO,OAAO;CACvC,OAAO;EAEL,OAAO;GACL,QAAQ,MAAM,UAAU;EAC1B;EACA,OAAO;GACL,QAAQ,oBAAoB,OAAO,OAAO;GAC1C,QAAQ,iBAAiB;GACzB,QAAQ,iBAAiB;GACzB,QAAQ,MAAM,UAAU;EAC1B;CACF;AACF"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dismiss-animation.d.ts","names":[],"sources":["../../../src/native/overlays/dismiss-animation.ts"],"mappings":";qBAGa,mBAAoB,SAAS,wCAAa"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { createAnimation } from "@ionic/core/components/index.js";
|
|
2
|
+
//#region src/native/overlays/dismiss-animation.ts
|
|
3
|
+
/** Let Ionic finish dismissal after the native presentation has closed. */
|
|
4
|
+
const dismissAnimation = (closing) => {
|
|
5
|
+
const animation = createAnimation();
|
|
6
|
+
const play = animation.play;
|
|
7
|
+
animation.play = async (options) => {
|
|
8
|
+
await closing.catch((error) => {
|
|
9
|
+
console.error("Native popover dismissal failed; completing the Ionic lifecycle.", error);
|
|
10
|
+
});
|
|
11
|
+
await play(options);
|
|
12
|
+
};
|
|
13
|
+
return animation;
|
|
14
|
+
};
|
|
15
|
+
//#endregion
|
|
16
|
+
export { dismissAnimation };
|
|
17
|
+
|
|
18
|
+
//# sourceMappingURL=dismiss-animation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dismiss-animation.js","names":[],"sources":["../../../src/native/overlays/dismiss-animation.ts"],"sourcesContent":["import { createAnimation } from '@ionic/core/components/index.js';\n\n/** Let Ionic finish dismissal after the native presentation has closed. */\nexport const dismissAnimation = (closing: Promise<void>) => {\n const animation = createAnimation();\n const play = animation.play;\n animation.play = async (options) => {\n // A native failure must not suppress Ionic's didDismiss event/data.\n await closing.catch((error) => {\n console.error('Native popover dismissal failed; completing the Ionic lifecycle.', error);\n });\n await play(options);\n };\n return animation;\n};\n"],"mappings":";;;AAGA,MAAa,oBAAoB,YAA2B;CAC1D,MAAM,YAAY,gBAAgB;CAClC,MAAM,OAAO,UAAU;CACvB,UAAU,OAAO,OAAO,YAAY;EAElC,MAAM,QAAQ,OAAO,UAAU;GAC7B,QAAQ,MAAM,oEAAoE,KAAK;EACzF,CAAC;EACD,MAAM,KAAK,OAAO;CACpB;CACA,OAAO;AACT"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"focus.d.ts","names":[],"sources":["../../../src/native/overlays/focus.ts"],"mappings":";qBAAa,gBAAiB,KAAK,aAAW;qBAiCjC,YAAa,MAAM,aAAa"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
//#region src/native/overlays/focus.ts
|
|
2
|
+
const activeElement = (doc) => {
|
|
3
|
+
let element = doc.activeElement;
|
|
4
|
+
while (element?.shadowRoot?.activeElement) element = element.shadowRoot.activeElement;
|
|
5
|
+
return element;
|
|
6
|
+
};
|
|
7
|
+
const focusableElements = (root) => {
|
|
8
|
+
const elements = [];
|
|
9
|
+
const win = root.ownerDocument.defaultView;
|
|
10
|
+
const collect = (scope) => {
|
|
11
|
+
for (const node of Array.from(scope.querySelectorAll("*"))) {
|
|
12
|
+
if (node.inert || node.closest("[inert]") || node.disabled) continue;
|
|
13
|
+
const before = elements.length;
|
|
14
|
+
if (node.shadowRoot) collect(node.shadowRoot);
|
|
15
|
+
const css = win.getComputedStyle(node);
|
|
16
|
+
if (elements.length === before && node.tabIndex >= 0 && !node.hidden && css.display !== "none" && css.visibility !== "hidden" && node.getClientRects().length) elements.push(node);
|
|
17
|
+
}
|
|
18
|
+
};
|
|
19
|
+
collect(root);
|
|
20
|
+
return elements.map((element, index) => ({
|
|
21
|
+
element,
|
|
22
|
+
index
|
|
23
|
+
})).sort((a, b) => (a.element.tabIndex || Infinity) - (b.element.tabIndex || Infinity) || a.index - b.index).map(({ element }) => element);
|
|
24
|
+
};
|
|
25
|
+
const trapFocus = (root, enabled) => {
|
|
26
|
+
const doc = root.ownerDocument;
|
|
27
|
+
const previous = root.getAttribute("tabindex");
|
|
28
|
+
root.tabIndex = -1;
|
|
29
|
+
let last;
|
|
30
|
+
const onFocus = (event) => {
|
|
31
|
+
if (!enabled()) return;
|
|
32
|
+
if (event.composedPath().includes(root)) {
|
|
33
|
+
last = activeElement(doc);
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
const elements = focusableElements(root);
|
|
37
|
+
(elements.find((element) => element === last) ?? elements[0] ?? root).focus({ preventScroll: true });
|
|
38
|
+
};
|
|
39
|
+
const onKey = (event) => {
|
|
40
|
+
if (event.key !== "Tab" || event.altKey || event.ctrlKey || event.metaKey || !enabled()) return;
|
|
41
|
+
const elements = focusableElements(root);
|
|
42
|
+
const index = elements.findIndex((element) => element === activeElement(doc));
|
|
43
|
+
const next = event.shiftKey ? index <= 0 ? elements.length - 1 : index - 1 : (index + 1) % elements.length;
|
|
44
|
+
event.preventDefault();
|
|
45
|
+
(elements[next] ?? root).focus();
|
|
46
|
+
};
|
|
47
|
+
doc.addEventListener("focusin", onFocus, true);
|
|
48
|
+
doc.addEventListener("keydown", onKey, true);
|
|
49
|
+
return () => {
|
|
50
|
+
doc.removeEventListener("focusin", onFocus, true);
|
|
51
|
+
doc.removeEventListener("keydown", onKey, true);
|
|
52
|
+
if (previous === null) root.removeAttribute("tabindex");
|
|
53
|
+
else root.setAttribute("tabindex", previous);
|
|
54
|
+
};
|
|
55
|
+
};
|
|
56
|
+
//#endregion
|
|
57
|
+
export { activeElement, trapFocus };
|
|
58
|
+
|
|
59
|
+
//# sourceMappingURL=focus.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"focus.js","names":[],"sources":["../../../src/native/overlays/focus.ts"],"sourcesContent":["export const activeElement = (doc: Document): Element | null => {\n let element = doc.activeElement;\n while (element?.shadowRoot?.activeElement) element = element.shadowRoot.activeElement;\n return element;\n};\n\nconst focusableElements = (root: HTMLElement): HTMLElement[] => {\n const elements: HTMLElement[] = [];\n const win = root.ownerDocument.defaultView!;\n const collect = (scope: HTMLElement | ShadowRoot) => {\n for (const node of Array.from(scope.querySelectorAll<HTMLElement>('*'))) {\n if (node.inert || node.closest('[inert]') || (node as HTMLButtonElement).disabled) continue;\n const before = elements.length;\n if (node.shadowRoot) collect(node.shadowRoot);\n const css = win.getComputedStyle(node);\n if (\n elements.length === before &&\n node.tabIndex >= 0 &&\n !node.hidden &&\n css.display !== 'none' &&\n css.visibility !== 'hidden' &&\n node.getClientRects().length\n )\n elements.push(node);\n }\n };\n collect(root);\n return elements\n .map((element, index) => ({ element, index }))\n .sort((a, b) => (a.element.tabIndex || Infinity) - (b.element.tabIndex || Infinity) || a.index - b.index)\n .map(({ element }) => element);\n};\n\nexport const trapFocus = (root: HTMLElement, enabled: () => boolean): (() => void) => {\n const doc = root.ownerDocument;\n const previous = root.getAttribute('tabindex');\n root.tabIndex = -1;\n let last: Element | null;\n const onFocus = (event: FocusEvent) => {\n if (!enabled()) return;\n if (event.composedPath().includes(root)) {\n last = activeElement(doc);\n return;\n }\n const elements = focusableElements(root);\n (elements.find((element) => element === last) ?? elements[0] ?? root).focus({ preventScroll: true });\n };\n const onKey = (event: KeyboardEvent) => {\n if (event.key !== 'Tab' || event.altKey || event.ctrlKey || event.metaKey || !enabled()) return;\n const elements = focusableElements(root);\n const index = elements.findIndex((element) => element === activeElement(doc));\n const next = event.shiftKey ? (index <= 0 ? elements.length - 1 : index - 1) : (index + 1) % elements.length;\n event.preventDefault();\n (elements[next] ?? root).focus();\n };\n doc.addEventListener('focusin', onFocus, true);\n doc.addEventListener('keydown', onKey, true);\n return () => {\n doc.removeEventListener('focusin', onFocus, true);\n doc.removeEventListener('keydown', onKey, true);\n if (previous === null) root.removeAttribute('tabindex');\n else root.setAttribute('tabindex', previous);\n };\n};\n"],"mappings":";AAAA,MAAa,iBAAiB,QAAkC;CAC9D,IAAI,UAAU,IAAI;CAClB,OAAO,SAAS,YAAY,eAAe,UAAU,QAAQ,WAAW;CACxE,OAAO;AACT;AAEA,MAAM,qBAAqB,SAAqC;CAC9D,MAAM,WAA0B,CAAC;CACjC,MAAM,MAAM,KAAK,cAAc;CAC/B,MAAM,WAAW,UAAoC;EACnD,KAAK,MAAM,QAAQ,MAAM,KAAK,MAAM,iBAA8B,GAAG,CAAC,GAAG;GACvE,IAAI,KAAK,SAAS,KAAK,QAAQ,SAAS,KAAM,KAA2B,UAAU;GACnF,MAAM,SAAS,SAAS;GACxB,IAAI,KAAK,YAAY,QAAQ,KAAK,UAAU;GAC5C,MAAM,MAAM,IAAI,iBAAiB,IAAI;GACrC,IACE,SAAS,WAAW,UACpB,KAAK,YAAY,KACjB,CAAC,KAAK,UACN,IAAI,YAAY,UAChB,IAAI,eAAe,YACnB,KAAK,eAAe,CAAC,CAAC,QAEtB,SAAS,KAAK,IAAI;EACtB;CACF;CACA,QAAQ,IAAI;CACZ,OAAO,SACJ,KAAK,SAAS,WAAW;EAAE;EAAS;CAAM,EAAE,CAAC,CAC7C,MAAM,GAAG,OAAO,EAAE,QAAQ,YAAY,aAAa,EAAE,QAAQ,YAAY,aAAa,EAAE,QAAQ,EAAE,KAAK,CAAC,CACxG,KAAK,EAAE,cAAc,OAAO;AACjC;AAEA,MAAa,aAAa,MAAmB,YAAyC;CACpF,MAAM,MAAM,KAAK;CACjB,MAAM,WAAW,KAAK,aAAa,UAAU;CAC7C,KAAK,WAAW;CAChB,IAAI;CACJ,MAAM,WAAW,UAAsB;EACrC,IAAI,CAAC,QAAQ,GAAG;EAChB,IAAI,MAAM,aAAa,CAAC,CAAC,SAAS,IAAI,GAAG;GACvC,OAAO,cAAc,GAAG;GACxB;EACF;EACA,MAAM,WAAW,kBAAkB,IAAI;EACvC,CAAC,SAAS,MAAM,YAAY,YAAY,IAAI,KAAK,SAAS,MAAM,KAAA,CAAM,MAAM,EAAE,eAAe,KAAK,CAAC;CACrG;CACA,MAAM,SAAS,UAAyB;EACtC,IAAI,MAAM,QAAQ,SAAS,MAAM,UAAU,MAAM,WAAW,MAAM,WAAW,CAAC,QAAQ,GAAG;EACzF,MAAM,WAAW,kBAAkB,IAAI;EACvC,MAAM,QAAQ,SAAS,WAAW,YAAY,YAAY,cAAc,GAAG,CAAC;EAC5E,MAAM,OAAO,MAAM,WAAY,SAAS,IAAI,SAAS,SAAS,IAAI,QAAQ,KAAM,QAAQ,KAAK,SAAS;EACtG,MAAM,eAAe;EACrB,CAAC,SAAS,SAAS,KAAA,CAAM,MAAM;CACjC;CACA,IAAI,iBAAiB,WAAW,SAAS,IAAI;CAC7C,IAAI,iBAAiB,WAAW,OAAO,IAAI;CAC3C,aAAa;EACX,IAAI,oBAAoB,WAAW,SAAS,IAAI;EAChD,IAAI,oBAAoB,WAAW,OAAO,IAAI;EAC9C,IAAI,aAAa,MAAM,KAAK,gBAAgB,UAAU;OACjD,KAAK,aAAa,YAAY,QAAQ;CAC7C;AACF"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { ShellPopoverPresentation } from "../definitions.js";
|
|
2
|
+
//#region src/native/overlays/popover.d.ts
|
|
3
|
+
export declare const popoverTrigger: (overlay: HTMLIonPopoverElement) => Element | undefined;
|
|
4
|
+
export declare const verticalBarsPopoverAnchor: (overlay: HTMLIonPopoverElement) => string | undefined;
|
|
5
|
+
export declare const popoverPresentation: (overlay: HTMLIonPopoverElement) => ShellPopoverPresentation;
|
|
6
|
+
export declare const relayPopover: (overlay: HTMLIonPopoverElement, destination: HTMLElement) => {
|
|
7
|
+
root: HTMLIonPopoverElement;
|
|
8
|
+
stop(): void;
|
|
9
|
+
};
|
|
10
|
+
//#endregion
|
|
11
|
+
//# sourceMappingURL=popover.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"popover.d.ts","names":[],"sources":["../../../src/native/overlays/popover.ts"],"mappings":";;qBAKa,iBAAkB,SAAS,0BAAwB;qBAOnD,4BAA6B,SAAS;qBAWtC,sBAAuB,SAAS,0BAAwB;qBAiBxD,eAAgB,SAAS,uBAAuB,aAAa"}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { projectionIds } from "../shared/projection-id.js";
|
|
2
|
+
import { isDark } from "../shared/dom.js";
|
|
3
|
+
import { moveContent } from "./content.js";
|
|
4
|
+
//#region src/native/overlays/popover.ts
|
|
5
|
+
const popoverTrigger = (overlay) => {
|
|
6
|
+
const event = overlay.event;
|
|
7
|
+
const target = overlay.trigger ? overlay.ownerDocument.getElementById(overlay.trigger) : event?.target;
|
|
8
|
+
return target?.closest?.("ion-button,ion-fab-button,ion-item") ?? target;
|
|
9
|
+
};
|
|
10
|
+
/** Only accepted native rail buttons can retain the page projection. */
|
|
11
|
+
const verticalBarsPopoverAnchor = (overlay) => {
|
|
12
|
+
const trigger = popoverTrigger(overlay);
|
|
13
|
+
if (!trigger?.matches("ion-button[data-native-ui-shell-vertical-bars-member]") || !trigger.closest("[data-native-ui-shell]") || trigger.disabled) return void 0;
|
|
14
|
+
return projectionIds.get(trigger);
|
|
15
|
+
};
|
|
16
|
+
const popoverPresentation = (overlay) => {
|
|
17
|
+
const content = overlay.shadowRoot.querySelector(".popover-content");
|
|
18
|
+
const rect = content.getBoundingClientRect();
|
|
19
|
+
const anchorId = verticalBarsPopoverAnchor(overlay);
|
|
20
|
+
if (!anchorId) throw new Error("Native Vertical Bars anchor unavailable");
|
|
21
|
+
return {
|
|
22
|
+
kind: "popover",
|
|
23
|
+
animated: overlay.animated,
|
|
24
|
+
dark: isDark(overlay.ownerDocument.defaultView.getComputedStyle(overlay)),
|
|
25
|
+
anchorId,
|
|
26
|
+
width: rect.width,
|
|
27
|
+
height: rect.height,
|
|
28
|
+
backgroundColor: overlay.ownerDocument.defaultView.getComputedStyle(content).backgroundColor,
|
|
29
|
+
backdropDismiss: overlay.backdropDismiss
|
|
30
|
+
};
|
|
31
|
+
};
|
|
32
|
+
const relayPopover = (overlay, destination) => {
|
|
33
|
+
const doc = destination.ownerDocument;
|
|
34
|
+
const source = overlay.shadowRoot.querySelector(".popover-content");
|
|
35
|
+
const root = doc.createElement("ion-popover");
|
|
36
|
+
for (const attribute of Array.from(overlay.attributes)) root.setAttribute(attribute.name, attribute.value);
|
|
37
|
+
Object.assign(root.style, {
|
|
38
|
+
position: "absolute",
|
|
39
|
+
inset: "0",
|
|
40
|
+
display: "block",
|
|
41
|
+
visibility: "visible",
|
|
42
|
+
opacity: "1",
|
|
43
|
+
pointerEvents: "auto"
|
|
44
|
+
});
|
|
45
|
+
root.setAttribute("role", "dialog");
|
|
46
|
+
root.setAttribute("aria-modal", "true");
|
|
47
|
+
const shadow = root.attachShadow({ mode: "open" });
|
|
48
|
+
const shell = doc.createElement("div");
|
|
49
|
+
const css = overlay.ownerDocument.defaultView.getComputedStyle(source);
|
|
50
|
+
Object.assign(shell.style, {
|
|
51
|
+
height: "100%",
|
|
52
|
+
overflow: css.overflow,
|
|
53
|
+
background: css.backgroundColor
|
|
54
|
+
});
|
|
55
|
+
shell.append(doc.createElement("slot"));
|
|
56
|
+
shadow.append(shell);
|
|
57
|
+
destination.append(root);
|
|
58
|
+
root.dismiss = (...args) => overlay.dismiss(...args);
|
|
59
|
+
const restore = moveContent(Array.from(source.querySelector("slot").assignedElements()), root);
|
|
60
|
+
const select = (event) => {
|
|
61
|
+
if (!event.composedPath().includes(shell)) {
|
|
62
|
+
if (overlay.backdropDismiss) overlay.dismiss(void 0, "backdrop");
|
|
63
|
+
} else if (overlay.dismissOnSelect) overlay.dismiss();
|
|
64
|
+
};
|
|
65
|
+
root.addEventListener("click", select);
|
|
66
|
+
return {
|
|
67
|
+
root,
|
|
68
|
+
stop() {
|
|
69
|
+
root.removeEventListener("click", select);
|
|
70
|
+
restore();
|
|
71
|
+
root.remove();
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
};
|
|
75
|
+
//#endregion
|
|
76
|
+
export { popoverPresentation, popoverTrigger, relayPopover, verticalBarsPopoverAnchor };
|
|
77
|
+
|
|
78
|
+
//# sourceMappingURL=popover.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"popover.js","names":[],"sources":["../../../src/native/overlays/popover.ts"],"sourcesContent":["import type { ShellPopoverPresentation } from '../definitions';\nimport { projectionIds } from '../shared/projection-id';\nimport { isDark } from '../shared/dom';\nimport { moveContent } from './content';\n\nexport const popoverTrigger = (overlay: HTMLIonPopoverElement): Element | undefined => {\n const event = overlay.event as MouseEvent | undefined;\n const target = (overlay.trigger ? overlay.ownerDocument.getElementById(overlay.trigger) : event?.target) as Element | undefined;\n return target?.closest?.('ion-button,ion-fab-button,ion-item') ?? target;\n};\n\n/** Only accepted native rail buttons can retain the page projection. */\nexport const verticalBarsPopoverAnchor = (overlay: HTMLIonPopoverElement): string | undefined => {\n const trigger = popoverTrigger(overlay);\n if (\n !trigger?.matches('ion-button[data-native-ui-shell-vertical-bars-member]') ||\n !trigger.closest('[data-native-ui-shell]') ||\n (trigger as HTMLIonButtonElement).disabled\n )\n return undefined;\n return projectionIds.get(trigger);\n};\n\nexport const popoverPresentation = (overlay: HTMLIonPopoverElement): ShellPopoverPresentation => {\n const content = overlay.shadowRoot!.querySelector<HTMLElement>('.popover-content')!;\n const rect = content.getBoundingClientRect();\n const anchorId = verticalBarsPopoverAnchor(overlay);\n if (!anchorId) throw new Error('Native Vertical Bars anchor unavailable');\n return {\n kind: 'popover',\n animated: overlay.animated,\n dark: isDark(overlay.ownerDocument.defaultView!.getComputedStyle(overlay)),\n anchorId,\n width: rect.width,\n height: rect.height,\n backgroundColor: overlay.ownerDocument.defaultView!.getComputedStyle(content).backgroundColor,\n backdropDismiss: overlay.backdropDismiss,\n };\n};\n\nexport const relayPopover = (overlay: HTMLIonPopoverElement, destination: HTMLElement) => {\n const doc = destination.ownerDocument;\n const source = overlay.shadowRoot!.querySelector<HTMLElement>('.popover-content')!;\n const root = doc.createElement('ion-popover');\n for (const attribute of Array.from(overlay.attributes)) root.setAttribute(attribute.name, attribute.value);\n Object.assign(root.style, {\n position: 'absolute',\n inset: '0',\n display: 'block',\n visibility: 'visible',\n opacity: '1',\n pointerEvents: 'auto',\n });\n root.setAttribute('role', 'dialog');\n root.setAttribute('aria-modal', 'true');\n const shadow = root.attachShadow({ mode: 'open' });\n const shell = doc.createElement('div');\n const css = overlay.ownerDocument.defaultView!.getComputedStyle(source);\n Object.assign(shell.style, { height: '100%', overflow: css.overflow, background: css.backgroundColor });\n shell.append(doc.createElement('slot'));\n shadow.append(shell);\n destination.append(root);\n // Components such as ion-select-popover dismiss their closest ion-popover.\n root.dismiss = (...args) => overlay.dismiss(...args);\n const restore = moveContent(Array.from(source.querySelector('slot')!.assignedElements()) as HTMLElement[], root);\n const select = (event: MouseEvent) => {\n if (!event.composedPath().includes(shell)) {\n if (overlay.backdropDismiss) void overlay.dismiss(undefined, 'backdrop');\n } else if (overlay.dismissOnSelect) void overlay.dismiss();\n };\n root.addEventListener('click', select);\n return {\n root,\n stop() {\n root.removeEventListener('click', select);\n restore();\n root.remove();\n },\n };\n};\n"],"mappings":";;;;AAKA,MAAa,kBAAkB,YAAwD;CACrF,MAAM,QAAQ,QAAQ;CACtB,MAAM,SAAU,QAAQ,UAAU,QAAQ,cAAc,eAAe,QAAQ,OAAO,IAAI,OAAO;CACjG,OAAO,QAAQ,UAAU,oCAAoC,KAAK;AACpE;;AAGA,MAAa,6BAA6B,YAAuD;CAC/F,MAAM,UAAU,eAAe,OAAO;CACtC,IACE,CAAC,SAAS,QAAQ,uDAAuD,KACzE,CAAC,QAAQ,QAAQ,wBAAwB,KACxC,QAAiC,UAElC,OAAO,KAAA;CACT,OAAO,cAAc,IAAI,OAAO;AAClC;AAEA,MAAa,uBAAuB,YAA6D;CAC/F,MAAM,UAAU,QAAQ,WAAY,cAA2B,kBAAkB;CACjF,MAAM,OAAO,QAAQ,sBAAsB;CAC3C,MAAM,WAAW,0BAA0B,OAAO;CAClD,IAAI,CAAC,UAAU,MAAM,IAAI,MAAM,yCAAyC;CACxE,OAAO;EACL,MAAM;EACN,UAAU,QAAQ;EAClB,MAAM,OAAO,QAAQ,cAAc,YAAa,iBAAiB,OAAO,CAAC;EACzE;EACA,OAAO,KAAK;EACZ,QAAQ,KAAK;EACb,iBAAiB,QAAQ,cAAc,YAAa,iBAAiB,OAAO,CAAC,CAAC;EAC9E,iBAAiB,QAAQ;CAC3B;AACF;AAEA,MAAa,gBAAgB,SAAgC,gBAA6B;CACxF,MAAM,MAAM,YAAY;CACxB,MAAM,SAAS,QAAQ,WAAY,cAA2B,kBAAkB;CAChF,MAAM,OAAO,IAAI,cAAc,aAAa;CAC5C,KAAK,MAAM,aAAa,MAAM,KAAK,QAAQ,UAAU,GAAG,KAAK,aAAa,UAAU,MAAM,UAAU,KAAK;CACzG,OAAO,OAAO,KAAK,OAAO;EACxB,UAAU;EACV,OAAO;EACP,SAAS;EACT,YAAY;EACZ,SAAS;EACT,eAAe;CACjB,CAAC;CACD,KAAK,aAAa,QAAQ,QAAQ;CAClC,KAAK,aAAa,cAAc,MAAM;CACtC,MAAM,SAAS,KAAK,aAAa,EAAE,MAAM,OAAO,CAAC;CACjD,MAAM,QAAQ,IAAI,cAAc,KAAK;CACrC,MAAM,MAAM,QAAQ,cAAc,YAAa,iBAAiB,MAAM;CACtE,OAAO,OAAO,MAAM,OAAO;EAAE,QAAQ;EAAQ,UAAU,IAAI;EAAU,YAAY,IAAI;CAAgB,CAAC;CACtG,MAAM,OAAO,IAAI,cAAc,MAAM,CAAC;CACtC,OAAO,OAAO,KAAK;CACnB,YAAY,OAAO,IAAI;CAEvB,KAAK,WAAW,GAAG,SAAS,QAAQ,QAAQ,GAAG,IAAI;CACnD,MAAM,UAAU,YAAY,MAAM,KAAK,OAAO,cAAc,MAAM,CAAC,CAAE,iBAAiB,CAAC,GAAoB,IAAI;CAC/G,MAAM,UAAU,UAAsB;EACpC,IAAI,CAAC,MAAM,aAAa,CAAC,CAAC,SAAS,KAAK,GAClC;OAAA,QAAQ,iBAAiB,QAAa,QAAQ,KAAA,GAAW,UAAU;EAAA,OAClE,IAAI,QAAQ,iBAAiB,QAAa,QAAQ;CAC3D;CACA,KAAK,iBAAiB,SAAS,MAAM;CACrC,OAAO;EACL;EACA,OAAO;GACL,KAAK,oBAAoB,SAAS,MAAM;GACxC,QAAQ;GACR,KAAK,OAAO;EACd;CACF;AACF"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
//#region src/native/overlays/styles.d.ts
|
|
2
|
+
export declare const sheetText: (sheets: readonly CSSStyleSheet[]) => string;
|
|
3
|
+
export declare const relayStyles: (source: Document, destination: Document, overlay: HTMLElement) => {
|
|
4
|
+
destination: HTMLElement;
|
|
5
|
+
ready: Promise<void>;
|
|
6
|
+
stop: () => void;
|
|
7
|
+
};
|
|
8
|
+
//#endregion
|
|
9
|
+
//# sourceMappingURL=styles.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"styles.d.ts","names":[],"sources":["../../../src/native/overlays/styles.ts"],"mappings":";qBAAa,YAAa,iBAAiB;qBAG9B,cACX,QAAQ,UACR,aAAa,UACb,SAAS;EACN,aAAa;EAAa,OAAO;EAAe"}
|