From d175dc00943542fda032290ff6cc38d4d1a07033 Mon Sep 17 00:00:00 2001 From: Maria Hutt Date: Thu, 29 Jan 2026 06:32:16 -0800 Subject: [PATCH 1/7] docs(datetime): add wheel part to styling section (#4383) --- .../styling/wheel-styling/angular/example_component_css.md | 4 ++++ static/usage/v8/datetime/styling/wheel-styling/demo.html | 4 ++++ static/usage/v8/datetime/styling/wheel-styling/javascript.md | 4 ++++ .../usage/v8/datetime/styling/wheel-styling/react/main_css.md | 4 ++++ static/usage/v8/datetime/styling/wheel-styling/vue.md | 4 ++++ 5 files changed, 20 insertions(+) diff --git a/static/usage/v8/datetime/styling/wheel-styling/angular/example_component_css.md b/static/usage/v8/datetime/styling/wheel-styling/angular/example_component_css.md index 3cd7e6ff8ac..d84fb8c9f09 100644 --- a/static/usage/v8/datetime/styling/wheel-styling/angular/example_component_css.md +++ b/static/usage/v8/datetime/styling/wheel-styling/angular/example_component_css.md @@ -7,6 +7,10 @@ ion-datetime { --wheel-fade-background-rgb: 245, 235, 247; } +ion-datetime::part(wheel):focus { + background: rgb(109, 208, 164); +} + ion-datetime::part(wheel-item) { color: rgb(255, 66, 97); } diff --git a/static/usage/v8/datetime/styling/wheel-styling/demo.html b/static/usage/v8/datetime/styling/wheel-styling/demo.html index b784a220c73..c19c18a8ac8 100644 --- a/static/usage/v8/datetime/styling/wheel-styling/demo.html +++ b/static/usage/v8/datetime/styling/wheel-styling/demo.html @@ -18,6 +18,10 @@ --wheel-fade-background-rgb: 245, 235, 247; } + ion-datetime::part(wheel):focus { + background: rgb(109, 208, 164); + } + ion-datetime::part(wheel-item) { color: rgb(255, 66, 97); } diff --git a/static/usage/v8/datetime/styling/wheel-styling/javascript.md b/static/usage/v8/datetime/styling/wheel-styling/javascript.md index 6ee932a323d..67a23fc7e7e 100644 --- a/static/usage/v8/datetime/styling/wheel-styling/javascript.md +++ b/static/usage/v8/datetime/styling/wheel-styling/javascript.md @@ -8,6 +8,10 @@ --wheel-fade-background-rgb: 245, 235, 247; } + ion-datetime::part(wheel):focus { + background: rgb(109, 208, 164); + } + ion-datetime::part(wheel-item) { color: rgb(255, 66, 97); } diff --git a/static/usage/v8/datetime/styling/wheel-styling/react/main_css.md b/static/usage/v8/datetime/styling/wheel-styling/react/main_css.md index 3cd7e6ff8ac..d84fb8c9f09 100644 --- a/static/usage/v8/datetime/styling/wheel-styling/react/main_css.md +++ b/static/usage/v8/datetime/styling/wheel-styling/react/main_css.md @@ -7,6 +7,10 @@ ion-datetime { --wheel-fade-background-rgb: 245, 235, 247; } +ion-datetime::part(wheel):focus { + background: rgb(109, 208, 164); +} + ion-datetime::part(wheel-item) { color: rgb(255, 66, 97); } diff --git a/static/usage/v8/datetime/styling/wheel-styling/vue.md b/static/usage/v8/datetime/styling/wheel-styling/vue.md index d2c73da038a..4001c881c43 100644 --- a/static/usage/v8/datetime/styling/wheel-styling/vue.md +++ b/static/usage/v8/datetime/styling/wheel-styling/vue.md @@ -16,6 +16,10 @@ --wheel-fade-background-rgb: 245, 235, 247; } + ion-datetime::part(wheel):focus { + background: rgb(109, 208, 164); + } + ion-datetime::part(wheel-item) { color: rgb(255, 66, 97); } From 5e3ced09f806c34d661cc28ab65e79dda8a06000 Mon Sep 17 00:00:00 2001 From: Shane Date: Thu, 29 Jan 2026 08:53:47 -0800 Subject: [PATCH 2/7] docs(angular): adding overlays documentation with explanation of 8.8's new custom injector functionality (#4372) * docs(angular): adding overlays documentation with explanation of 8.8's new custom injector functionality * chore(spelling): adding CDK to word list * chore(modal-options): updating interfaces to follow consistent naming patterns * docs(overlays): adding docs for overlays section --- cspell-wordlist.txt | 1 + docs/angular/overlays.md | 213 +++++++++++++++++++++++++++++++++++++++ sidebars.js | 1 + 3 files changed, 215 insertions(+) create mode 100644 docs/angular/overlays.md diff --git a/cspell-wordlist.txt b/cspell-wordlist.txt index bf433d7615a..96a53460ab9 100644 --- a/cspell-wordlist.txt +++ b/cspell-wordlist.txt @@ -13,6 +13,7 @@ Udemy Vetur Wistia WCAG +CDK actionsheet fabs diff --git a/docs/angular/overlays.md b/docs/angular/overlays.md new file mode 100644 index 00000000000..2e4335effcd --- /dev/null +++ b/docs/angular/overlays.md @@ -0,0 +1,213 @@ +--- +title: Overlay Components +sidebar_label: Overlays +--- + + + Angular Overlay Components: Modals, Popovers with Custom Injectors + + + +Ionic provides overlay components such as modals and popovers that display content on top of your application. In Angular, these overlays can be created using controllers like `ModalController` and `PopoverController`. + +## Creating Overlays + +Overlays can be created programmatically using their respective controllers: + +```typescript +import { Component } from '@angular/core'; +import { ModalController } from '@ionic/angular/standalone'; +import { MyModalComponent } from './my-modal.component'; + +@Component({ + selector: 'app-home', + templateUrl: './home.component.html', +}) +export class HomeComponent { + constructor(private modalController: ModalController) {} + + async openModal() { + const modal = await this.modalController.create({ + component: MyModalComponent, + componentProps: { + title: 'My Modal', + }, + }); + await modal.present(); + } +} +``` + +## Custom Injectors + +By default, overlay components use the root injector for dependency injection. This means that services or tokens provided at the route level or within a specific component tree are not accessible inside the overlay. + +The `injector` option allows you to pass a custom Angular `Injector` when creating a modal or popover. This enables overlay components to access services and tokens that are not available in the root injector. + +### Use Cases + +Custom injectors are useful when you need to: + +- Access route-scoped services from within an overlay +- Use Angular CDK's `Dir` directive for bidirectional text support +- Access any providers that are not registered at the root level + +### Usage + +To use a custom injector, pass it to the `create()` method: + +```typescript +import { Component, Injector } from '@angular/core'; +import { ModalController } from '@ionic/angular/standalone'; +import { MyModalComponent } from './my-modal.component'; +import { MyRouteService } from './my-route.service'; + +@Component({ + selector: 'app-feature', + templateUrl: './feature.component.html', + providers: [MyRouteService], // Service provided at route level +}) +export class FeatureComponent { + constructor(private modalController: ModalController, private injector: Injector) {} + + async openModal() { + const modal = await this.modalController.create({ + component: MyModalComponent, + injector: this.injector, // Pass the component's injector + }); + await modal.present(); + } +} +``` + +The modal component can now inject `MyRouteService`: + +```typescript +import { Component, inject } from '@angular/core'; +import { MyRouteService } from '../my-route.service'; + +@Component({ + selector: 'app-my-modal', + templateUrl: './my-modal.component.html', +}) +export class MyModalComponent { + private myRouteService = inject(MyRouteService); +} +``` + +### Creating a Custom Injector + +You can also create a custom injector with specific providers: + +```typescript +import { Component, Injector } from '@angular/core'; +import { ModalController } from '@ionic/angular/standalone'; +import { MyModalComponent } from './my-modal.component'; +import { MyService } from './my.service'; + +@Component({ + selector: 'app-feature', + templateUrl: './feature.component.html', +}) +export class FeatureComponent { + constructor(private modalController: ModalController, private injector: Injector) {} + + async openModal() { + const myService = new MyService(); + myService.configure({ someOption: true }); + + const customInjector = Injector.create({ + providers: [{ provide: MyService, useValue: myService }], + parent: this.injector, + }); + + const modal = await this.modalController.create({ + component: MyModalComponent, + injector: customInjector, + }); + await modal.present(); + } +} +``` + +### Using with Angular CDK Directionality + +A common use case is providing the Angular CDK `Dir` directive to overlays for bidirectional text support: + +```typescript +import { Component, Injector } from '@angular/core'; +import { Dir } from '@angular/cdk/bidi'; +import { ModalController } from '@ionic/angular/standalone'; +import { MyModalComponent } from './my-modal.component'; + +@Component({ + selector: 'app-feature', + templateUrl: './feature.component.html', +}) +export class FeatureComponent { + constructor(private modalController: ModalController, private injector: Injector) {} + + async openModal() { + const modal = await this.modalController.create({ + component: MyModalComponent, + injector: this.injector, // Includes Dir from component tree + }); + await modal.present(); + } +} +``` + +### Popover Controller + +The `PopoverController` supports the same `injector` option: + +```typescript +import { Component, Injector } from '@angular/core'; +import { PopoverController } from '@ionic/angular/standalone'; +import { MyPopoverComponent } from './my-popover.component'; + +@Component({ + selector: 'app-feature', + templateUrl: './feature.component.html', +}) +export class FeatureComponent { + constructor(private popoverController: PopoverController, private injector: Injector) {} + + async openPopover(event: Event) { + const popover = await this.popoverController.create({ + component: MyPopoverComponent, + event: event, + injector: this.injector, + }); + await popover.present(); + } +} +``` + +## Angular Options Types + +Ionic Angular exports its own `ModalOptions` and `PopoverOptions` types that extend the core options with Angular-specific properties like `injector`: + +- `ModalOptions` - Extends core `ModalOptions` with the `injector` property +- `PopoverOptions` - Extends core `PopoverOptions` with the `injector` property + +These types are exported from `@ionic/angular` and `@ionic/angular/standalone`: + +```typescript +import type { ModalOptions, PopoverOptions } from '@ionic/angular/standalone'; +``` + +## Docs for Overlays in Ionic + +For full docs and to see usage examples, visit the docs page for each of the overlays in Ionic: + +- [Action Sheet](https://ionicframework.com/docs/api/action-sheet) +- [Alert](https://ionicframework.com/docs/api/alert) +- [Loading](https://ionicframework.com/docs/api/loading) +- [Modal](https://ionicframework.com/docs/api/modal) +- [Picker](https://ionicframework.com/docs/api/picker) +- [Popover](https://ionicframework.com/docs/api/popover) +- [Toast](https://ionicframework.com/docs/api/toast) diff --git a/sidebars.js b/sidebars.js index d8cacb17545..06689adc351 100644 --- a/sidebars.js +++ b/sidebars.js @@ -87,6 +87,7 @@ module.exports = { 'angular/build-options', 'angular/lifecycle', 'angular/navigation', + 'angular/overlays', 'angular/injection-tokens', 'angular/virtual-scroll', 'angular/slides', From a259d4aefe0fdf05afc0aa7db4dd35fa4d0557e8 Mon Sep 17 00:00:00 2001 From: Shane Date: Thu, 19 Feb 2026 08:58:31 -0800 Subject: [PATCH 3/7] docs(refresher): adding documentation for refresher changes (#4397) --- .gitignore | 1 + docs/api/refresher.md | 19 +++++++++++++++++++ 2 files changed, 20 insertions(+) diff --git a/.gitignore b/.gitignore index 71e2c8eba4d..a1ae05b6ea1 100644 --- a/.gitignore +++ b/.gitignore @@ -22,3 +22,4 @@ yarn-debug.log* yarn-error.log* static/**/node_modules/ +.idea diff --git a/docs/api/refresher.md b/docs/api/refresher.md index 891478317cf..b7e006e5de0 100644 --- a/docs/api/refresher.md +++ b/docs/api/refresher.md @@ -113,6 +113,14 @@ interface RefresherEventDetail { } ``` +### RefresherPullEndEventDetail + +```typescript +interface RefresherPullEndEventDetail { + reason: 'complete' | 'cancel'; +} +``` + ### RefresherCustomEvent While not required, this interface can be used in place of the `CustomEvent` interface for stronger typing with Ionic events emitted from this component. @@ -124,6 +132,17 @@ interface RefresherCustomEvent extends CustomEvent { } ``` +### RefresherPullEndCustomEvent + +While not required, this interface can be used in place of the `CustomEvent` interface for stronger typing with the `ionPullEnd` event. + +```typescript +interface RefresherPullEndCustomEvent extends CustomEvent { + detail: RefresherPullEndEventDetail; + target: HTMLIonRefresherElement; +} +``` + ## Properties From 4080b0db2009770f8c063808de75960803c60046 Mon Sep 17 00:00:00 2001 From: Maria Hutt Date: Fri, 20 Feb 2026 13:37:01 -0800 Subject: [PATCH 4/7] feat(datetime): add new header parts (#4402) --- docs/api/datetime.md | 11 +++++ .../angular/example_component_css.md | 23 +++++++++- .../styling/calendar-header/demo.html | 21 +++++++++ .../styling/calendar-header/javascript.md | 23 +++++++++- .../styling/calendar-header/react/main_css.md | 23 +++++++++- .../datetime/styling/calendar-header/vue.md | 21 +++++++++ .../angular/example_component_css.md | 17 ++++++++ .../angular/example_component_html.md | 5 +++ .../angular/example_component_ts.md | 12 ++++++ .../styling/datetime-header/demo.html | 43 +++++++++++++++++++ .../datetime/styling/datetime-header/index.md | 35 +++++++++++++++ .../styling/datetime-header/javascript.md | 23 ++++++++++ .../styling/datetime-header/react/main_css.md | 17 ++++++++ .../styling/datetime-header/react/main_tsx.md | 17 ++++++++ .../datetime/styling/datetime-header/vue.md | 30 +++++++++++++ 15 files changed, 318 insertions(+), 3 deletions(-) create mode 100644 static/usage/v8/datetime/styling/datetime-header/angular/example_component_css.md create mode 100644 static/usage/v8/datetime/styling/datetime-header/angular/example_component_html.md create mode 100644 static/usage/v8/datetime/styling/datetime-header/angular/example_component_ts.md create mode 100644 static/usage/v8/datetime/styling/datetime-header/demo.html create mode 100644 static/usage/v8/datetime/styling/datetime-header/index.md create mode 100644 static/usage/v8/datetime/styling/datetime-header/javascript.md create mode 100644 static/usage/v8/datetime/styling/datetime-header/react/main_css.md create mode 100644 static/usage/v8/datetime/styling/datetime-header/react/main_tsx.md create mode 100644 static/usage/v8/datetime/styling/datetime-header/vue.md diff --git a/docs/api/datetime.md b/docs/api/datetime.md index 252d8c8d945..9ba9253dd43 100644 --- a/docs/api/datetime.md +++ b/docs/api/datetime.md @@ -43,6 +43,7 @@ import MultipleDateSelection from '@site/static/usage/v8/datetime/multiple/index import GlobalTheming from '@site/static/usage/v8/datetime/styling/global-theming/index.md'; import CalendarHeaderStyling from '@site/static/usage/v8/datetime/styling/calendar-header/index.md'; import CalendarDaysStyling from '@site/static/usage/v8/datetime/styling/calendar-days/index.md'; +import DatetimeHeaderStyling from '@site/static/usage/v8/datetime/styling/datetime-header/index.md'; import WheelStyling from '@site/static/usage/v8/datetime/styling/wheel-styling/index.md'; @@ -353,6 +354,16 @@ The benefit of this approach is that every component, not just `ion-datetime`, c +### Datetime Header + +The datetime header manages the content for the `title` slot and the selected date. + +:::note +The selected date will not render if `preferWheel` is set to `true`. +::: + + + ### Calender Header The calendar header manages the date navigation controls (month/year picker and prev/next buttons) and the days of the week when using a grid style layout. diff --git a/static/usage/v8/datetime/styling/calendar-header/angular/example_component_css.md b/static/usage/v8/datetime/styling/calendar-header/angular/example_component_css.md index f95379ddaa9..f3ebd715337 100644 --- a/static/usage/v8/datetime/styling/calendar-header/angular/example_component_css.md +++ b/static/usage/v8/datetime/styling/calendar-header/angular/example_component_css.md @@ -1,9 +1,30 @@ ```css /* - * Custom Datetime Calendar Header Part + * Custom Datetime Calendar Header Parts * ------------------------------------------- */ +ion-datetime::part(calendar-header) { + background-color: orange; +} + ion-datetime::part(month-year-button) { background-color: lightblue; } + +ion-datetime::part(navigation-button) { + background-color: firebrick; +} + +ion-datetime::part(previous-button) { + color: white; +} + +ion-datetime::part(next-button) { + color: black; +} + +ion-datetime::part(calendar-days-of-week) { + background-color: #9ad162; + color: white; +} ``` diff --git a/static/usage/v8/datetime/styling/calendar-header/demo.html b/static/usage/v8/datetime/styling/calendar-header/demo.html index 331140d4bb4..5dfef5ca9bb 100644 --- a/static/usage/v8/datetime/styling/calendar-header/demo.html +++ b/static/usage/v8/datetime/styling/calendar-header/demo.html @@ -15,9 +15,30 @@ * ------------------------------------------- */ + ion-datetime::part(calendar-header) { + background-color: orange; + } + ion-datetime::part(month-year-button) { background-color: lightblue; } + + ion-datetime::part(navigation-button) { + background-color: firebrick; + } + + ion-datetime::part(previous-button) { + color: white; + } + + ion-datetime::part(next-button) { + color: black; + } + + ion-datetime::part(calendar-days-of-week) { + background-color: #9ad162; + color: white; + } diff --git a/static/usage/v8/datetime/styling/calendar-header/javascript.md b/static/usage/v8/datetime/styling/calendar-header/javascript.md index fc936e5efea..84ed00226c7 100644 --- a/static/usage/v8/datetime/styling/calendar-header/javascript.md +++ b/static/usage/v8/datetime/styling/calendar-header/javascript.md @@ -3,11 +3,32 @@ ``` diff --git a/static/usage/v8/datetime/styling/calendar-header/react/main_css.md b/static/usage/v8/datetime/styling/calendar-header/react/main_css.md index f95379ddaa9..f3ebd715337 100644 --- a/static/usage/v8/datetime/styling/calendar-header/react/main_css.md +++ b/static/usage/v8/datetime/styling/calendar-header/react/main_css.md @@ -1,9 +1,30 @@ ```css /* - * Custom Datetime Calendar Header Part + * Custom Datetime Calendar Header Parts * ------------------------------------------- */ +ion-datetime::part(calendar-header) { + background-color: orange; +} + ion-datetime::part(month-year-button) { background-color: lightblue; } + +ion-datetime::part(navigation-button) { + background-color: firebrick; +} + +ion-datetime::part(previous-button) { + color: white; +} + +ion-datetime::part(next-button) { + color: black; +} + +ion-datetime::part(calendar-days-of-week) { + background-color: #9ad162; + color: white; +} ``` diff --git a/static/usage/v8/datetime/styling/calendar-header/vue.md b/static/usage/v8/datetime/styling/calendar-header/vue.md index 16b7da10a40..72d57d77391 100644 --- a/static/usage/v8/datetime/styling/calendar-header/vue.md +++ b/static/usage/v8/datetime/styling/calendar-header/vue.md @@ -12,8 +12,29 @@ * Custom Datetime Calendar Header Part * ------------------------------------------- */ + ion-datetime::part(calendar-header) { + background-color: orange; + } + ion-datetime::part(month-year-button) { background-color: lightblue; } + + ion-datetime::part(navigation-button) { + background-color: firebrick; + } + + ion-datetime::part(previous-button) { + color: white; + } + + ion-datetime::part(next-button) { + color: black; + } + + ion-datetime::part(calendar-days-of-week) { + background-color: #9ad162; + color: white; + } ``` diff --git a/static/usage/v8/datetime/styling/datetime-header/angular/example_component_css.md b/static/usage/v8/datetime/styling/datetime-header/angular/example_component_css.md new file mode 100644 index 00000000000..d8e9e9e6341 --- /dev/null +++ b/static/usage/v8/datetime/styling/datetime-header/angular/example_component_css.md @@ -0,0 +1,17 @@ +```css +/* + * Custom Datetime Header Parts + * ------------------------------------------- + */ +ion-datetime::part(datetime-header) { + background-color: orange; +} + +ion-datetime::part(datetime-title) { + background-color: pink; +} + +ion-datetime::part(datetime-selected-date) { + background-color: violet; +} +``` diff --git a/static/usage/v8/datetime/styling/datetime-header/angular/example_component_html.md b/static/usage/v8/datetime/styling/datetime-header/angular/example_component_html.md new file mode 100644 index 00000000000..64b144ee4e8 --- /dev/null +++ b/static/usage/v8/datetime/styling/datetime-header/angular/example_component_html.md @@ -0,0 +1,5 @@ +```html + + Select Date + +``` diff --git a/static/usage/v8/datetime/styling/datetime-header/angular/example_component_ts.md b/static/usage/v8/datetime/styling/datetime-header/angular/example_component_ts.md new file mode 100644 index 00000000000..75ed761f6e8 --- /dev/null +++ b/static/usage/v8/datetime/styling/datetime-header/angular/example_component_ts.md @@ -0,0 +1,12 @@ +```ts +import { Component } from '@angular/core'; +import { IonDatetime } from '@ionic/angular/standalone'; + +@Component({ + selector: 'app-example', + templateUrl: 'example.component.html', + styleUrls: ['./example.component.css'], + imports: [IonDatetime], +}) +export class ExampleComponent {} +``` diff --git a/static/usage/v8/datetime/styling/datetime-header/demo.html b/static/usage/v8/datetime/styling/datetime-header/demo.html new file mode 100644 index 00000000000..45155774edf --- /dev/null +++ b/static/usage/v8/datetime/styling/datetime-header/demo.html @@ -0,0 +1,43 @@ + + + + + + Datetime + + + + + + + + + + + +
+ + Select Date + +
+
+
+ + diff --git a/static/usage/v8/datetime/styling/datetime-header/index.md b/static/usage/v8/datetime/styling/datetime-header/index.md new file mode 100644 index 00000000000..db105f3fe61 --- /dev/null +++ b/static/usage/v8/datetime/styling/datetime-header/index.md @@ -0,0 +1,35 @@ +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_example_component_css from './angular/example_component_css.md'; +import angular_example_component_ts from './angular/example_component_ts.md'; + + diff --git a/static/usage/v8/datetime/styling/datetime-header/javascript.md b/static/usage/v8/datetime/styling/datetime-header/javascript.md new file mode 100644 index 00000000000..ebd98aff22d --- /dev/null +++ b/static/usage/v8/datetime/styling/datetime-header/javascript.md @@ -0,0 +1,23 @@ +```html + + Select Date + + + +``` diff --git a/static/usage/v8/datetime/styling/datetime-header/react/main_css.md b/static/usage/v8/datetime/styling/datetime-header/react/main_css.md new file mode 100644 index 00000000000..d8e9e9e6341 --- /dev/null +++ b/static/usage/v8/datetime/styling/datetime-header/react/main_css.md @@ -0,0 +1,17 @@ +```css +/* + * Custom Datetime Header Parts + * ------------------------------------------- + */ +ion-datetime::part(datetime-header) { + background-color: orange; +} + +ion-datetime::part(datetime-title) { + background-color: pink; +} + +ion-datetime::part(datetime-selected-date) { + background-color: violet; +} +``` diff --git a/static/usage/v8/datetime/styling/datetime-header/react/main_tsx.md b/static/usage/v8/datetime/styling/datetime-header/react/main_tsx.md new file mode 100644 index 00000000000..b1cf2088469 --- /dev/null +++ b/static/usage/v8/datetime/styling/datetime-header/react/main_tsx.md @@ -0,0 +1,17 @@ +```tsx +import React from 'react'; +import { IonDatetime } from '@ionic/react'; + +import './main.css'; + +function Example() { + return ( + <> + + Select Date + + + ); +} +export default Example; +``` diff --git a/static/usage/v8/datetime/styling/datetime-header/vue.md b/static/usage/v8/datetime/styling/datetime-header/vue.md new file mode 100644 index 00000000000..f837edba6ef --- /dev/null +++ b/static/usage/v8/datetime/styling/datetime-header/vue.md @@ -0,0 +1,30 @@ +```html + + + + + +``` From e360ac89975cfad28077705d6c0caaf11f956f60 Mon Sep 17 00:00:00 2001 From: Brandy Smith Date: Wed, 4 Mar 2026 10:37:49 -0500 Subject: [PATCH 5/7] docs(range): add new parts for dual knobs to css shadow parts section (#4420) Co-authored-by: Brandy Smith <6577830+brandyscarney@users.noreply.github.com> --- docs/api/range.md | 2 + .../angular/example_component_css.md | 117 ++++++++++--- .../angular/example_component_html.md | 24 +++ .../range/theming/css-shadow-parts/demo.html | 154 ++++++++++++++--- .../range/theming/css-shadow-parts/index.md | 1 + .../theming/css-shadow-parts/javascript.md | 159 +++++++++++++++--- .../css-shadow-parts/react/main_css.md | 117 ++++++++++--- .../css-shadow-parts/react/main_tsx.md | 36 +++- .../v8/range/theming/css-shadow-parts/vue.md | 141 +++++++++++++--- 9 files changed, 634 insertions(+), 117 deletions(-) diff --git a/docs/api/range.md b/docs/api/range.md index fdb042bb3e9..0a949acd1d0 100644 --- a/docs/api/range.md +++ b/docs/api/range.md @@ -124,6 +124,8 @@ import CSSProps from '@site/static/usage/v8/range/theming/css-properties/index.m Range includes [CSS Shadow Parts](#css-shadow-parts) to allow complete customization of specific element nodes within the Range component. CSS Shadow Parts offer the most customization capabilities and are the recommended approach when requiring advance styling with the Range component. +When `dualKnobs` is enabled, additional Shadow Parts are exposed to allow each knob to be styled independently. These are available in two forms: **static identity parts** (`A` and `B`) and **dynamic position parts** (`lower` and `upper`). The A and B parts always refer to the same physical knobs, even if the knobs cross. In contrast, the lower and upper parts reflect the current value position and automatically swap if the knobs cross. This allows styling by consistent identity or by relative value within the range. + import CSSParts from '@site/static/usage/v8/range/theming/css-shadow-parts/index.md'; diff --git a/static/usage/v8/range/theming/css-shadow-parts/angular/example_component_css.md b/static/usage/v8/range/theming/css-shadow-parts/angular/example_component_css.md index 0092daad827..2c6bf85e09e 100644 --- a/static/usage/v8/range/theming/css-shadow-parts/angular/example_component_css.md +++ b/static/usage/v8/range/theming/css-shadow-parts/angular/example_component_css.md @@ -1,43 +1,114 @@ ```css -ion-range::part(tick) { - background: #a2d2ff; +/* Shared styles for all ranges */ +ion-range { + --knob-size: 34px; } -ion-range::part(tick-active) { - background: #bde0fe; +ion-range::part(knob) { + background: #fff; + + box-sizing: border-box; } -ion-range::part(pin) { - display: inline-flex; - align-items: center; - justify-content: center; +/* Hide the Material Design indicator */ +ion-range::part(knob)::before { + display: none; +} - background: #ffafcc; +ion-range::part(pin) { color: #fff; - border-radius: 50%; - transform: scale(1.01); +} + +ion-range::part(tick), +ion-range::part(tick-active) { + height: 8px; + top: 17px; +} - top: -20px; +/* Single Knob Range */ +#range-single::part(bar), +#range-single::part(tick) { + background: #bed4ff; +} - min-width: 28px; - height: 28px; - transition: transform 120ms ease, background 120ms ease; +#range-single::part(bar-active), +#range-single::part(tick-active) { + background: #40a2fd; } -ion-range::part(pin)::before { - content: none; +#range-single::part(knob) { + border: 4px solid #40a2fd; } -ion-range::part(knob) { - background: #ffc8dd; +#range-single::part(knob pressed) { + background: #bed4ff; +} + +#range-single::part(pin), +#range-single::part(pin)::before { + background: #40a2fd; +} + +/* Dual Knobs (A/B) Range */ +#range-a-b::part(bar-active) { + background: #1ea9ca; +} + +#range-a-b::part(knob-a) { + border: 4px solid #0f8fd6; +} + +#range-a-b::part(knob-a pressed) { + background: #cfeefe; +} + +#range-a-b::part(pin-a), +#range-a-b::part(pin-a)::before { + background: #0f8fd6; +} + +#range-a-b::part(knob-b) { + border: 4px solid #2dc2bd; +} + +#range-a-b::part(knob-b pressed) { + background: #d4f5f3; +} + +#range-a-b::part(pin-b), +#range-a-b::part(pin-b)::before { + background: #2dc2bd; +} + +/* Dual Knobs (Lower/Upper) Range */ +#range-lower-upper::part(bar-active) { + background: linear-gradient(to right, #0059ff 0%, #b77bff 100%); +} + +#range-lower-upper::part(knob-lower) { + border: 4px solid #0059ff; +} + +#range-lower-upper::part(knob-lower pressed) { + background: #bed4ff; +} + +#range-lower-upper::part(pin-lower), +#range-lower-upper::part(pin-lower)::before { + background: #0059ff; +} + +#range-lower-upper::part(knob-upper) { + border: 4px solid #b77bff; } -ion-range::part(bar) { - background: #a2d2ff; +#range-lower-upper::part(knob-upper pressed) { + background: #f1e5ff; } -ion-range::part(bar-active) { - background: #bde0fe; +#range-lower-upper::part(pin-upper), +#range-lower-upper::part(pin-upper)::before { + background: #b77bff; } ``` diff --git a/static/usage/v8/range/theming/css-shadow-parts/angular/example_component_html.md b/static/usage/v8/range/theming/css-shadow-parts/angular/example_component_html.md index d8b5aad8076..eaee00e99c6 100644 --- a/static/usage/v8/range/theming/css-shadow-parts/angular/example_component_html.md +++ b/static/usage/v8/range/theming/css-shadow-parts/angular/example_component_html.md @@ -1,5 +1,7 @@ ```html +

Single Knob

+ +

Dual Knobs (A/B)

+ + +

Dual Knobs (Lower/Upper)

+ ``` diff --git a/static/usage/v8/range/theming/css-shadow-parts/demo.html b/static/usage/v8/range/theming/css-shadow-parts/demo.html index 5ee2f25b113..bfa8f9e3ed6 100644 --- a/static/usage/v8/range/theming/css-shadow-parts/demo.html +++ b/static/usage/v8/range/theming/css-shadow-parts/demo.html @@ -9,50 +9,126 @@ @@ -61,7 +137,9 @@
+

Single Knob

+ +

Dual Knobs (A/B)

+ + +

Dual Knobs (Lower/Upper)

+
+ + diff --git a/static/usage/v8/range/theming/css-shadow-parts/index.md b/static/usage/v8/range/theming/css-shadow-parts/index.md index 26f678c892a..0ca0902e211 100644 --- a/static/usage/v8/range/theming/css-shadow-parts/index.md +++ b/static/usage/v8/range/theming/css-shadow-parts/index.md @@ -13,6 +13,7 @@ import angular_example_component_ts from './angular/example_component_ts.md'; +

Single Knob

+ + +

Dual Knobs (A/B)

+ + +

Dual Knobs (Lower/Upper)

+ + + ``` diff --git a/static/usage/v8/range/theming/css-shadow-parts/react/main_css.md b/static/usage/v8/range/theming/css-shadow-parts/react/main_css.md index 0092daad827..2c6bf85e09e 100644 --- a/static/usage/v8/range/theming/css-shadow-parts/react/main_css.md +++ b/static/usage/v8/range/theming/css-shadow-parts/react/main_css.md @@ -1,43 +1,114 @@ ```css -ion-range::part(tick) { - background: #a2d2ff; +/* Shared styles for all ranges */ +ion-range { + --knob-size: 34px; } -ion-range::part(tick-active) { - background: #bde0fe; +ion-range::part(knob) { + background: #fff; + + box-sizing: border-box; } -ion-range::part(pin) { - display: inline-flex; - align-items: center; - justify-content: center; +/* Hide the Material Design indicator */ +ion-range::part(knob)::before { + display: none; +} - background: #ffafcc; +ion-range::part(pin) { color: #fff; - border-radius: 50%; - transform: scale(1.01); +} + +ion-range::part(tick), +ion-range::part(tick-active) { + height: 8px; + top: 17px; +} - top: -20px; +/* Single Knob Range */ +#range-single::part(bar), +#range-single::part(tick) { + background: #bed4ff; +} - min-width: 28px; - height: 28px; - transition: transform 120ms ease, background 120ms ease; +#range-single::part(bar-active), +#range-single::part(tick-active) { + background: #40a2fd; } -ion-range::part(pin)::before { - content: none; +#range-single::part(knob) { + border: 4px solid #40a2fd; } -ion-range::part(knob) { - background: #ffc8dd; +#range-single::part(knob pressed) { + background: #bed4ff; +} + +#range-single::part(pin), +#range-single::part(pin)::before { + background: #40a2fd; +} + +/* Dual Knobs (A/B) Range */ +#range-a-b::part(bar-active) { + background: #1ea9ca; +} + +#range-a-b::part(knob-a) { + border: 4px solid #0f8fd6; +} + +#range-a-b::part(knob-a pressed) { + background: #cfeefe; +} + +#range-a-b::part(pin-a), +#range-a-b::part(pin-a)::before { + background: #0f8fd6; +} + +#range-a-b::part(knob-b) { + border: 4px solid #2dc2bd; +} + +#range-a-b::part(knob-b pressed) { + background: #d4f5f3; +} + +#range-a-b::part(pin-b), +#range-a-b::part(pin-b)::before { + background: #2dc2bd; +} + +/* Dual Knobs (Lower/Upper) Range */ +#range-lower-upper::part(bar-active) { + background: linear-gradient(to right, #0059ff 0%, #b77bff 100%); +} + +#range-lower-upper::part(knob-lower) { + border: 4px solid #0059ff; +} + +#range-lower-upper::part(knob-lower pressed) { + background: #bed4ff; +} + +#range-lower-upper::part(pin-lower), +#range-lower-upper::part(pin-lower)::before { + background: #0059ff; +} + +#range-lower-upper::part(knob-upper) { + border: 4px solid #b77bff; } -ion-range::part(bar) { - background: #a2d2ff; +#range-lower-upper::part(knob-upper pressed) { + background: #f1e5ff; } -ion-range::part(bar-active) { - background: #bde0fe; +#range-lower-upper::part(pin-upper), +#range-lower-upper::part(pin-upper)::before { + background: #b77bff; } ``` diff --git a/static/usage/v8/range/theming/css-shadow-parts/react/main_tsx.md b/static/usage/v8/range/theming/css-shadow-parts/react/main_tsx.md index 801f80be7a5..a48f62e3a09 100644 --- a/static/usage/v8/range/theming/css-shadow-parts/react/main_tsx.md +++ b/static/usage/v8/range/theming/css-shadow-parts/react/main_tsx.md @@ -6,7 +6,41 @@ import './main.css'; function Example() { return ( - + <> +

Single Knob

+ + +

Dual Knobs (A/B)

+ + +

Dual Knobs (Lower/Upper)

+ + ); } diff --git a/static/usage/v8/range/theming/css-shadow-parts/vue.md b/static/usage/v8/range/theming/css-shadow-parts/vue.md index 6aa7c85c795..09528691310 100644 --- a/static/usage/v8/range/theming/css-shadow-parts/vue.md +++ b/static/usage/v8/range/theming/css-shadow-parts/vue.md @@ -1,50 +1,123 @@ ```html + + + + + + + + + App + + + + Open Sheet Modal + + + +
+ Drag the handle to adjust the header's visibility. +
+
+
+
+
+ + + + diff --git a/static/usage/v8/modal/drag-move-event/index.md b/static/usage/v8/modal/drag-move-event/index.md new file mode 100644 index 00000000000..6fbefa4f21b --- /dev/null +++ b/static/usage/v8/modal/drag-move-event/index.md @@ -0,0 +1,29 @@ +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_example_component_html from './angular/example_component_html.md'; +import angular_example_component_ts from './angular/example_component_ts.md'; + + diff --git a/static/usage/v8/modal/drag-move-event/javascript.md b/static/usage/v8/modal/drag-move-event/javascript.md new file mode 100644 index 00000000000..64d64b70a1f --- /dev/null +++ b/static/usage/v8/modal/drag-move-event/javascript.md @@ -0,0 +1,77 @@ +```html + + + App + + + + Open Sheet Modal + + + +
+ Drag the handle to adjust the header's visibility. +
+
+
+
+ + +``` diff --git a/static/usage/v8/modal/drag-move-event/react.md b/static/usage/v8/modal/drag-move-event/react.md new file mode 100644 index 00000000000..83d85246e8f --- /dev/null +++ b/static/usage/v8/modal/drag-move-event/react.md @@ -0,0 +1,99 @@ +```tsx +import React, { useRef } from 'react'; +import { IonButton, IonModal, IonHeader, IonContent, IonToolbar, IonTitle, IonPage, IonLabel } from '@ionic/react'; +import type { ModalDragEventDetail } from '@ionic/react'; + +function Example() { + const header = useRef(null); + // Assign the current snap breakpoint to the initial breakpoint so + // that we can track changes during the drag + const currentSnap = useRef(0.25); + + const onDragMove = (event: CustomEvent) => { + // `progress` is a value from 1 (top) to 0 (bottom) + // `snapBreakpoint` tells us which snap point the modal will animate to after the drag ends + const { progress, snapBreakpoint } = event.detail; + const headerEl = header.current!; + + if (currentSnap.current !== snapBreakpoint) { + currentSnap.current = snapBreakpoint as number; + + console.log('Current snap breakpoint:', snapBreakpoint); + } + + /** + * Inverse relationship: + * 1.0 progress = 0 opacity + * 0 progress = 1.0 opacity + */ + const currentOpacity = 1 - progress; + + headerEl.style.opacity = currentOpacity.toString(); + }; + + const onDragEnd = (event: CustomEvent) => { + // `progress` is a value from 1 (top) to 0 (bottom) + // `snapBreakpoint` tells us which snap point the modal will animate to after the drag ends + const { progress, snapBreakpoint } = event.detail; + const headerEl = header.current!; + + /** + * If the modal is snapping to the closed state (0), reset the + * styles. + */ + if (snapBreakpoint === 0) { + headerEl.style.removeProperty('opacity'); + headerEl.style.removeProperty('transition'); + return; + } + + // Smooth transition to the final resting opacity + headerEl.style.transition = 'opacity 0.4s ease'; + // The final opacity matches the inverse of the resting progress + headerEl.style.opacity = (1 - progress).toString(); + }; + + /** + * If the user dismisses the modal (e.g. tapping the backdrop), + * reset the styles. + */ + const onWillDismiss = () => { + const headerEl = header.current!; + + // Reset styles when the modal is dismissed + headerEl.style.removeProperty('opacity'); + headerEl.style.removeProperty('transition'); + }; + + return ( + + + + App + + + + + Open Sheet Modal + + + +
+ Drag the handle to adjust the header's visibility. +
+
+
+
+
+ ); +} + +export default Example; +``` diff --git a/static/usage/v8/modal/drag-move-event/vue.md b/static/usage/v8/modal/drag-move-event/vue.md new file mode 100644 index 00000000000..c95b3329259 --- /dev/null +++ b/static/usage/v8/modal/drag-move-event/vue.md @@ -0,0 +1,94 @@ +```vue + + + +``` diff --git a/static/usage/v8/modal/drag-start-end-events/angular/example_component_html.md b/static/usage/v8/modal/drag-start-end-events/angular/example_component_html.md new file mode 100644 index 00000000000..dca7cbd4b07 --- /dev/null +++ b/static/usage/v8/modal/drag-start-end-events/angular/example_component_html.md @@ -0,0 +1,26 @@ +```html + + + App + + + + Open Sheet Modal + + + + +
+ Drag the handle to adjust the header's visibility. +
+
+
+
+
+``` diff --git a/static/usage/v8/modal/drag-start-end-events/angular/example_component_ts.md b/static/usage/v8/modal/drag-start-end-events/angular/example_component_ts.md new file mode 100644 index 00000000000..a8cedb06efb --- /dev/null +++ b/static/usage/v8/modal/drag-start-end-events/angular/example_component_ts.md @@ -0,0 +1,32 @@ +```ts +import { Component, ElementRef, ViewChild } from '@angular/core'; +import { IonButton, IonContent, IonHeader, IonLabel, IonModal, IonTitle, IonToolbar } from '@ionic/angular/standalone'; +import type { ModalDragEventDetail } from '@ionic/angular/standalone'; + +@Component({ + selector: 'app-example', + templateUrl: 'example.component.html', + standalone: true, + imports: [IonButton, IonContent, IonHeader, IonLabel, IonModal, IonTitle, IonToolbar], +}) +export class ExampleComponent { + @ViewChild('header', { read: ElementRef }) + header!: ElementRef; + + onDragStart() { + console.log('Drag started'); + + const headerEl = this.header.nativeElement; + + headerEl.style.opacity = '0'; + } + + onDragEnd(event: CustomEvent) { + console.log('Drag ended'); + + const headerEl = this.header.nativeElement; + + headerEl.style.opacity = '1'; + } +} +``` diff --git a/static/usage/v8/modal/drag-start-end-events/demo.html b/static/usage/v8/modal/drag-start-end-events/demo.html new file mode 100644 index 00000000000..5e6f2aac600 --- /dev/null +++ b/static/usage/v8/modal/drag-start-end-events/demo.html @@ -0,0 +1,52 @@ + + + + + + Modal + + + + + + + + + + + App + + + + Open Sheet Modal + + + +
+ Drag the handle to adjust the header's visibility. +
+
+
+
+
+ + + + diff --git a/static/usage/v8/modal/drag-start-end-events/index.md b/static/usage/v8/modal/drag-start-end-events/index.md new file mode 100644 index 00000000000..ec32fa698a7 --- /dev/null +++ b/static/usage/v8/modal/drag-start-end-events/index.md @@ -0,0 +1,29 @@ +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_example_component_html from './angular/example_component_html.md'; +import angular_example_component_ts from './angular/example_component_ts.md'; + + diff --git a/static/usage/v8/modal/drag-start-end-events/javascript.md b/static/usage/v8/modal/drag-start-end-events/javascript.md new file mode 100644 index 00000000000..5e36cff2e8e --- /dev/null +++ b/static/usage/v8/modal/drag-start-end-events/javascript.md @@ -0,0 +1,37 @@ +```html + + + App + + + + Open Sheet Modal + + + +
+ Drag the handle to adjust the header's visibility. +
+
+
+
+ + +``` diff --git a/static/usage/v8/modal/drag-start-end-events/react.md b/static/usage/v8/modal/drag-start-end-events/react.md new file mode 100644 index 00000000000..00b2a34fcab --- /dev/null +++ b/static/usage/v8/modal/drag-start-end-events/react.md @@ -0,0 +1,55 @@ +```tsx +import React, { useRef } from 'react'; +import { IonButton, IonModal, IonHeader, IonContent, IonToolbar, IonTitle, IonPage, IonLabel } from '@ionic/react'; +import type { ModalDragEventDetail } from '@ionic/react'; + +function Example() { + const header = useRef(null); + + const onDragStart = () => { + console.log('Drag started'); + + const headerEl = header.current!; + + headerEl.style.opacity = '0'; + }; + + const onDragEnd = (event: CustomEvent) => { + console.log('Drag ended'); + + const headerEl = header.current!; + + headerEl.style.opacity = '1'; + }; + + return ( + + + + App + + + + + Open Sheet Modal + + + +
+ Drag the handle to adjust the header's visibility. +
+
+
+
+
+ ); +} + +export default Example; +``` diff --git a/static/usage/v8/modal/drag-start-end-events/vue.md b/static/usage/v8/modal/drag-start-end-events/vue.md new file mode 100644 index 00000000000..a8f8b06acaf --- /dev/null +++ b/static/usage/v8/modal/drag-start-end-events/vue.md @@ -0,0 +1,51 @@ +```vue + + + +``` From 4cc648dca546de8850c858421748b1170d7bf560 Mon Sep 17 00:00:00 2001 From: Brandy Smith Date: Thu, 5 Mar 2026 17:05:32 -0500 Subject: [PATCH 7/7] docs(refresher): add new section for pull start and end events (#4421) --- docs/api/refresher.md | 15 +++- .../angular/example_component_html.md | 30 +++++++ .../angular/example_component_ts.md | 72 +++++++++++++++ .../refresher/pull-start-end-events/demo.html | 82 +++++++++++++++++ .../refresher/pull-start-end-events/index.md | 27 ++++++ .../pull-start-end-events/javascript.md | 67 ++++++++++++++ .../refresher/pull-start-end-events/react.md | 90 +++++++++++++++++++ .../v8/refresher/pull-start-end-events/vue.md | 82 +++++++++++++++++ 8 files changed, 463 insertions(+), 2 deletions(-) create mode 100644 static/usage/v8/refresher/pull-start-end-events/angular/example_component_html.md create mode 100644 static/usage/v8/refresher/pull-start-end-events/angular/example_component_ts.md create mode 100644 static/usage/v8/refresher/pull-start-end-events/demo.html create mode 100644 static/usage/v8/refresher/pull-start-end-events/index.md create mode 100644 static/usage/v8/refresher/pull-start-end-events/javascript.md create mode 100644 static/usage/v8/refresher/pull-start-end-events/react.md create mode 100644 static/usage/v8/refresher/pull-start-end-events/vue.md diff --git a/docs/api/refresher.md b/docs/api/refresher.md index b7e006e5de0..ca0ad55a025 100644 --- a/docs/api/refresher.md +++ b/docs/api/refresher.md @@ -73,10 +73,10 @@ Developers should apply the following CSS to the scrollable container. This CSS .ion-content-scroll-host::before, .ion-content-scroll-host::after { position: absolute; - + width: 1px; height: 1px; - + content: ""; } @@ -102,6 +102,17 @@ import Advanced from '@site/static/usage/v8/refresher/advanced/index.md'; +## Event Handling + +### Using `ionPullStart` and `ionPullEnd` + +The `ionPullStart` event is emitted when the user begins a pull gesture. This event fires when the user starts to pull the refresher down. + +The `ionPullEnd` event is emitted when the refresher returns to an inactive state, with a reason property of `'complete'` or `'cancel'` indicating whether the refresh operation completed successfully or was canceled. + +import PullStartEndEvents from '@site/static/usage/v8/refresher/pull-start-end-events/index.md'; + + ## Interfaces diff --git a/static/usage/v8/refresher/pull-start-end-events/angular/example_component_html.md b/static/usage/v8/refresher/pull-start-end-events/angular/example_component_html.md new file mode 100644 index 00000000000..3ee329401af --- /dev/null +++ b/static/usage/v8/refresher/pull-start-end-events/angular/example_component_html.md @@ -0,0 +1,30 @@ +```html + + + Pull to Refresh + + + + + + + + +

Pull this content down to trigger the refresh.

+ + + @for (item of items; track item; let i = $index) { + + + {{ item.label }} + + } + +
+``` diff --git a/static/usage/v8/refresher/pull-start-end-events/angular/example_component_ts.md b/static/usage/v8/refresher/pull-start-end-events/angular/example_component_ts.md new file mode 100644 index 00000000000..1fba72b54b0 --- /dev/null +++ b/static/usage/v8/refresher/pull-start-end-events/angular/example_component_ts.md @@ -0,0 +1,72 @@ +```ts +import { Component } from '@angular/core'; +import { + IonCheckbox, + IonContent, + IonHeader, + IonItem, + IonLabel, + IonList, + IonRefresher, + IonRefresherContent, + IonTitle, + IonToolbar, + RefresherCustomEvent, + RefresherPullEndCustomEvent, +} from '@ionic/angular/standalone'; + +@Component({ + selector: 'app-example', + templateUrl: 'example.component.html', + styleUrls: ['example.component.css'], + imports: [ + IonCheckbox, + IonContent, + IonHeader, + IonItem, + IonLabel, + IonList, + IonRefresher, + IonRefresherContent, + IonTitle, + IonToolbar, + ], +}) +export class ExampleComponent { + items = [ + { label: 'Finalize Q1 budget proposal', checked: false, disabled: false }, + { label: 'Review design mockups', checked: true, disabled: false }, + { label: 'Sync with engineering on API docs', checked: true, disabled: false }, + { label: 'Approve PTO requests for March', checked: false, disabled: false }, + { label: 'Draft monthly newsletter', checked: false, disabled: false }, + ]; + + constructor() {} + + handlePullStart() { + console.log('Pull started'); + + // Disable the checkboxes when the pull starts + this.items.forEach((item) => { + item.disabled = true; + }); + } + + handlePullEnd(event: RefresherPullEndCustomEvent) { + console.log('Pull ended with reason: "' + event.detail.reason + '"'); + + // Enable the checkboxes when the pull ends + this.items.forEach((item) => { + item.disabled = false; + }); + } + + handleRefresh(event: RefresherCustomEvent) { + setTimeout(() => { + // Any calls to load data go here + event.target.complete(); + console.log('Refresh completed'); + }, 2000); + } +} +``` diff --git a/static/usage/v8/refresher/pull-start-end-events/demo.html b/static/usage/v8/refresher/pull-start-end-events/demo.html new file mode 100644 index 00000000000..a246d308ec6 --- /dev/null +++ b/static/usage/v8/refresher/pull-start-end-events/demo.html @@ -0,0 +1,82 @@ + + + + + + Refresher + + + + + + + + + + + Pull to Refresh + + + + + + + + +

Pull this content down to trigger the refresh.

+ + + + + Finalize Q1 budget proposal + + + + Review design mockups + + + + Sync with engineering on API docs + + + + Approve PTO requests for March + + + + Draft monthly newsletter + + +
+ + +
+ + diff --git a/static/usage/v8/refresher/pull-start-end-events/index.md b/static/usage/v8/refresher/pull-start-end-events/index.md new file mode 100644 index 00000000000..1adc1ddc06a --- /dev/null +++ b/static/usage/v8/refresher/pull-start-end-events/index.md @@ -0,0 +1,27 @@ +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_example_component_html from './angular/example_component_html.md'; +import angular_example_component_ts from './angular/example_component_ts.md'; + + diff --git a/static/usage/v8/refresher/pull-start-end-events/javascript.md b/static/usage/v8/refresher/pull-start-end-events/javascript.md new file mode 100644 index 00000000000..f52af7c6d15 --- /dev/null +++ b/static/usage/v8/refresher/pull-start-end-events/javascript.md @@ -0,0 +1,67 @@ +```html + + + Pull to Refresh + + + + + + + + +

Pull this content down to trigger the refresh.

+ + + + + Finalize Q1 budget proposal + + + + Review design mockups + + + + Sync with engineering on API docs + + + + Approve PTO requests for March + + + + Draft monthly newsletter + + +
+ + +``` diff --git a/static/usage/v8/refresher/pull-start-end-events/react.md b/static/usage/v8/refresher/pull-start-end-events/react.md new file mode 100644 index 00000000000..7278b39be6a --- /dev/null +++ b/static/usage/v8/refresher/pull-start-end-events/react.md @@ -0,0 +1,90 @@ +```tsx +import React, { useState } from 'react'; +import { + IonCheckbox, + IonContent, + IonHeader, + IonItem, + IonLabel, + IonList, + IonTitle, + IonToolbar, + IonRefresher, + IonRefresherContent, + RefresherCustomEvent, + RefresherPullEndCustomEvent, +} from '@ionic/react'; + +interface TodoItem { + label: string; + checked: boolean; + disabled: boolean; +} + +function Example() { + const [items, setItems] = useState([ + { label: 'Finalize Q1 budget proposal', checked: false, disabled: false }, + { label: 'Review design mockups', checked: true, disabled: false }, + { label: 'Sync with engineering on API docs', checked: true, disabled: false }, + { label: 'Approve PTO requests for March', checked: false, disabled: false }, + { label: 'Draft monthly newsletter', checked: false, disabled: false }, + ]); + + function handlePullStart() { + console.log('Pull started'); + + // Disable the checkboxes when the pull starts + setItems((prev) => prev.map((item) => ({ ...item, disabled: true }))); + } + + function handlePullEnd(event: RefresherPullEndCustomEvent) { + console.log('Pull ended with reason: "' + event.detail.reason + '"'); + + // Enable the checkboxes when the pull ends + setItems((prev) => prev.map((item) => ({ ...item, disabled: false }))); + } + + function handleRefresh(event: RefresherCustomEvent) { + setTimeout(() => { + // Any calls to load data go here + event.target.complete(); + console.log('Refresh completed'); + }, 2000); + } + + return ( + <> + + + Pull to Refresh + + + + + + + + +

Pull this content down to trigger the refresh.

+ + + {items.map((item: TodoItem) => ( + + + {item.label} + + ))} + +
+ + ); +} + +export default Example; +``` diff --git a/static/usage/v8/refresher/pull-start-end-events/vue.md b/static/usage/v8/refresher/pull-start-end-events/vue.md new file mode 100644 index 00000000000..51590f93722 --- /dev/null +++ b/static/usage/v8/refresher/pull-start-end-events/vue.md @@ -0,0 +1,82 @@ +```vue + + + +```