mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2025-11-08 15:51:16 +08:00
feat(app): move ion-app init logic to initialize function (#29930)
Issue number: resolves internal --------- <!-- Please do not submit updates to dependencies unless it fixes an issue. --> <!-- Please try to limit your pull request to one type (bugfix, feature, etc). Submit multiple pull requests if needed. --> ## What is the current behavior? <!-- Please describe the current behavior that you are modifying. --> Some app functionality (like focus management, keyboard utils, and shimming) are tied to the `ion-app` which requires all Ionic applications to have a root `ion-app` element. ## What is the new behavior? <!-- Please describe the behavior or changes that are being added by this PR. --> `ion-app` specific init functionality is moved to the global `initialize` function ## Does this introduce a breaking change? - [ ] Yes - [x] No <!-- If this introduces a breaking change: 1. Describe the impact and migration path for existing applications below. 2. Update the BREAKING.md file with the breaking change. 3. Add "BREAKING CHANGE: [...]" to the commit description when merging. See https://github.com/ionic-team/ionic-framework/blob/main/docs/CONTRIBUTING.md#footer for more information. --> Although it is not expected that this introduces a breaking change, these changes were introduced on the `next` branch as a precaution. ## Other information <!-- Any other information that is important to this PR such as screenshots of how the component looks before and after the change. --> NOTE: This is **NOT** a recommended pattern for Ionic applications and was created for a specific internal use case --------- Co-authored-by: Brandy Carney <brandyscarney@users.noreply.github.com>
This commit is contained in:
@ -884,7 +884,7 @@ ion-infinite-scroll-content,prop,theme,"ios" | "md" | "ionic",undefined,false,fa
|
|||||||
|
|
||||||
ion-input,scoped
|
ion-input,scoped
|
||||||
ion-input,prop,autocapitalize,string,'off',false,false
|
ion-input,prop,autocapitalize,string,'off',false,false
|
||||||
ion-input,prop,autocomplete,"name" | "email" | "tel" | "url" | "on" | "off" | "honorific-prefix" | "given-name" | "additional-name" | "family-name" | "honorific-suffix" | "nickname" | "username" | "new-password" | "current-password" | "one-time-code" | "organization-title" | "organization" | "street-address" | "address-line1" | "address-line2" | "address-line3" | "address-level4" | "address-level3" | "address-level2" | "address-level1" | "country" | "country-name" | "postal-code" | "cc-name" | "cc-given-name" | "cc-additional-name" | "cc-family-name" | "cc-number" | "cc-exp" | "cc-exp-month" | "cc-exp-year" | "cc-csc" | "cc-type" | "transaction-currency" | "transaction-amount" | "language" | "bday" | "bday-day" | "bday-month" | "bday-year" | "sex" | "tel-country-code" | "tel-national" | "tel-area-code" | "tel-local" | "tel-extension" | "impp" | "photo",'off',false,false
|
ion-input,prop,autocomplete,"name" | "url" | "off" | "on" | "additional-name" | "address-level1" | "address-level2" | "address-level3" | "address-level4" | "address-line1" | "address-line2" | "address-line3" | "bday-day" | "bday-month" | "bday-year" | "cc-csc" | "cc-exp" | "cc-exp-month" | "cc-exp-year" | "cc-family-name" | "cc-given-name" | "cc-name" | "cc-number" | "cc-type" | "country" | "country-name" | "current-password" | "family-name" | "given-name" | "honorific-prefix" | "honorific-suffix" | "new-password" | "one-time-code" | "organization" | "postal-code" | "street-address" | "transaction-amount" | "transaction-currency" | "username" | "email" | "tel" | "tel-area-code" | "tel-country-code" | "tel-extension" | "tel-local" | "tel-national" | "nickname" | "organization-title" | "cc-additional-name" | "language" | "bday" | "sex" | "impp" | "photo",'off',false,false
|
||||||
ion-input,prop,autocorrect,"off" | "on",'off',false,false
|
ion-input,prop,autocorrect,"off" | "on",'off',false,false
|
||||||
ion-input,prop,autofocus,boolean,false,false,false
|
ion-input,prop,autofocus,boolean,false,false,false
|
||||||
ion-input,prop,clearInput,boolean,false,false,false
|
ion-input,prop,clearInput,boolean,false,false,false
|
||||||
@ -1867,7 +1867,7 @@ ion-row,prop,theme,"ios" | "md" | "ionic",undefined,false,false
|
|||||||
ion-searchbar,scoped
|
ion-searchbar,scoped
|
||||||
ion-searchbar,prop,animated,boolean,false,false,false
|
ion-searchbar,prop,animated,boolean,false,false,false
|
||||||
ion-searchbar,prop,autocapitalize,string,'off',false,false
|
ion-searchbar,prop,autocapitalize,string,'off',false,false
|
||||||
ion-searchbar,prop,autocomplete,"name" | "email" | "tel" | "url" | "on" | "off" | "honorific-prefix" | "given-name" | "additional-name" | "family-name" | "honorific-suffix" | "nickname" | "username" | "new-password" | "current-password" | "one-time-code" | "organization-title" | "organization" | "street-address" | "address-line1" | "address-line2" | "address-line3" | "address-level4" | "address-level3" | "address-level2" | "address-level1" | "country" | "country-name" | "postal-code" | "cc-name" | "cc-given-name" | "cc-additional-name" | "cc-family-name" | "cc-number" | "cc-exp" | "cc-exp-month" | "cc-exp-year" | "cc-csc" | "cc-type" | "transaction-currency" | "transaction-amount" | "language" | "bday" | "bday-day" | "bday-month" | "bday-year" | "sex" | "tel-country-code" | "tel-national" | "tel-area-code" | "tel-local" | "tel-extension" | "impp" | "photo",'off',false,false
|
ion-searchbar,prop,autocomplete,"name" | "url" | "off" | "on" | "additional-name" | "address-level1" | "address-level2" | "address-level3" | "address-level4" | "address-line1" | "address-line2" | "address-line3" | "bday-day" | "bday-month" | "bday-year" | "cc-csc" | "cc-exp" | "cc-exp-month" | "cc-exp-year" | "cc-family-name" | "cc-given-name" | "cc-name" | "cc-number" | "cc-type" | "country" | "country-name" | "current-password" | "family-name" | "given-name" | "honorific-prefix" | "honorific-suffix" | "new-password" | "one-time-code" | "organization" | "postal-code" | "street-address" | "transaction-amount" | "transaction-currency" | "username" | "email" | "tel" | "tel-area-code" | "tel-country-code" | "tel-extension" | "tel-local" | "tel-national" | "nickname" | "organization-title" | "cc-additional-name" | "language" | "bday" | "sex" | "impp" | "photo",'off',false,false
|
||||||
ion-searchbar,prop,autocorrect,"off" | "on",'off',false,false
|
ion-searchbar,prop,autocorrect,"off" | "on",'off',false,false
|
||||||
ion-searchbar,prop,cancelButtonIcon,string | undefined,undefined,false,false
|
ion-searchbar,prop,cancelButtonIcon,string | undefined,undefined,false,false
|
||||||
ion-searchbar,prop,cancelButtonText,string,'Cancel',false,false
|
ion-searchbar,prop,cancelButtonText,string,'Cancel',false,false
|
||||||
|
|||||||
@ -1,9 +1,6 @@
|
|||||||
import type { ComponentInterface } from '@stencil/core';
|
import type { ComponentInterface } from '@stencil/core';
|
||||||
import { Build, Component, Element, Host, Method, h } from '@stencil/core';
|
import { Component, Element, Host, Method, h } from '@stencil/core';
|
||||||
import type { FocusVisibleUtility } from '@utils/focus-visible';
|
import { getOrInitFocusVisibleUtility } from '@utils/focus-visible';
|
||||||
import { shouldUseCloseWatcher } from '@utils/hardware-back-button';
|
|
||||||
import { printIonWarning } from '@utils/logging';
|
|
||||||
import { isPlatform } from '@utils/platform';
|
|
||||||
|
|
||||||
import { config } from '../../global/config';
|
import { config } from '../../global/config';
|
||||||
import { getIonTheme } from '../../global/ionic-global';
|
import { getIonTheme } from '../../global/ionic-global';
|
||||||
@ -17,53 +14,8 @@ import { getIonTheme } from '../../global/ionic-global';
|
|||||||
styleUrl: 'app.scss',
|
styleUrl: 'app.scss',
|
||||||
})
|
})
|
||||||
export class App implements ComponentInterface {
|
export class App implements ComponentInterface {
|
||||||
private focusVisible?: FocusVisibleUtility;
|
|
||||||
|
|
||||||
@Element() el!: HTMLElement;
|
@Element() el!: HTMLElement;
|
||||||
|
|
||||||
componentDidLoad() {
|
|
||||||
if (Build.isBrowser) {
|
|
||||||
rIC(async () => {
|
|
||||||
const isHybrid = isPlatform(window, 'hybrid');
|
|
||||||
if (!config.getBoolean('_testing')) {
|
|
||||||
import('../../utils/tap-click').then((module) => module.startTapClick(config));
|
|
||||||
}
|
|
||||||
if (config.getBoolean('statusTap', isHybrid)) {
|
|
||||||
import('../../utils/status-tap').then((module) => module.startStatusTap());
|
|
||||||
}
|
|
||||||
if (config.getBoolean('inputShims', needInputShims())) {
|
|
||||||
/**
|
|
||||||
* needInputShims() ensures that only iOS and Android
|
|
||||||
* platforms proceed into this block.
|
|
||||||
*/
|
|
||||||
const platform = isPlatform(window, 'ios') ? 'ios' : 'android';
|
|
||||||
import('../../utils/input-shims/input-shims').then((module) => module.startInputShims(config, platform));
|
|
||||||
}
|
|
||||||
const hardwareBackButtonModule = await import('../../utils/hardware-back-button');
|
|
||||||
const supportsHardwareBackButtonEvents = isHybrid || shouldUseCloseWatcher();
|
|
||||||
if (config.getBoolean('hardwareBackButton', supportsHardwareBackButtonEvents)) {
|
|
||||||
hardwareBackButtonModule.startHardwareBackButton();
|
|
||||||
} else {
|
|
||||||
/**
|
|
||||||
* If an app sets hardwareBackButton: false and experimentalCloseWatcher: true
|
|
||||||
* then the close watcher will not be used.
|
|
||||||
*/
|
|
||||||
if (shouldUseCloseWatcher()) {
|
|
||||||
printIonWarning(
|
|
||||||
'experimentalCloseWatcher was set to `true`, but hardwareBackButton was set to `false`. Both config options must be `true` for the Close Watcher API to be used.'
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
hardwareBackButtonModule.blockHardwareBackButton();
|
|
||||||
}
|
|
||||||
if (typeof (window as any) !== 'undefined') {
|
|
||||||
import('../../utils/keyboard/keyboard').then((module) => module.startKeyboardAssist(window));
|
|
||||||
}
|
|
||||||
import('../../utils/focus-visible').then((module) => (this.focusVisible = module.startFocusVisible()));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @internal
|
* @internal
|
||||||
* Used to set focus on an element that uses `ion-focusable`.
|
* Used to set focus on an element that uses `ion-focusable`.
|
||||||
@ -76,9 +28,8 @@ export class App implements ComponentInterface {
|
|||||||
*/
|
*/
|
||||||
@Method()
|
@Method()
|
||||||
async setFocus(elements: HTMLElement[]) {
|
async setFocus(elements: HTMLElement[]) {
|
||||||
if (this.focusVisible) {
|
const focusVisible = getOrInitFocusVisibleUtility();
|
||||||
this.focusVisible.setFocus(elements);
|
focusVisible.setFocus(elements);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
@ -94,33 +45,3 @@ export class App implements ComponentInterface {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const needInputShims = () => {
|
|
||||||
/**
|
|
||||||
* iOS always needs input shims
|
|
||||||
*/
|
|
||||||
const needsShimsIOS = isPlatform(window, 'ios') && isPlatform(window, 'mobile');
|
|
||||||
if (needsShimsIOS) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Android only needs input shims when running
|
|
||||||
* in the browser and only if the browser is using the
|
|
||||||
* new Chrome 108+ resize behavior: https://developer.chrome.com/blog/viewport-resize-behavior/
|
|
||||||
*/
|
|
||||||
const isAndroidMobileWeb = isPlatform(window, 'android') && isPlatform(window, 'mobileweb');
|
|
||||||
if (isAndroidMobileWeb) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
};
|
|
||||||
|
|
||||||
const rIC = (callback: () => void) => {
|
|
||||||
if ('requestIdleCallback' in window) {
|
|
||||||
(window as any).requestIdleCallback(callback);
|
|
||||||
} else {
|
|
||||||
setTimeout(callback, 32);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|||||||
@ -6,6 +6,7 @@ import { isPlatform } from '@utils/platform';
|
|||||||
import { isRTL } from '@utils/rtl';
|
import { isRTL } from '@utils/rtl';
|
||||||
import { createColorClasses, hostContext } from '@utils/theme';
|
import { createColorClasses, hostContext } from '@utils/theme';
|
||||||
|
|
||||||
|
import { config } from '../../global/config';
|
||||||
import { getIonMode, getIonTheme } from '../../global/ionic-global';
|
import { getIonMode, getIonTheme } from '../../global/ionic-global';
|
||||||
import type { Color, Mode } from '../../interface';
|
import type { Color, Mode } from '../../interface';
|
||||||
|
|
||||||
@ -518,7 +519,8 @@ const getPageElement = (el: HTMLElement) => {
|
|||||||
* between the popover and the edges of the screen. But if the popover contains
|
* between the popover and the edges of the screen. But if the popover contains
|
||||||
* its own page element, we should use that instead.
|
* its own page element, we should use that instead.
|
||||||
*/
|
*/
|
||||||
const page = el.closest('ion-app, ion-page, .ion-page, page-inner, .popover-content');
|
const appRootSelector = config.get('appRootSelector', 'ion-app');
|
||||||
|
const page = el.closest(`${appRootSelector}, ion-page, .ion-page, page-inner, .popover-content`);
|
||||||
if (page) {
|
if (page) {
|
||||||
return page;
|
return page;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,6 +4,7 @@ import { findIonContent, getScrollElement, printIonContentErrorMsg } from '@util
|
|||||||
import type { KeyboardController } from '@utils/keyboard/keyboard-controller';
|
import type { KeyboardController } from '@utils/keyboard/keyboard-controller';
|
||||||
import { createKeyboardController } from '@utils/keyboard/keyboard-controller';
|
import { createKeyboardController } from '@utils/keyboard/keyboard-controller';
|
||||||
|
|
||||||
|
import { config } from '../../global/config';
|
||||||
import { getIonTheme } from '../../global/ionic-global';
|
import { getIonTheme } from '../../global/ionic-global';
|
||||||
|
|
||||||
import { handleFooterFade } from './footer.utils';
|
import { handleFooterFade } from './footer.utils';
|
||||||
@ -86,7 +87,8 @@ export class Footer implements ComponentInterface {
|
|||||||
this.destroyCollapsibleFooter();
|
this.destroyCollapsibleFooter();
|
||||||
|
|
||||||
if (hasFade) {
|
if (hasFade) {
|
||||||
const pageEl = this.el.closest('ion-app,ion-page,.ion-page,page-inner');
|
const appRootSelector = config.get('appRootSelector', 'ion-app');
|
||||||
|
const pageEl = this.el.closest(`${appRootSelector},ion-page,.ion-page,page-inner`);
|
||||||
const contentEl = pageEl ? findIonContent(pageEl) : null;
|
const contentEl = pageEl ? findIonContent(pageEl) : null;
|
||||||
|
|
||||||
if (!contentEl) {
|
if (!contentEl) {
|
||||||
|
|||||||
@ -5,6 +5,7 @@ import type { Attributes } from '@utils/helpers';
|
|||||||
import { inheritAriaAttributes } from '@utils/helpers';
|
import { inheritAriaAttributes } from '@utils/helpers';
|
||||||
import { hostContext } from '@utils/theme';
|
import { hostContext } from '@utils/theme';
|
||||||
|
|
||||||
|
import { config } from '../../global/config';
|
||||||
import { getIonTheme } from '../../global/ionic-global';
|
import { getIonTheme } from '../../global/ionic-global';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
@ -91,8 +92,10 @@ export class Header implements ComponentInterface {
|
|||||||
|
|
||||||
this.destroyCollapsibleHeader();
|
this.destroyCollapsibleHeader();
|
||||||
|
|
||||||
|
const appRootSelector = config.get('appRootSelector', 'ion-app');
|
||||||
|
|
||||||
if (hasCondense) {
|
if (hasCondense) {
|
||||||
const pageEl = this.el.closest('ion-app,ion-page,.ion-page,page-inner');
|
const pageEl = this.el.closest(`${appRootSelector},ion-page,.ion-page,page-inner`);
|
||||||
const contentEl = pageEl ? findIonContent(pageEl) : null;
|
const contentEl = pageEl ? findIonContent(pageEl) : null;
|
||||||
|
|
||||||
// Cloned elements are always needed in iOS transition
|
// Cloned elements are always needed in iOS transition
|
||||||
@ -104,7 +107,7 @@ export class Header implements ComponentInterface {
|
|||||||
|
|
||||||
await this.setupCondenseHeader(contentEl, pageEl);
|
await this.setupCondenseHeader(contentEl, pageEl);
|
||||||
} else if (hasFade) {
|
} else if (hasFade) {
|
||||||
const pageEl = this.el.closest('ion-app,ion-page,.ion-page,page-inner');
|
const pageEl = this.el.closest(`${appRootSelector},ion-page,.ion-page,page-inner`);
|
||||||
const contentEl = pageEl ? findIonContent(pageEl) : null;
|
const contentEl = pageEl ? findIonContent(pageEl) : null;
|
||||||
|
|
||||||
if (!contentEl) {
|
if (!contentEl) {
|
||||||
|
|||||||
@ -1,7 +1,8 @@
|
|||||||
import { getMode, setMode, setPlatformHelpers, getElement } from '@stencil/core';
|
import { Build, getMode, setMode, setPlatformHelpers, getElement } from '@stencil/core';
|
||||||
import { printIonWarning } from '@utils/logging';
|
import { printIonWarning } from '@utils/logging';
|
||||||
|
|
||||||
import type { IonicConfig, Mode, Theme } from '../interface';
|
import type { IonicConfig, Mode, Theme } from '../interface';
|
||||||
|
import { shouldUseCloseWatcher } from '../utils/hardware-back-button';
|
||||||
import { isPlatform, setupPlatforms } from '../utils/platform';
|
import { isPlatform, setupPlatforms } from '../utils/platform';
|
||||||
|
|
||||||
import { config, configFromSession, configFromURL, saveConfig } from './config';
|
import { config, configFromSession, configFromURL, saveConfig } from './config';
|
||||||
@ -131,6 +132,36 @@ export const getIonTheme = (ref?: any): Theme => {
|
|||||||
return defaultTheme;
|
return defaultTheme;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const rIC = (callback: () => void) => {
|
||||||
|
if ('requestIdleCallback' in window) {
|
||||||
|
(window as any).requestIdleCallback(callback);
|
||||||
|
} else {
|
||||||
|
setTimeout(callback, 32);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const needInputShims = () => {
|
||||||
|
/**
|
||||||
|
* iOS always needs input shims
|
||||||
|
*/
|
||||||
|
const needsShimsIOS = isPlatform(window, 'ios') && isPlatform(window, 'mobile');
|
||||||
|
if (needsShimsIOS) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Android only needs input shims when running
|
||||||
|
* in the browser and only if the browser is using the
|
||||||
|
* new Chrome 108+ resize behavior: https://developer.chrome.com/blog/viewport-resize-behavior/
|
||||||
|
*/
|
||||||
|
const isAndroidMobileWeb = isPlatform(window, 'android') && isPlatform(window, 'mobileweb');
|
||||||
|
if (isAndroidMobileWeb) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
export const initialize = (userConfig: IonicConfig = {}) => {
|
export const initialize = (userConfig: IonicConfig = {}) => {
|
||||||
if (typeof (window as any) === 'undefined') {
|
if (typeof (window as any) === 'undefined') {
|
||||||
return;
|
return;
|
||||||
@ -255,6 +286,50 @@ export const initialize = (userConfig: IonicConfig = {}) => {
|
|||||||
}
|
}
|
||||||
return defaultTheme;
|
return defaultTheme;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// `IonApp` code
|
||||||
|
// ----------------------------------------------
|
||||||
|
|
||||||
|
if (Build.isBrowser) {
|
||||||
|
rIC(async () => {
|
||||||
|
const isHybrid = isPlatform(window, 'hybrid');
|
||||||
|
if (!config.getBoolean('_testing')) {
|
||||||
|
import('../utils/tap-click').then((module) => module.startTapClick(config));
|
||||||
|
}
|
||||||
|
if (config.getBoolean('statusTap', isHybrid)) {
|
||||||
|
import('../utils/status-tap').then((module) => module.startStatusTap());
|
||||||
|
}
|
||||||
|
if (config.getBoolean('inputShims', needInputShims())) {
|
||||||
|
/**
|
||||||
|
* needInputShims() ensures that only iOS and Android
|
||||||
|
* platforms proceed into this block.
|
||||||
|
*/
|
||||||
|
const platform = isPlatform(window, 'ios') ? 'ios' : 'android';
|
||||||
|
import('../utils/input-shims/input-shims').then((module) => module.startInputShims(config, platform));
|
||||||
|
}
|
||||||
|
const hardwareBackButtonModule = await import('../utils/hardware-back-button');
|
||||||
|
const supportsHardwareBackButtonEvents = isHybrid || shouldUseCloseWatcher();
|
||||||
|
if (config.getBoolean('hardwareBackButton', supportsHardwareBackButtonEvents)) {
|
||||||
|
hardwareBackButtonModule.startHardwareBackButton();
|
||||||
|
} else {
|
||||||
|
/**
|
||||||
|
* If an app sets hardwareBackButton: false and experimentalCloseWatcher: true
|
||||||
|
* then the close watcher will not be used.
|
||||||
|
*/
|
||||||
|
if (shouldUseCloseWatcher()) {
|
||||||
|
printIonWarning(
|
||||||
|
'experimentalCloseWatcher was set to `true`, but hardwareBackButton was set to `false`. Both config options must be `true` for the Close Watcher API to be used.'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
hardwareBackButtonModule.blockHardwareBackButton();
|
||||||
|
}
|
||||||
|
if (typeof (window as any) !== 'undefined') {
|
||||||
|
import('../utils/keyboard/keyboard').then((module) => module.startKeyboardAssist(window));
|
||||||
|
}
|
||||||
|
import('../utils/focus-visible').then((module) => module.getOrInitFocusVisibleUtility());
|
||||||
|
});
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export default initialize;
|
export default initialize;
|
||||||
|
|||||||
@ -124,6 +124,14 @@ export interface IonicConfig {
|
|||||||
*/
|
*/
|
||||||
toastDuration?: number;
|
toastDuration?: number;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The selector that will be used to query the root of the Ionic application.
|
||||||
|
* This element is used for things like injecting overlay elements into the DOM and managing focus.
|
||||||
|
*
|
||||||
|
* @default 'ion-app'
|
||||||
|
*/
|
||||||
|
appRootSelector?: string;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Overrides the toggle icon for all `ion-accordion` components.
|
* Overrides the toggle icon for all `ion-accordion` components.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@ -20,6 +20,16 @@ export interface FocusVisibleUtility {
|
|||||||
setFocus: (elements: Element[]) => void;
|
setFocus: (elements: Element[]) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let focusVisibleUtility: FocusVisibleUtility | null = null;
|
||||||
|
|
||||||
|
export const getOrInitFocusVisibleUtility = () => {
|
||||||
|
if (!focusVisibleUtility) {
|
||||||
|
focusVisibleUtility = startFocusVisible();
|
||||||
|
}
|
||||||
|
|
||||||
|
return focusVisibleUtility;
|
||||||
|
};
|
||||||
|
|
||||||
export const startFocusVisible = (rootEl?: HTMLElement): FocusVisibleUtility => {
|
export const startFocusVisible = (rootEl?: HTMLElement): FocusVisibleUtility => {
|
||||||
let currentFocus: Element[] = [];
|
let currentFocus: Element[] = [];
|
||||||
let keyboardMode = true;
|
let keyboardMode = true;
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
import { config } from '../global/config';
|
||||||
import type { ComponentRef, FrameworkDelegate } from '../interface';
|
import type { ComponentRef, FrameworkDelegate } from '../interface';
|
||||||
|
|
||||||
import { componentOnReady } from './helpers';
|
import { componentOnReady } from './helpers';
|
||||||
@ -128,7 +129,8 @@ export const CoreDelegate = () => {
|
|||||||
* Get the root of the app and
|
* Get the root of the app and
|
||||||
* add the overlay there.
|
* add the overlay there.
|
||||||
*/
|
*/
|
||||||
const app = document.querySelector('ion-app') || document.body;
|
const appRootSelector = config.get('appRootSelector', 'ion-app');
|
||||||
|
const app = document.querySelector(appRootSelector) || document.body;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a placeholder comment so that
|
* Create a placeholder comment so that
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
import type { EventEmitter } from '@stencil/core';
|
import type { EventEmitter } from '@stencil/core';
|
||||||
|
|
||||||
import type { Side } from '../components/menu/menu-interface';
|
import type { Side } from '../components/menu/menu-interface';
|
||||||
|
import { config } from '../global/config';
|
||||||
|
|
||||||
// TODO(FW-2832): types
|
// TODO(FW-2832): types
|
||||||
|
|
||||||
@ -266,7 +267,8 @@ export const focusVisibleElement = (el: HTMLElement) => {
|
|||||||
* which will let us explicitly set the elements to focus.
|
* which will let us explicitly set the elements to focus.
|
||||||
*/
|
*/
|
||||||
if (el.classList.contains('ion-focusable')) {
|
if (el.classList.contains('ion-focusable')) {
|
||||||
const app = el.closest('ion-app');
|
const appRootSelector = config.get('appRootSelector', 'ion-app');
|
||||||
|
const app = el.closest(appRootSelector) as HTMLIonAppElement | null;
|
||||||
if (app) {
|
if (app) {
|
||||||
app.setFocus([el]);
|
app.setFocus([el]);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
import { doc, win } from '@utils/browser';
|
import { doc, win } from '@utils/browser';
|
||||||
|
|
||||||
|
import { config } from '../../global/config';
|
||||||
import { Keyboard, KeyboardResize } from '../native/keyboard';
|
import { Keyboard, KeyboardResize } from '../native/keyboard';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -25,7 +26,8 @@ const getResizeContainer = (resizeMode?: KeyboardResize): HTMLElement | null =>
|
|||||||
* on that. In the event `ion-app` is not available then
|
* on that. In the event `ion-app` is not available then
|
||||||
* we can fall back to `body`.
|
* we can fall back to `body`.
|
||||||
*/
|
*/
|
||||||
const ionApp = doc.querySelector('ion-app');
|
const appRootSelector = config.get('appRootSelector', 'ion-app');
|
||||||
|
const ionApp = doc.querySelector(appRootSelector) as HTMLIonAppElement | null;
|
||||||
|
|
||||||
return ionApp ?? doc.body;
|
return ionApp ?? doc.body;
|
||||||
};
|
};
|
||||||
|
|||||||
@ -697,7 +697,8 @@ export const dismiss = async <OverlayDismissOptions>(
|
|||||||
};
|
};
|
||||||
|
|
||||||
const getAppRoot = (doc: Document) => {
|
const getAppRoot = (doc: Document) => {
|
||||||
return doc.querySelector('ion-app') || doc.body;
|
const appRootSelector = config.get('appRootSelector', 'ion-app');
|
||||||
|
return doc.querySelector(appRootSelector) || doc.body;
|
||||||
};
|
};
|
||||||
|
|
||||||
const overlayAnimation = async (
|
const overlayAnimation = async (
|
||||||
|
|||||||
Reference in New Issue
Block a user