@rdlabo/capacitor-native-ui 1.2.1 → 2.0.0-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.
Files changed (67) hide show
  1. package/README.md +38 -25
  2. package/dist/core.d.ts +2 -2
  3. package/dist/native/activation.d.ts.map +1 -1
  4. package/dist/native/activation.js +19 -19
  5. package/dist/native/activation.js.map +1 -1
  6. package/dist/native/definitions.d.ts +22 -40
  7. package/dist/native/definitions.d.ts.map +1 -1
  8. package/dist/native/html.d.ts.map +1 -1
  9. package/dist/native/html.js +10 -1
  10. package/dist/native/html.js.map +1 -1
  11. package/dist/native/index.d.ts +2 -2
  12. package/dist/native/index.d.ts.map +1 -1
  13. package/dist/native/index.js +2 -11
  14. package/dist/native/index.js.map +1 -1
  15. package/dist/native/overlays/controller.js +1 -1
  16. package/dist/native/overlays/controller.js.map +1 -1
  17. package/dist/native/prehide.js +1 -1
  18. package/dist/native/prehide.js.map +1 -1
  19. package/dist/native/runtime.d.ts +1 -1
  20. package/dist/native/runtime.d.ts.map +1 -1
  21. package/dist/native/runtime.js +8 -20
  22. package/dist/native/runtime.js.map +1 -1
  23. package/dist/native/shared/candidate.d.ts +2 -2
  24. package/dist/native/shared/candidate.d.ts.map +1 -1
  25. package/dist/native/shared/candidate.js +2 -2
  26. package/dist/native/shared/candidate.js.map +1 -1
  27. package/dist/native/shared/control-options.d.ts +6 -0
  28. package/dist/native/shared/control-options.d.ts.map +1 -0
  29. package/dist/native/shared/control-options.js +17 -0
  30. package/dist/native/shared/control-options.js.map +1 -0
  31. package/dist/native/shared/toolbar-options.d.ts +6 -2
  32. package/dist/native/shared/toolbar-options.d.ts.map +1 -1
  33. package/dist/native/shared/toolbar-options.js +7 -15
  34. package/dist/native/shared/toolbar-options.js.map +1 -1
  35. package/dist/native/startup.d.ts.map +1 -1
  36. package/dist/native/startup.js +0 -16
  37. package/dist/native/startup.js.map +1 -1
  38. package/dist/native/vertical-bars-web.d.ts.map +1 -1
  39. package/dist/native/vertical-bars-web.js +3 -2
  40. package/dist/native/vertical-bars-web.js.map +1 -1
  41. package/dist/vertical-bars.d.ts +2 -2
  42. package/docs/appearance.md +45 -0
  43. package/docs/configuration.md +106 -74
  44. package/docs/framework-independent.md +35 -10
  45. package/docs/ionic-vertical-bars.md +173 -0
  46. package/docs/migration.md +65 -87
  47. package/docs/native-ui-extensions.md +60 -31
  48. package/docs/native-ui.md +186 -67
  49. package/docs/setup.md +43 -24
  50. package/docs/vertical-bars.md +124 -72
  51. package/ios/Sources/NativeUIPlugin/NativeUIPlugin.swift +1 -1
  52. package/package.json +1 -1
  53. package/src/core.ts +1 -2
  54. package/src/native/activation.ts +13 -22
  55. package/src/native/definitions.ts +40 -90
  56. package/src/native/html.ts +11 -5
  57. package/src/native/index.ts +2 -11
  58. package/src/native/overlays/controller.ts +1 -1
  59. package/src/native/prehide.ts +1 -1
  60. package/src/native/runtime.ts +7 -16
  61. package/src/native/shared/candidate.ts +4 -4
  62. package/src/native/shared/control-options.ts +20 -0
  63. package/src/native/shared/toolbar-options.ts +8 -12
  64. package/src/native/startup.ts +0 -16
  65. package/src/native/vertical-bars-web.ts +4 -2
  66. package/src/vertical-bars.ts +2 -1
  67. package/docs/native-mini-player.md +0 -47
@@ -168,7 +168,7 @@ const createOverlayController = async (doc, plugin, options, retainProjection, n
168
168
  connection.stopAnimation?.();
169
169
  connection.stopAnimation = void 0;
170
170
  }
171
- const enabled = overlay.localName === "ion-popover" && overlay.mode !== "md" && !overlay.classList.contains("md") && options.controls?.popover === true && nativeVerticalBars() && verticalBarsPopoverAnchor(overlay) !== void 0;
171
+ const enabled = overlay.localName === "ion-popover" && overlay.mode !== "md" && !overlay.classList.contains("md") && options.ionic?.verticalBars?.popover === true && nativeVerticalBars() && verticalBarsPopoverAnchor(overlay) !== void 0;
172
172
  if (stopped || suspensions.size || !enabled || excluded(overlay)) {
173
173
  opening = opening.then(restoreWeb).catch(console.error);
174
174
  return;
@@ -1 +1 @@
1
- {"version":3,"file":"controller.js","names":[],"sources":["../../../src/native/overlays/controller.ts"],"sourcesContent":["import type { NativeUIOptions, NativeUIPlugin } from '../definitions';\nimport { bounded, boundedListener } from '../shared/bridge';\nimport { isExcluded, isPermanentlyExcluded, isNativeUIDisabled } from '../shared/dom';\nimport { activeElement, trapFocus } from './focus';\nimport { relayPopover, popoverPresentation, verticalBarsPopoverAnchor } from './popover';\nimport { nativeDialogAnimation } from './dialog-animation';\nimport { relayLayoutSupported, relayStyles } from './styles';\nimport { moveContent } from './content';\nimport { validVerticalBarFrame } from '../shared/vertical-bars-geometry';\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: NativeUIPlugin,\n options: NativeUIOptions,\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) || isNativeUIDisabled(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 if (connection.stopContent) void release(connection).catch(console.error);\n });\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 if (!relayLayoutSupported(overlay) || !content.matchesSourceAppearance()) {\n await release(connection);\n return;\n }\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) || isNativeUIDisabled(content.root))\n void release(connection).catch(console.error);\n });\n const attributes = { attributes: true, attributeFilter: ['class', 'data-native-ui', '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 const fromChild = overlay.ownerDocument === connection.win?.document;\n if (fromChild) {\n // A child-document overlay may be a sibling of the relayed content.\n // Move it before cleanup closes that document; keep the live node,\n // component handlers and adopted shadow styles in the source WebView.\n moveContent([overlay], doc.querySelector<HTMLElement>('ion-app') ?? doc.body);\n }\n current = undefined;\n opening = opening.then(() => release(connection)).catch(console.error);\n // The original event path still belongs to the child document. Let the\n // source runtime observe presentation before releasing retained controls.\n if (fromChild) doc.dispatchEvent(new CustomEvent('nativeUIOverlayAdopted', { detail: overlay }));\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 overlay.mode !== 'md' &&\n !overlay.classList.contains('md') &&\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 viewport = `${win.innerWidth}:${win.innerHeight}`;\n const anchor = bounded(plugin.getVerticalBarsGeometry({ viewportWidth: win.innerWidth, ids: [anchorId!] }))\n .then(({ items }) => {\n const frame = items[anchorId!];\n return frame && validVerticalBarFrame(frame) && viewport === `${win.innerWidth}:${win.innerHeight}` ? frame : undefined;\n })\n .catch(() => undefined);\n const animation = nativeDialogAnimation(overlay, closeNative, anchor);\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 const visibility = () => {\n if (doc.hidden) invalidate();\n };\n const listener = await boundedListener(\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 win.addEventListener('resize', invalidate);\n doc.addEventListener('ionViewWillLeave', invalidate);\n doc.addEventListener('visibilitychange', visibility);\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 bounded(listener.remove());\n }\n }\n },\n };\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"controller.js","names":[],"sources":["../../../src/native/overlays/controller.ts"],"sourcesContent":["import type { NativeUIOptions, NativeUIPlugin } from '../definitions';\nimport { bounded, boundedListener } from '../shared/bridge';\nimport { isExcluded, isPermanentlyExcluded, isNativeUIDisabled } from '../shared/dom';\nimport { activeElement, trapFocus } from './focus';\nimport { relayPopover, popoverPresentation, verticalBarsPopoverAnchor } from './popover';\nimport { nativeDialogAnimation } from './dialog-animation';\nimport { relayLayoutSupported, relayStyles } from './styles';\nimport { moveContent } from './content';\nimport { validVerticalBarFrame } from '../shared/vertical-bars-geometry';\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: NativeUIPlugin,\n options: NativeUIOptions,\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) || isNativeUIDisabled(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 if (connection.stopContent) void release(connection).catch(console.error);\n });\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 if (!relayLayoutSupported(overlay) || !content.matchesSourceAppearance()) {\n await release(connection);\n return;\n }\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) || isNativeUIDisabled(content.root))\n void release(connection).catch(console.error);\n });\n const attributes = { attributes: true, attributeFilter: ['class', 'data-native-ui', '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 const fromChild = overlay.ownerDocument === connection.win?.document;\n if (fromChild) {\n // A child-document overlay may be a sibling of the relayed content.\n // Move it before cleanup closes that document; keep the live node,\n // component handlers and adopted shadow styles in the source WebView.\n moveContent([overlay], doc.querySelector<HTMLElement>('ion-app') ?? doc.body);\n }\n current = undefined;\n opening = opening.then(() => release(connection)).catch(console.error);\n // The original event path still belongs to the child document. Let the\n // source runtime observe presentation before releasing retained controls.\n if (fromChild) doc.dispatchEvent(new CustomEvent('nativeUIOverlayAdopted', { detail: overlay }));\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 overlay.mode !== 'md' &&\n !overlay.classList.contains('md') &&\n options.ionic?.verticalBars?.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 viewport = `${win.innerWidth}:${win.innerHeight}`;\n const anchor = bounded(plugin.getVerticalBarsGeometry({ viewportWidth: win.innerWidth, ids: [anchorId!] }))\n .then(({ items }) => {\n const frame = items[anchorId!];\n return frame && validVerticalBarFrame(frame) && viewport === `${win.innerWidth}:${win.innerHeight}` ? frame : undefined;\n })\n .catch(() => undefined);\n const animation = nativeDialogAnimation(overlay, closeNative, anchor);\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 const visibility = () => {\n if (doc.hidden) invalidate();\n };\n const listener = await boundedListener(\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 win.addEventListener('resize', invalidate);\n doc.addEventListener('ionViewWillLeave', invalidate);\n doc.addEventListener('visibilitychange', visibility);\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 bounded(listener.remove());\n }\n }\n },\n };\n};\n"],"mappings":"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"}
@@ -67,7 +67,7 @@ const prehideVerticalBarsToolbarSources = (doc, enabled = () => true, sourceRoot
67
67
  if (element.matches("ion-buttons")) {
68
68
  const children = childElements(element);
69
69
  const actions = children.filter((child) => eligible(child) && selectedAction(child, options));
70
- const group = (localPlacement(element) ?? toolbarOptions(options, true).buttons) && eligible(element) && actions.length === children.length && (actions.length > 1 || actions.length === 1 && actions[0].matches("ion-menu-button"));
70
+ const group = (localPlacement(element) ?? toolbarOptions(options, true).buttonGroup) && eligible(element) && actions.length === children.length && (actions.length > 1 || actions.length === 1 && actions[0].matches("ion-menu-button"));
71
71
  const freshGroup = !owned.has(element);
72
72
  place(element, group);
73
73
  children.forEach((child) => {
@@ -1 +1 @@
1
- {"version":3,"file":"prehide.js","names":[],"sources":["../../src/native/prehide.ts"],"sourcesContent":["import { localToolbarOption, toolbarOptions } from './shared/toolbar-options';\nimport type { VerticalControlAreaOptions } from './definitions';\nimport { modalUsesVerticalBars } from './shared/modal';\nimport { LIFECYCLE_DID_ENTER, LIFECYCLE_DID_LEAVE, LIFECYCLE_WILL_ENTER, LIFECYCLE_WILL_LEAVE } from './shared/events';\nimport { VERTICAL_BARS_TRANSITION_CANCELED } from '../native-integration';\nimport {\n childElements,\n clearVerticalBarsPlacement,\n createVerticalBarsPageState,\n verticalBarsActionCandidate,\n verticalBarsBackCandidate,\n verticalBarsBackWebClass,\n verticalBarsOwned,\n isVerticalBarsToolbarActionShape,\n isPermanentlyExcluded,\n isNativeUIDisabled,\n prehiddenClass,\n prehideRootClass,\n rejectedClass,\n setVerticalBarsPlacement,\n syncToolbarText,\n} from './shared/dom';\n\nconst overlays = 'ion-menu, ion-popover';\nconst sourceSelector = 'ion-back-button, ion-menu-button, ion-buttons, ion-button';\n// Realm- and teardown-safe: globals such as Element/HTMLElement may be gone\n// when a queued mutation microtask or a stale listener still runs.\nconst isHtmlElement = (node: unknown): node is HTMLElement => !!node && (node as Node).nodeType === 1;\nconst asElement = (node: Node | EventTarget | null): HTMLElement | undefined => (isHtmlElement(node) ? node : undefined);\nconst backSupported = (element: HTMLElement): boolean => {\n const back = element as HTMLIonBackButtonElement;\n return back.icon === undefined && back.color === undefined && !!back.shadowRoot;\n};\nconst eligibleBack = verticalBarsBackCandidate;\nconst eligible = verticalBarsActionCandidate;\nconst localPlacement = (element: HTMLElement) => localToolbarOption(element, true);\nconst selectedAction = (element: HTMLElement, options: VerticalControlAreaOptions): boolean => {\n if (!element.matches('ion-button, ion-menu-button')) return false;\n const explicit = localPlacement(element);\n return explicit ?? (toolbarOptions(options, true).button && isVerticalBarsToolbarActionShape(element));\n};\n\n/** Capture toolbar placement once per routed-page epoch, before Ionic paints its transition. */\nexport const prehideVerticalBarsToolbarSources = (\n doc: Document,\n enabled: () => boolean = () => true,\n sourceRoot: HTMLElement = doc.body,\n options: VerticalControlAreaOptions = {},\n): { suspend: () => () => void; stop: () => void } => {\n doc.documentElement.classList.toggle(prehideRootClass, enabled());\n const scopes = new Map<HTMLElement, Set<HTMLElement>>();\n const owner = new WeakMap<HTMLElement, HTMLElement>();\n const pendingBacks = new Map<HTMLElement, { scope: HTMLElement; timer: ReturnType<typeof setTimeout> }>();\n const verticalBarsPages = createVerticalBarsPageState();\n const listeners = new AbortController();\n const root = () =>\n sourceRoot.closest<HTMLElement>('ion-app.ios-theme-vertical-bars') ??\n sourceRoot.querySelector<HTMLElement>('ion-app.ios-theme-vertical-bars');\n let suspended = 0;\n let stopped = false;\n const routedPage = (element: HTMLElement) => element.closest<HTMLElement>('.ion-page:not(ion-app, body)');\n const release = (scope: HTMLElement) => {\n for (const [element, pending] of pendingBacks) {\n if (pending.scope !== scope) continue;\n clearTimeout(pending.timer);\n pendingBacks.delete(element);\n }\n const owned = scopes.get(scope);\n if (!owned) return;\n owned.forEach((element) => {\n if (owner.get(element) !== scope) return;\n owner.delete(element);\n element.classList.remove(prehiddenClass);\n element.classList.remove(verticalBarsBackWebClass);\n clearVerticalBarsPlacement(element);\n });\n scopes.delete(scope);\n };\n const capture = (scope: HTMLElement) => {\n if (!root()?.contains(scope) || scope.closest(overlays) || !modalUsesVerticalBars(scope) || verticalBarsPages.isDeparted(scope)) return;\n const owned = scopes.get(scope) ?? new Set<HTMLElement>();\n const place = (element: HTMLElement, rail: boolean) => {\n if (owned.has(element)) return;\n const previousScope = owner.get(element);\n if (previousScope) scopes.get(previousScope)?.delete(element);\n owner.set(element, scope);\n setVerticalBarsPlacement(element, rail);\n if (element.matches('ion-back-button')) element.classList.toggle(verticalBarsBackWebClass, !rail);\n owned.add(element);\n };\n const sources = scope.matches('ion-back-button') ? [scope] : Array.from(scope.querySelectorAll<HTMLElement>(sourceSelector));\n sources.forEach((element) => {\n if (!sourceRoot.contains(element)) return;\n // Shell opt-out is temporary; decide initial placement once it is lifted.\n if (isNativeUIDisabled(element)) return;\n if (!modalUsesVerticalBars(element) || element.closest(overlays) || (scope.matches('.ion-page') && routedPage(element) !== scope))\n return;\n if (element.matches('ion-buttons')) {\n const children = childElements(element);\n const actions = children.filter((child) => eligible(child) && selectedAction(child, options));\n const group =\n (localPlacement(element) ?? toolbarOptions(options, true).buttons) &&\n eligible(element) &&\n actions.length === children.length &&\n (actions.length > 1 || (actions.length === 1 && actions[0].matches('ion-menu-button')));\n const freshGroup = !owned.has(element);\n place(element, group);\n children.forEach((child) => {\n if (!child.matches('ion-back-button')) place(child, freshGroup && actions.includes(child));\n });\n } else if (element.matches('ion-back-button') || !element.parentElement?.matches('ion-buttons')) {\n if (owned.has(element)) return;\n if (\n element.matches('ion-back-button') &&\n eligibleBack(element) &&\n (element as HTMLIonBackButtonElement).icon === undefined &&\n (element as HTMLIonBackButtonElement).color === undefined &&\n !element.shadowRoot &&\n !element.classList.contains('hydrated')\n ) {\n const previousPending = pendingBacks.get(element);\n if (previousPending && previousPending.scope !== scope) {\n clearTimeout(previousPending.timer);\n pendingBacks.delete(element);\n }\n if (!pendingBacks.has(element)) {\n const timer = setTimeout(() => {\n if (pendingBacks.get(element)?.scope !== scope) return;\n pendingBacks.delete(element);\n // A custom element that never hydrates must remain usable on Web.\n place(element, false);\n reconcile();\n }, 1500);\n pendingBacks.set(element, { scope, timer });\n }\n return;\n }\n const pending = pendingBacks.get(element);\n if (pending) {\n clearTimeout(pending.timer);\n pendingBacks.delete(element);\n }\n place(\n element,\n element.matches('ion-back-button')\n ? (localPlacement(element) ?? toolbarOptions(options, true).backButton) && eligibleBack(element) && backSupported(element)\n : eligible(element) && selectedAction(element, options),\n );\n }\n });\n scopes.set(scope, owned);\n };\n const reconcile = () => {\n doc.documentElement.classList.toggle(prehideRootClass, enabled() && !suspended);\n syncToolbarText(doc);\n const verticalBars = root();\n if (!verticalBars) {\n Array.from(scopes.keys()).forEach(release);\n return;\n }\n for (const scope of scopes.keys()) if (!scope.isConnected || !modalUsesVerticalBars(scope)) release(scope);\n for (const [element, pending] of pendingBacks) if (element.shadowRoot || element.classList.contains('hydrated')) capture(pending.scope);\n // Ionic inserts the destination as invisible before WillEnter. Hide its\n // sources in that same mutation microtask; capture new DOM identities on active pages too.\n verticalBars.querySelectorAll<HTMLElement>('.ion-page:not(ion-app, body, .ion-page-hidden)').forEach(capture);\n // Root toolbars can mount after startup; each new DOM identity is captured once.\n verticalBars.querySelectorAll<HTMLElement>(sourceSelector).forEach((element) => {\n if (routedPage(element) || element.closest(overlays)) return;\n const scope = element.closest<HTMLElement>('ion-toolbar') ?? (element.matches('ion-back-button') ? element : null);\n if (scope) capture(scope);\n });\n for (const owned of scopes.values()) {\n for (const element of owned) {\n const groupOwnsChildren =\n element.parentElement?.matches('ion-buttons') &&\n verticalBarsOwned(element.parentElement) &&\n childElements(element.parentElement).every((child) => verticalBarsOwned(child));\n const hide =\n enabled() &&\n !element.closest('[data-native-ui-placement=\"element\"]') &&\n !suspended &&\n verticalBarsOwned(element) &&\n !groupOwnsChildren &&\n element.isConnected &&\n !element.closest(`.${rejectedClass}`) &&\n !isPermanentlyExcluded(element) &&\n !isNativeUIDisabled(element) &&\n (!element.matches('ion-back-button') || backSupported(element) || !element.classList.contains('hydrated')) &&\n (!element.matches('ion-buttons') || childElements(element).every((child) => verticalBarsOwned(child)));\n element.classList.toggle(prehiddenClass, hide);\n if (element.matches('ion-back-button')) element.classList.toggle(verticalBarsBackWebClass, !hide);\n }\n }\n };\n const onWillEnter = (event: Event) => {\n const page = asElement(event.target);\n if (page?.matches('.ion-page')) {\n verticalBarsPages.lifecycle(event);\n release(page); // Cached pages begin a new placement epoch.\n capture(page);\n reconcile();\n }\n };\n const onPageLifecycle: EventListener = (event) => {\n verticalBarsPages.lifecycle(event);\n if (event.type !== LIFECYCLE_DID_LEAVE) return;\n const page = asElement(event.target);\n if (page) release(page);\n };\n // A canceled transition keeps the leaving page active and abandons the entering one.\n const onTransitionCanceled: EventListener = (event) => {\n const leaving = asElement(event.target);\n if (!leaving) return;\n const entering = (event as CustomEvent<{ entering?: HTMLElement }>).detail?.entering;\n verticalBarsPages.cancel(entering, leaving);\n reconcile();\n };\n doc.addEventListener(LIFECYCLE_WILL_ENTER, onWillEnter, { capture: true, signal: listeners.signal });\n for (const name of [LIFECYCLE_WILL_LEAVE, LIFECYCLE_DID_ENTER, LIFECYCLE_DID_LEAVE])\n doc.addEventListener(name, onPageLifecycle, { capture: true, signal: listeners.signal });\n doc.addEventListener(VERTICAL_BARS_TRANSITION_CANCELED, onTransitionCanceled, { capture: true, signal: listeners.signal });\n doc.defaultView?.addEventListener('nativeUIRefresh', reconcile, { signal: listeners.signal });\n reconcile();\n const mutationRelevant = (record: MutationRecord) => {\n if (record.type === 'characterData') return record.target.parentElement?.matches('ion-toolbar') === true;\n if (record.type === 'childList' && asElement(record.target)?.matches('ion-toolbar')) return true;\n if (record.type === 'childList')\n return [...Array.from(record.addedNodes), ...Array.from(record.removedNodes)].some(\n (node) =>\n isHtmlElement(node) &&\n (node.matches(`${sourceSelector}, .ion-page, ion-toolbar`) || !!node.querySelector(`${sourceSelector}, .ion-page, ion-toolbar`)),\n );\n if (record.attributeName !== 'class') return true;\n const current = (record.target as Element).className;\n const previous = record.oldValue ?? '';\n const withoutOwned = (value: string) =>\n value\n .split(/\\s+/)\n .filter((name) => name && ![prehiddenClass, prehideRootClass, verticalBarsBackWebClass].includes(name))\n .join(' ');\n return typeof current !== 'string' || withoutOwned(previous) !== withoutOwned(current);\n };\n let scheduled = false;\n const observer = new (doc.defaultView?.MutationObserver ?? MutationObserver)((records) => {\n if (scheduled || stopped || !records.some(mutationRelevant)) return;\n scheduled = true;\n queueMicrotask(() => {\n scheduled = false;\n if (!stopped) reconcile();\n });\n });\n observer.observe(doc.documentElement, {\n subtree: true,\n childList: true,\n characterData: true,\n attributes: true,\n attributeOldValue: true,\n attributeFilter: ['class', 'data-native-ui', 'hidden', 'inert', 'icon', 'color'],\n });\n return {\n suspend: () => {\n if (stopped) return () => {};\n suspended++;\n doc.documentElement.classList.remove(prehideRootClass);\n reconcile();\n let resumed = false;\n return () => {\n if (resumed || stopped) return;\n resumed = true;\n if (--suspended === 0) {\n doc.documentElement.classList.add(prehideRootClass);\n reconcile();\n }\n };\n },\n stop: () => {\n if (stopped) return;\n stopped = true;\n observer.disconnect();\n listeners.abort();\n doc.documentElement.classList.remove(prehideRootClass);\n Array.from(scopes.keys()).forEach(release);\n },\n };\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"prehide.js","names":[],"sources":["../../src/native/prehide.ts"],"sourcesContent":["import { localToolbarOption, toolbarOptions } from './shared/toolbar-options';\nimport type { VerticalControlAreaOptions } from './definitions';\nimport { modalUsesVerticalBars } from './shared/modal';\nimport { LIFECYCLE_DID_ENTER, LIFECYCLE_DID_LEAVE, LIFECYCLE_WILL_ENTER, LIFECYCLE_WILL_LEAVE } from './shared/events';\nimport { VERTICAL_BARS_TRANSITION_CANCELED } from '../native-integration';\nimport {\n childElements,\n clearVerticalBarsPlacement,\n createVerticalBarsPageState,\n verticalBarsActionCandidate,\n verticalBarsBackCandidate,\n verticalBarsBackWebClass,\n verticalBarsOwned,\n isVerticalBarsToolbarActionShape,\n isPermanentlyExcluded,\n isNativeUIDisabled,\n prehiddenClass,\n prehideRootClass,\n rejectedClass,\n setVerticalBarsPlacement,\n syncToolbarText,\n} from './shared/dom';\n\nconst overlays = 'ion-menu, ion-popover';\nconst sourceSelector = 'ion-back-button, ion-menu-button, ion-buttons, ion-button';\n// Realm- and teardown-safe: globals such as Element/HTMLElement may be gone\n// when a queued mutation microtask or a stale listener still runs.\nconst isHtmlElement = (node: unknown): node is HTMLElement => !!node && (node as Node).nodeType === 1;\nconst asElement = (node: Node | EventTarget | null): HTMLElement | undefined => (isHtmlElement(node) ? node : undefined);\nconst backSupported = (element: HTMLElement): boolean => {\n const back = element as HTMLIonBackButtonElement;\n return back.icon === undefined && back.color === undefined && !!back.shadowRoot;\n};\nconst eligibleBack = verticalBarsBackCandidate;\nconst eligible = verticalBarsActionCandidate;\nconst localPlacement = (element: HTMLElement) => localToolbarOption(element, true);\nconst selectedAction = (element: HTMLElement, options: VerticalControlAreaOptions): boolean => {\n if (!element.matches('ion-button, ion-menu-button')) return false;\n const explicit = localPlacement(element);\n return explicit ?? (toolbarOptions(options, true).button && isVerticalBarsToolbarActionShape(element));\n};\n\n/** Capture toolbar placement once per routed-page epoch, before Ionic paints its transition. */\nexport const prehideVerticalBarsToolbarSources = (\n doc: Document,\n enabled: () => boolean = () => true,\n sourceRoot: HTMLElement = doc.body,\n options: VerticalControlAreaOptions = {},\n): { suspend: () => () => void; stop: () => void } => {\n doc.documentElement.classList.toggle(prehideRootClass, enabled());\n const scopes = new Map<HTMLElement, Set<HTMLElement>>();\n const owner = new WeakMap<HTMLElement, HTMLElement>();\n const pendingBacks = new Map<HTMLElement, { scope: HTMLElement; timer: ReturnType<typeof setTimeout> }>();\n const verticalBarsPages = createVerticalBarsPageState();\n const listeners = new AbortController();\n const root = () =>\n sourceRoot.closest<HTMLElement>('ion-app.ios-theme-vertical-bars') ??\n sourceRoot.querySelector<HTMLElement>('ion-app.ios-theme-vertical-bars');\n let suspended = 0;\n let stopped = false;\n const routedPage = (element: HTMLElement) => element.closest<HTMLElement>('.ion-page:not(ion-app, body)');\n const release = (scope: HTMLElement) => {\n for (const [element, pending] of pendingBacks) {\n if (pending.scope !== scope) continue;\n clearTimeout(pending.timer);\n pendingBacks.delete(element);\n }\n const owned = scopes.get(scope);\n if (!owned) return;\n owned.forEach((element) => {\n if (owner.get(element) !== scope) return;\n owner.delete(element);\n element.classList.remove(prehiddenClass);\n element.classList.remove(verticalBarsBackWebClass);\n clearVerticalBarsPlacement(element);\n });\n scopes.delete(scope);\n };\n const capture = (scope: HTMLElement) => {\n if (!root()?.contains(scope) || scope.closest(overlays) || !modalUsesVerticalBars(scope) || verticalBarsPages.isDeparted(scope)) return;\n const owned = scopes.get(scope) ?? new Set<HTMLElement>();\n const place = (element: HTMLElement, rail: boolean) => {\n if (owned.has(element)) return;\n const previousScope = owner.get(element);\n if (previousScope) scopes.get(previousScope)?.delete(element);\n owner.set(element, scope);\n setVerticalBarsPlacement(element, rail);\n if (element.matches('ion-back-button')) element.classList.toggle(verticalBarsBackWebClass, !rail);\n owned.add(element);\n };\n const sources = scope.matches('ion-back-button') ? [scope] : Array.from(scope.querySelectorAll<HTMLElement>(sourceSelector));\n sources.forEach((element) => {\n if (!sourceRoot.contains(element)) return;\n // Shell opt-out is temporary; decide initial placement once it is lifted.\n if (isNativeUIDisabled(element)) return;\n if (!modalUsesVerticalBars(element) || element.closest(overlays) || (scope.matches('.ion-page') && routedPage(element) !== scope))\n return;\n if (element.matches('ion-buttons')) {\n const children = childElements(element);\n const actions = children.filter((child) => eligible(child) && selectedAction(child, options));\n const group =\n (localPlacement(element) ?? toolbarOptions(options, true).buttonGroup) &&\n eligible(element) &&\n actions.length === children.length &&\n (actions.length > 1 || (actions.length === 1 && actions[0].matches('ion-menu-button')));\n const freshGroup = !owned.has(element);\n place(element, group);\n children.forEach((child) => {\n if (!child.matches('ion-back-button')) place(child, freshGroup && actions.includes(child));\n });\n } else if (element.matches('ion-back-button') || !element.parentElement?.matches('ion-buttons')) {\n if (owned.has(element)) return;\n if (\n element.matches('ion-back-button') &&\n eligibleBack(element) &&\n (element as HTMLIonBackButtonElement).icon === undefined &&\n (element as HTMLIonBackButtonElement).color === undefined &&\n !element.shadowRoot &&\n !element.classList.contains('hydrated')\n ) {\n const previousPending = pendingBacks.get(element);\n if (previousPending && previousPending.scope !== scope) {\n clearTimeout(previousPending.timer);\n pendingBacks.delete(element);\n }\n if (!pendingBacks.has(element)) {\n const timer = setTimeout(() => {\n if (pendingBacks.get(element)?.scope !== scope) return;\n pendingBacks.delete(element);\n // A custom element that never hydrates must remain usable on Web.\n place(element, false);\n reconcile();\n }, 1500);\n pendingBacks.set(element, { scope, timer });\n }\n return;\n }\n const pending = pendingBacks.get(element);\n if (pending) {\n clearTimeout(pending.timer);\n pendingBacks.delete(element);\n }\n place(\n element,\n element.matches('ion-back-button')\n ? (localPlacement(element) ?? toolbarOptions(options, true).backButton) && eligibleBack(element) && backSupported(element)\n : eligible(element) && selectedAction(element, options),\n );\n }\n });\n scopes.set(scope, owned);\n };\n const reconcile = () => {\n doc.documentElement.classList.toggle(prehideRootClass, enabled() && !suspended);\n syncToolbarText(doc);\n const verticalBars = root();\n if (!verticalBars) {\n Array.from(scopes.keys()).forEach(release);\n return;\n }\n for (const scope of scopes.keys()) if (!scope.isConnected || !modalUsesVerticalBars(scope)) release(scope);\n for (const [element, pending] of pendingBacks) if (element.shadowRoot || element.classList.contains('hydrated')) capture(pending.scope);\n // Ionic inserts the destination as invisible before WillEnter. Hide its\n // sources in that same mutation microtask; capture new DOM identities on active pages too.\n verticalBars.querySelectorAll<HTMLElement>('.ion-page:not(ion-app, body, .ion-page-hidden)').forEach(capture);\n // Root toolbars can mount after startup; each new DOM identity is captured once.\n verticalBars.querySelectorAll<HTMLElement>(sourceSelector).forEach((element) => {\n if (routedPage(element) || element.closest(overlays)) return;\n const scope = element.closest<HTMLElement>('ion-toolbar') ?? (element.matches('ion-back-button') ? element : null);\n if (scope) capture(scope);\n });\n for (const owned of scopes.values()) {\n for (const element of owned) {\n const groupOwnsChildren =\n element.parentElement?.matches('ion-buttons') &&\n verticalBarsOwned(element.parentElement) &&\n childElements(element.parentElement).every((child) => verticalBarsOwned(child));\n const hide =\n enabled() &&\n !element.closest('[data-native-ui-placement=\"element\"]') &&\n !suspended &&\n verticalBarsOwned(element) &&\n !groupOwnsChildren &&\n element.isConnected &&\n !element.closest(`.${rejectedClass}`) &&\n !isPermanentlyExcluded(element) &&\n !isNativeUIDisabled(element) &&\n (!element.matches('ion-back-button') || backSupported(element) || !element.classList.contains('hydrated')) &&\n (!element.matches('ion-buttons') || childElements(element).every((child) => verticalBarsOwned(child)));\n element.classList.toggle(prehiddenClass, hide);\n if (element.matches('ion-back-button')) element.classList.toggle(verticalBarsBackWebClass, !hide);\n }\n }\n };\n const onWillEnter = (event: Event) => {\n const page = asElement(event.target);\n if (page?.matches('.ion-page')) {\n verticalBarsPages.lifecycle(event);\n release(page); // Cached pages begin a new placement epoch.\n capture(page);\n reconcile();\n }\n };\n const onPageLifecycle: EventListener = (event) => {\n verticalBarsPages.lifecycle(event);\n if (event.type !== LIFECYCLE_DID_LEAVE) return;\n const page = asElement(event.target);\n if (page) release(page);\n };\n // A canceled transition keeps the leaving page active and abandons the entering one.\n const onTransitionCanceled: EventListener = (event) => {\n const leaving = asElement(event.target);\n if (!leaving) return;\n const entering = (event as CustomEvent<{ entering?: HTMLElement }>).detail?.entering;\n verticalBarsPages.cancel(entering, leaving);\n reconcile();\n };\n doc.addEventListener(LIFECYCLE_WILL_ENTER, onWillEnter, { capture: true, signal: listeners.signal });\n for (const name of [LIFECYCLE_WILL_LEAVE, LIFECYCLE_DID_ENTER, LIFECYCLE_DID_LEAVE])\n doc.addEventListener(name, onPageLifecycle, { capture: true, signal: listeners.signal });\n doc.addEventListener(VERTICAL_BARS_TRANSITION_CANCELED, onTransitionCanceled, { capture: true, signal: listeners.signal });\n doc.defaultView?.addEventListener('nativeUIRefresh', reconcile, { signal: listeners.signal });\n reconcile();\n const mutationRelevant = (record: MutationRecord) => {\n if (record.type === 'characterData') return record.target.parentElement?.matches('ion-toolbar') === true;\n if (record.type === 'childList' && asElement(record.target)?.matches('ion-toolbar')) return true;\n if (record.type === 'childList')\n return [...Array.from(record.addedNodes), ...Array.from(record.removedNodes)].some(\n (node) =>\n isHtmlElement(node) &&\n (node.matches(`${sourceSelector}, .ion-page, ion-toolbar`) || !!node.querySelector(`${sourceSelector}, .ion-page, ion-toolbar`)),\n );\n if (record.attributeName !== 'class') return true;\n const current = (record.target as Element).className;\n const previous = record.oldValue ?? '';\n const withoutOwned = (value: string) =>\n value\n .split(/\\s+/)\n .filter((name) => name && ![prehiddenClass, prehideRootClass, verticalBarsBackWebClass].includes(name))\n .join(' ');\n return typeof current !== 'string' || withoutOwned(previous) !== withoutOwned(current);\n };\n let scheduled = false;\n const observer = new (doc.defaultView?.MutationObserver ?? MutationObserver)((records) => {\n if (scheduled || stopped || !records.some(mutationRelevant)) return;\n scheduled = true;\n queueMicrotask(() => {\n scheduled = false;\n if (!stopped) reconcile();\n });\n });\n observer.observe(doc.documentElement, {\n subtree: true,\n childList: true,\n characterData: true,\n attributes: true,\n attributeOldValue: true,\n attributeFilter: ['class', 'data-native-ui', 'hidden', 'inert', 'icon', 'color'],\n });\n return {\n suspend: () => {\n if (stopped) return () => {};\n suspended++;\n doc.documentElement.classList.remove(prehideRootClass);\n reconcile();\n let resumed = false;\n return () => {\n if (resumed || stopped) return;\n resumed = true;\n if (--suspended === 0) {\n doc.documentElement.classList.add(prehideRootClass);\n reconcile();\n }\n };\n },\n stop: () => {\n if (stopped) return;\n stopped = true;\n observer.disconnect();\n listeners.abort();\n doc.documentElement.classList.remove(prehideRootClass);\n Array.from(scopes.keys()).forEach(release);\n },\n };\n};\n"],"mappings":"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"}
@@ -1,6 +1,6 @@
1
1
  import { NativeUIPlugin, NativeUIRuntimeHandle, NativeUIRuntimeOptions } from "./definitions.js";
2
2
  //#region src/native/runtime.d.ts
3
- export declare const createRuntime: (doc: Document, plugin: NativeUIPlugin, options?: NativeUIRuntimeOptions, nativeVerticalBars?: () => boolean, verticalBarsOnly?: boolean, onStop?: (cause: "failure" | "pagehide") => void) => Promise<NativeUIRuntimeHandle & {
3
+ export declare const createRuntime: (doc: Document, plugin: NativeUIPlugin, options?: NativeUIRuntimeOptions, nativeVerticalBars?: () => boolean, onStop?: (cause: "failure" | "pagehide") => void) => Promise<NativeUIRuntimeHandle & {
4
4
  retain: (deferActivation?: (id: string) => boolean) => () => void;
5
5
  }>;
6
6
  //#endregion
@@ -1 +1 @@
1
- {"version":3,"file":"runtime.d.ts","names":[],"sources":["../../src/native/runtime.ts"],"mappings":";;qBAkDa,gBACX,KAAK,UACL,QAAQ,gBACR,UAAS,wBACT,oCACA,4BACA,UAAU,2CACT,QAAQ;EAA0B,SAAS,mBAAmB"}
1
+ {"version":3,"file":"runtime.d.ts","names":[],"sources":["../../src/native/runtime.ts"],"mappings":";;qBAmDa,gBACX,KAAK,UACL,QAAQ,gBACR,UAAS,wBACT,oCACA,UAAU,2CACT,QAAQ;EAA0B,SAAS,mBAAmB"}
@@ -1,3 +1,4 @@
1
+ import { controlSelected } from "./shared/control-options.js";
1
2
  import { inVerticalBarsSurface, modalUsesVerticalBars, modalVerticalBarFrame, topModal } from "./shared/modal.js";
2
3
  import { createCrossfade, fadeMarker } from "./shared/crossfade.js";
3
4
  import { activateProjectedElement, createVerticalBarsPageState, isVerticalBarsSource, marker, preferredVerticalBarsBack, prehideOnlyMutation, rejectedClass, syncToolbarText, unprojected } from "./shared/dom.js";
@@ -28,7 +29,7 @@ const overlayNames = [
28
29
  ];
29
30
  const verticalBarsMarker = "data-native-ui-vertical-bars";
30
31
  const verticalBarsMemberMarker = "data-native-ui-vertical-bars-member";
31
- const createRuntime = async (doc, plugin, options = {}, nativeVerticalBars = () => true, verticalBarsOnly = false, onStop) => {
32
+ const createRuntime = async (doc, plugin, options = {}, nativeVerticalBars = () => true, onStop) => {
32
33
  const win = doc.defaultView;
33
34
  const extensions = options.extensions ?? [];
34
35
  const diagnose = createDiagnostics(options.debug);
@@ -82,16 +83,10 @@ const createRuntime = async (doc, plugin, options = {}, nativeVerticalBars = ()
82
83
  let listener;
83
84
  let searchListener;
84
85
  let waiters = [];
85
- const control = (kind) => {
86
- if (kind === "tab-bar") return "tabs";
87
- if (kind === "segment") return "segment";
88
- if (kind === "floating-action-button") return "fab";
89
- return "toolbar";
90
- };
91
86
  const controlEnabled = (candidate) => {
92
- if (candidate.control.kind === "extension") return true;
93
- const controls = options.controls;
94
- return controls === void 0 || controls[control(candidate.control.kind)] === true;
87
+ const kind = candidate.control.kind;
88
+ const key = kind === "tab-bar" ? "tabs" : kind === "segment" ? "segment" : kind === "floating-action-button" ? "fab" : void 0;
89
+ return !key || controlSelected(candidate.element, options, key, candidate.control.placement === "vertical-bars");
95
90
  };
96
91
  const id = (element) => {
97
92
  let value = ids.get(element);
@@ -161,12 +156,7 @@ const createRuntime = async (doc, plugin, options = {}, nativeVerticalBars = ()
161
156
  diagnose(`${extension.name}: ${unavailable}`, element);
162
157
  return;
163
158
  }
164
- const candidate = readExtension(element, extensions, id, (reason) => diagnose(`${extension.name}: ${reason}`, element));
165
- if (verticalBarsOnly && (!candidate?.control.extension?.tabBarId || !nativeVerticalBars())) {
166
- if (candidate) diagnose("rail-only: element extensions stay on the Web when verticalBars.only is true.", element);
167
- return;
168
- }
169
- return candidate;
159
+ return readExtension(element, extensions, id, (reason) => diagnose(`${extension.name}: ${reason}`, element));
170
160
  }
171
161
  if (options.source === "extensions") return;
172
162
  if (options.source === "html" || element.matches("[data-native-ui=\"button\"], [data-native-ui=\"back-button\"]")) {
@@ -175,13 +165,11 @@ const createRuntime = async (doc, plugin, options = {}, nativeVerticalBars = ()
175
165
  diagnose("unsupported-control: check the source visibility, accessible name, disabled ancestors, and supported HTML markup.", element);
176
166
  return;
177
167
  }
178
- const rail = candidate.control.placement === "vertical-bars";
179
- if (verticalBarsOnly && !rail) return;
180
- if (rail && !nativeVerticalBars()) return;
168
+ if (candidate.control.placement === "vertical-bars" && !nativeVerticalBars()) return;
181
169
  return candidate;
182
170
  }
183
171
  if (element.querySelector("[data-native-ui=\"button\"], [data-native-ui=\"back-button\"]")) return;
184
- if (verticalBarsOnly && (!isVerticalBarsCandidate(element) || element.closest("[data-native-ui-placement=\"element\"]"))) return;
172
+ if (options.ionic?.auto === false) return;
185
173
  if (options.verticalBars?.enabled === false && isVerticalBarsCandidate(element)) return;
186
174
  const pointerPage = isVerticalBarsCandidate(element) ? element.closest(".ion-page") : void 0;
187
175
  let candidate;