@rdlabo/ionic-theme-ios27 1.2.1-beta.pr275.shad339e7688db7 → 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.
- package/README.md +24 -280
- 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/index.d.ts +3 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -2
- package/dist/index.js.map +1 -1
- package/dist/layout/index.d.ts +18 -0
- package/dist/layout/index.d.ts.map +1 -0
- package/dist/layout/index.js +26 -0
- package/dist/layout/index.js.map +1 -0
- package/dist/segment/index.js +3 -3
- package/dist/segment/index.js.map +1 -1
- package/dist/sheets-of-glass/index.js +7 -7
- package/dist/sheets-of-glass/index.js.map +1 -1
- package/dist/tab-bar-searchable/dismiss-button.js +53 -3
- package/dist/tab-bar-searchable/dismiss-button.js.map +1 -1
- package/dist/tab-bar-searchable/index.js +18 -3
- package/dist/tab-bar-searchable/index.js.map +1 -1
- package/dist/transition/ios.transition.d.ts.map +1 -1
- package/dist/transition/ios.transition.js +7 -4
- package/dist/transition/ios.transition.js.map +1 -1
- package/dist/transition/native-ui.d.ts +6 -0
- package/dist/transition/native-ui.d.ts.map +1 -0
- package/dist/{native-integration/index.js → transition/native-ui.js} +16 -30
- package/dist/transition/native-ui.js.map +1 -0
- package/docs/adaptive-themes.md +161 -0
- package/docs/add-native-ui.md +53 -0
- package/docs/animation.md +19 -3
- package/docs/eslint.md +3 -1
- package/docs/features.md +9 -5
- package/docs/iphone-duo.md +62 -35
- package/docs/migration.md +17 -26
- package/docs/setup.md +92 -0
- package/docs/special-markup.md +41 -10
- package/docs/using-ion-item-group.md +2 -2
- package/docs/vertical-bars-layout.md +67 -0
- package/package.json +12 -42
- package/src/index.ts +3 -1
- package/src/layout/index.ts +29 -0
- package/src/segment/index.ts +3 -3
- package/src/sheets-of-glass/index.ts +7 -7
- package/src/styles/components/ion-toolbar.scss +1 -1
- package/src/styles/vertical-bars.scss +15 -18
- package/src/tab-bar-searchable/dismiss-button.ts +73 -4
- package/src/tab-bar-searchable/index.ts +20 -3
- package/src/transition/ios.transition.ts +7 -4
- package/src/{native-integration/index.ts → transition/native-ui.ts} +17 -46
- package/Package.swift +0 -21
- package/dist/_virtual/_rolldown/runtime.js +0 -13
- package/dist/native/components/index.d.ts +0 -19
- package/dist/native/components/index.d.ts.map +0 -1
- package/dist/native/components/index.js +0 -40
- package/dist/native/components/index.js.map +0 -1
- package/dist/native/components/ion-back-button.d.ts +0 -9
- package/dist/native/components/ion-back-button.d.ts.map +0 -1
- package/dist/native/components/ion-back-button.js +0 -20
- package/dist/native/components/ion-back-button.js.map +0 -1
- package/dist/native/components/ion-button.d.ts +0 -10
- package/dist/native/components/ion-button.d.ts.map +0 -1
- package/dist/native/components/ion-button.js +0 -23
- package/dist/native/components/ion-button.js.map +0 -1
- package/dist/native/components/ion-buttons.d.ts +0 -11
- package/dist/native/components/ion-buttons.d.ts.map +0 -1
- package/dist/native/components/ion-buttons.js +0 -30
- package/dist/native/components/ion-buttons.js.map +0 -1
- package/dist/native/components/ion-fab.d.ts +0 -10
- package/dist/native/components/ion-fab.d.ts.map +0 -1
- package/dist/native/components/ion-fab.js +0 -132
- package/dist/native/components/ion-fab.js.map +0 -1
- package/dist/native/components/ion-menu-button.d.ts +0 -13
- package/dist/native/components/ion-menu-button.d.ts.map +0 -1
- package/dist/native/components/ion-menu-button.js +0 -31
- package/dist/native/components/ion-menu-button.js.map +0 -1
- package/dist/native/components/ion-segment.d.ts +0 -11
- package/dist/native/components/ion-segment.d.ts.map +0 -1
- package/dist/native/components/ion-segment.js +0 -30
- package/dist/native/components/ion-segment.js.map +0 -1
- package/dist/native/components/ion-tab-bar.d.ts +0 -11
- package/dist/native/components/ion-tab-bar.d.ts.map +0 -1
- package/dist/native/components/ion-tab-bar.js +0 -50
- package/dist/native/components/ion-tab-bar.js.map +0 -1
- package/dist/native/components/searchable-tabs.d.ts +0 -20
- package/dist/native/components/searchable-tabs.d.ts.map +0 -1
- package/dist/native/components/searchable-tabs.js +0 -383
- package/dist/native/components/searchable-tabs.js.map +0 -1
- package/dist/native/definitions.d.ts +0 -191
- package/dist/native/definitions.d.ts.map +0 -1
- package/dist/native/definitions.js +0 -0
- package/dist/native/foldable/fold-state.d.ts +0 -5
- package/dist/native/foldable/fold-state.d.ts.map +0 -1
- package/dist/native/foldable/fold-state.js +0 -14
- package/dist/native/foldable/fold-state.js.map +0 -1
- package/dist/native/foldable/types.d.ts +0 -12
- package/dist/native/foldable/types.d.ts.map +0 -1
- package/dist/native/foldable/types.js +0 -0
- package/dist/native/index.d.ts +0 -11
- package/dist/native/index.d.ts.map +0 -1
- package/dist/native/index.js +0 -267
- package/dist/native/index.js.map +0 -1
- package/dist/native/overlays/content.d.ts +0 -4
- package/dist/native/overlays/content.d.ts.map +0 -1
- package/dist/native/overlays/content.js +0 -51
- package/dist/native/overlays/content.js.map +0 -1
- package/dist/native/overlays/controller.d.ts +0 -8
- package/dist/native/overlays/controller.d.ts.map +0 -1
- package/dist/native/overlays/controller.js +0 -285
- package/dist/native/overlays/controller.js.map +0 -1
- package/dist/native/overlays/dialog-animation.d.ts +0 -9
- package/dist/native/overlays/dialog-animation.d.ts.map +0 -1
- package/dist/native/overlays/dialog-animation.js +0 -53
- package/dist/native/overlays/dialog-animation.js.map +0 -1
- package/dist/native/overlays/dismiss-animation.d.ts +0 -4
- package/dist/native/overlays/dismiss-animation.d.ts.map +0 -1
- package/dist/native/overlays/dismiss-animation.js +0 -18
- package/dist/native/overlays/dismiss-animation.js.map +0 -1
- package/dist/native/overlays/focus.d.ts +0 -5
- package/dist/native/overlays/focus.d.ts.map +0 -1
- package/dist/native/overlays/focus.js +0 -59
- package/dist/native/overlays/focus.js.map +0 -1
- package/dist/native/overlays/popover.d.ts +0 -12
- package/dist/native/overlays/popover.d.ts.map +0 -1
- package/dist/native/overlays/popover.js +0 -110
- package/dist/native/overlays/popover.js.map +0 -1
- package/dist/native/overlays/styles.d.ts +0 -11
- package/dist/native/overlays/styles.d.ts.map +0 -1
- package/dist/native/overlays/styles.js +0 -167
- package/dist/native/overlays/styles.js.map +0 -1
- package/dist/native/prehide.d.ts +0 -7
- package/dist/native/prehide.d.ts.map +0 -1
- package/dist/native/prehide.js +0 -230
- package/dist/native/prehide.js.map +0 -1
- package/dist/native/runtime.d.ts +0 -7
- package/dist/native/runtime.d.ts.map +0 -1
- package/dist/native/runtime.js +0 -710
- package/dist/native/runtime.js.map +0 -1
- package/dist/native/shared/bridge.d.ts +0 -6
- package/dist/native/shared/bridge.d.ts.map +0 -1
- package/dist/native/shared/bridge.js +0 -18
- package/dist/native/shared/bridge.js.map +0 -1
- package/dist/native/shared/candidate.d.ts +0 -21
- package/dist/native/shared/candidate.d.ts.map +0 -1
- package/dist/native/shared/candidate.js +0 -98
- package/dist/native/shared/candidate.js.map +0 -1
- package/dist/native/shared/crossfade.d.ts +0 -12
- package/dist/native/shared/crossfade.d.ts.map +0 -1
- package/dist/native/shared/crossfade.js +0 -67
- package/dist/native/shared/crossfade.js.map +0 -1
- package/dist/native/shared/dom.d.ts +0 -46
- package/dist/native/shared/dom.d.ts.map +0 -1
- package/dist/native/shared/dom.js +0 -172
- package/dist/native/shared/dom.js.map +0 -1
- package/dist/native/shared/events.d.ts +0 -11
- package/dist/native/shared/events.d.ts.map +0 -1
- package/dist/native/shared/events.js +0 -13
- package/dist/native/shared/events.js.map +0 -1
- package/dist/native/shared/icons.d.ts +0 -8
- package/dist/native/shared/icons.d.ts.map +0 -1
- package/dist/native/shared/icons.js +0 -61
- package/dist/native/shared/icons.js.map +0 -1
- package/dist/native/shared/modal.d.ts +0 -10
- package/dist/native/shared/modal.d.ts.map +0 -1
- package/dist/native/shared/modal.js +0 -160
- package/dist/native/shared/modal.js.map +0 -1
- package/dist/native/shared/projection-id.d.ts +0 -4
- package/dist/native/shared/projection-id.d.ts.map +0 -1
- package/dist/native/shared/projection-id.js +0 -7
- package/dist/native/shared/projection-id.js.map +0 -1
- package/dist/native/shared/vertical-bars-geometry.d.ts +0 -10
- package/dist/native/shared/vertical-bars-geometry.d.ts.map +0 -1
- package/dist/native/shared/vertical-bars-geometry.js +0 -85
- package/dist/native/shared/vertical-bars-geometry.js.map +0 -1
- package/dist/native/vertical-bars-web-search.d.ts +0 -10
- package/dist/native/vertical-bars-web-search.d.ts.map +0 -1
- package/dist/native/vertical-bars-web-search.js +0 -90
- package/dist/native/vertical-bars-web-search.js.map +0 -1
- package/dist/native/vertical-bars-web.d.ts +0 -5
- package/dist/native/vertical-bars-web.d.ts.map +0 -1
- package/dist/native/vertical-bars-web.js +0 -411
- package/dist/native/vertical-bars-web.js.map +0 -1
- package/dist/native-integration/index.d.ts +0 -23
- package/dist/native-integration/index.d.ts.map +0 -1
- package/dist/native-integration/index.js.map +0 -1
- package/dist/native-integration/transition.d.ts +0 -5
- package/dist/native-integration/transition.d.ts.map +0 -1
- package/dist/native-integration/transition.js +0 -16
- package/dist/native-integration/transition.js.map +0 -1
- package/dist/vertical-bars.d.ts +0 -5
- package/dist/vertical-bars.js +0 -4
- package/docs/e2e-testing.md +0 -51
- package/docs/feedback.md +0 -65
- package/docs/iphone-duo-with-original-theme.md +0 -158
- package/docs/native-ui-shell.md +0 -325
- package/docs/vertical-bars.md +0 -300
- package/ios/Sources/IonicNativeUIShellPlugin/Components/ShellButton.swift +0 -71
- package/ios/Sources/IonicNativeUIShellPlugin/Components/ShellButtons.swift +0 -24
- package/ios/Sources/IonicNativeUIShellPlugin/Components/ShellComponents.swift +0 -18
- package/ios/Sources/IonicNativeUIShellPlugin/Components/ShellFab.swift +0 -80
- package/ios/Sources/IonicNativeUIShellPlugin/Components/ShellSearchController.swift +0 -432
- package/ios/Sources/IonicNativeUIShellPlugin/Components/ShellSegment.swift +0 -91
- package/ios/Sources/IonicNativeUIShellPlugin/Components/ShellTabBar.swift +0 -218
- package/ios/Sources/IonicNativeUIShellPlugin/Components/ShellVerticalBars.swift +0 -565
- package/ios/Sources/IonicNativeUIShellPlugin/Components/ShellVerticalSearch.swift +0 -118
- package/ios/Sources/IonicNativeUIShellPlugin/IonicNativeUIShellPlugin.swift +0 -502
- package/ios/Sources/IonicNativeUIShellPlugin/Overlays/ShellOverlayController.swift +0 -119
- package/ios/Sources/IonicNativeUIShellPlugin/Overlays/ShellOverlayHost.swift +0 -75
- package/ios/Sources/IonicNativeUIShellPlugin/Shared/ShellCrossfade.swift +0 -35
- package/ios/Sources/IonicNativeUIShellPlugin/Shared/ShellHost.swift +0 -8
- package/ios/Sources/IonicNativeUIShellPlugin/Shared/ShellRendering.swift +0 -27
- package/ios/Sources/IonicNativeUIShellPlugin/Shared/ShellSnapshot.swift +0 -135
- package/ios/Sources/IonicNativeUIShellPlugin/Shared/ShellVerticalBarsGeometry.swift +0 -52
- package/ios/Tests/IonicNativeUIShellPluginTests/ShellSnapshotTests.swift +0 -788
- package/src/native/components/index.ts +0 -53
- package/src/native/components/ion-back-button.ts +0 -19
- package/src/native/components/ion-button.ts +0 -24
- package/src/native/components/ion-buttons.ts +0 -38
- package/src/native/components/ion-fab.ts +0 -151
- package/src/native/components/ion-menu-button.ts +0 -24
- package/src/native/components/ion-segment.ts +0 -27
- package/src/native/components/ion-tab-bar.ts +0 -50
- package/src/native/components/searchable-tabs.ts +0 -459
- package/src/native/definitions.ts +0 -218
- package/src/native/foldable/fold-state.ts +0 -9
- package/src/native/foldable/types.ts +0 -4
- package/src/native/index.ts +0 -315
- package/src/native/overlays/content.ts +0 -51
- package/src/native/overlays/controller.ts +0 -349
- package/src/native/overlays/dialog-animation.ts +0 -53
- package/src/native/overlays/dismiss-animation.ts +0 -15
- package/src/native/overlays/focus.ts +0 -64
- package/src/native/overlays/popover.ts +0 -120
- package/src/native/overlays/styles.ts +0 -181
- package/src/native/prehide.ts +0 -265
- package/src/native/runtime.ts +0 -885
- package/src/native/shared/bridge.ts +0 -18
- package/src/native/shared/candidate.ts +0 -150
- package/src/native/shared/crossfade.ts +0 -67
- package/src/native/shared/dom.ts +0 -286
- package/src/native/shared/events.ts +0 -8
- package/src/native/shared/icons.ts +0 -57
- package/src/native/shared/modal.ts +0 -157
- package/src/native/shared/projection-id.ts +0 -2
- package/src/native/shared/vertical-bars-geometry.ts +0 -80
- package/src/native/vertical-bars-web-search.ts +0 -86
- package/src/native/vertical-bars-web.ts +0 -485
- package/src/native-integration/transition.ts +0 -15
- package/src/vertical-bars.ts +0 -11
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"vertical-bars-web.js","names":[],"sources":["../../src/native/vertical-bars-web.ts"],"sourcesContent":["import { createCrossfade, fadeMarker } from './shared/crossfade';\nimport { alignVerticalBarClone, verticalBarFrame } from './shared/vertical-bars-geometry';\nimport { createVerticalBarsWebSearch, searchProjectionClass } from './vertical-bars-web-search';\nimport { inVerticalBarsSurface, topModal } from './shared/modal';\nimport type { NativeUIShellHandle, NativeUIShellOptions, NativeUIShellStatus } from './definitions';\nimport { VERTICAL_BARS_TRANSITION_CANCELED } from '../native-integration';\nimport {\n activateProjectedElement,\n createVerticalBarsPageState,\n verticalBarsActionCandidate,\n verticalBarsBackCandidate,\n verticalBarsEnteringPage,\n verticalBarsToolbarActions,\n isExcluded,\n isShellDisabled,\n isVerticalBarsToolbarGroup,\n preferredVerticalBarsBack,\n verticalBarsOwned,\n marker,\n syncToolbarText,\n toolbarTextMarker,\n prehideOnlyMutation,\n prehiddenClass,\n unprojected,\n withoutPrehide,\n} from './shared/dom';\n\nconst backProjectionClass = 'ios-theme-vertical-bars-back-button-projection';\nconst toolbarProjectionClass = 'ios-theme-vertical-bars-toolbar-projection';\nconst readyClass = 'ios-theme-vertical-bars-toolbar-ready';\nconst toolbarControlSize = 46;\nconst toolbarControlGap = 10;\n\ninterface ToolbarProjection {\n source: HTMLIonButtonsElement;\n projection: HTMLElement;\n actions: { source: HTMLElement; projection: HTMLElement; previousAriaHidden: string | null }[];\n}\n\ninterface ToolbarSource {\n group: HTMLIonButtonsElement;\n actions: HTMLElement[];\n}\n\nexport const createVerticalBarsWebProjection = (\n doc: Document,\n options: NativeUIShellOptions,\n enabled: () => boolean = () => true,\n): NativeUIShellHandle => {\n const win = doc.defaultView!;\n const crossfade = createCrossfade(win);\n let incomingInstant: boolean | undefined;\n const toolbarEnabled = options.controls === undefined || options.controls.toolbar === true;\n let root: HTMLElement | undefined;\n let backSource: HTMLIonBackButtonElement | undefined;\n let backProjection: HTMLIonBackButtonElement | undefined;\n let previousBackAriaHidden: string | null = null;\n let toolbarProjections: ToolbarProjection[] = [];\n let suspended = 0;\n let stopped = false;\n let frame = 0;\n let updates = 0;\n let observingVerticalBars = false;\n const verticalBarsPages = createVerticalBarsPageState();\n let sourceObserver: MutationObserver | undefined;\n let waiters: (() => void)[] = [];\n const listeners = new AbortController();\n const verticalBarsRoot = () => doc.querySelector<HTMLElement>('ion-app.ios-theme-vertical-bars');\n const projectedSources = () =>\n [backSource, search.source, ...toolbarProjections.flatMap(({ actions }) => actions.map(({ source }) => source))].filter(\n (source): source is HTMLElement => !!source,\n );\n const isRendered = (element: HTMLElement) =>\n withoutPrehide(element, () => {\n const style = getComputedStyle(element);\n const rect = element.getBoundingClientRect();\n return element.isConnected && style.display !== 'none' && style.visibility === 'visible' && rect.width > 0 && rect.height > 0;\n });\n const search = createVerticalBarsWebSearch(\n doc,\n (element) =>\n (options.controls === undefined || options.controls.tabs === true) &&\n !!verticalBarsRoot()?.contains(element) &&\n inVerticalBarsSurface(element) &&\n !isExcluded(element, verticalBarsEnteringPage(element)) &&\n !isShellDisabled(element) &&\n !verticalBarsPages.isDeparted(element) &&\n isRendered(element),\n () => {\n void schedule();\n },\n );\n const inEligibleToolbar = (element: HTMLElement) =>\n !!verticalBarsRoot()?.contains(element) &&\n inVerticalBarsSurface(element) &&\n verticalBarsActionCandidate(element) &&\n !isExcluded(element, verticalBarsEnteringPage(element)) &&\n !verticalBarsPages.isDeparted(element);\n const isEligibleBack = (element: HTMLIonBackButtonElement) =>\n !!verticalBarsRoot()?.contains(element) &&\n inVerticalBarsSurface(element) &&\n verticalBarsOwned(element) &&\n verticalBarsBackCandidate(element) &&\n !isExcluded(element, verticalBarsEnteringPage(element)) &&\n !verticalBarsPages.isDeparted(element) &&\n unprojected(projectedSources(), () => isRendered(element));\n const pageOrder = (element: Element) => Array.from(doc.querySelectorAll('.ion-page')).indexOf(element.closest('.ion-page')!);\n const findBack = () => {\n const candidates = Array.from(doc.querySelectorAll<HTMLIonBackButtonElement>(`ion-back-button:not(.${backProjectionClass})`)).filter(\n isEligibleBack,\n );\n return preferredVerticalBarsBack(candidates, doc);\n };\n const findToolbarGroups = () => {\n const candidates = Array.from(doc.querySelectorAll<HTMLIonButtonsElement>(`ion-buttons:not(.${toolbarProjectionClass})`)).flatMap(\n (group): ToolbarSource[] => {\n const actions = verticalBarsToolbarActions(group).filter((action) => unprojected(projectedSources(), () => isRendered(action)));\n if (!actions.length) return [];\n if (!isVerticalBarsToolbarGroup(group)) return actions.filter(inEligibleToolbar).map((action) => ({ group, actions: [action] }));\n return inEligibleToolbar(group) && unprojected(projectedSources(), () => isRendered(group)) ? [{ group, actions }] : [];\n },\n );\n const highestPage = Math.max(...candidates.map(({ group }) => pageOrder(group)));\n return candidates.filter(({ group }) => pageOrder(group) === highestPage);\n };\n const isCurrentToolbarAction = (source: HTMLElement) => findToolbarGroups().some(({ actions }) => actions.includes(source));\n const restore = (retire?: (surface: HTMLElement) => void) => {\n search.restore(retire);\n if (backProjection) {\n if (retire) retire(backProjection);\n else backProjection.remove();\n }\n backProjection = undefined;\n if (backSource) {\n backSource.removeAttribute(marker);\n if (previousBackAriaHidden === null) backSource.removeAttribute('aria-hidden');\n else backSource.setAttribute('aria-hidden', previousBackAriaHidden);\n backSource.dispatchEvent(new CustomEvent('nativeUIShellChange'));\n }\n backSource = undefined;\n previousBackAriaHidden = null;\n for (const { projection, actions } of toolbarProjections) {\n if (retire) retire(projection);\n else projection.remove();\n for (const { source, previousAriaHidden } of actions) {\n source.removeAttribute(marker);\n if (previousAriaHidden === null) source.removeAttribute('aria-hidden');\n else source.setAttribute('aria-hidden', previousAriaHidden);\n source.dispatchEvent(new CustomEvent('nativeUIShellChange'));\n }\n }\n toolbarProjections = [];\n sourceObserver?.disconnect();\n sourceObserver = undefined;\n root?.classList.remove(readyClass);\n root = undefined;\n };\n const copyAttributes = (target: HTMLElement, original: HTMLElement) => {\n for (const name of ['id', 'slot', marker, 'aria-hidden']) target.removeAttribute(name);\n for (const attribute of Array.from(target.attributes))\n if (!['class', marker, fadeMarker].includes(attribute.name) && !original.hasAttribute(attribute.name))\n target.removeAttribute(attribute.name);\n for (const attribute of Array.from(original.attributes))\n if (!['id', 'slot', marker, 'aria-hidden'].includes(attribute.name)) target.setAttribute(attribute.name, attribute.value);\n target.classList.remove(prehiddenClass);\n };\n const syncBack = (target: HTMLIonBackButtonElement, original: HTMLIonBackButtonElement) => {\n copyAttributes(target, original);\n target.classList.add(backProjectionClass, 'ion-cloned-element');\n target.disabled = original.disabled;\n target.defaultHref = original.defaultHref;\n target.icon = original.icon;\n target.color = original.color;\n target.mode = original.mode;\n target.text = '';\n if (root) alignVerticalBarClone(target, original, root);\n };\n const syncAction = (target: HTMLElement, original: HTMLElement) => {\n copyAttributes(target, original);\n target.classList.add('ios-theme-vertical-bars-toolbar-action', 'ion-cloned-element');\n const label =\n original.getAttribute('aria-label') ??\n original.shadowRoot?.querySelector('[part~=native]')?.getAttribute('aria-label') ??\n original.textContent?.trim();\n if (label) target.setAttribute('aria-label', label);\n if ('disabled' in original) (target as HTMLIonButtonElement).disabled = (original as HTMLIonButtonElement).disabled;\n if (original.matches('ion-button')) {\n const icon = original.querySelector<HTMLElement>('ion-icon, svg');\n const clonedIcon = icon?.cloneNode(true) as HTMLElement | undefined;\n clonedIcon?.setAttribute('slot', 'icon-only');\n target.replaceChildren(...(clonedIcon ? [clonedIcon] : []));\n }\n };\n const sameSources = (back: HTMLIonBackButtonElement | undefined, groups: ToolbarSource[]) =>\n back === backSource &&\n groups.length === toolbarProjections.length &&\n groups.every(({ group, actions }, index) => {\n const current = toolbarProjections[index];\n return (\n group === current.source &&\n actions.length === current.actions.length &&\n actions.every((action, actionIndex) => action === current.actions[actionIndex].source)\n );\n });\n const syncExisting = () => {\n if (backSource && backProjection) syncBack(backProjection, backSource);\n let topOffset = backSource ? toolbarControlSize + toolbarControlGap : 0;\n for (const { projection, source, actions } of toolbarProjections) {\n if (actions.length === 1 && !projection.matches('ion-buttons')) syncAction(projection, actions[0].source);\n else copyAttributes(projection, source);\n projection.classList.add(toolbarProjectionClass, 'ion-cloned-element');\n projection.style.setProperty('--ios-theme-vertical-bars-toolbar-offset', `${topOffset}px`);\n if (projection.matches('ion-buttons')) actions.forEach(({ source: action, projection: clone }) => syncAction(clone, action));\n const frames = actions.map(({ source }) => verticalBarFrame(source));\n if (root && frames.every((frame) => !!frame)) {\n if (actions.length === 1) alignVerticalBarClone(projection, actions[0].source, root);\n else {\n const left = Math.min(...frames.map((frame) => frame!.x));\n const top = Math.min(...frames.map((frame) => frame!.y));\n const right = Math.max(...frames.map((frame) => frame!.x + frame!.width));\n const bottom = Math.max(...frames.map((frame) => frame!.y + frame!.height));\n const origin = root.getBoundingClientRect();\n Object.assign(projection.style, {\n left: `${left - origin.left}px`,\n top: `${top - origin.top}px`,\n right: 'auto',\n bottom: 'auto',\n width: `${right - left}px`,\n height: `${bottom - top}px`,\n padding: '0',\n });\n actions.forEach(({ source, projection: clone }) => alignVerticalBarClone(clone, source, projection));\n }\n }\n topOffset += actions.length * toolbarControlSize + toolbarControlGap;\n }\n };\n const project = (nextBack: HTMLIonBackButtonElement | undefined, groups: ToolbarSource[]) => {\n root = topModal(doc) ?? verticalBarsRoot()!;\n let topOffset = 0;\n if (nextBack) {\n backSource = nextBack;\n previousBackAriaHidden = nextBack.getAttribute('aria-hidden');\n backProjection = nextBack.cloneNode(false) as HTMLIonBackButtonElement;\n syncBack(backProjection, nextBack);\n backProjection.addEventListener(\n 'click',\n (event) => {\n event.preventDefault();\n event.stopImmediatePropagation();\n if (backSource && backSource === findBack()) activateProjectedElement(backSource);\n },\n { capture: true },\n );\n root.append(backProjection);\n nextBack.setAttribute(marker, '');\n nextBack.setAttribute('aria-hidden', 'true');\n nextBack.dispatchEvent(new CustomEvent('nativeUIShellChange'));\n topOffset = toolbarControlSize + toolbarControlGap;\n }\n for (const { group, actions: sources } of groups) {\n const projection = (sources.length === 1 ? sources[0] : group).cloneNode(false) as HTMLElement;\n if (sources.length === 1) syncAction(projection, sources[0]);\n else copyAttributes(projection, group);\n projection.classList.add(toolbarProjectionClass, 'ion-cloned-element');\n projection.style.setProperty('--ios-theme-vertical-bars-toolbar-offset', `${topOffset}px`);\n const actions = sources.map((source) => {\n const clone = sources.length === 1 ? projection : (source.cloneNode(false) as HTMLElement);\n if (sources.length > 1) syncAction(clone, source);\n clone.addEventListener(\n 'click',\n (event) => {\n event.preventDefault();\n event.stopImmediatePropagation();\n if (isCurrentToolbarAction(source)) activateProjectedElement(source);\n },\n { capture: true },\n );\n if (sources.length > 1) projection.append(clone);\n const previousAriaHidden = source.getAttribute('aria-hidden');\n source.setAttribute(marker, '');\n source.setAttribute('aria-hidden', 'true');\n source.dispatchEvent(new CustomEvent('nativeUIShellChange'));\n return { source, projection: clone, previousAriaHidden };\n });\n root.append(projection);\n toolbarProjections.push({ source: group, projection, actions });\n topOffset += actions.length * toolbarControlSize + toolbarControlGap;\n }\n syncExisting();\n if (nextBack || toolbarProjections.length) root.classList.add(readyClass);\n sourceObserver = new MutationObserver((records) => {\n if (\n records.some(\n (record) =>\n record.type !== 'attributes' || (![marker, 'aria-hidden'].includes(record.attributeName ?? '') && !prehideOnlyMutation(record)),\n )\n )\n schedule();\n });\n if (nextBack?.shadowRoot)\n sourceObserver.observe(nextBack.shadowRoot, { subtree: true, childList: true, attributes: true, attributeOldValue: true });\n for (const { group } of groups)\n sourceObserver.observe(group, { subtree: true, childList: true, attributes: true, attributeOldValue: true });\n };\n const reveal = () => {\n if (incomingInstant === undefined) return;\n for (const surface of [backProjection, ...toolbarProjections.map(({ projection }) => projection), search.surface])\n if (surface) crossfade.play(surface, false, incomingInstant);\n incomingInstant = undefined;\n };\n const performUpdate = () => {\n frame = 0;\n syncToolbarText(doc);\n const currentRoot = verticalBarsRoot();\n if (observingVerticalBars !== !!currentRoot) {\n observingVerticalBars = !!currentRoot;\n observer.disconnect();\n observer.observe(doc.documentElement, {\n subtree: true,\n childList: true,\n characterData: observingVerticalBars,\n attributes: true,\n attributeOldValue: true,\n attributeFilter: observingVerticalBars ? undefined : ['class'],\n });\n }\n if (stopped || suspended || !currentRoot || !enabled()) return restore();\n const nextBack = toolbarEnabled ? findBack() : undefined;\n const groups = toolbarEnabled ? findToolbarGroups() : [];\n const projectionRoot = topModal(doc) ?? currentRoot;\n search.update(projectionRoot);\n if (!nextBack && !groups.length && !search.source) return restore();\n if (root === (topModal(doc) ?? currentRoot) && sameSources(nextBack, groups)) {\n syncExisting();\n reveal();\n return;\n }\n restore();\n project(nextBack, groups);\n search.update(projectionRoot);\n reveal();\n updates++;\n };\n const update = () => {\n try {\n performUpdate();\n } catch {\n restore();\n } finally {\n const pending = waiters;\n waiters = [];\n pending.forEach((resolve) => resolve());\n }\n };\n const schedule = (): Promise<void> => {\n if (stopped) return Promise.resolve();\n // Release Web ownership before the native runtime can acknowledge its next frame.\n if (!enabled()) restore();\n const done = new Promise<void>((resolve) => waiters.push(resolve));\n if (!frame) frame = win.requestAnimationFrame(update);\n return done;\n };\n const observer = new MutationObserver((records) => {\n const insideProjection = (target: Node) => {\n const element = target instanceof Element ? target : target.parentNode instanceof Element ? target.parentNode : undefined;\n const rootNode = target.getRootNode();\n const shadowHost = rootNode instanceof ShadowRoot ? rootNode.host : undefined;\n return !![element, shadowHost].some((candidate) =>\n candidate?.closest(`.${backProjectionClass}, .${toolbarProjectionClass}, .${searchProjectionClass}`),\n );\n };\n const verticalBarsChanged = records.some(\n (record) =>\n (record.type === 'attributes' && (record.target as Element).matches('ion-app')) ||\n (record.type === 'childList' &&\n Array.from(record.addedNodes).some(\n (node) =>\n node instanceof Element &&\n (node.matches('ion-app.ios-theme-vertical-bars') || !!node.querySelector('ion-app.ios-theme-vertical-bars')),\n )),\n );\n if (\n (observingVerticalBars &&\n records.some(\n (record) =>\n record.attributeName !== marker &&\n record.attributeName !== toolbarTextMarker &&\n !prehideOnlyMutation(record) &&\n !insideProjection(record.target),\n )) ||\n (!observingVerticalBars && verticalBarsChanged && records.some((record) => !prehideOnlyMutation(record)))\n )\n schedule();\n });\n observer.observe(doc.documentElement, {\n subtree: true,\n childList: true,\n attributes: true,\n attributeOldValue: true,\n attributeFilter: ['class'],\n });\n const pageLifecycle = (event: Event) => {\n const page = event.target;\n if (!(page instanceof HTMLElement) || !page.matches('.ion-page')) return;\n if (event.type === VERTICAL_BARS_TRANSITION_CANCELED) {\n const entering = (event as CustomEvent<{ entering?: HTMLElement }>).detail?.entering;\n verticalBarsPages.cancel(entering, page);\n } else verticalBarsPages.lifecycle(event);\n if (verticalBarsRoot()) schedule();\n };\n for (const name of ['ionViewWillEnter', 'ionViewWillLeave', 'ionViewDidEnter', 'ionViewDidLeave'])\n doc.addEventListener(name, pageLifecycle, { capture: true, signal: listeners.signal });\n doc.addEventListener(VERTICAL_BARS_TRANSITION_CANCELED, pageLifecycle, { capture: true, signal: listeners.signal });\n for (const name of ['ionModalWillPresent', 'ionModalDidDismiss'])\n doc.addEventListener(name, schedule, { capture: true, signal: listeners.signal });\n win.addEventListener(\n 'nativeUIShellVerticalBarsAcquire',\n (event) => {\n const instant = (event as CustomEvent<{ instant: boolean }>).detail?.instant ?? true;\n incomingInstant = undefined;\n restore((surface) => {\n // Keep only the outgoing visual at its last rectangle after Web ownership\n // is released. The native acknowledgement transfers input exactly once.\n const box = surface.getBoundingClientRect();\n surface.style.setProperty('position', 'fixed', 'important');\n surface.style.setProperty('display', 'block', 'important');\n surface.style.setProperty('left', `${box.left}px`, 'important');\n surface.style.setProperty('top', `${box.top}px`, 'important');\n surface.style.setProperty('right', 'auto', 'important');\n surface.style.setProperty('bottom', 'auto', 'important');\n surface.style.setProperty('width', `${box.width}px`, 'important');\n surface.style.setProperty('height', `${box.height}px`, 'important');\n crossfade.retire(surface, instant);\n });\n },\n { signal: listeners.signal },\n );\n win.addEventListener(\n 'nativeUIShellVerticalBarsRelease',\n (event) => {\n incomingInstant = (event as CustomEvent<{ instant: boolean }>).detail.instant;\n void schedule();\n },\n { signal: listeners.signal },\n );\n win.addEventListener('resize', schedule, { signal: listeners.signal });\n win.addEventListener('nativeUIShellRefresh', schedule, { signal: listeners.signal });\n schedule();\n\n return {\n getStatus: (): NativeUIShellStatus => ({\n state: stopped ? 'stopped' : 'web',\n projected:\n Number(!!backSource) + Number(!!search.source) + toolbarProjections.reduce((count, group) => count + group.actions.length, 0),\n updates,\n }),\n async suspend() {\n suspended++;\n restore();\n let resumed = false;\n return {\n async resume() {\n if (resumed) return;\n resumed = true;\n suspended = Math.max(0, suspended - 1);\n if (!suspended) await schedule();\n },\n };\n },\n async destroy() {\n if (stopped) return;\n stopped = true;\n win.cancelAnimationFrame(frame);\n frame = 0;\n waiters.forEach((resolve) => resolve());\n waiters = [];\n observer.disconnect();\n sourceObserver?.disconnect();\n listeners.abort();\n crossfade.destroy();\n restore();\n },\n };\n};\n"],"mappings":"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"}
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
import { Animation } from "@ionic/core/components";
|
|
2
|
-
//#region src/native-integration/index.d.ts
|
|
3
|
-
export interface NativeUIShellIntegration {
|
|
4
|
-
suspend(scopes: HTMLElement[]): Promise<(canceled?: boolean) => void>;
|
|
5
|
-
search?(binding: NativeSearchBinding, active: boolean, focus?: boolean): Promise<boolean>;
|
|
6
|
-
}
|
|
7
|
-
export interface NativeSearchBinding {
|
|
8
|
-
tabBar: HTMLElement;
|
|
9
|
-
trigger: HTMLElement;
|
|
10
|
-
footer: HTMLElement;
|
|
11
|
-
active: boolean;
|
|
12
|
-
focused: boolean;
|
|
13
|
-
}
|
|
14
|
-
export declare const registerNativeSearch: (tabBar: HTMLElement, trigger: HTMLElement, footer: HTMLElement) => NativeSearchBinding;
|
|
15
|
-
export declare const getNativeSearchBindings: (doc: Document) => NativeSearchBinding[];
|
|
16
|
-
export declare const requestNativeSearch: (binding: NativeSearchBinding, active: boolean, focus?: boolean) => Promise<boolean>;
|
|
17
|
-
export declare const VERTICAL_BARS_TRANSITION_CANCELED = "iosThemeVerticalBarsTransitionCanceled";
|
|
18
|
-
export declare const setNativeUIShellIntegration: (doc: Document, runtime?: NativeUIShellIntegration) => void;
|
|
19
|
-
export declare const isNativeUIShell: (element: HTMLElement) => boolean;
|
|
20
|
-
export declare const suspendNativeUIShell: (scopes: HTMLElement[]) => Promise<(canceled?: boolean) => void>;
|
|
21
|
-
export declare const connectNativeUIShellTransition: (animation: Animation, entering: HTMLElement, leaving?: HTMLElement) => void;
|
|
22
|
-
//#endregion
|
|
23
|
-
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","names":[],"sources":["../../src/native-integration/index.ts"],"mappings":";;iBAGiB;EACf,QAAQ,QAAQ,gBAAgB,SAAS;EACzC,QAAQ,SAAS,qBAAqB,iBAAiB,kBAAkB;;iBAG1D;EACf,QAAQ;EACR,SAAS;EACT,QAAQ;EACR;EACA;;qBAKW,uBAAwB,QAAQ,aAAa,SAAS,aAAa,QAAQ,gBAAc;qBASzF,0BAA2B,KAAK,aAAW;qBAG3C,sBAA6B,SAAS,qBAAqB,iBAAiB,oBAAkB;qBAI9F;qBAEA,8BAA+B,KAAK,UAAU,UAAU;qBAKxD,kBAAmB,SAAS;qBAE5B,uBAA8B,QAAQ,kBAAgB,SAAS;qBAO/D,iCAAkC,WAAW,WAAW,UAAU,aAAa,UAAU"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../../src/native-integration/index.ts"],"sourcesContent":["/// <reference lib=\"es2021.weakref\" />\nimport type { Animation } from '@ionic/core/components';\n\nexport interface NativeUIShellIntegration {\n suspend(scopes: HTMLElement[]): Promise<(canceled?: boolean) => void>;\n search?(binding: NativeSearchBinding, active: boolean, focus?: boolean): Promise<boolean>;\n}\n\nexport interface NativeSearchBinding {\n tabBar: HTMLElement;\n trigger: HTMLElement;\n footer: HTMLElement;\n active: boolean;\n focused: boolean;\n}\n\nconst searches = new WeakMap<Document, WeakRef<NativeSearchBinding>[]>();\n\nexport const registerNativeSearch = (tabBar: HTMLElement, trigger: HTMLElement, footer: HTMLElement): NativeSearchBinding => {\n const binding = { tabBar, trigger, footer, active: false, focused: false };\n const entries = (searches.get(tabBar.ownerDocument) ?? []).filter((entry) => entry.deref() && entry.deref()?.footer !== footer);\n entries.push(new WeakRef(binding));\n searches.set(tabBar.ownerDocument, entries);\n tabBar.ownerDocument.defaultView?.dispatchEvent(new Event('nativeUIShellRefresh'));\n return binding;\n};\n\nexport const getNativeSearchBindings = (doc: Document): NativeSearchBinding[] =>\n (searches.get(doc) ?? []).flatMap((entry) => entry.deref() ?? []);\n\nexport const requestNativeSearch = async (binding: NativeSearchBinding, active: boolean, focus?: boolean): Promise<boolean> =>\n (await runtimes.get(binding.tabBar.ownerDocument)?.search?.(binding, active, focus)) ?? false;\n\nconst runtimes = new WeakMap<Document, NativeUIShellIntegration>();\nexport const VERTICAL_BARS_TRANSITION_CANCELED = 'iosThemeVerticalBarsTransitionCanceled';\n\nexport const setNativeUIShellIntegration = (doc: Document, runtime?: NativeUIShellIntegration) => {\n if (runtime) runtimes.set(doc, runtime);\n else runtimes.delete(doc);\n};\n\nexport const isNativeUIShell = (element: HTMLElement) => element.hasAttribute('data-native-ui-shell');\n\nexport const suspendNativeUIShell = async (scopes: HTMLElement[]): Promise<(canceled?: boolean) => void> =>\n (await runtimes.get(scopes[0]?.ownerDocument)?.suspend(scopes)) ?? (() => {});\n\nconst connectedTransitions = new WeakSet<Animation>();\nconst cancellationTransitions = new WeakSet<Animation>();\n\n/** Ionic write hooks cannot await the bridge. Gate playback, including interactive playback. */\nexport const connectNativeUIShellTransition = (animation: Animation, entering: HTMLElement, leaving?: HTMLElement) => {\n if (leaving && !cancellationTransitions.has(animation)) {\n cancellationTransitions.add(animation);\n animation.onFinish((step) => {\n if (step === 0) leaving.dispatchEvent(new CustomEvent(VERTICAL_BARS_TRANSITION_CANCELED, { bubbles: true, detail: { entering } }));\n });\n }\n if (connectedTransitions.has(animation) || !runtimes.has(entering.ownerDocument)) return;\n connectedTransitions.add(animation);\n const scopes = leaving ? [entering, leaving] : [entering];\n const play = animation.play.bind(animation);\n const progressStart = animation.progressStart.bind(animation);\n const progressStep = animation.progressStep.bind(animation);\n const progressEnd = animation.progressEnd.bind(animation);\n const destroy = animation.destroy.bind(animation);\n let release: ((canceled?: boolean) => void) | undefined;\n let preparation: Promise<void> | undefined;\n let disposed = false;\n let interactiveReady = false;\n let step: number | undefined;\n let end: Parameters<Animation['progressEnd']> | undefined;\n const prepare = () =>\n (preparation ??= suspendNativeUIShell(scopes).then((resume) => {\n if (disposed) resume(true);\n else release = resume;\n }));\n const finish = (canceled = false) => {\n release?.(canceled);\n release = undefined;\n preparation = undefined;\n };\n animation.onFinish((step) => finish(step === 0));\n animation.play = async (options) => {\n await prepare();\n if (disposed) return;\n try {\n await play(options);\n } finally {\n finish();\n }\n };\n animation.progressStart = (...args) => {\n interactiveReady = false;\n step = undefined;\n end = undefined;\n void prepare().then(() => {\n if (disposed) return;\n progressStart(...args);\n interactiveReady = true;\n if (step !== undefined) progressStep(step);\n if (end) progressEnd(...end);\n });\n return animation;\n };\n animation.progressStep = (value) => {\n step = value;\n if (interactiveReady) progressStep(value);\n return animation;\n };\n animation.progressEnd = (...args) => {\n end = args;\n if (interactiveReady) progressEnd(...args);\n return animation;\n };\n animation.destroy = (...args) => {\n disposed = true;\n finish(true);\n return destroy(...args);\n };\n};\n"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"transition.d.ts","names":[],"sources":["../../src/native-integration/transition.ts"],"mappings":";;qBAQa,8BACV,SAAS,qBAAmB"}
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
import { connectNativeUIShellTransition } from "./index.js";
|
|
2
|
-
//#region src/native-integration/transition.ts
|
|
3
|
-
/**
|
|
4
|
-
* Keeps an existing Ionic navigation animation and coordinates native control handoff.
|
|
5
|
-
* Register the returned builder as navAnimation. Do not use it for overlay animations.
|
|
6
|
-
* The builder must return a fresh Animation for each navigation; Ionic destroys it afterward.
|
|
7
|
-
*/
|
|
8
|
-
const withNativeUIShellTransition = (builder) => (baseEl, opts) => {
|
|
9
|
-
const animation = builder(baseEl, opts);
|
|
10
|
-
if (opts?.enteringEl) connectNativeUIShellTransition(animation, opts.enteringEl, opts.leavingEl);
|
|
11
|
-
return animation;
|
|
12
|
-
};
|
|
13
|
-
//#endregion
|
|
14
|
-
export { withNativeUIShellTransition };
|
|
15
|
-
|
|
16
|
-
//# sourceMappingURL=transition.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"transition.js","names":[],"sources":["../../src/native-integration/transition.ts"],"sourcesContent":["import type { AnimationBuilder } from '@ionic/core/components';\nimport { connectNativeUIShellTransition } from './index';\n\n/**\n * Keeps an existing Ionic navigation animation and coordinates native control handoff.\n * Register the returned builder as navAnimation. Do not use it for overlay animations.\n * The builder must return a fresh Animation for each navigation; Ionic destroys it afterward.\n */\nexport const withNativeUIShellTransition =\n (builder: AnimationBuilder): AnimationBuilder =>\n (baseEl, opts) => {\n const animation = builder(baseEl, opts);\n if (opts?.enteringEl) connectNativeUIShellTransition(animation, opts.enteringEl, opts.leavingEl);\n return animation;\n };\n"],"mappings":";;;;;;;AAQA,MAAa,+BACV,aACA,QAAQ,SAAS;CAChB,MAAM,YAAY,QAAQ,QAAQ,IAAI;CACtC,IAAI,MAAM,YAAY,+BAA+B,WAAW,KAAK,YAAY,KAAK,SAAS;CAC/F,OAAO;AACT"}
|
package/dist/vertical-bars.d.ts
DELETED
|
@@ -1,5 +0,0 @@
|
|
|
1
|
-
import { withNativeUIShellTransition } from "./native-integration/transition.js";
|
|
2
|
-
import { NativeUIShellStatus, NativeUIShellSuspension, VerticalBarEdge, VerticalControlAreaHandle, VerticalControlAreaOptions } from "./native/definitions.js";
|
|
3
|
-
import { applyFoldStateClasses } from "./native/foldable/fold-state.js";
|
|
4
|
-
import { IonicNativeUIShell, enableVerticalControlArea, setVerticalControlAreaPlacement } from "./native/index.js";
|
|
5
|
-
export { IonicNativeUIShell, type NativeUIShellStatus, type NativeUIShellSuspension, type VerticalBarEdge, type VerticalControlAreaHandle, type VerticalControlAreaOptions, applyFoldStateClasses, enableVerticalControlArea, setVerticalControlAreaPlacement, withNativeUIShellTransition };
|
package/dist/vertical-bars.js
DELETED
|
@@ -1,4 +0,0 @@
|
|
|
1
|
-
import { withNativeUIShellTransition } from "./native-integration/transition.js";
|
|
2
|
-
import { IonicNativeUIShell, enableVerticalControlArea, setVerticalControlAreaPlacement } from "./native/index.js";
|
|
3
|
-
import { applyFoldStateClasses } from "./native/foldable/fold-state.js";
|
|
4
|
-
export { IonicNativeUIShell, applyFoldStateClasses, enableVerticalControlArea, setVerticalControlAreaPlacement, withNativeUIShellTransition };
|
package/docs/e2e-testing.md
DELETED
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
# E2E screenshot testing
|
|
2
|
-
|
|
3
|
-
This maintainer guide explains how to run the Playwright visual-regression suite for the iOS26 demo. The suite covers every entry declared in `demo/e2e/screenshot.spec.ts` in both light and dark modes. Overlay variants are generated from the shared arrays in `demo/src/app/overlay-types.ts`.
|
|
4
|
-
|
|
5
|
-
## Run the suite
|
|
6
|
-
|
|
7
|
-
Install the demo dependencies first:
|
|
8
|
-
|
|
9
|
-
```bash
|
|
10
|
-
cd demo
|
|
11
|
-
npm install
|
|
12
|
-
```
|
|
13
|
-
|
|
14
|
-
Then choose the command that matches the task:
|
|
15
|
-
|
|
16
|
-
```bash
|
|
17
|
-
npm run test:e2e # Run the suite
|
|
18
|
-
npm run test:e2e:ui # Open Playwright UI mode
|
|
19
|
-
npm run test:e2e:debug # Run with the Playwright debugger
|
|
20
|
-
npm run test:e2e:update # Regenerate intentional baseline changes
|
|
21
|
-
```
|
|
22
|
-
|
|
23
|
-
To reproduce the Linux environment used by CI, run the Docker variants from `demo/`:
|
|
24
|
-
|
|
25
|
-
```bash
|
|
26
|
-
npm run test:e2e:docker
|
|
27
|
-
npm run test:e2e:docker:update
|
|
28
|
-
```
|
|
29
|
-
|
|
30
|
-
The Docker commands use the Playwright image pinned in `demo/package.json`.
|
|
31
|
-
|
|
32
|
-
## Review a failure
|
|
33
|
-
|
|
34
|
-
A screenshot mismatch can be a regression or an intentional visual change. Before updating a baseline:
|
|
35
|
-
|
|
36
|
-
1. Inspect the actual, expected, and diff images in `demo/test-results/`.
|
|
37
|
-
2. Check the affected route in both light and dark modes.
|
|
38
|
-
3. Confirm that the component change is intentional.
|
|
39
|
-
4. Regenerate the baseline with `npm run test:e2e:update`, or use the Docker variant when matching CI rendering.
|
|
40
|
-
|
|
41
|
-
The HTML report is written to `demo/playwright-report/` and can be opened with:
|
|
42
|
-
|
|
43
|
-
```bash
|
|
44
|
-
npx playwright show-report
|
|
45
|
-
```
|
|
46
|
-
|
|
47
|
-
## Add coverage
|
|
48
|
-
|
|
49
|
-
When adding a demo route or overlay variant, update `demo/e2e/screenshot.spec.ts` and regenerate the relevant baselines. Commit baseline changes only after reviewing the visual diff.
|
|
50
|
-
|
|
51
|
-
Pull requests targeting `main` or `ios26` run the E2E workflow in `.github/workflows/e2e-pull_request.yml`; pushes to either branch run `.github/workflows/e2e-main.yml`. GitHub builds the pull-request merge ref against its target branch, so an `ios26` pull request starts with the iOS 26 snapshots. Snapshot changes intentionally committed by the pull request are then tested as part of that merge result. The `/update-screenshots` command loads its update procedure from the pull request's target branch and commits the regenerated screenshots to the pull request branch.
|
package/docs/feedback.md
DELETED
|
@@ -1,65 +0,0 @@
|
|
|
1
|
-
# Feature requests for Ionic Framework
|
|
2
|
-
|
|
3
|
-
## feat(): change `--knob-handle-size` to `--knob-handle-width` / `--knob-handle-height`
|
|
4
|
-
|
|
5
|
-
Currently, only square sizes are taken into consideration.
|
|
6
|
-
Overwriting the `knob` itself is possible, but `--knob-handle-size` cannot be ignored as it plays a crucial role in determining the `top` and `margin-inline-start` of `div.range-knob-handle`.
|
|
7
|
-
|
|
8
|
-
## feat(): ion-config new property for `collapse`
|
|
9
|
-
|
|
10
|
-
Currently, `collapse` behavior is automatically enabled in iOS mode, but this should be configurable through `ion-config` for better control.
|
|
11
|
-
|
|
12
|
-
ex:
|
|
13
|
-
|
|
14
|
-
```typescript
|
|
15
|
-
export interface IonicConfig {
|
|
16
|
-
...,
|
|
17
|
-
collapseLargeTitle: {
|
|
18
|
-
ios: boolean;
|
|
19
|
-
md: boolean;
|
|
20
|
-
ionic: boolean;
|
|
21
|
-
},
|
|
22
|
-
collapseBackButtonAnimation: {
|
|
23
|
-
ios: boolean;
|
|
24
|
-
md: boolean;
|
|
25
|
-
ionic: boolean;
|
|
26
|
-
},
|
|
27
|
-
}
|
|
28
|
-
```
|
|
29
|
-
|
|
30
|
-
## feat(): add native shadow-part for design
|
|
31
|
-
|
|
32
|
-
### native-inner(or item-inner) part to ion-item
|
|
33
|
-
|
|
34
|
-
The styling for `ion-item[lines=inset]` is applied to `.item-inner`, which cannot be styled directly. This limitation means that for iOS 27 styling, I can only modify the border-bottom style through `::part(native)` with padding-right, preventing me from utilizing the full right side of `ion-item`. Adding `::part(native-inner)` would increase styling flexibility.
|
|
35
|
-
|
|
36
|
-
```diff
|
|
37
|
-
<ion-item>
|
|
38
|
-
<button type="button" class="item-native" part="native">
|
|
39
|
-
- <div class="item-inner">
|
|
40
|
-
+ <div class="item-inner" part="native-inner">
|
|
41
|
-
...
|
|
42
|
-
</div>
|
|
43
|
-
</button>
|
|
44
|
-
</ion-item>
|
|
45
|
-
```
|
|
46
|
-
|
|
47
|
-
### native part to ion-toast
|
|
48
|
-
|
|
49
|
-
Resolved: https://github.com/ionic-team/ionic-framework/pull/30992#event-23306774962
|
|
50
|
-
|
|
51
|
-
## docs(): Naming conventions for Ionic theme classes
|
|
52
|
-
|
|
53
|
-
Resolved.
|
|
54
|
-
|
|
55
|
-
### should disable ion-back-button Animation
|
|
56
|
-
|
|
57
|
-
Resolved: by created https://github.com/rdlabo-dev/ionic-theme-ios27/tree/main/src/transition
|
|
58
|
-
|
|
59
|
-
## feat(): ion-content[fullscreen=true] will have .content-fullscreen class
|
|
60
|
-
|
|
61
|
-
Resolved: https://github.com/ionic-team/ionic-framework/pull/30926
|
|
62
|
-
|
|
63
|
-
## feat(): add .range-knob-min and .range-knob-max directly to ion-range
|
|
64
|
-
|
|
65
|
-
Resolved: https://github.com/ionic-team/ionic-framework/pull/30932
|
|
@@ -1,158 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
title: iPhone Duo with your existing theme (preview)
|
|
3
|
-
---
|
|
4
|
-
|
|
5
|
-
# iPhone Duo with your existing theme (preview)
|
|
6
|
-
|
|
7
|
-
Add a vertical navigation area to your Ionic app while keeping its existing theme. Tabs and supported toolbar actions move to the side of the screen; your content and horizontal controls keep their current appearance. Both Ionic `ios` and `md` modes are supported.
|
|
8
|
-
|
|
9
|
-
**Try it in Chrome first.** You can preview the layout with Web controls before setting up an iPhone Duo or an iOS build. On supported Capacitor iOS, the same Ionic markup supplies native SwiftUI controls in the system rail.
|
|
10
|
-
|
|
11
|
-
Available in `1.2.0` as a **preview** feature. APIs and supported behavior may change.
|
|
12
|
-
|
|
13
|
-
## Try it in your existing Ionic app
|
|
14
|
-
|
|
15
|
-
### 1. Install and load the standalone stylesheet
|
|
16
|
-
|
|
17
|
-
This guide assumes an existing Ionic app with Ionic `>=8.8.1 <10` and Capacitor Core `>=8 <9`. Keep your existing Capacitor 8 installation. If your app uses another Capacitor major, migrate its Core, CLI, and platform packages together before following this guide. For a Web-only app without Capacitor, also install `@capacitor/core@^8`; the JavaScript entry point needs it even in Chrome.
|
|
18
|
-
|
|
19
|
-
```bash
|
|
20
|
-
npm install @rdlabo/ionic-theme-ios27@1.2.0
|
|
21
|
-
```
|
|
22
|
-
|
|
23
|
-
Keep your existing theme imports. Add this to your global Sass file:
|
|
24
|
-
|
|
25
|
-
```scss
|
|
26
|
-
@use '@rdlabo/ionic-theme-ios27/dist/css/vertical-bars.css';
|
|
27
|
-
```
|
|
28
|
-
|
|
29
|
-
The standalone JavaScript entry point needs `@capacitor/core` even in Chrome. The iOS 27 theme stylesheets are not required.
|
|
30
|
-
|
|
31
|
-
### 2. Opt your app into the side layout
|
|
32
|
-
|
|
33
|
-
Add the class to your existing app root and keep the content inside it:
|
|
34
|
-
|
|
35
|
-
```html
|
|
36
|
-
<ion-app class="ios-theme-vertical-bars">
|
|
37
|
-
<!-- Keep your existing pages, tabs, and toolbar controls here. -->
|
|
38
|
-
</ion-app>
|
|
39
|
-
```
|
|
40
|
-
|
|
41
|
-
The preview reserves `80px` on the physical right. To preview the left side, also add `ios-theme-vertical-bars-left`.
|
|
42
|
-
|
|
43
|
-
### 3. Connect your navigation animation
|
|
44
|
-
|
|
45
|
-
Configure `navAnimation` before Ionic initializes. Starting the rail runtime does not register this option. The adapter waits for native control retirement and coordinates swipe progress and cancellation while keeping your existing animation.
|
|
46
|
-
|
|
47
|
-
#### Keep Ionic's default animation
|
|
48
|
-
|
|
49
|
-
If you have not configured `navAnimation`, wrap Ionic's standard builders. Select the builder from Ionic's transition `mode` so both `ios` and `md` keep their usual animation:
|
|
50
|
-
|
|
51
|
-
```ts
|
|
52
|
-
import { iosTransitionAnimation, mdTransitionAnimation, type AnimationBuilder } from '@ionic/core';
|
|
53
|
-
import { withNativeUIShellTransition } from '@rdlabo/ionic-theme-ios27/vertical-bars';
|
|
54
|
-
|
|
55
|
-
const defaultTransition: AnimationBuilder = (baseEl, opts) =>
|
|
56
|
-
(opts.mode === 'ios' ? iosTransitionAnimation : mdTransitionAnimation)(baseEl, opts);
|
|
57
|
-
|
|
58
|
-
const ionicConfig = {
|
|
59
|
-
navAnimation: withNativeUIShellTransition(defaultTransition),
|
|
60
|
-
};
|
|
61
|
-
```
|
|
62
|
-
|
|
63
|
-
Merge this option into your existing Ionic configuration before initialization: pass it to Angular's `provideIonicAngular()`, React's `setupIonicReact()`, or Vue's `IonicVue` plugin options. Keep your existing theme stylesheet imports. No iOS 27 theme stylesheet is required.
|
|
64
|
-
|
|
65
|
-
#### Use this package's iOS animation
|
|
66
|
-
|
|
67
|
-
If you already use the iOS 27 transition, keep this configuration. It includes the native adapter and excludes the horizontal back-button effect in vertical layouts; no additional wrapper is needed. Importing this JavaScript entry point does not load the theme stylesheets.
|
|
68
|
-
|
|
69
|
-
```ts
|
|
70
|
-
import { iosTransitionAnimation } from '@rdlabo/ionic-theme-ios27';
|
|
71
|
-
|
|
72
|
-
const ionicConfig = {
|
|
73
|
-
navAnimation: iosTransitionAnimation,
|
|
74
|
-
};
|
|
75
|
-
```
|
|
76
|
-
|
|
77
|
-
Apply this option to your existing iOS-mode configuration and keep your MD configuration.
|
|
78
|
-
|
|
79
|
-
#### Keep your custom animation
|
|
80
|
-
|
|
81
|
-
If your app uses another builder for `navAnimation`, wrap it:
|
|
82
|
-
|
|
83
|
-
```ts
|
|
84
|
-
import type { AnimationBuilder } from '@ionic/core';
|
|
85
|
-
import { withNativeUIShellTransition } from '@rdlabo/ionic-theme-ios27/vertical-bars';
|
|
86
|
-
|
|
87
|
-
// Pass the animation builder your app already uses.
|
|
88
|
-
const configureNavigation = (existingTransition: AnimationBuilder) => ({
|
|
89
|
-
navAnimation: withNativeUIShellTransition(existingTransition),
|
|
90
|
-
});
|
|
91
|
-
```
|
|
92
|
-
|
|
93
|
-
The adapter returns the original `Animation`, preserving its effects, duration, and easing. Use it only for navigation, not modal or popover animations. The builder must return a fresh `Animation` for each navigation; Ionic destroys it after the transition. Keep lifecycle events for control registration and transitions without animation.
|
|
94
|
-
|
|
95
|
-
The adapter keeps the builder's animation targets, including any horizontal back-button effect. If you need the iOS 27 transition with that effect excluded in vertical layouts, use `iosTransitionAnimation` from `@rdlabo/ionic-theme-ios27` as your `navAnimation` instead. It already includes the adapter, so no wrapper is needed.
|
|
96
|
-
|
|
97
|
-
### 4. Start the controls after the app root is mounted
|
|
98
|
-
|
|
99
|
-
Call this once from your application startup after `ion-app` exists in the DOM:
|
|
100
|
-
|
|
101
|
-
```ts
|
|
102
|
-
import { enableVerticalControlArea } from '@rdlabo/ionic-theme-ios27/vertical-bars';
|
|
103
|
-
|
|
104
|
-
const rail = await enableVerticalControlArea();
|
|
105
|
-
```
|
|
106
|
-
|
|
107
|
-
**What you should see:** your existing tab bar moves to the side, and fixed-toolbar buttons with an `ion-icon` or SVG using `slot="icon-only"` appear there too. Content keeps its existing theme and leaves room for the controls. The Web tab rail displays icons; pressing and dragging reveals tab labels.
|
|
108
|
-
|
|
109
|
-
Use your existing Ionic click handlers, routing, and form associations. All button fills (`default`, `clear`, `solid`, and `outline`) use the same `icon-only` rule, including submit buttons. Actions without that slot remain horizontal. Add `.ios-theme-horizontal-only` to an `ion-buttons` group or individual `ion-button` to keep an action in the horizontal toolbar.
|
|
110
|
-
|
|
111
|
-
When the application owner is disposed, call `await rail.destroy()` to restore the original controls and release the runtime. If you already use `enableNativeUIShell()`, keep that runtime and follow the [shared placement guide](https://docs.rdlabo.dev/projects/ionic-theme-ios27/docs/iphone-duo#project-controls-into-the-rail).
|
|
112
|
-
|
|
113
|
-
### Optional: choose native button appearance
|
|
114
|
-
|
|
115
|
-
`buttonProjection` and local projection settings are available in `1.2.0`. See [Choose button appearance](https://docs.rdlabo.dev/projects/ionic-theme-ios27/docs/vertical-bars#choose-button-appearance) for availability and migration details.
|
|
116
|
-
|
|
117
|
-
The new default is `system`: SwiftUI styles vertical buttons and tints their icons. If your existing theme should supply their fill and colors, use:
|
|
118
|
-
|
|
119
|
-
```ts
|
|
120
|
-
const rail = await enableVerticalControlArea({ buttonProjection: 'source', buttonDefaultFill: 'solid' });
|
|
121
|
-
```
|
|
122
|
-
|
|
123
|
-
The `solid` default suits ordinary Ionic buttons. Buttons inside `ion-buttons` still default to clear; set `fill="solid"` explicitly to project their background. For one-off exceptions, use the [local projection settings](https://docs.rdlabo.dev/projects/ionic-theme-ios27/docs/vertical-bars#override-individual-buttons-or-groups). These settings affect native vertical buttons only; Web clones keep their existing appearance.
|
|
124
|
-
|
|
125
|
-
### If the preview does not appear
|
|
126
|
-
|
|
127
|
-
| What you see | What to check |
|
|
128
|
-
| --- | --- |
|
|
129
|
-
| No space at the side | Load `vertical-bars.css` and put the class on `ion-app`. |
|
|
130
|
-
| Space appears, but controls stay horizontal | Start `enableVerticalControlArea()` after mounting the app root. Use existing tabs or `slot="icon-only"` actions in a fixed header/footer toolbar. |
|
|
131
|
-
| One action stays horizontal | Check for `slot="icon-only"` on the icon and a fixed toolbar outside scrolling content. Explicitly excluded controls and controls in centered modals stay horizontal; `fill` and `type="submit"` do not prevent movement. See [control requirements](https://docs.rdlabo.dev/projects/ionic-theme-ios27/docs/vertical-bars#toolbar-actions). |
|
|
132
|
-
|
|
133
|
-
## Connect an iPhone Duo
|
|
134
|
-
|
|
135
|
-
Install [`@erkamyaman/capacitor-foldable`](https://github.com/erkamyaman/capacitor-foldable) for device state:
|
|
136
|
-
|
|
137
|
-
```bash
|
|
138
|
-
npm install @erkamyaman/capacitor-foldable
|
|
139
|
-
npx cap sync ios
|
|
140
|
-
```
|
|
141
|
-
|
|
142
|
-
Use Capacitor 8.5 or later and build with Xcode 27.1 or newer for actual rail placement and hinge posture on iOS 27.1. Native UI Shell uses Swift Package Manager; existing CocoaPods apps can follow [Native UI Shell setup](https://docs.rdlabo.dev/projects/ionic-theme-ios27/docs/native-ui-shell#enable-the-shell). Keep this package's `vertical-bars.css`; the device plugin's `ionic-tabs.css` is not needed with our rail projection.
|
|
143
|
-
|
|
144
|
-
Replace the browser-only startup with the [device placement setup](https://docs.rdlabo.dev/projects/ionic-theme-ios27/docs/iphone-duo#project-controls-into-the-rail). That setup sends initial values and `barPlacementChange` events to `setVerticalControlAreaPlacement({ edge: verticalBarEdge, nativeEdge: verticalBarEdge, inset })`, with subscriptions and cleanup kept in your application.
|
|
145
|
-
|
|
146
|
-
Pass both the requested and native edge with Foldable's measured inset. The placement API resolves RTL. A null edge restores the ordinary layout.
|
|
147
|
-
|
|
148
|
-
On supported iOS, controls in the rail use native SwiftUI rendering; your custom Web styling still applies to ordinary content and horizontal controls. Web and Android use Web clones.
|
|
149
|
-
|
|
150
|
-
## Use hinge posture without projecting controls
|
|
151
|
-
|
|
152
|
-
If your existing theme needs only a posture-driven split pane or a layout switch, do not start a projection runtime or add `.ios-theme-vertical-bars`. Pass `Foldable.getFoldState()` results and `foldStateChange` events to `applyFoldStateClasses(root, fold)`, removing the listener when finished. There is no separate start/stop monitoring call.
|
|
153
|
-
|
|
154
|
-
See [Read the device layout](https://docs.rdlabo.dev/projects/ionic-theme-ios27/docs/iphone-duo#read-the-device-layout) for the subscription example, null values, and monitoring lifetime. See [Adapt the split pane](https://docs.rdlabo.dev/projects/ionic-theme-ios27/docs/iphone-duo#adapt-the-split-pane) for the opt-in width rules and half-open state.
|
|
155
|
-
|
|
156
|
-
## Shared layout rules and API
|
|
157
|
-
|
|
158
|
-
Safe-area handling, overlays, RTL, control eligibility, Web simulation, and the handle API are documented in [Vertical Bars](https://docs.rdlabo.dev/projects/ionic-theme-ios27/docs/vertical-bars). Those rules apply to this standalone setup too.
|