From 9187065f94fb1a35f99cba2392e956be16312b2e Mon Sep 17 00:00:00 2001 From: Liam DeBeasi Date: Mon, 11 Sep 2023 14:30:12 -0400 Subject: [PATCH 01/11] docs: add docs for dynamic font scaling (#3111) --- docs/layout/dynamic-font-scaling.md | 220 ++++++++++++++++++ docs/layout/global-stylesheets.md | 2 +- sidebars.js | 1 + .../angular/example_component_html.md | 45 ++++ .../angular/global_css.md | 5 + .../v7/layout/dynamic-font-scaling/demo.html | 71 ++++++ .../v7/layout/dynamic-font-scaling/index.md | 34 +++ .../layout/dynamic-font-scaling/javascript.md | 51 ++++ .../dynamic-font-scaling/react/main_css.md | 5 + .../dynamic-font-scaling/react/main_tsx.md | 74 ++++++ .../v7/layout/dynamic-font-scaling/vue.md | 98 ++++++++ 11 files changed, 605 insertions(+), 1 deletion(-) create mode 100644 docs/layout/dynamic-font-scaling.md create mode 100644 static/usage/v7/layout/dynamic-font-scaling/angular/example_component_html.md create mode 100644 static/usage/v7/layout/dynamic-font-scaling/angular/global_css.md create mode 100644 static/usage/v7/layout/dynamic-font-scaling/demo.html create mode 100644 static/usage/v7/layout/dynamic-font-scaling/index.md create mode 100644 static/usage/v7/layout/dynamic-font-scaling/javascript.md create mode 100644 static/usage/v7/layout/dynamic-font-scaling/react/main_css.md create mode 100644 static/usage/v7/layout/dynamic-font-scaling/react/main_tsx.md create mode 100644 static/usage/v7/layout/dynamic-font-scaling/vue.md diff --git a/docs/layout/dynamic-font-scaling.md b/docs/layout/dynamic-font-scaling.md new file mode 100644 index 00000000000..384a71fd635 --- /dev/null +++ b/docs/layout/dynamic-font-scaling.md @@ -0,0 +1,220 @@ +# Dynamic Font Scaling + +Dynamic Font Scaling is a feature that allows users to choose the size of the text displayed on the screen. This helps users who need larger text for better readability, and it also accommodates users who can read smaller text. + +Dynamic Font Scaling is supported on Android, iOS, and iPadOS starting in Ionic v7.5. + +## Try It Out + +:::tip +Be sure to try this on an Android, iOS, or iPadOS device. + +If you are testing on Chrome for Android, make sure ["Accessibility Page Zoom"](#chrome-for-android) is enabled. +::: + +Follow the [Changing the Font Size on a Device](#changing-the-font-size-on-a-device) guide to set your preferred font size, and watch the text in the demo below grow or shrink according to your preferences. + +import DynamicFontScaling from '@site/static/usage/v7/layout/dynamic-font-scaling/index.md'; + + + +## Enabling Dynamic Font Scaling in Ionic + +:::info +This feature is currently opt-in on iOS. However, it will be enabled by default starting in Ionic 8 at which point the following CSS will no longer be necessary. +::: + +Dynamic Font Scaling is already enabled by default on Android. Developers can enable it on iOS using the following steps: + +1. Ensure that the [typography.css](./global-stylesheets#typographycss) file is imported. +2. Add the following CSS to a global stylesheet: + +```css +html { + --ion-dynamic-font: var(--ion-default-dynamic-font); +} +``` + +:::note +Under the hood, Ionic sets the following CSS on iOS devices to enable Dynamic Font Scaling: + +```css +html { + font: var(--ion-dynamic-font); +} +``` + +::: + +## Using Dynamic Font Scaling + +### Integrating Custom Components + +Developers can configure their custom components to take advantage of Dynamic Font Scaling by converting any `font-size` declarations that use `px` units to use [rem units](https://developer.mozilla.org/en-US/docs/Learn/CSS/Building_blocks/Values_and_units#lengths) instead. An easy way to convert from `px` to `rem` is to divide the pixel font size by the default browser font size, which is typically `16px`. For example, if a component has a font size of `14px`, then this could be converted to `rem` by doing `14px / 16px = 0.875rem`. Also note that any Ionic components that have had their font sizes overridden should also be updated to use `rem` units. + +One thing to keep in mind is that the dimensions of your components may need to change to accommodate the larger font sizes. For example, `width` and `height` properties may need to change to `min-width` and `min-height`, respectively. Developers should audit their applications for any CSS properties that use [length values](https://developer.mozilla.org/en-US/docs/Web/CSS/length) and make any applicable conversions from `px` to `rem`. We also recommend having long text wrap to the next line instead of truncating to keep large text readable. + +### Custom Font Family + +We recommend using the default fonts in Ionic as they are designed to look good at any size and ensure consistency with other mobile apps. However, developers can use a custom font family with Dynamic Font Scaling via CSS: + +```css +html { + --ion-dynamic-font: var(--ion-default-dynamic-font); + --ion-font-family: 'Comic Sans'; +} +``` + +### `em` units versus `rem` units + +Developers have two options for relative font sizes: [`em` and `rem`](https://developer.mozilla.org/en-US/docs/Learn/CSS/Building_blocks/Values_and_units#ems_and_rems). + +`em` units set the font size of an element relative to the font size of its parent. + +In the following example, the computed font size of `.child` is `40px` because it is a child of `.parent` (`2em * 20px = 40px`). + +```css +.parent { + font-size: 20px; +} + +.child { + font-size: 2em; +} +``` + +However, the `em` unit has a compounding effect which can cause issues. In the following example, the second `.child` element has a computed font size of `80px` since the font sizes compound. + +```html +
+ Parent element with 20px +
+ Child element with 40px +
Child element with 80px
+
+
+``` + +
+ Parent element with 20px +
+ Child element with 40px +
Child element with 80px
+
+
+ +Due to this compounding effect, we strongly recommend using `rem` units instead of `em` units when working with Dynamic Font Scaling. `rem` units set the font size of an element relative to the font size of the root element, which is typically ``. The default font size of the root element is typically `16px`. + +In the following example, the computed font size of `.child` is `32px` because the font size is being computed relative to `html`, not `.parent`. + +```css +.parent { + font-size: 20px; +} + +.child { + font-size: 2rem; +} +``` + +## How Dynamic Font Scaling works in Ionic + +Ionic components that define font sizes and participate in Dynamic Font Scaling typically use [rem units](https://developer.mozilla.org/en-US/docs/Learn/CSS/Building_blocks/Values_and_units#lengths). This sizes the text in each component relative to the font size of the root element, which is usually the `html` element. This means that as the root element's font size changes, the text in all Ionic components scale in a consistent manner. This avoids the need to manually override each component's font size. Some elements inside of these components, such as icons, use `em` units instead so the elements are sized relative to the text, though the text itself is sized using `rem` units. + +### iOS + +Dynamic Font Scaling in Ionic builds on top of an iOS feature called [Dynamic Type](https://developer.apple.com/documentation/uikit/uifont/scaling_fonts_automatically#overview). To do this, Ionic sets the [font](https://developer.mozilla.org/en-US/docs/Web/CSS/font) of the root element to an Apple-defined text style. For consistency, Ionic uses the [body](https://developer.apple.com/documentation/uikit/uifont/textstyle/1616682-body) text style. + +Using the Apple-defined text style enables Dynamic Type, allowing all text in Ionic components to scale according to the system-level preference. Note that these Apple-defined fonts only work on Apple devices. As a result, these fonts will not work on Android devices even if your app is using `ios` mode. + +Ionic follows [Apple's Human Interface Guidelines for Typography](https://developer.apple.com/design/human-interface-guidelines/typography) when an app is in `ios` mode. As a result, important content is prioritized when the text size changes. This means a few things: + +1. Content in an `ion-header` or an `ion-footer` will have maximum font sizes to prioritize content in `ion-content` which is deemed more important than content in the `ion-header` and `ion-footer`. +2. Components such as `ion-badge` and `ion-back-button` will have minimum font sizes so they remain readable. +3. Text in components such as `ion-tab-bar` and `ion-picker` do not participate in Dynamic Font Scaling according to Apple's Human Interface Guidelines. + +### Android Web View + +The Android Web View's font scaling mechanism is always enabled in web content and will automatically scale font sizes defined using the `px` unit. This means that any maximum or minimum font sizes specified using `px` will still be scaled even if the final font size does not align with the maximum or minimum font sizes specified. + +In the following example we are using the [min()](https://developer.mozilla.org/en-US/docs/Web/CSS/min) function to indicate that the font size of `.foo` should be no larger than `14px`. + +```css +.foo { + font-size: min(1rem, 14px); +} +``` + +If the root element's default font size is `16px`, and the system-level font scale is `1.5` (i.e text sizes should be increased by 50%), then `1rem` will evaluate to `24px` because `16 * 1.5 = 24`. + +This is larger than our defined maximum of `14px`, so one might assume that the evaluated font size of `.foo` is `14px`. However, since the Android Web View scales any font sizes defined using the `px` unit, this means the `14px` used in our `min()` function will also be scaled by 1.5. + +As a result, this means that the maximum computed font size is actually `21px` since `14 * 1.5 = 21` and therefore the overall computed font size of `.foo` is `21px`. + +### Chrome for Android + +The Chrome Web Browser on Android behaves differently than the Android Web View. By default, Chrome for Android does not respect the system-level font scale setting. However, the Chromium team is working on a new feature to allow for this. When enabled, this feature will change the `zoom` level of the `html` element which will cause the layout to increase in size in addition to the text. + +Developers can test this behavior by enabling the experimental "Accessibility Page Zoom" feature in `chrome://flags`. + +See https://bugs.chromium.org/p/chromium/issues/detail?id=645717 for more information. + +### Using Modes on Different Platforms + +Each platform has slightly different font scaling behaviors, and the `ios` and `md` modes have been implemented to take advantage of the scaling behaviors on their respective platforms. + +For example, `ios` mode makes use of maximum and minimum font sizes to follow [Apple's Human Interface Guidelines for Typography](https://developer.apple.com/design/human-interface-guidelines/typography). `md` mode does not implement this same behavior because Material Design does not have that same guidance. This means that using `md` mode on an iOS device may allow for very large font sizes in headers and footers. + +As a result, we strongly recommend using `ios` mode on iOS devices and `md` mode on Android devices when using Dynamic Font Scaling. + +## Changing the Font Size on a Device + +Font scaling preferences are configured on a per-device basis by the user. This allows the user to scale the font for all applications that support this behavior. This guide shows how to enable font scaling for each platform. + +### iOS + +Font scaling on iOS can be configured in the Settings app. + +See [Apple Support](https://support.apple.com/en-us/102453) for more information. + +### Android + +Where users access the font scaling configuration varies across devices, but it is typically found in the "Accessibility" page in the Settings app. + +:::info +The Chrome Web Browser on Android has some limitations with respecting system-level font scales. See [Chrome for Android](#chrome-for-android) for more information. +::: + +## Troubleshooting + +### Dynamic Font Scaling is not working + +There are a number of reasons why Dynamic Font Scaling may not have any effect on an app. The following list, while not exhaustive, provides some things to check to debug why Dynamic Font Scaling is not working. + +1. Verify that your version of Ionic supports Dynamic Font Scaling. Dynamic Font Scaling was added starting in Ionic v7.5. +2. Dynamic Font Scaling is opt-in on iOS in Ionic 7. Verify that the proper CSS has been set. See [Enabling Dynamic Font Scaling in Ionic](#enabling-dynamic-font-scaling-in-ionic) for more information. +3. Verify that your code does not override the root element's default font size. Manually setting a font size on the root element will prevent Dynamic Font Scaling from working as intended. +4. Verify that your code does not override font sizes on Ionic components. Ionic components that set `font-size` rules will use `rem` units. However, if your app overrides that to use `px`, then that custom rule will need to be converted to use `rem`. See [Integrating Custom Components](#integrating-custom-components) for more information. +5. Verify "Accessibility Page Zoom" is enabled if using Chrome for Android. See [Chrome for Android](#chrome-for-android) for more information. + +### Maximum and minimum font sizes are not being respected on Android + +The Android Web View scales any font sizes defined using the `px` unit by the system-level font scale preference. This means that actual font sizes may be larger or smaller than the font sizes defined in [min()](https://developer.mozilla.org/en-US/docs/Web/CSS/min), [max()](https://developer.mozilla.org/en-US/docs/Web/CSS/max), or [clamp()](https://developer.mozilla.org/en-US/docs/Web/CSS/clamp). + +See [how font scaling works on Android](#android) for more information. + +### Font sizes are larger/smaller even with Dynamic Font Scaling disabled + +Ionic components define font sizes using [rem units](https://developer.mozilla.org/en-US/docs/Learn/CSS/Building_blocks/Values_and_units#lengths) even when Dynamic Font Scaling is disabled. This sizes the text in each component relative to the font size of the root element, which is usually the `html` element. As a result, if the font size of `html` changes, the computed font size of all Ionic components will change too. + +### Scaled Ionic iOS component font sizes do not exactly match native iOS equivalents + +Certain native iOS components such as the Action Sheet make use of private font scales that Ionic does not have access to. While we try to stay as close as possible to the native behavior, text in some components may render slightly larger or smaller than their native counterparts. + +### The text size in my Ionic app on iOS changed when enabling Dynamic Font Scaling + +The root element's default font size is typically `16px`. However, Dynamic Font Scaling on iOS devices make use of the ["Body" text style](https://developer.apple.com/design/human-interface-guidelines/typography#Specifications) which has a default font size of `17px`. Since the text in Ionic components is scaled relative to the root element's font size, some text may get larger or smaller when Dynamic Font Scaling is enabled, even if the system-level text scale did not change. + +:::info +iOS provides a "Callout" text style which has a default font size of `16px`. However, this font style is currently not exposed to web content. See [the supported text styles in WebKit](https://webkit.org/blog/3709/using-the-system-font-in-web-content/) for more information. +::: diff --git a/docs/layout/global-stylesheets.md b/docs/layout/global-stylesheets.md index 0078859a52b..a271094eafe 100644 --- a/docs/layout/global-stylesheets.md +++ b/docs/layout/global-stylesheets.md @@ -34,7 +34,7 @@ Applies styles to `` and defaults `box-sizing` to `border-box`. It ensures #### typography.css -Typography changes the font-family of the entire document and modifies the font styles for heading elements. It also applies positioning styles to some native text elements. +Typography changes the font-family of the entire document and modifies the font styles for heading elements. It also applies positioning styles to some native text elements. This file is necessary for [Dynamic Font Scaling](./dynamic-font-scaling) to work. #### normalize.css diff --git a/sidebars.js b/sidebars.js index c08a43ada8f..76e4d28bc4c 100644 --- a/sidebars.js +++ b/sidebars.js @@ -41,6 +41,7 @@ module.exports = { }, 'layout/global-stylesheets', 'layout/css-utilities', + 'layout/dynamic-font-scaling', ], }, { diff --git a/static/usage/v7/layout/dynamic-font-scaling/angular/example_component_html.md b/static/usage/v7/layout/dynamic-font-scaling/angular/example_component_html.md new file mode 100644 index 00000000000..76687ae8b81 --- /dev/null +++ b/static/usage/v7/layout/dynamic-font-scaling/angular/example_component_html.md @@ -0,0 +1,45 @@ +```html + + + + + + Title + + + + + + + + + + + + + + Check for a free puppy + + + Enable Notifications + + + + + + Item 1 + + + Item 2 + + + Item 3 + + + + + + Footer + + +``` diff --git a/static/usage/v7/layout/dynamic-font-scaling/angular/global_css.md b/static/usage/v7/layout/dynamic-font-scaling/angular/global_css.md new file mode 100644 index 00000000000..2817f808062 --- /dev/null +++ b/static/usage/v7/layout/dynamic-font-scaling/angular/global_css.md @@ -0,0 +1,5 @@ +```css +html { + --ion-dynamic-font: var(--ion-default-dynamic-font); +} +``` diff --git a/static/usage/v7/layout/dynamic-font-scaling/demo.html b/static/usage/v7/layout/dynamic-font-scaling/demo.html new file mode 100644 index 00000000000..bdec1645a91 --- /dev/null +++ b/static/usage/v7/layout/dynamic-font-scaling/demo.html @@ -0,0 +1,71 @@ + + + + + + Label + + + + + + + + + + + + + + + Title + + + + + + + + + + + + + + Check for a free puppy + + + Enable Notifications + + + + + + Item 1 + + + Item 2 + + + Item 3 + + + + + + Footer + + + + + diff --git a/static/usage/v7/layout/dynamic-font-scaling/index.md b/static/usage/v7/layout/dynamic-font-scaling/index.md new file mode 100644 index 00000000000..b5bd9d0cec2 --- /dev/null +++ b/static/usage/v7/layout/dynamic-font-scaling/index.md @@ -0,0 +1,34 @@ +import Playground from '@site/src/components/global/Playground'; + +import javascript from './javascript.md'; + +import react_main_tsx from './react/main_tsx.md'; +import react_main_css from './react/main_css.md'; + +import vue from './vue.md'; + +import angular_example_component_html from './angular/example_component_html.md'; +import angular_global_css from './angular/global_css.md'; + + diff --git a/static/usage/v7/layout/dynamic-font-scaling/javascript.md b/static/usage/v7/layout/dynamic-font-scaling/javascript.md new file mode 100644 index 00000000000..3240686f9c5 --- /dev/null +++ b/static/usage/v7/layout/dynamic-font-scaling/javascript.md @@ -0,0 +1,51 @@ +```html + + + + + + Title + + + + + + + + + + + + + + Check for a free puppy + + + Enable Notifications + + + + + + Item 1 + + + Item 2 + + + Item 3 + + + + + + Footer + + + + +``` diff --git a/static/usage/v7/layout/dynamic-font-scaling/react/main_css.md b/static/usage/v7/layout/dynamic-font-scaling/react/main_css.md new file mode 100644 index 00000000000..2817f808062 --- /dev/null +++ b/static/usage/v7/layout/dynamic-font-scaling/react/main_css.md @@ -0,0 +1,5 @@ +```css +html { + --ion-dynamic-font: var(--ion-default-dynamic-font); +} +``` diff --git a/static/usage/v7/layout/dynamic-font-scaling/react/main_tsx.md b/static/usage/v7/layout/dynamic-font-scaling/react/main_tsx.md new file mode 100644 index 00000000000..863d4e269d3 --- /dev/null +++ b/static/usage/v7/layout/dynamic-font-scaling/react/main_tsx.md @@ -0,0 +1,74 @@ +```tsx +import React from 'react'; +import { + IonBackButton, + IonButton, + IonButtons, + IonCheckbox, + IonContent, + IonFooter, + IonHeader, + IonIcon, + IonInput, + IonItem, + IonLabel, + IonList, + IonTitle, + IonToggle, + IonToolbar, +} from '@ionic/react'; +import { create } from 'ionicons/icons'; + +import './main.css'; + +function Example() { + return ( + <> + + + + + + Title + + + + + + + + + + + + + + Check for a free puppy + + + Enable Notifications + + + + + + Item 1 + + + Item 2 + + + Item 3 + + + + + + Footer + + + + ); +} +export default Example; +``` diff --git a/static/usage/v7/layout/dynamic-font-scaling/vue.md b/static/usage/v7/layout/dynamic-font-scaling/vue.md new file mode 100644 index 00000000000..5e0f13156c6 --- /dev/null +++ b/static/usage/v7/layout/dynamic-font-scaling/vue.md @@ -0,0 +1,98 @@ +```html + + + + + +``` From 927182733d8c0c123fb7e98f575d2f788d0e7d3b Mon Sep 17 00:00:00 2001 From: Liam DeBeasi Date: Tue, 19 Sep 2023 11:05:31 -0400 Subject: [PATCH 02/11] docs(angular): add standalone documentation (#3115) --- docs/angular/build-options.md | 381 ++++++++++++++++++++++++++++++++++ docs/angular/overview.md | 4 + sidebars.js | 1 + 3 files changed, 386 insertions(+) create mode 100644 docs/angular/build-options.md diff --git a/docs/angular/build-options.md b/docs/angular/build-options.md new file mode 100644 index 00000000000..ba05435cdfb --- /dev/null +++ b/docs/angular/build-options.md @@ -0,0 +1,381 @@ +# Build Options + +Developers have two options for using Ionic components: Standalone or Modules. This guide covers both options as well as the benefits and downsides of each approach. + +While the Standalone approach is newer and makes use of more modern Angular APIs, the Modules approach will continue to be supported in Ionic. Most of the Angular examples on this documentation website use the Modules approach. + +## Standalone + +:::info +Ionic UI components as Angular standalone components is supported starting in Ionic v7.5. +::: + +### Overview + +Developers can use Ionic components as standalone components to take advantage of treeshaking and newer Angular features. This option involves importing specific Ionic components in the Angular components you want to use them in. Developers can use Ionic standalone components even if their Angular application is NgModule-based. + +See the [Standalone Migration Guide](#migrating-from-modules-to-standalone) for instructions on how to update your Ionic app to make use of Ionic standalone components. + +**Benefits** + +1. Enables treeshaking so the final build output only includes the code necessary to run your app which reduces overall build size. +2. Avoids the use of `NgModule`s to streamline the development experience and make your code easier to understand. +3. Allows developers to also use newer Angular features such as [ESBuild](https://angular.io/guide/esbuild). + +**Drawbacks** + +1. Ionic components need to be imported into every Angular component they are used in which can be time consuming to set up. + +### Usage + +:::caution +All Ionic imports should be imported from the `@ionic/angular/standalone` submodule. This includes imports such as components, directives, providers, and types. Importing from `@ionic/angular` may pull in lazy loaded Ionic code which can interfere with treeshaking. +::: + +**Bootstrapping and Configuration** + +Ionic Angular needs to be configured when the Angular application calls `bootstrapApplication` using the `provideIonicAngular` function. Developers can pass any [IonicConfig](../developing/config#ionicconfig) values as an object in this function. Note that `provideIonicAngular` needs to be called even if no custom config is passed. + +```typescript +import { enableProdMode, importProvidersFrom } from '@angular/core'; +import { bootstrapApplication } from '@angular/platform-browser'; +import { RouteReuseStrategy, provideRouter } from '@angular/router'; +import { provideIonicAngular, IonicRouteStrategy } from '@ionic/angular/standalone'; + +import { routes } from './app/app.routes'; +import { AppComponent } from './app/app.component'; +import { environment } from './environments/environment'; + +if (environment.production) { + enableProdMode(); +} + +bootstrapApplication(AppComponent, { + providers: [ + { provide: RouteReuseStrategy, useClass: IonicRouteStrategy }, + provideIonicAngular({ mode: 'ios' }), + provideRouter(routes), + ], +}); +``` + +**Components** + +In the example below, we are importing `IonContent` and `IonButton` from `@ionic/angular/standalone` and passing them to `imports` for use in the component template. We would get a compiler error if these components were not imported and provided to the `imports` array. + +```typescript +import { Component } from '@angular/core'; +import { IonButton, IonContent } from '@ionic/angular/standalone'; + +@Component({ + selector: 'app-home', + templateUrl: 'home.page.html', + styleUrls: ['home.page.scss'], + standalone: true, + imports: [IonButton, IonContent], +}) +export class HomePage { + constructor() {} +} +``` + +**Icons** + +The icon SVG data needs to be defined in the Angular component so it can be loaded correctly. Developers can use the `addIcons` function from `ionicons` to map the SVG data to a string name. Developers can then reference the icon by its string name using the `name` property on `IonIcon`. + +We recommend calling `addIcons` in the Angular component `constructor` so the data is only added if the Angular component is being used. + +For developers using Ionicons 7.2 or newer, passing only the SVG data will cause the string name to be automatically generated. + +```typescript +import { Component } from '@angular/core'; +import { IonIcon } from '@ionic/angular/standalone'; +import { addIcons } from 'ionicons'; +import { logoIonic } from 'ionicons/icons'; + +@Component({ + selector: 'app-home', + templateUrl: 'home.page.html', + styleUrls: ['home.page.scss'], + standalone: true, + imports: [IonIcon], +}) +export class HomePage { + constructor() { + /** + * On Ionicons 7.2+ this icon + * gets mapped to a "logo-ionic" key. + * Alternatively, developers can do: + * addIcons({ 'logo-ionic': logoIonic }); + */ + addIcons({ logoIonic }); + } +} +``` + +## Modules + +### Overview + +Developers can also use the Modules approach by importing `IonicModule` and calling `IonicModule.forRoot()` in the `imports` array in `app.module.ts`. This registers a version of Ionic where Ionic components will be lazily loaded at runtime. + +**Benefits** + +1. Since components are lazily loaded as needed, developers do not need to spend time manually importing and registering each Ionic component. + +**Drawbacks** + +1. Lazily loading Ionic components means that the compiler does not know which components are needed at build time. This means your final application bundle may be much larger than it needs to be. +2. Developers are unable to use newer Angular features such as [ESBuild](https://angular.io/guide/esbuild). + +### Usage + +In the example below, we are using `IonicModule` to create a lazily loaded version of Ionic. We can then reference any Ionic component without needing to explicitly import it. + +```typescript +import { NgModule } from '@angular/core'; +import { BrowserModule } from '@angular/platform-browser'; + +import { IonicModule } from '@ionic/angular'; + +import { AppComponent } from './app.component'; + +@NgModule({ + declarations: [AppComponent], + imports: [BrowserModule, IonicModule.forRoot()], + bootstrap: [AppComponent], +}) +export class AppModule {} +``` + +## Migrating from Modules to Standalone + +The Standalone option is newer than the Modules option, so developers may wish to switch during the development of their application. This guide details the steps needed to migrate. + +Migrating to Ionic standalone components must be done all at the same time and cannot be done gradually. The Modules and Standalone approaches use two different build systems of Ionic that cannot be used at the same time. + +### Standalone-based Applications + +Follow these steps if your Angular application is already using the standalone architecture, and you want to use Ionic UI components as standalone components too. + +1. Run `npm install @ionic/angular@latest` to ensure you are running the latest version of Ionic. Ionic UI Standalone Components is supported in Ionic v7.5 or newer. + +2. Run `npm install ionicons@latest` to ensure you are running the latest version of Ionicons. Ionicons v7.2 brings usability improvements that reduce the code boilerplate needed to use icons with standalone components. + +3. Remove the `IonicModule` call in `main.ts` in favor of `provideIonicAngular` imported from `@ionic/angular/standalone`. Any config passed to `IonicModule.forRoot` can be passed as an object to this new function. + +```diff title="main.ts" +import { enableProdMode, importProvidersFrom } from '@angular/core'; +import { bootstrapApplication } from '@angular/platform-browser'; +import { RouteReuseStrategy, provideRouter } from '@angular/router'; +- import { IonicModule, IonicRouteStrategy } from '@ionic/angular'; ++ import { provideIonicAngular, IonicRouteStrategy } from '@ionic/angular/standalone'; + +import { routes } from './app/app.routes'; +import { AppComponent } from './app/app.component'; +import { environment } from './environments/environment'; + +if (environment.production) { + enableProdMode(); +} + +bootstrapApplication(AppComponent, { + providers: [ + { provide: RouteReuseStrategy, useClass: IonicRouteStrategy }, + /** + * The custom config serves as an example + * of how to pass a config to provideIonicAngular. + * You do not need to set "mode: 'ios'" to + * use Ionic standalone components. + */ +- importProvidersFrom(IonicModule.forRoot({ mode: 'ios' })), ++ provideIonicAngular({ mode: 'ios' }), + provideRouter(routes), + ], +}); +``` + +4. Remove any references to `IonicModule` found elsewhere in your application. + +5. Update any existing imports from `@ionic/angular` to import from `@ionic/angular/standalone` instead. + +```diff +- import { Platform } from '@ionic/angular'; ++ import { Platform } from '@ionic/angular/standalone'; +``` + +6. Add imports for each Ionic component in the Angular component where they are used. Be sure to pass the imports to the `imports` array on your Angular component. + +```diff title="app.component.ts" +import { Component } from '@angular/core'; ++ import { IonApp, IonRouterOutlet } from '@ionic/angular/standalone'; + +@Component({ + selector: 'app-root', + templateUrl: 'app.component.html', + styleUrls: ['app.component.scss'], + standalone: true, ++ imports: [IonApp, IonRouterOutlet], +}) +export class AppComponent { + constructor() {} +} +``` + +7. If you are using Ionicons, define the icon SVG data used in each Angular component using `addIcons`. This allows you to continue referencing icons by string name in your component template. Note that you will need to do this for any additional icons added. + +```diff title="test.component.ts" +import { Component } from '@angular/core'; ++ import { IonIcon } from '@ionic/angular/standalone'; ++ import { addIcons } from 'ionicons'; ++ import { alarm, logoIonic } from 'ionicons/icons'; + +@Component({ + selector: 'app-root', + templateUrl: 'app.component.html', + styleUrls: ['app.component.scss'], + standalone: true, ++ imports: [IonIcon], +}) +export class TestComponent { + constructor() { ++ addIcons({ alarm, logoIonic }); + } +} +``` + +8. Remove the following code from your `angular.json` file if present. Note that it may appear multiple times. + +```diff title="angular.json" +- { +- "glob": "**/*.svg", +- "input": "node_modules/ionicons/dist/ionicons/svg", +- "output": "./svg" +- } +``` + +### NgModule-based Applications + +Follow these steps if your Angular application is still using the NgModule architecture, but you want to adopt Ionic UI components as standalone components now. + +1. Run `npm install @ionic/angular@latest` to ensure you are running the latest version of Ionic. Ionic UI Standalone Components is supported in Ionic v7.5 or newer. + +2. Run `npm install ionicons@latest` to ensure you are running the latest version of Ionicons. Ionicons v7.2 brings usability improvements that reduce the code boilerplate needed to use icons with standalone components. + +3. Remove the `IonicModule` call in `app.module.ts` in favor of `provideIonicAngular` imported from `@ionic/angular/standalone`. Any config passed to `IonicModule.forRoot` can be passed as an object to this new function. + +```diff title="app.module.ts" +import { enableProdMode, importProvidersFrom } from '@angular/core'; +import { bootstrapApplication } from '@angular/platform-browser'; +import { RouteReuseStrategy, provideRouter } from '@angular/router'; +- import { IonicModule, IonicRouteStrategy } from '@ionic/angular'; ++ import { provideIonicAngular, IonicRouteStrategy } from '@ionic/angular/standalone'; + +import { routes } from './app/app.routes'; +import { AppComponent } from './app/app.component'; +import { environment } from './environments/environment'; + +if (environment.production) { + enableProdMode(); +} + +@NgModule({ + declarations: [AppComponent], +- imports: [BrowserModule, IonicModule.forRoot({ mode: 'ios' }), AppRoutingModule], ++ imports: [BrowserModule, AppRoutingModule], + providers: [ + { provide: RouteReuseStrategy, useClass: IonicRouteStrategy }, + /** + * The custom config serves as an example + * of how to pass a config to provideIonicAngular. + * You do not need to set "mode: 'ios'" to + * use Ionic standalone components. + */ ++ provideIonicAngular({ mode: 'ios' }), + ], + bootstrap: [AppComponent], +}) +export class AppModule {} +``` + +4. Remove any references to `IonicModule` found elsewhere in your application. + +5. Update any existing imports from `@ionic/angular` to import from `@ionic/angular/standalone` instead. + +```diff +- import { Platform } from '@ionic/angular'; ++ import { Platform } from '@ionic/angular/standalone'; +``` + +6. Add imports for each Ionic component in the NgModule for the Angular component where they are used. Be sure to pass the components to the `imports` array on the module. + +```diff title="app.module.ts" +import { enableProdMode, importProvidersFrom } from '@angular/core'; +import { bootstrapApplication } from '@angular/platform-browser'; +import { RouteReuseStrategy, provideRouter } from '@angular/router'; +- import { provideIonicAngular, IonicRouteStrategy } from '@ionic/angular/standalone'; ++ import { provideIonicAngular, IonicRouteStrategy, IonApp, IonRouterOutlet } from '@ionic/angular/standalone'; + +import { routes } from './app/app.routes'; +import { AppComponent } from './app/app.component'; +import { environment } from './environments/environment'; + +if (environment.production) { + enableProdMode(); +} + +@NgModule({ + declarations: [AppComponent], +- imports: [BrowserModule, AppRoutingModule], ++ imports: [BrowserModule, AppRoutingModule, IonApp, IonRouterOutlet], + providers: [ + { provide: RouteReuseStrategy, useClass: IonicRouteStrategy }, + provideIonicAngular({ mode: 'ios' }) + ], + bootstrap: [AppComponent], +}) +export class AppModule {} +``` + +7. If you are using Ionicons, define the icon SVG data used in each Angular component using `addIcons`. This allows you to continue referencing icons by string name in your component template. Note that you will need to do this for any additional icons added. The `IonIcon` component should still be provided in the NgModule. + +```diff title="test.component.ts" +import { Component } from '@angular/core'; ++ import { addIcons } from 'ionicons'; ++ import { alarm, logoIonic } from 'ionicons/icons'; + +@Component({ + selector: 'app-root', + templateUrl: 'app.component.html', + styleUrls: ['app.component.scss'], +}) +export class TestComponent { + constructor() { + addIcons({ alarm, logoIonic }); + } +} +``` + +```diff title="test.module.ts" +import { NgModule } from '@angular/core'; +import { TestComponent } from './test.component'; ++ import { IonIcon } from '@ionic/angular/standalone'; + +@NgModule({ + imports: [ ++ IonIcon, + ], + declarations: [TestComponent] +}) +export class TestComponentModule {} +``` + +8. Remove the following code from your `angular.json` file if present. Note that it may appear multiple times. + +```diff title="angular.json" +- { +- "glob": "**/*.svg", +- "input": "node_modules/ionicons/dist/ionicons/svg", +- "output": "./svg" +- } +``` diff --git a/docs/angular/overview.md b/docs/angular/overview.md index 10bae7ff7b0..4b1182bb76a 100644 --- a/docs/angular/overview.md +++ b/docs/angular/overview.md @@ -39,4 +39,8 @@ With Ionic 4+, the official Angular stack for building an app and routing are us

Learn about using Ionic lifecycle events in class components and with hooks

+ +

Learn about using Modules or Standalone Components in Ionic.

+
+ diff --git a/sidebars.js b/sidebars.js index 76e4d28bc4c..3ecdcbaa36b 100644 --- a/sidebars.js +++ b/sidebars.js @@ -66,6 +66,7 @@ module.exports = { collapsed: false, items: [ 'angular/overview', + 'angular/build-options', { type: 'category', label: 'Build Your First App', From 0feb5f20b602dc778c09e40c7a400148c12ab6a0 Mon Sep 17 00:00:00 2001 From: Amanda Johnston <90629384+amandaejohnston@users.noreply.github.com> Date: Mon, 25 Sep 2023 14:15:16 -0500 Subject: [PATCH 03/11] docs(dynamic-font): fix font family name (#3149) --- docs/layout/dynamic-font-scaling.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/layout/dynamic-font-scaling.md b/docs/layout/dynamic-font-scaling.md index 384a71fd635..bfce2ea3485 100644 --- a/docs/layout/dynamic-font-scaling.md +++ b/docs/layout/dynamic-font-scaling.md @@ -61,7 +61,7 @@ We recommend using the default fonts in Ionic as they are designed to look good ```css html { --ion-dynamic-font: var(--ion-default-dynamic-font); - --ion-font-family: 'Comic Sans'; + --ion-font-family: 'Comic Sans MS'; } ``` From 38486e5c3891e4e9677e9d68951e6aff5cdeb01f Mon Sep 17 00:00:00 2001 From: Liam DeBeasi Date: Tue, 26 Sep 2023 12:11:43 -0400 Subject: [PATCH 04/11] docs: add modules architecture usage (#3151) --- docs/angular/build-options.md | 80 ++++++++++++++++++++++++++++++++++- 1 file changed, 79 insertions(+), 1 deletion(-) diff --git a/docs/angular/build-options.md b/docs/angular/build-options.md index ba05435cdfb..5228149864b 100644 --- a/docs/angular/build-options.md +++ b/docs/angular/build-options.md @@ -26,7 +26,7 @@ See the [Standalone Migration Guide](#migrating-from-modules-to-standalone) for 1. Ionic components need to be imported into every Angular component they are used in which can be time consuming to set up. -### Usage +### Usage with Standalone-based Applications :::caution All Ionic imports should be imported from the `@ionic/angular/standalone` submodule. This includes imports such as components, directives, providers, and types. Importing from `@ionic/angular` may pull in lazy loaded Ionic code which can interfere with treeshaking. @@ -113,6 +113,84 @@ export class HomePage { } ``` +### Usage with NgModule-based Applications + +:::caution +All Ionic imports should be imported from the `@ionic/angular/standalone` submodule. This includes imports such as components, directives, providers, and types. Importing from `@ionic/angular` may pull in lazy loaded Ionic code which can interfere with treeshaking. +::: + +**Bootstrapping and Configuration** + +Ionic Angular needs to be configured in the `providers` array of `app.module.ts` using the `provideIonicAngular` function. Developers can pass any [IonicConfig](../developing/config#ionicconfig) values as an object in this function. Note that `provideIonicAngular` needs to be called even if no custom config is passed. + +```typescript +import { NgModule } from '@angular/core'; +import { BrowserModule } from '@angular/platform-browser'; +import { RouteReuseStrategy } from '@angular/router'; + +import { IonicRouteStrategy, provideIonicAngular } from '@ionic/angular/standalone'; + +import { AppComponent } from './app.component'; +import { AppRoutingModule } from './app-routing.module'; + +@NgModule({ + declarations: [AppComponent], + imports: [BrowserModule, AppRoutingModule], + providers: [provideIonicAngular(), { provide: RouteReuseStrategy, useClass: IonicRouteStrategy }], + bootstrap: [AppComponent], +}) +export class AppModule {} +``` + +**Components** + +In the example below, we are importing `IonContent` and `IonButton` from `@ionic/angular/standalone` and passing them to `imports` array in the Angular component's NgModule for use in the component template. We would get a compiler error if these components were not imported and provided to the `imports` array. + +```typescript +import { NgModule } from '@angular/core'; +import { IonButton, IonContent } from '@ionic/angular/standalone'; +import { HomePage } from './home.page'; + +import { HomePageRoutingModule } from './home-routing.module'; + +@NgModule({ + imports: [IonButton, IonContent, HomePageRoutingModule], + declarations: [HomePage], +}) +export class HomePageModule {} +``` + +**Icons** + +The icon SVG data needs to be defined in the Angular component so it can be loaded correctly. Developers can use the `addIcons` function from `ionicons` to map the SVG data to a string name. Developers can then reference the icon by its string name using the `name` property on `IonIcon`. The `IonIcon` component should be added in `app.module.ts` just like the other Ionic components. + +We recommend calling `addIcons` in the Angular component `constructor` so the data is only added if the Angular component is being used. + +For developers using Ionicons 7.2 or newer, passing only the SVG data will cause the string name to be automatically generated. + +```typescript +import { Component } from '@angular/core'; +import { addIcons } from 'ionicons'; +import { logoIonic } from 'ionicons/icons'; + +@Component({ + selector: 'app-home', + templateUrl: 'home.page.html', + styleUrls: ['home.page.scss'], +}) +export class HomePage { + constructor() { + /** + * On Ionicons 7.2+ this icon + * gets mapped to a "logo-ionic" key. + * Alternatively, developers can do: + * addIcons({ 'logo-ionic': logoIonic }); + */ + addIcons({ logoIonic }); + } +} +``` + ## Modules ### Overview From 91c0b939adc63b006c3db12ab69821cd598ae1c5 Mon Sep 17 00:00:00 2001 From: Liam DeBeasi Date: Tue, 26 Sep 2023 15:20:18 -0400 Subject: [PATCH 05/11] docs(angular): add angular standalone syntax to non-playground examples (#3116) --- docs/angular/platform.md | 30 +++++++++ docs/angular/slides.md | 34 ++++++++++- docs/developing/config.md | 57 +++++++++++++++++ docs/developing/config/global/index.md | 18 ++++++ docs/developing/config/per-component/index.md | 37 +++++++++++ .../config/per-platform-fallback/index.md | 27 ++++++++ .../config/per-platform-overrides/index.md | 30 +++++++++ docs/developing/config/per-platform/index.md | 23 +++++++ docs/developing/hardware-back-button.md | 61 +++++++++++++++++++ docs/developing/keyboard.md | 20 ++++++ docs/techniques/security.md | 20 ++++++ docs/utilities/animations.md | 20 ++++++ docs/utilities/gestures.md | 25 ++++++++ 13 files changed, 401 insertions(+), 1 deletion(-) diff --git a/docs/angular/platform.md b/docs/angular/platform.md index 9668f3632a1..3cd0d7833f3 100644 --- a/docs/angular/platform.md +++ b/docs/angular/platform.md @@ -2,6 +2,9 @@ title: Platform --- +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + Platform | Ionic Platform to Customize Apps to Fit Any Device + + ```tsx import { Platform } from '@ionic/angular'; @@ -25,6 +38,23 @@ export class MyPage { } ``` + + + +```tsx +import { Platform } from '@ionic/angular/standalone'; + +@Component({...}) +export class MyPage { + constructor(public platform: Platform) { + + } +} +``` + + + + ## Methods ### `is` diff --git a/docs/angular/slides.md b/docs/angular/slides.md index 6e2d2a3aa15..4ceb1d3f6df 100644 --- a/docs/angular/slides.md +++ b/docs/angular/slides.md @@ -2,6 +2,9 @@ title: Migrating from ion-slides to Swiper.js --- +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + Set Up Swiper.js for Angular Slides [Example] | Ionic + ```typescript // home.page.ts @@ -152,6 +165,25 @@ export class HomePage { } ``` + + + +```typescript +// home.page.ts + +import { IonicSlides } from '@ionic/angular/standalone'; + +@Component({ + ... +}) +export class HomePage { + swiperModules = [IonicSlides]; +} +``` + + + + ```html diff --git a/docs/developing/config.md b/docs/developing/config.md index 1bb117883fa..b5cb57a36c6 100644 --- a/docs/developing/config.md +++ b/docs/developing/config.md @@ -2,6 +2,9 @@ title: Config --- +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + Ionic Config provides a way to change the properties of components globally across an app. It can set the app mode, tab button layout, animations, and more. ## Global Config @@ -61,6 +64,16 @@ Ionic Angular provides a `Config` provider for accessing the Ionic Config. #### Examples + + + ```ts import { Config } from '@ionic/angular'; @@ -72,6 +85,23 @@ class AppComponent { } ``` + + + +```ts +import { Config } from '@ionic/angular/standalone'; + +@Component(...) +class AppComponent { + constructor(config: Config) { + const mode = config.get('mode'); + } +} +``` + + + + ### getBoolean | | | @@ -81,6 +111,16 @@ class AppComponent { #### Examples + + + ```ts import { Config } from '@ionic/angular'; @@ -92,6 +132,23 @@ class AppComponent { } ``` + + + +```ts +import { Config } from '@ionic/angular/standalone'; + +@Component(...) +class AppComponent { + constructor(config: Config) { + const swipeBackEnabled = config.getBoolean('swipeBackEnabled'); + } +} +``` + + + + ### getNumber | | | diff --git a/docs/developing/config/global/index.md b/docs/developing/config/global/index.md index b58bc0610a4..19d3494dd66 100644 --- a/docs/developing/config/global/index.md +++ b/docs/developing/config/global/index.md @@ -7,6 +7,7 @@ import TabItem from '@theme/TabItem'; values={[ { value: 'javascript', label: 'JavaScript' }, { value: 'angular', label: 'Angular' }, + { value: 'angular-standalone', label: 'Angular (Standalone)' }, { value: 'react', label: 'React' }, { value: 'vue', label: 'Vue' }, ]} @@ -40,6 +41,23 @@ import { IonicModule } from '@ionic/angular'; }) ``` + + + +```ts title="main.ts" +import { provideIonicAngular } from '@ionic/angular/standalone'; + +bootstrapApplication(AppComponent, { + providers: [ + ..., + provideIonicAngular({ + rippleEffect: false, + mode: 'md' + }) + ] +}) +``` + diff --git a/docs/developing/config/per-component/index.md b/docs/developing/config/per-component/index.md index 4b6a5427bc3..83546ca0cd7 100644 --- a/docs/developing/config/per-component/index.md +++ b/docs/developing/config/per-component/index.md @@ -7,6 +7,7 @@ import TabItem from '@theme/TabItem'; values={[ { value: 'javascript', label: 'JavaScript' }, { value: 'angular', label: 'Angular' }, + { value: 'angular-standalone', label: 'Angular (Standalone)' }, { value: 'react', label: 'React' }, { value: 'vue', label: 'Vue' }, ]} @@ -77,6 +78,42 @@ class MyComponent { } ``` + + + +**Not recommended** + +```ts +import { provideIonicAngular } from '@ionic/angular/standalone'; + +bootstrapApplication(AppComponent, { + providers: [ + ..., + provideIonicAngular({ + // Not recommended when your app requires reactive values + backButtonText: 'Go Back' + }) + ] +}) +``` + +**Recommended** + +```html + +``` + +```ts +@Component(...) +class MyComponent { + /** + * The back button text can be updated + * anytime the locale changes. + */ + backButtonText = 'Go Back'; +} +``` + diff --git a/docs/developing/config/per-platform-fallback/index.md b/docs/developing/config/per-platform-fallback/index.md index c7d12df30ba..a3bbd7fa45a 100644 --- a/docs/developing/config/per-platform-fallback/index.md +++ b/docs/developing/config/per-platform-fallback/index.md @@ -6,6 +6,7 @@ import TabItem from '@theme/TabItem'; defaultValue="angular" values={[ { value: 'angular', label: 'Angular' }, + { value: 'angular-standalone', label: 'Angular (Standalone)' }, { value: 'react', label: 'React' }, { value: 'vue', label: 'Vue' }, ]} @@ -35,6 +36,32 @@ const getConfig = () => { }); ``` + + + +```ts title="main.ts" +import { isPlatform, provideIonicAngular } from '@ionic/angular/standalone'; + +const getConfig = () => { + if (isPlatform('hybrid')) { + return { + tabButtonLayout: 'label-hide' + } + } + + return { + tabButtonLayout: 'icon-top' + }; +} + +bootstrapApplication(AppComponent, { + providers: [ + ..., + provideIonicAngular(getConfig()) + ] +}) +``` + diff --git a/docs/developing/config/per-platform-overrides/index.md b/docs/developing/config/per-platform-overrides/index.md index e8f723408b2..6ee558f9e41 100644 --- a/docs/developing/config/per-platform-overrides/index.md +++ b/docs/developing/config/per-platform-overrides/index.md @@ -6,6 +6,7 @@ import TabItem from '@theme/TabItem'; defaultValue="angular" values={[ { value: 'angular', label: 'Angular' }, + { value: 'angular-standalone', label: 'Angular (Standalone)' }, { value: 'react', label: 'React' }, { value: 'vue', label: 'Vue' }, ]} @@ -38,6 +39,35 @@ const getConfig = () => { }); ``` + + + +```ts title="main.ts" +import { isPlatform, provideIonicAngular } from '@ionic/angular/standalone'; + +const getConfig = () => { + let config = { + animated: false + }; + + if (isPlatform('iphone')) { + config = { + ...config, + backButtonText: 'Previous' + } + } + + return config; +} + +bootstrapApplication(AppComponent, { + providers: [ + ..., + provideIonicAngular(getConfig()) + ] +}) +``` + diff --git a/docs/developing/config/per-platform/index.md b/docs/developing/config/per-platform/index.md index 3deba7540b0..b088a7e6bf6 100644 --- a/docs/developing/config/per-platform/index.md +++ b/docs/developing/config/per-platform/index.md @@ -6,6 +6,7 @@ import TabItem from '@theme/TabItem'; defaultValue="angular" values={[ { value: 'angular', label: 'Angular' }, + { value: 'angular-standalone', label: 'Angular (Standalone)' }, { value: 'react', label: 'React' }, { value: 'vue', label: 'Vue' }, ]} @@ -32,6 +33,28 @@ import { isPlatform, IonicModule } from '@ionic/angular'; }) ``` + + + +:::note +Since the config is set at runtime, you will not have access to the Platform Dependency Injection. Instead, you can use the underlying functions that the provider uses directly. + +See the [Angular Platform Documentation](../angular/platform) for the types of platforms you can detect. +::: + +```ts title="main.ts" +import { isPlatform, provideIonicAngular } from '@ionic/angular/standalone'; + +bootstrapApplication(AppComponent, { + providers: [ + ..., + provideIonicAngular({ + animated: !isPlatform('mobileweb') + }) + ] +}) +``` + diff --git a/docs/developing/hardware-back-button.md b/docs/developing/hardware-back-button.md index 6813f9b4ceb..d41db0d4e91 100644 --- a/docs/developing/hardware-back-button.md +++ b/docs/developing/hardware-back-button.md @@ -50,6 +50,7 @@ The `ionBackButton` event will not be emitted when running an app in a browser o values={[ { value: 'javascript', label: 'JavaScript' }, { value: 'angular', label: 'Angular' }, + { value: 'angular-standalone', label: 'Angular (Standalone)' }, { value: 'react', label: 'React' }, { value: 'vue', label: 'Vue' }, ] @@ -78,6 +79,21 @@ constructor(private platform: Platform) { }); } +``` + + + +```tsx +import { Platform } from '@ionic/angular/standalone'; + +... + +constructor(private platform: Platform) { + this.platform.backButton.subscribeWithPriority(10, () => { + console.log('Handler was called!'); + }); +} + ``` @@ -124,6 +140,7 @@ Each hardware back button callback has a `processNextHandler` parameter. Calling values={[ { value: 'javascript', label: 'JavaScript' }, { value: 'angular', label: 'Angular' }, + { value: 'angular-standalone', label: 'Angular (Standalone)' }, { value: 'react', label: 'React' }, { value: 'vue', label: 'Vue' }, ] @@ -164,6 +181,27 @@ constructor(private platform: Platform) { }); } +``` + + + +```tsx +import { Platform } from '@ionic/angular/standalone'; + +... + +constructor(private platform: Platform) { + this.platform.backButton.subscribeWithPriority(5, () => { + console.log('Another handler was called!'); + }); + + this.platform.backButton.subscribeWithPriority(10, (processNextHandler) => { + console.log('Handler was called!'); + + processNextHandler(); + }); +} + ``` @@ -244,6 +282,7 @@ In some scenarios, it may be desirable to quit the app when pressing the hardwar values={[ { value: 'javascript', label: 'JavaScript' }, { value: 'angular', label: 'Angular' }, + { value: 'angular-standalone', label: 'Angular (Standalone)' }, { value: 'react', label: 'React' }, { value: 'vue', label: 'Vue' }, ] @@ -287,6 +326,28 @@ constructor( }); } +``` + + + +```tsx +import { Optional } from '@angular/core'; +import { IonRouterOutlet, Platform } from '@ionic/angular/standalone'; +import { App } from '@capacitor/app'; + +... + +constructor( + private platform: Platform, + @Optional() private routerOutlet?: IonRouterOutlet +) { + this.platform.backButton.subscribeWithPriority(-1, () => { + if (!this.routerOutlet.canGoBack()) { + App.exitApp(); + } + }); +} + ``` diff --git a/docs/developing/keyboard.md b/docs/developing/keyboard.md index 6c166c9b11a..ad0fb4c0eb0 100644 --- a/docs/developing/keyboard.md +++ b/docs/developing/keyboard.md @@ -199,6 +199,7 @@ Detecting the presence of an on-screen keyboard is useful for adjusting the posi values={[ { value: 'javascript', label: 'JavaScript' }, { value: 'angular', label: 'Angular' }, + { value: 'angular-standalone', label: 'Angular (Standalone)' }, { value: 'react', label: 'React' }, { value: 'vue', label: 'Vue' }, ] @@ -223,6 +224,25 @@ import { Platform } from '@ionic/angular'; ... +constructor(private platform: Platform) { + this.platform.keyboardDidShow.subscribe(ev => { + const { keyboardHeight } = ev; + // Do something with the keyboard height such as translating an input above the keyboard. + }); + + this.platform.keyboardDidHide.subscribe(() => { + // Move input back to original location + }); +} +``` + + + +```tsx +import { Platform } from '@ionic/angular/standalone'; + +... + constructor(private platform: Platform) { this.platform.keyboardDidShow.subscribe(ev => { const { keyboardHeight } = ev; diff --git a/docs/techniques/security.md b/docs/techniques/security.md index 2bc7a648ac9..9545d43c1b7 100644 --- a/docs/techniques/security.md +++ b/docs/techniques/security.md @@ -94,6 +94,7 @@ In order to bypass the sanitizer and use unsanitized custom HTML in the relevant defaultValue="angular" values={[ { value: 'angular', label: 'Angular' }, + { value: 'angular-standalone', label: 'Angular (Standalone)' }, { value: 'javascript', label: 'JavaScript' }, { value: 'react', label: 'React' }, ] @@ -115,6 +116,25 @@ async presentToast() { toast.present(); } +``` + + + +```tsx +import { IonicSafeString, ToastController } from '@ionic/angular/standalone'; + +... + +constructor(private toastController: ToastController) {} + +async presentToast() { + const toast = await this.toastController.create({ + message: new IonicSafeString('Hello!'), + duration: 2000 + }); + toast.present(); +} + ``` diff --git a/docs/utilities/animations.md b/docs/utilities/animations.md index 1c7a43769c1..27558786ff7 100644 --- a/docs/utilities/animations.md +++ b/docs/utilities/animations.md @@ -32,6 +32,7 @@ Ionic Animations, on the other hand, uses the [Web Animations API](https://devel { value: 'javascript', label: 'JavaScript' }, { value: 'typescript', label: 'TypeScript' }, { value: 'angular', label: 'Angular' }, + { value: 'angular-standalone', label: 'Angular (Standalone)' }, { value: 'react', label: 'React' }, { value: 'vue', label: 'Vue' }, ] @@ -86,6 +87,25 @@ constructor(private animationCtrl: AnimationController) { .fromTo('opacity', '1', '0.5'); } +``` + + + +Developers using Angular should install the latest version of `@ionic/angular`. Animations can be created via the `AnimationController` dependency injection. + +```tsx + +import { Animation, AnimationController } from '@ionic/angular/standalone'; + +... + +constructor(private animationCtrl: AnimationController) { + const animation: Animation = this.animationCtrl.create() + .addElement(myElementRef) + .duration(1000) + .fromTo('opacity', '1', '0.5'); +} + ``` diff --git a/docs/utilities/gestures.md b/docs/utilities/gestures.md index a4c44d3b833..875876b3b56 100644 --- a/docs/utilities/gestures.md +++ b/docs/utilities/gestures.md @@ -29,6 +29,7 @@ Building complex gestures can be time consuming. Other libraries that provide cu values={[ { value: 'javascript', label: 'JavaScript' }, { value: 'angular', label: 'Angular' }, + { value: 'angular-standalone', label: 'Angular (Standalone)' }, { value: 'react', label: 'React' }, { value: 'vue', label: 'Vue' }, ] @@ -90,6 +91,30 @@ constructor(private gestureCtrl: GestureController) { // The `true` above ensures that callbacks run inside NgZone. } +``` + + + +Developers using Angular should install the latest version of `@ionic/angular`. Animations can be created via the `AnimationController` dependency injection. + +By default, gesture callbacks do not run inside of NgZone. Developers can either set the `runInsideAngularZone` parameter to `true` when creating a gesture, +or they can wrap their callbacks in an `NgZone.run()` call. + +```tsx +import { Gesture, GestureController } from '@ionic/angular/standalone'; + +... + +constructor(private gestureCtrl: GestureController) { + const gesture: Gesture = this.gestureCtrl.create({ + el: this.element.nativeElement, + threshold: 15, + gestureName: 'my-gesture', + onMove: ev => this.onMoveHandler(ev) + }, true); + // The `true` above ensures that callbacks run inside NgZone. +} + ``` From 6f194e792702ce5261a7788d6b26b59c932da134 Mon Sep 17 00:00:00 2001 From: Sean Perkins <13732623+sean-perkins@users.noreply.github.com> Date: Wed, 27 Sep 2023 14:51:23 -0400 Subject: [PATCH 06/11] fix(angular): standalone guide formatting (#3152) --- docs/angular/build-options.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/angular/build-options.md b/docs/angular/build-options.md index 5228149864b..d9570f92d9a 100644 --- a/docs/angular/build-options.md +++ b/docs/angular/build-options.md @@ -19,7 +19,7 @@ See the [Standalone Migration Guide](#migrating-from-modules-to-standalone) for **Benefits** 1. Enables treeshaking so the final build output only includes the code necessary to run your app which reduces overall build size. -2. Avoids the use of `NgModule`s to streamline the development experience and make your code easier to understand. +2. Avoids the use of NgModules to streamline the development experience and make your code easier to understand. 3. Allows developers to also use newer Angular features such as [ESBuild](https://angular.io/guide/esbuild). **Drawbacks** From ffb5380f4b2d5c7db50ea803e385f2a0e8d8d496 Mon Sep 17 00:00:00 2001 From: Maria Hutt Date: Thu, 28 Sep 2023 08:05:51 -0700 Subject: [PATCH 07/11] docs(angular): include add diff sign (#3157) --- docs/angular/build-options.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/angular/build-options.md b/docs/angular/build-options.md index d9570f92d9a..17d91a70970 100644 --- a/docs/angular/build-options.md +++ b/docs/angular/build-options.md @@ -429,7 +429,7 @@ import { Component } from '@angular/core'; }) export class TestComponent { constructor() { - addIcons({ alarm, logoIonic }); ++ addIcons({ alarm, logoIonic }); } } ``` From a26ba8376f02b9d8d17a7b4778c00c95946e23d6 Mon Sep 17 00:00:00 2001 From: Amanda Johnston <90629384+amandaejohnston@users.noreply.github.com> Date: Wed, 4 Oct 2023 14:06:59 -0500 Subject: [PATCH 08/11] docs(toast): add playground for positionAnchor (#3158) --- docs/api/toast.md | 8 +++ .../usage/v7/toast/position-anchor/angular.md | 33 +++++++++++ .../usage/v7/toast/position-anchor/demo.html | 57 +++++++++++++++++++ .../usage/v7/toast/position-anchor/index.md | 19 +++++++ .../v7/toast/position-anchor/javascript.md | 33 +++++++++++ .../usage/v7/toast/position-anchor/react.md | 43 ++++++++++++++ static/usage/v7/toast/position-anchor/vue.md | 52 +++++++++++++++++ 7 files changed, 245 insertions(+) create mode 100644 static/usage/v7/toast/position-anchor/angular.md create mode 100644 static/usage/v7/toast/position-anchor/demo.html create mode 100644 static/usage/v7/toast/position-anchor/index.md create mode 100644 static/usage/v7/toast/position-anchor/javascript.md create mode 100644 static/usage/v7/toast/position-anchor/react.md create mode 100644 static/usage/v7/toast/position-anchor/vue.md diff --git a/docs/api/toast.md b/docs/api/toast.md index 10ccf46f2d6..3ecf502a043 100644 --- a/docs/api/toast.md +++ b/docs/api/toast.md @@ -64,6 +64,14 @@ import ButtonsPlayground from '@site/static/usage/v7/toast/buttons/index.md'; Toasts can be positioned at the top, bottom or middle of the viewport. The position can be passed upon creation. The possible values are `top`, `bottom` and `middle`. If the position is not specified, the toast will be displayed at the bottom of the viewport. +### Relative Positioning + +If a toast is presented alongside navigation elements such as a header, footer, or [FAB](./fab.md), the toast may overlap these elements by default. This can be fixed using the `positionAnchor` property, which takes either an element reference or an ID. The toast will be positioned relative to the chosen element, appearing below it when using `position="top"` or above it when using `position="bottom"`. When using `position="middle"`, the `positionAnchor` property is ignored. + +import PositionAnchor from '@site/static/usage/v7/toast/position-anchor/index.md'; + + + ## Layout Button containers within the toast can be displayed either on the same line as the message or stacked on separate lines using the `layout` property. The stacked layout should be used with buttons that have long text values. Additionally, buttons in a stacked toast layout can use a `side` value of either `start` or `end`, but not both. diff --git a/static/usage/v7/toast/position-anchor/angular.md b/static/usage/v7/toast/position-anchor/angular.md new file mode 100644 index 00000000000..f27b09cf057 --- /dev/null +++ b/static/usage/v7/toast/position-anchor/angular.md @@ -0,0 +1,33 @@ +```html + + + Header + + + + + Anchor to Header + Anchor to Footer + + + + + + + + Footer + + +``` diff --git a/static/usage/v7/toast/position-anchor/demo.html b/static/usage/v7/toast/position-anchor/demo.html new file mode 100644 index 00000000000..f39ea5681ea --- /dev/null +++ b/static/usage/v7/toast/position-anchor/demo.html @@ -0,0 +1,57 @@ + + + + + + Toast + + + + + + + + + + + + + Header + + + + +
+ Anchor to Header + Anchor to Footer + + + +
+
+ + + + Footer + + +
+ + diff --git a/static/usage/v7/toast/position-anchor/index.md b/static/usage/v7/toast/position-anchor/index.md new file mode 100644 index 00000000000..dfcf9edde77 --- /dev/null +++ b/static/usage/v7/toast/position-anchor/index.md @@ -0,0 +1,19 @@ +import Playground from '@site/src/components/global/Playground'; + +import javascript from './javascript.md'; +import react from './react.md'; +import vue from './vue.md'; +import angular from './angular.md'; + + diff --git a/static/usage/v7/toast/position-anchor/javascript.md b/static/usage/v7/toast/position-anchor/javascript.md new file mode 100644 index 00000000000..0dc3e5b4a5a --- /dev/null +++ b/static/usage/v7/toast/position-anchor/javascript.md @@ -0,0 +1,33 @@ +```html + + + Header + + + + + Anchor to Header + Anchor to Footer + + + + + + + + Footer + + +``` diff --git a/static/usage/v7/toast/position-anchor/react.md b/static/usage/v7/toast/position-anchor/react.md new file mode 100644 index 00000000000..14149bff4d8 --- /dev/null +++ b/static/usage/v7/toast/position-anchor/react.md @@ -0,0 +1,43 @@ +```tsx +import React from 'react'; +import { IonButton, IonContent, IonFooter, IonHeader, IonTitle, IonToast, IonToolbar } from '@ionic/react'; + +function Example() { + return ( + <> + + + Header + + + + + Anchor to Header + Anchor to Footer + + + + + + + + Footer + + + + ); +} +export default Example; +``` diff --git a/static/usage/v7/toast/position-anchor/vue.md b/static/usage/v7/toast/position-anchor/vue.md new file mode 100644 index 00000000000..b06b61b44da --- /dev/null +++ b/static/usage/v7/toast/position-anchor/vue.md @@ -0,0 +1,52 @@ +```html + + + +``` From 85c39215492a2af4ca2ad661f4063a7fe51f2308 Mon Sep 17 00:00:00 2001 From: Sean Perkins <13732623+sean-perkins@users.noreply.github.com> Date: Wed, 4 Oct 2023 15:32:21 -0400 Subject: [PATCH 09/11] chore: resolve cspell errors (#3165) * chore: resolve cspell errors * chore: ignore all node_modules folders * chore: adjust sorting --- cspell-wordlist.txt | 1 + cspell.json | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/cspell-wordlist.txt b/cspell-wordlist.txt index 607ba8200db..b9f8543eff6 100644 --- a/cspell-wordlist.txt +++ b/cspell-wordlist.txt @@ -30,6 +30,7 @@ authed autogrow automations autoplay +Callout comparewith composables engageable diff --git a/cspell.json b/cspell.json index 5fcd46d4140..385200d0e10 100644 --- a/cspell.json +++ b/cspell.json @@ -17,7 +17,8 @@ "docs/native", "versioned_docs/**/api", "versioned_docs/**/cli", - "versioned_docs/**/native" + "versioned_docs/**/native", + "node_modules" ], "flagWords": [ "hte" From 60e17780099186eba4f11933f1ae4e911e296fab Mon Sep 17 00:00:00 2001 From: Sean Perkins <13732623+sean-perkins@users.noreply.github.com> Date: Thu, 5 Oct 2023 11:05:51 -0400 Subject: [PATCH 10/11] docs(select): keyboard navigation (#3156) --- docs/api/select.md | 63 ++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 61 insertions(+), 2 deletions(-) diff --git a/docs/api/select.md b/docs/api/select.md index b5a3455aec4..8dc81783b05 100644 --- a/docs/api/select.md +++ b/docs/api/select.md @@ -18,7 +18,7 @@ import EncapsulationPill from '@components/page/api/EncapsulationPill'; -Selects are form controls to select an option, or options, from a set of options, similar to a native `