diff --git a/core/api.txt b/core/api.txt index f2af11afc9b..e95bcfc0f98 100644 --- a/core/api.txt +++ b/core/api.txt @@ -800,7 +800,6 @@ ion-content,prop,mode,"ios" | "md",undefined,false,false ion-content,prop,scrollEvents,boolean,false,false,false ion-content,prop,scrollX,boolean,false,false,false ion-content,prop,scrollY,boolean,true,false,false -ion-content,prop,theme,"ios" | "md" | "ionic",undefined,false,false ion-content,method,getScrollElement,getScrollElement() => Promise ion-content,method,scrollByPoint,scrollByPoint(x: number, y: number, duration: number) => Promise ion-content,method,scrollToBottom,scrollToBottom(duration?: number) => Promise @@ -809,15 +808,14 @@ ion-content,method,scrollToTop,scrollToTop(duration?: number) => Promise ion-content,event,ionScroll,ScrollDetail,true ion-content,event,ionScrollEnd,ScrollBaseDetail,true ion-content,event,ionScrollStart,ScrollBaseDetail,true -ion-content,css-prop,--background -ion-content,css-prop,--color -ion-content,css-prop,--keyboard-offset -ion-content,css-prop,--offset-bottom -ion-content,css-prop,--offset-top -ion-content,css-prop,--padding-bottom -ion-content,css-prop,--padding-end -ion-content,css-prop,--padding-start -ion-content,css-prop,--padding-top +ion-content,css-prop,--ion-content-background +ion-content,css-prop,--ion-content-color +ion-content,css-prop,--ion-content-font-family +ion-content,css-prop,--ion-content-overflow +ion-content,css-prop,--ion-content-padding-bottom +ion-content,css-prop,--ion-content-padding-end +ion-content,css-prop,--ion-content-padding-start +ion-content,css-prop,--ion-content-padding-top ion-content,part,background ion-content,part,scroll diff --git a/core/scripts/testing/scripts.js b/core/scripts/testing/scripts.js index f47b2cf670d..1e0d7418c41 100644 --- a/core/scripts/testing/scripts.js +++ b/core/scripts/testing/scripts.js @@ -24,7 +24,7 @@ const DEFAULT_THEME = 'md'; const DEFAULT_PALETTE = 'light'; (function() { - + /** * The `rtl` param is used to set the directionality of the * document. This can be `true` or `false`. @@ -128,6 +128,27 @@ const DEFAULT_PALETTE = 'light'; ); } + /** + * Deep merges two objects, with source properties overriding target properties + * @param target The target object to merge into + * @param source The source object to merge from + * @returns The merged object + */ + // TODO(FW-6750): Remove this once the theme tokens can be imported directly into the test pages + const deepMerge = (target, source) => { + const result = { ...target }; + + for (const key in source) { + if (source[key] && typeof source[key] === 'object' && !Array.isArray(source[key])) { + result[key] = deepMerge(result[key] ?? {}, source[key]); + } else { + result[key] = source[key]; + } + } + return result; + }; + + // TODO(FW-6750): Determine if this function can be removed once the theme tokens can be imported directly into the test pages async function loadThemeTokens(themeName, paletteName) { try { // Store existing theme set from the app initialization @@ -138,14 +159,7 @@ const DEFAULT_PALETTE = 'light'; // Merge with existing theme to preserve any customizations if (customTheme) { - theme = { - ...theme, - ...customTheme, - palette: { - ...theme.palette, - ...customTheme.palette, - }, - }; + theme = deepMerge(theme, customTheme); } // If a specific palette is requested, modify the palette structure diff --git a/core/src/components.d.ts b/core/src/components.d.ts index fb7e5f9cff6..8212cdfa246 100644 --- a/core/src/components.d.ts +++ b/core/src/components.d.ts @@ -16,7 +16,7 @@ import { RouteID, RouterDirection, RouterEventDetail, RouteWrite } from "./compo import { BreadcrumbCollapsedClickEventDetail } from "./components/breadcrumb/breadcrumb-interface"; import { CheckboxChangeEventDetail } from "./components/checkbox/checkbox-interface"; import { IonChipFill, IonChipHue, IonChipShape, IonChipSize } from "./components/chip/chip.interfaces"; -import { ScrollBaseDetail, ScrollDetail } from "./components/content/content-interface"; +import { ScrollBaseDetail, ScrollDetail } from "./components/content/content.interfaces"; import { DatetimeChangeEventDetail, DatetimeHighlight, DatetimeHighlightCallback, DatetimeHourCycle, DatetimePresentation, FormatOptions, TitleSelectedDatesFormatter } from "./components/datetime/datetime-interface"; import { SpinnerTypes } from "./components/spinner/spinner-configs"; import { InputChangeEventDetail, InputInputEventDetail } from "./components/input/input-interface"; @@ -58,7 +58,7 @@ export { RouteID, RouterDirection, RouterEventDetail, RouteWrite } from "./compo export { BreadcrumbCollapsedClickEventDetail } from "./components/breadcrumb/breadcrumb-interface"; export { CheckboxChangeEventDetail } from "./components/checkbox/checkbox-interface"; export { IonChipFill, IonChipHue, IonChipShape, IonChipSize } from "./components/chip/chip.interfaces"; -export { ScrollBaseDetail, ScrollDetail } from "./components/content/content-interface"; +export { ScrollBaseDetail, ScrollDetail } from "./components/content/content.interfaces"; export { DatetimeChangeEventDetail, DatetimeHighlight, DatetimeHighlightCallback, DatetimeHourCycle, DatetimePresentation, FormatOptions, TitleSelectedDatesFormatter } from "./components/datetime/datetime-interface"; export { SpinnerTypes } from "./components/spinner/spinner-configs"; export { InputChangeEventDetail, InputInputEventDetail } from "./components/input/input-interface"; @@ -1118,10 +1118,6 @@ export namespace Components { * @default true */ "scrollY": boolean; - /** - * The theme determines the visual appearance of the component. - */ - "theme"?: "ios" | "md" | "ionic"; } interface IonDatetime { /** @@ -7052,10 +7048,6 @@ declare namespace LocalJSX { * @default true */ "scrollY"?: boolean; - /** - * The theme determines the visual appearance of the component. - */ - "theme"?: "ios" | "md" | "ionic"; } interface IonDatetime { /** diff --git a/core/src/components/action-sheet/action-sheet.scss b/core/src/components/action-sheet/action-sheet.scss index 2a2f85bb456..495e8421015 100644 --- a/core/src/components/action-sheet/action-sheet.scss +++ b/core/src/components/action-sheet/action-sheet.scss @@ -143,7 +143,6 @@ flex-shrink: 2; overscroll-behavior-y: contain; overflow-y: auto; - -webkit-overflow-scrolling: touch; pointer-events: all; background: var(--background); diff --git a/core/src/components/alert/alert.ios.scss b/core/src/components/alert/alert.ios.scss index 714efc03baf..b31b2577b3a 100644 --- a/core/src/components/alert/alert.ios.scss +++ b/core/src/components/alert/alert.ios.scss @@ -148,7 +148,6 @@ border-top: $alert-ios-list-border-top; overflow-y: auto; - -webkit-overflow-scrolling: touch; } .alert-tappable { diff --git a/core/src/components/alert/alert.scss b/core/src/components/alert/alert.scss index 9948a4127a9..0382b6bb898 100644 --- a/core/src/components/alert/alert.scss +++ b/core/src/components/alert/alert.scss @@ -94,7 +94,6 @@ .alert-message, .alert-input-group { box-sizing: border-box; - -webkit-overflow-scrolling: touch; overflow-y: auto; overscroll-behavior-y: contain; } diff --git a/core/src/components/card/test/shape/card.e2e.ts-snapshots/card-default-ionic-md-ltr-light-Mobile-Chrome-linux.png b/core/src/components/card/test/shape/card.e2e.ts-snapshots/card-default-ionic-md-ltr-light-Mobile-Chrome-linux.png index 755182fc4d2..edfe607838f 100644 Binary files a/core/src/components/card/test/shape/card.e2e.ts-snapshots/card-default-ionic-md-ltr-light-Mobile-Chrome-linux.png and b/core/src/components/card/test/shape/card.e2e.ts-snapshots/card-default-ionic-md-ltr-light-Mobile-Chrome-linux.png differ diff --git a/core/src/components/card/test/shape/card.e2e.ts-snapshots/card-default-ionic-md-ltr-light-Mobile-Firefox-linux.png b/core/src/components/card/test/shape/card.e2e.ts-snapshots/card-default-ionic-md-ltr-light-Mobile-Firefox-linux.png index 2e934d1a783..2568ea5cfe3 100644 Binary files a/core/src/components/card/test/shape/card.e2e.ts-snapshots/card-default-ionic-md-ltr-light-Mobile-Firefox-linux.png and b/core/src/components/card/test/shape/card.e2e.ts-snapshots/card-default-ionic-md-ltr-light-Mobile-Firefox-linux.png differ diff --git a/core/src/components/card/test/shape/card.e2e.ts-snapshots/card-default-ionic-md-ltr-light-Mobile-Safari-linux.png b/core/src/components/card/test/shape/card.e2e.ts-snapshots/card-default-ionic-md-ltr-light-Mobile-Safari-linux.png index fe0ff96e586..b6c4e813296 100644 Binary files a/core/src/components/card/test/shape/card.e2e.ts-snapshots/card-default-ionic-md-ltr-light-Mobile-Safari-linux.png and b/core/src/components/card/test/shape/card.e2e.ts-snapshots/card-default-ionic-md-ltr-light-Mobile-Safari-linux.png differ diff --git a/core/src/components/card/test/shape/card.e2e.ts-snapshots/card-rectangular-ionic-md-ltr-light-Mobile-Chrome-linux.png b/core/src/components/card/test/shape/card.e2e.ts-snapshots/card-rectangular-ionic-md-ltr-light-Mobile-Chrome-linux.png index cfdd6297ba1..cdd772f1049 100644 Binary files a/core/src/components/card/test/shape/card.e2e.ts-snapshots/card-rectangular-ionic-md-ltr-light-Mobile-Chrome-linux.png and b/core/src/components/card/test/shape/card.e2e.ts-snapshots/card-rectangular-ionic-md-ltr-light-Mobile-Chrome-linux.png differ diff --git a/core/src/components/card/test/shape/card.e2e.ts-snapshots/card-rectangular-ionic-md-ltr-light-Mobile-Firefox-linux.png b/core/src/components/card/test/shape/card.e2e.ts-snapshots/card-rectangular-ionic-md-ltr-light-Mobile-Firefox-linux.png index 40f4c1f0eb3..4096a5b9b6c 100644 Binary files a/core/src/components/card/test/shape/card.e2e.ts-snapshots/card-rectangular-ionic-md-ltr-light-Mobile-Firefox-linux.png and b/core/src/components/card/test/shape/card.e2e.ts-snapshots/card-rectangular-ionic-md-ltr-light-Mobile-Firefox-linux.png differ diff --git a/core/src/components/card/test/shape/card.e2e.ts-snapshots/card-rectangular-ionic-md-ltr-light-Mobile-Safari-linux.png b/core/src/components/card/test/shape/card.e2e.ts-snapshots/card-rectangular-ionic-md-ltr-light-Mobile-Safari-linux.png index 8d6308e8c45..c8908d7a15a 100644 Binary files a/core/src/components/card/test/shape/card.e2e.ts-snapshots/card-rectangular-ionic-md-ltr-light-Mobile-Safari-linux.png and b/core/src/components/card/test/shape/card.e2e.ts-snapshots/card-rectangular-ionic-md-ltr-light-Mobile-Safari-linux.png differ diff --git a/core/src/components/card/test/shape/card.e2e.ts-snapshots/card-round-ionic-md-ltr-light-Mobile-Chrome-linux.png b/core/src/components/card/test/shape/card.e2e.ts-snapshots/card-round-ionic-md-ltr-light-Mobile-Chrome-linux.png index 69fce0e831c..edfe607838f 100644 Binary files a/core/src/components/card/test/shape/card.e2e.ts-snapshots/card-round-ionic-md-ltr-light-Mobile-Chrome-linux.png and b/core/src/components/card/test/shape/card.e2e.ts-snapshots/card-round-ionic-md-ltr-light-Mobile-Chrome-linux.png differ diff --git a/core/src/components/card/test/shape/card.e2e.ts-snapshots/card-round-ionic-md-ltr-light-Mobile-Firefox-linux.png b/core/src/components/card/test/shape/card.e2e.ts-snapshots/card-round-ionic-md-ltr-light-Mobile-Firefox-linux.png index 2e934d1a783..2568ea5cfe3 100644 Binary files a/core/src/components/card/test/shape/card.e2e.ts-snapshots/card-round-ionic-md-ltr-light-Mobile-Firefox-linux.png and b/core/src/components/card/test/shape/card.e2e.ts-snapshots/card-round-ionic-md-ltr-light-Mobile-Firefox-linux.png differ diff --git a/core/src/components/card/test/shape/card.e2e.ts-snapshots/card-round-ionic-md-ltr-light-Mobile-Safari-linux.png b/core/src/components/card/test/shape/card.e2e.ts-snapshots/card-round-ionic-md-ltr-light-Mobile-Safari-linux.png index fe0ff96e586..b6c4e813296 100644 Binary files a/core/src/components/card/test/shape/card.e2e.ts-snapshots/card-round-ionic-md-ltr-light-Mobile-Safari-linux.png and b/core/src/components/card/test/shape/card.e2e.ts-snapshots/card-round-ionic-md-ltr-light-Mobile-Safari-linux.png differ diff --git a/core/src/components/card/test/shape/card.e2e.ts-snapshots/card-soft-ionic-md-ltr-light-Mobile-Chrome-linux.png b/core/src/components/card/test/shape/card.e2e.ts-snapshots/card-soft-ionic-md-ltr-light-Mobile-Chrome-linux.png index ef06aedfcf9..f237e7885c9 100644 Binary files a/core/src/components/card/test/shape/card.e2e.ts-snapshots/card-soft-ionic-md-ltr-light-Mobile-Chrome-linux.png and b/core/src/components/card/test/shape/card.e2e.ts-snapshots/card-soft-ionic-md-ltr-light-Mobile-Chrome-linux.png differ diff --git a/core/src/components/card/test/shape/card.e2e.ts-snapshots/card-soft-ionic-md-ltr-light-Mobile-Firefox-linux.png b/core/src/components/card/test/shape/card.e2e.ts-snapshots/card-soft-ionic-md-ltr-light-Mobile-Firefox-linux.png index d42d2e749be..3367531f71d 100644 Binary files a/core/src/components/card/test/shape/card.e2e.ts-snapshots/card-soft-ionic-md-ltr-light-Mobile-Firefox-linux.png and b/core/src/components/card/test/shape/card.e2e.ts-snapshots/card-soft-ionic-md-ltr-light-Mobile-Firefox-linux.png differ diff --git a/core/src/components/card/test/shape/card.e2e.ts-snapshots/card-soft-ionic-md-ltr-light-Mobile-Safari-linux.png b/core/src/components/card/test/shape/card.e2e.ts-snapshots/card-soft-ionic-md-ltr-light-Mobile-Safari-linux.png index 2ecec60b4ec..b328ec1c7b7 100644 Binary files a/core/src/components/card/test/shape/card.e2e.ts-snapshots/card-soft-ionic-md-ltr-light-Mobile-Safari-linux.png and b/core/src/components/card/test/shape/card.e2e.ts-snapshots/card-soft-ionic-md-ltr-light-Mobile-Safari-linux.png differ diff --git a/core/src/components/card/test/shape/index.html b/core/src/components/card/test/shape/index.html index 77eaf9301ab..321998795a4 100644 --- a/core/src/components/card/test/shape/index.html +++ b/core/src/components/card/test/shape/index.html @@ -9,16 +9,33 @@ /> + diff --git a/core/src/components/input/input.tsx b/core/src/components/input/input.tsx index 94829b29bdd..d2b2cbbd299 100644 --- a/core/src/components/input/input.tsx +++ b/core/src/components/input/input.tsx @@ -17,7 +17,7 @@ import { import type { NotchController } from '@utils/forms'; import { createNotchController, checkInvalidState } from '@utils/forms'; import type { Attributes } from '@utils/helpers'; -import { inheritAriaAttributes, debounceEvent, inheritAttributes, componentOnReady } from '@utils/helpers'; +import { inheritAriaAttributes, debounceEvent, inheritAttributes, waitForComponent } from '@utils/helpers'; import { printIonWarning } from '@utils/logging'; import { createSlotMutationController } from '@utils/slot-mutation-controller'; import type { SlotMutationController } from '@utils/slot-mutation-controller'; @@ -537,7 +537,7 @@ export class Input implements ComponentInterface { * nativeInput won't be defined yet with the custom elements build, so wait for it to load in. */ if (!this.nativeInput) { - await new Promise((resolve) => componentOnReady(this.el, resolve)); + await waitForComponent(this.el); } return Promise.resolve(this.nativeInput!); } diff --git a/core/src/components/list-header/test/basic/index.html b/core/src/components/list-header/test/basic/index.html index 165396b57a4..1e70a76f927 100644 --- a/core/src/components/list-header/test/basic/index.html +++ b/core/src/components/list-header/test/basic/index.html @@ -10,6 +10,21 @@ + @@ -81,11 +96,5 @@ - - diff --git a/core/src/components/list/test/inset/index.html b/core/src/components/list/test/inset/index.html index 23f3f9a2271..83de999dfbb 100644 --- a/core/src/components/list/test/inset/index.html +++ b/core/src/components/list/test/inset/index.html @@ -9,6 +9,21 @@ /> + @@ -92,11 +107,5 @@ - - diff --git a/core/src/components/list/test/shape/index.html b/core/src/components/list/test/shape/index.html index f65d6052e94..e5f95b27f73 100644 --- a/core/src/components/list/test/shape/index.html +++ b/core/src/components/list/test/shape/index.html @@ -9,14 +9,24 @@ /> + - diff --git a/core/src/components/list/test/shape/list.e2e.ts b/core/src/components/list/test/shape/list.e2e.ts index 76ca85311eb..4b27340e21e 100644 --- a/core/src/components/list/test/shape/list.e2e.ts +++ b/core/src/components/list/test/shape/list.e2e.ts @@ -9,7 +9,7 @@ configs({ directions: ['ltr'], modes: ['ionic-md'] }).forEach(({ title, screensh ` @@ -45,7 +45,7 @@ configs({ directions: ['ltr'], modes: ['ionic-md'] }).forEach(({ title, screensh ` @@ -86,7 +86,7 @@ configs({ directions: ['ltr'], modes: ['ionic-md'] }).forEach(({ title, screensh ` @@ -122,7 +122,7 @@ configs({ directions: ['ltr'], modes: ['ionic-md'] }).forEach(({ title, screensh ` @@ -163,7 +163,7 @@ configs({ directions: ['ltr'], modes: ['ionic-md'] }).forEach(({ title, screensh ` @@ -199,7 +199,7 @@ configs({ directions: ['ltr'], modes: ['ionic-md'] }).forEach(({ title, screensh ` diff --git a/core/src/components/popover/test/arrow/index.html b/core/src/components/popover/test/arrow/index.html index a5bd12f420e..161169490a5 100644 --- a/core/src/components/popover/test/arrow/index.html +++ b/core/src/components/popover/test/arrow/index.html @@ -14,7 +14,7 @@ diff --git a/core/src/components/tab-bar/test/shape/index.html b/core/src/components/tab-bar/test/shape/index.html index 8bf2e5db15a..d7366d36fed 100644 --- a/core/src/components/tab-bar/test/shape/index.html +++ b/core/src/components/tab-bar/test/shape/index.html @@ -7,6 +7,21 @@ name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover" /> + @@ -92,10 +107,6 @@

Rectangular

diff --git a/core/src/components/textarea/textarea.tsx b/core/src/components/textarea/textarea.tsx index 272b21f3077..b119c94f1a7 100644 --- a/core/src/components/textarea/textarea.tsx +++ b/core/src/components/textarea/textarea.tsx @@ -17,7 +17,7 @@ import { import type { NotchController } from '@utils/forms'; import { createNotchController, checkInvalidState } from '@utils/forms'; import type { Attributes } from '@utils/helpers'; -import { inheritAriaAttributes, debounceEvent, inheritAttributes, componentOnReady } from '@utils/helpers'; +import { inheritAriaAttributes, debounceEvent, inheritAttributes, waitForComponent } from '@utils/helpers'; import { createSlotMutationController } from '@utils/slot-mutation-controller'; import type { SlotMutationController } from '@utils/slot-mutation-controller'; import { createColorClasses, hostContext } from '@utils/theme'; @@ -452,7 +452,7 @@ export class Textarea implements ComponentInterface { * nativeInput won't be defined yet with the custom elements build, so wait for it to load in. */ if (!this.nativeInput) { - await new Promise((resolve) => componentOnReady(this.el, resolve)); + await waitForComponent(this.el); } return Promise.resolve(this.nativeInput!); } diff --git a/core/src/css/core.scss b/core/src/css/core.scss index b80d307d56e..ba8923075fb 100644 --- a/core/src/css/core.scss +++ b/core/src/css/core.scss @@ -247,8 +247,8 @@ ion-card-header.ion-color .ion-inherit-color { * The code below accounts for both ion-content and then custom * scroll containers within ion-content (such as virtual scroll) */ -.menu-content-open ion-content { - --overflow: hidden; +.menu-content-open ion-content::part(scroll) { + overflow: hidden; } .menu-content-open .ion-content-scroll-host { diff --git a/core/src/css/ionic/core.ionic.scss b/core/src/css/ionic/core.ionic.scss index f42a9b0ff1e..3c81a167718 100644 --- a/core/src/css/ionic/core.ionic.scss +++ b/core/src/css/ionic/core.ionic.scss @@ -65,9 +65,9 @@ html.ionic .modal-footer-moving ion-toolbar { * within a modal. */ html.ionic ion-modal.modal-sheet ion-content { - --padding-start: #{globals.$ion-space-400}; - --padding-end: #{globals.$ion-space-400}; - --padding-bottom: #{globals.$ion-space-400}; + --ion-content-padding-start: #{globals.$ion-space-400}; + --ion-content-padding-end: #{globals.$ion-space-400}; + --ion-content-padding-bottom: #{globals.$ion-space-400}; } /** @@ -254,8 +254,8 @@ ion-card-header.ion-color .ion-inherit-color { * The code below accounts for both ion-content and then custom * scroll containers within ion-content (such as virtual scroll) */ -.menu-content-open ion-content { - --overflow: hidden; +.menu-content-open ion-content::part(scroll) { + overflow: hidden; } .menu-content-open .ion-content-scroll-host { diff --git a/core/src/global/config.ts b/core/src/global/config.ts index d9795de6495..9cb262a4bea 100644 --- a/core/src/global/config.ts +++ b/core/src/global/config.ts @@ -2,6 +2,8 @@ import type { IonicConfig } from '../themes/themes.interfaces'; // TODO(FW-2832): types +type ObjectConfigValue = string | boolean; + export class Config { private m = new Map(); @@ -21,7 +23,7 @@ export class Config { * @param fallback Default value if the key is not found * @returns The value found at the nested key or the fallback */ - getObjectValue(key: string, fallback?: string): string | undefined { + getObjectValue(key: string, fallback?: ObjectConfigValue): ObjectConfigValue | undefined { const [firstKey, ...remainingKeys] = key.split('.'); let root: any; diff --git a/core/src/interface.d.ts b/core/src/interface.d.ts index 158b8ad1c19..8e303941aee 100644 --- a/core/src/interface.d.ts +++ b/core/src/interface.d.ts @@ -7,7 +7,7 @@ export { AccordionGroupCustomEvent } from './components/accordion-group/accordio export { AlertOptions } from './components/alert/alert-interface'; export { ActionSheetOptions } from './components/action-sheet/action-sheet-interface'; export { BreadcrumbCustomEvent } from './components/breadcrumb/breadcrumb-interface'; -export { ScrollBaseCustomEvent, ScrollCallback, ScrollCustomEvent } from './components/content/content-interface'; +export { ScrollBaseCustomEvent, ScrollCallback, ScrollCustomEvent } from './components/content/content.interfaces'; export { CheckboxCustomEvent } from './components/checkbox/checkbox-interface'; export { DatetimeCustomEvent, DatetimeHighlightStyle } from './components/datetime/datetime-interface'; export { InfiniteScrollCustomEvent } from './components/infinite-scroll/infinite-scroll-interface'; diff --git a/core/src/themes/ionic/default.tokens.ts b/core/src/themes/ionic/default.tokens.ts index bde15b539a0..56a9b4c08bb 100644 --- a/core/src/themes/ionic/default.tokens.ts +++ b/core/src/themes/ionic/default.tokens.ts @@ -461,6 +461,24 @@ export const defaultTheme: DefaultTheme = { }, }, + IonContent: { + background: baseColors.backgroundColor, + color: baseColors.textColor, + + font: { + family: 'var(--ion-font-family, inherit)', + }, + + overflow: 'auto', + + padding: { + bottom: 'var(--ion-spacing-0)', + end: 'var(--ion-spacing-0)', + start: 'var(--ion-spacing-0)', + top: 'var(--ion-spacing-0)', + }, + }, + IonItemDivider: { background: baseColors.backgroundColor, color: `var(--ion-text-color-step-600, ${mix(baseColors.white, baseColors.black, '40%')})`, diff --git a/core/src/themes/ios/default.tokens.ts b/core/src/themes/ios/default.tokens.ts index 00f09285cec..2bd1eb4f695 100644 --- a/core/src/themes/ios/default.tokens.ts +++ b/core/src/themes/ios/default.tokens.ts @@ -599,6 +599,24 @@ export const defaultTheme: DefaultTheme = { }, }, + IonContent: { + background: baseColors.backgroundColor, + color: baseColors.textColor, + + font: { + family: 'var(--ion-font-family, inherit)', + }, + + overflow: 'auto', + + padding: { + bottom: 'var(--ion-spacing-0)', + end: 'var(--ion-spacing-0)', + start: 'var(--ion-spacing-0)', + top: 'var(--ion-spacing-0)', + }, + }, + IonItemDivider: { background: `var(--ion-background-color-step-100, ${mix(baseColors.black, baseColors.white, '90%')})`, color: `var(--ion-text-color-step-150, ${mix(baseColors.white, baseColors.black, '85%')})`, diff --git a/core/src/themes/md/default.tokens.ts b/core/src/themes/md/default.tokens.ts index 48cbb301023..3a909ef16bd 100644 --- a/core/src/themes/md/default.tokens.ts +++ b/core/src/themes/md/default.tokens.ts @@ -602,6 +602,24 @@ export const defaultTheme: DefaultTheme = { }, }, + IonContent: { + background: baseColors.backgroundColor, + color: baseColors.textColor, + + font: { + family: 'var(--ion-font-family, inherit)', + }, + + overflow: 'auto', + + padding: { + bottom: 'var(--ion-spacing-0)', + end: 'var(--ion-spacing-0)', + start: 'var(--ion-spacing-0)', + top: 'var(--ion-spacing-0)', + }, + }, + IonItemDivider: { background: baseColors.backgroundColor, color: `var(--ion-text-color-step-600, ${mix(baseColors.white, baseColors.black, '40%')})`, diff --git a/core/src/themes/themes.interfaces.ts b/core/src/themes/themes.interfaces.ts index 4d552c77be7..0a87addcbff 100644 --- a/core/src/themes/themes.interfaces.ts +++ b/core/src/themes/themes.interfaces.ts @@ -1,5 +1,6 @@ import type { IonBadgeConfig, IonBadgeRecipe } from '../components/badge/badge.interfaces'; import type { IonChipConfig, IonChipRecipe } from '../components/chip/chip.interfaces'; +import type { IonContentRecipe } from '../components/content/content.interfaces'; import type { IonItemDividerRecipe } from '../components/item-divider/item-divider.interfaces'; import type { IonProgressBarConfig, IonProgressBarRecipe } from '../components/progress-bar/progress-bar.interfaces'; import type { IonSpinnerConfig, IonSpinnerRecipe } from '../components/spinner/spinner.interfaces'; @@ -287,6 +288,7 @@ type Components = { IonAvatar?: any; IonBadge?: IonBadgeRecipe; IonChip?: IonChipRecipe; + IonContent?: IonContentRecipe; IonItemDivider?: IonItemDividerRecipe; IonProgressBar?: IonProgressBarRecipe; IonSpinner?: IonSpinnerRecipe; diff --git a/core/src/utils/content/index.ts b/core/src/utils/content/index.ts index 44a6f7bff13..0a73a672b5a 100644 --- a/core/src/utils/content/index.ts +++ b/core/src/utils/content/index.ts @@ -1,4 +1,4 @@ -import { componentOnReady } from '../helpers'; +import { waitForComponent } from '../helpers'; import { printRequiredElementError } from '../logging'; const ION_CONTENT_TAG_NAME = 'ION-CONTENT'; @@ -27,7 +27,7 @@ export const isIonContent = (el: Element) => el.tagName === ION_CONTENT_TAG_NAME */ export const getScrollElement = async (el: Element) => { if (isIonContent(el)) { - await new Promise((resolve) => componentOnReady(el, resolve)); + await waitForComponent(el); return (el as HTMLIonContentElement).getScrollElement(); } diff --git a/core/src/utils/framework-delegate.ts b/core/src/utils/framework-delegate.ts index 37616c037ac..ad858d2e636 100644 --- a/core/src/utils/framework-delegate.ts +++ b/core/src/utils/framework-delegate.ts @@ -1,7 +1,7 @@ import { config } from '../global/config'; import type { ComponentRef, FrameworkDelegate } from '../interface'; -import { componentOnReady } from './helpers'; +import { waitForComponent } from './helpers'; // TODO(FW-2832): types @@ -31,7 +31,7 @@ export const attachComponent = async ( container.appendChild(el); - await new Promise((resolve) => componentOnReady(el, resolve)); + await waitForComponent(el); return el; }; @@ -91,7 +91,7 @@ export const CoreDelegate = () => { ChildComponent = el; - await new Promise((resolve) => componentOnReady(el, resolve)); + await waitForComponent(el); } else if ( BaseComponent.children.length > 0 && (BaseComponent.tagName === 'ION-MODAL' || BaseComponent.tagName === 'ION-POPOVER') diff --git a/core/src/utils/helpers.ts b/core/src/utils/helpers.ts index 17a563b601e..d3a3d327d05 100644 --- a/core/src/utils/helpers.ts +++ b/core/src/utils/helpers.ts @@ -83,6 +83,15 @@ export const componentOnReady = (el: any, callback: any) => { } }; +/** + * Promise-based wrapper around componentOnReady. Use when you need to await + * component readiness before accessing internal refs (e.g. in early lifecycle + * hooks like Vue onMounted with the custom elements build). + */ +export const waitForComponent = (el: T): Promise => { + return new Promise((resolve) => componentOnReady(el, () => resolve(el))); +}; + /** * This functions checks if a Stencil component is using * the lazy loaded build of Stencil. Returns `true` if diff --git a/core/src/utils/input-shims/hacks/scroll-padding.ts b/core/src/utils/input-shims/hacks/scroll-padding.ts index 8dc266972f2..8216d0f6cbe 100644 --- a/core/src/utils/input-shims/hacks/scroll-padding.ts +++ b/core/src/utils/input-shims/hacks/scroll-padding.ts @@ -26,10 +26,10 @@ export const setScrollPadding = (contentEl: HTMLElement, paddingAmount: number, } if (paddingAmount > 0) { - contentEl.style.setProperty('--keyboard-offset', `${paddingAmount}px`); + contentEl.style.setProperty('--internal-keyboard-offset', `${paddingAmount}px`); } else { (contentEl as any)[PADDING_TIMER_KEY] = setTimeout(() => { - contentEl.style.setProperty('--keyboard-offset', '0px'); + contentEl.style.setProperty('--internal-keyboard-offset', '0px'); if (clearCallback) { clearCallback(); } diff --git a/core/src/utils/input-shims/hacks/test/scroll-assist.e2e.ts b/core/src/utils/input-shims/hacks/test/scroll-assist.e2e.ts index 9edfc45e7d2..2a45d285c92 100644 --- a/core/src/utils/input-shims/hacks/test/scroll-assist.e2e.ts +++ b/core/src/utils/input-shims/hacks/test/scroll-assist.e2e.ts @@ -167,7 +167,7 @@ class ScrollAssistFixture { await this.focusInput(interactiveSelector, inputSelector); - await expect(content).not.toHaveCSS('--keyboard-offset', '0px'); + await expect(content).not.toHaveCSS('--internal-keyboard-offset', '0px'); } async expectNotToHaveScrollPadding(interactiveSelector: string, inputSelector: string) { @@ -175,6 +175,6 @@ class ScrollAssistFixture { await this.focusInput(interactiveSelector, inputSelector); - await expect(content).toHaveCSS('--keyboard-offset', '0px'); + await expect(content).toHaveCSS('--internal-keyboard-offset', '0px'); } } diff --git a/core/src/utils/input-shims/input-shims.ts b/core/src/utils/input-shims/input-shims.ts index 879fbfff870..9f7879790af 100644 --- a/core/src/utils/input-shims/input-shims.ts +++ b/core/src/utils/input-shims/input-shims.ts @@ -2,7 +2,7 @@ import { doc } from '@utils/browser'; import type { Config } from '../../interface'; import { findClosestIonContent } from '../content'; -import { componentOnReady } from '../helpers'; +import { waitForComponent } from '../helpers'; import { Keyboard } from '../native/keyboard'; import { enableHideCaretOnScroll } from './hacks/hide-caret'; @@ -59,7 +59,7 @@ export const startInputShims = async (config: Config, platform: 'ios' | 'android const keyboardResizeMode = await Keyboard.getResizeMode(); const registerInput = async (componentEl: HTMLElement) => { - await new Promise((resolve) => componentOnReady(componentEl, resolve)); + await waitForComponent(componentEl); const inputRoot = componentEl.shadowRoot || componentEl; const inputEl = inputRoot.querySelector('input') || inputRoot.querySelector('textarea'); diff --git a/core/src/utils/menu-controller/index.ts b/core/src/utils/menu-controller/index.ts index dca6c5bc8ce..bdb72963f41 100644 --- a/core/src/utils/menu-controller/index.ts +++ b/core/src/utils/menu-controller/index.ts @@ -5,7 +5,7 @@ import { printIonWarning } from '@utils/logging'; import type { MenuI, MenuControllerI } from '../../components/menu/menu-interface'; import type { AnimationBuilder } from '../../interface'; -import { componentOnReady } from '../helpers'; +import { waitForComponent } from '../helpers'; import { menuOverlayAnimation } from './animations/overlay'; import { menuPushAnimation } from './animations/push'; @@ -218,11 +218,7 @@ const createMenuController = (): MenuControllerI => { }; const waitUntilReady = () => { - return Promise.all( - Array.from(document.querySelectorAll('ion-menu')).map( - (menu) => new Promise((resolve) => componentOnReady(menu, resolve)) - ) - ); + return Promise.all(Array.from(document.querySelectorAll('ion-menu')).map((menu) => waitForComponent(menu))); }; registerAnimation('reveal', menuRevealAnimation); diff --git a/core/src/utils/overlays.ts b/core/src/utils/overlays.ts index 1271a999285..25485bf0ea7 100644 --- a/core/src/utils/overlays.ts +++ b/core/src/utils/overlays.ts @@ -27,10 +27,10 @@ import { BACKDROP_NO_SCROLL } from './gesture/gesture-controller'; import { OVERLAY_BACK_BUTTON_PRIORITY } from './hardware-back-button'; import { addEventListener, - componentOnReady, focusVisibleElement, getElementRoot, removeEventListener, + waitForComponent, } from './helpers'; let lastOverlayIndex = 0; @@ -126,7 +126,7 @@ export const createOverlay = ( // eslint-disable-next-line @typescript-eslint/prefer-optional-chain if (typeof window !== 'undefined' && typeof window.customElements !== 'undefined') { return window.customElements.whenDefined(tagName).then(() => { - const element = document.createElement(tagName) as HTMLIonOverlayElement; + const element = document.createElement(tagName) as T; element.classList.add('overlay-hidden'); /** @@ -138,7 +138,7 @@ export const createOverlay = ( // append the overlay element to the document body getAppRoot(document).appendChild(element); - return new Promise((resolve) => componentOnReady(element, resolve)); + return waitForComponent(element); }); } return Promise.resolve() as any; diff --git a/core/src/utils/status-tap.ts b/core/src/utils/status-tap.ts index 6f267fcaa59..d5ae5efaccb 100644 --- a/core/src/utils/status-tap.ts +++ b/core/src/utils/status-tap.ts @@ -1,7 +1,7 @@ import { readTask, writeTask } from '@stencil/core'; import { findClosestIonContent, scrollToTop } from './content'; -import { componentOnReady } from './helpers'; +import { waitForComponent } from './helpers'; export const startStatusTap = () => { const win = window; @@ -15,7 +15,7 @@ export const startStatusTap = () => { } const contentEl = findClosestIonContent(el); if (contentEl) { - new Promise((resolve) => componentOnReady(contentEl, resolve)).then(() => { + waitForComponent(contentEl).then(() => { writeTask(async () => { /** * If scrolling and user taps status bar, diff --git a/packages/angular/src/directives/proxies.ts b/packages/angular/src/directives/proxies.ts index 209bb68e4a9..4268794797b 100644 --- a/packages/angular/src/directives/proxies.ts +++ b/packages/angular/src/directives/proxies.ts @@ -594,7 +594,7 @@ export declare interface IonCol extends Components.IonCol {} @ProxyCmp({ - inputs: ['color', 'fixedSlotPlacement', 'forceOverscroll', 'fullscreen', 'mode', 'scrollEvents', 'scrollX', 'scrollY', 'theme'], + inputs: ['color', 'fixedSlotPlacement', 'forceOverscroll', 'fullscreen', 'mode', 'scrollEvents', 'scrollX', 'scrollY'], methods: ['getScrollElement', 'scrollToTop', 'scrollToBottom', 'scrollByPoint', 'scrollToPoint'] }) @Component({ @@ -602,7 +602,7 @@ export declare interface IonCol extends Components.IonCol {} changeDetection: ChangeDetectionStrategy.OnPush, template: '', // eslint-disable-next-line @angular-eslint/no-inputs-metadata-property - inputs: ['color', 'fixedSlotPlacement', 'forceOverscroll', 'fullscreen', 'mode', 'scrollEvents', 'scrollX', 'scrollY', 'theme'], + inputs: ['color', 'fixedSlotPlacement', 'forceOverscroll', 'fullscreen', 'mode', 'scrollEvents', 'scrollX', 'scrollY'], }) export class IonContent { protected el: HTMLIonContentElement; diff --git a/packages/angular/standalone/src/directives/proxies.ts b/packages/angular/standalone/src/directives/proxies.ts index ecff6aba767..2bd93c24163 100644 --- a/packages/angular/standalone/src/directives/proxies.ts +++ b/packages/angular/standalone/src/directives/proxies.ts @@ -666,7 +666,7 @@ export declare interface IonCol extends Components.IonCol {} @ProxyCmp({ defineCustomElementFn: defineIonContent, - inputs: ['color', 'fixedSlotPlacement', 'forceOverscroll', 'fullscreen', 'mode', 'scrollEvents', 'scrollX', 'scrollY', 'theme'], + inputs: ['color', 'fixedSlotPlacement', 'forceOverscroll', 'fullscreen', 'mode', 'scrollEvents', 'scrollX', 'scrollY'], methods: ['getScrollElement', 'scrollToTop', 'scrollToBottom', 'scrollByPoint', 'scrollToPoint'] }) @Component({ @@ -674,7 +674,7 @@ export declare interface IonCol extends Components.IonCol {} changeDetection: ChangeDetectionStrategy.OnPush, template: '', // eslint-disable-next-line @angular-eslint/no-inputs-metadata-property - inputs: ['color', 'fixedSlotPlacement', 'forceOverscroll', 'fullscreen', 'mode', 'scrollEvents', 'scrollX', 'scrollY', 'theme'], + inputs: ['color', 'fixedSlotPlacement', 'forceOverscroll', 'fullscreen', 'mode', 'scrollEvents', 'scrollX', 'scrollY'], standalone: true }) export class IonContent {