mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2026-03-13 10:22:08 +08:00
feat(angular): standalone form controls can participate in forms (#28125)
Issue number: N/A --------- <!-- Please do not submit updates to dependencies unless it fixes an issue. --> <!-- Please try to limit your pull request to one type (bugfix, feature, etc). Submit multiple pull requests if needed. --> ## What is the current behavior? <!-- Please describe the current behavior that you are modifying. --> Ionic standalone form controls cannot participate in Angular forms. ## What is the new behavior? <!-- Please describe the behavior or changes that are being added by this PR. --> - Ionic form controls can participate in Angular forms by importing the standalone component - Applies to: `ion-input`, `ion-textarea`, `ion-searchbar`, `ion-toggle`, `ion-checkbox`, `ion-segment`, `ion-radio`, `ion-radio-group`, `ion-datetime` and `ion-range`. - Refactors `ValueAccessor` from `@ionic/angular` to `@ionic/angular/common` - Refactors `raf` utility from `@ionic/angular` to `@ionic/angular/common` ## Does this introduce a breaking change? - [ ] Yes - [x] No <!-- If this introduces a breaking change, please describe the impact and migration path for existing applications below. --> ## Other information <!-- Any other information that is important to this PR such as screenshots of how the component looks before and after the change. --> ---------
This commit is contained in:
@@ -50,7 +50,22 @@ const getAngularOutputTargets = () => {
|
||||
* are reliant on the CE build will reference the wrong
|
||||
* import location.
|
||||
*/
|
||||
'ion-icon'
|
||||
'ion-icon',
|
||||
/**
|
||||
* Value Accessors are manually implemented in the `@ionic/angular/standalone` package.
|
||||
*/
|
||||
'ion-input',
|
||||
'ion-textarea',
|
||||
'ion-searchbar',
|
||||
'ion-datetime',
|
||||
'ion-radio',
|
||||
'ion-segment',
|
||||
'ion-checkbox',
|
||||
'ion-toggle',
|
||||
'ion-range',
|
||||
'ion-radio-group',
|
||||
'ion-select'
|
||||
|
||||
],
|
||||
outputType: 'standalone',
|
||||
})
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
export * from './value-accessor';
|
||||
@@ -2,7 +2,7 @@ import { AfterViewInit, ElementRef, Injector, OnDestroy, Directive, HostListener
|
||||
import { ControlValueAccessor, NgControl } from '@angular/forms';
|
||||
import { Subscription } from 'rxjs';
|
||||
|
||||
import { raf } from '../../util/util';
|
||||
import { raf } from '../../utils/util';
|
||||
|
||||
// TODO(FW-2827): types
|
||||
|
||||
@@ -17,11 +17,11 @@ export class ValueAccessor implements ControlValueAccessor, AfterViewInit, OnDes
|
||||
protected lastValue: any;
|
||||
private statusChanges?: Subscription;
|
||||
|
||||
constructor(protected injector: Injector, protected el: ElementRef) {}
|
||||
constructor(protected injector: Injector, protected elementRef: ElementRef) {}
|
||||
|
||||
writeValue(value: any): void {
|
||||
this.el.nativeElement.value = this.lastValue = value;
|
||||
setIonicClasses(this.el);
|
||||
this.elementRef.nativeElement.value = this.lastValue = value;
|
||||
setIonicClasses(this.elementRef);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -38,20 +38,20 @@ export class ValueAccessor implements ControlValueAccessor, AfterViewInit, OnDes
|
||||
* @param value The new value of the control.
|
||||
*/
|
||||
handleValueChange(el: HTMLElement, value: any): void {
|
||||
if (el === this.el.nativeElement) {
|
||||
if (el === this.elementRef.nativeElement) {
|
||||
if (value !== this.lastValue) {
|
||||
this.lastValue = value;
|
||||
this.onChange(value);
|
||||
}
|
||||
setIonicClasses(this.el);
|
||||
setIonicClasses(this.elementRef);
|
||||
}
|
||||
}
|
||||
|
||||
@HostListener('ionBlur', ['$event.target'])
|
||||
_handleBlurEvent(el: any): void {
|
||||
if (el === this.el.nativeElement) {
|
||||
if (el === this.elementRef.nativeElement) {
|
||||
this.onTouched();
|
||||
setIonicClasses(this.el);
|
||||
setIonicClasses(this.elementRef);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -64,7 +64,7 @@ export class ValueAccessor implements ControlValueAccessor, AfterViewInit, OnDes
|
||||
}
|
||||
|
||||
setDisabledState(isDisabled: boolean): void {
|
||||
this.el.nativeElement.disabled = isDisabled;
|
||||
this.elementRef.nativeElement.disabled = isDisabled;
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
@@ -87,7 +87,7 @@ export class ValueAccessor implements ControlValueAccessor, AfterViewInit, OnDes
|
||||
|
||||
// Listen for changes in validity, disabled, or pending states
|
||||
if (ngControl.statusChanges) {
|
||||
this.statusChanges = ngControl.statusChanges.subscribe(() => setIonicClasses(this.el));
|
||||
this.statusChanges = ngControl.statusChanges.subscribe(() => setIonicClasses(this.elementRef));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -102,7 +102,7 @@ export class ValueAccessor implements ControlValueAccessor, AfterViewInit, OnDes
|
||||
const oldFn = formControl[method].bind(formControl);
|
||||
formControl[method] = (...params: any[]) => {
|
||||
oldFn(...params);
|
||||
setIonicClasses(this.el);
|
||||
setIonicClasses(this.elementRef);
|
||||
};
|
||||
}
|
||||
});
|
||||
@@ -129,7 +129,7 @@ export const setIonicClasses = (element: ElementRef): void => {
|
||||
|
||||
const getClasses = (element: HTMLElement) => {
|
||||
const classList = element.classList;
|
||||
const classes = [];
|
||||
const classes: string[] = [];
|
||||
for (let i = 0; i < classList.length; i++) {
|
||||
const item = classList.item(i);
|
||||
if (item !== null && startsWith(item, 'ng-')) {
|
||||
@@ -33,7 +33,10 @@ export {
|
||||
} from './directives/navigation/router-link-delegate';
|
||||
export { IonNav } from './directives/navigation/nav';
|
||||
export { IonTabs } from './directives/navigation/tabs';
|
||||
export * from './directives/control-value-accessors';
|
||||
|
||||
export { ProxyCmp } from './utils/proxy';
|
||||
|
||||
export { IonicRouteStrategy } from './utils/routing';
|
||||
|
||||
export { raf } from './utils/util';
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
import { NgZone } from '@angular/core';
|
||||
import type { Config, IonicWindow } from '@ionic/angular/common';
|
||||
import { raf } from '@ionic/angular/common';
|
||||
import { setupConfig } from '@ionic/core';
|
||||
import { applyPolyfills, defineCustomElements } from '@ionic/core/loader';
|
||||
|
||||
import { raf } from './util/util';
|
||||
|
||||
// TODO(FW-2827): types
|
||||
|
||||
export const appInitialize = (config: Config, doc: Document, zone: NgZone) => {
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { Directive, HostListener, ElementRef, Injector } from '@angular/core';
|
||||
import { NG_VALUE_ACCESSOR } from '@angular/forms';
|
||||
|
||||
import { ValueAccessor, setIonicClasses } from './value-accessor';
|
||||
import { ValueAccessor, setIonicClasses } from '@ionic/angular/common';
|
||||
|
||||
@Directive({
|
||||
selector: 'ion-checkbox,ion-toggle',
|
||||
@@ -19,8 +18,8 @@ export class BooleanValueAccessorDirective extends ValueAccessor {
|
||||
}
|
||||
|
||||
writeValue(value: boolean): void {
|
||||
this.el.nativeElement.checked = this.lastValue = value;
|
||||
setIonicClasses(this.el);
|
||||
this.elementRef.nativeElement.checked = this.lastValue = value;
|
||||
setIonicClasses(this.elementRef);
|
||||
}
|
||||
|
||||
@HostListener('ionChange', ['$event.target'])
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { Directive, HostListener, ElementRef, Injector } from '@angular/core';
|
||||
import { NG_VALUE_ACCESSOR } from '@angular/forms';
|
||||
|
||||
import { ValueAccessor } from './value-accessor';
|
||||
import { ValueAccessor } from '@ionic/angular/common';
|
||||
|
||||
@Directive({
|
||||
selector: 'ion-input[type=number]',
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { ElementRef, Injector, Directive, HostListener } from '@angular/core';
|
||||
import { NG_VALUE_ACCESSOR } from '@angular/forms';
|
||||
|
||||
import { ValueAccessor } from './value-accessor';
|
||||
import { ValueAccessor } from '@ionic/angular/common';
|
||||
|
||||
@Directive({
|
||||
/* tslint:disable-next-line:directive-selector */
|
||||
@@ -19,9 +18,12 @@ export class RadioValueAccessorDirective extends ValueAccessor {
|
||||
super(injector, el);
|
||||
}
|
||||
|
||||
// TODO(FW-2827): type (HTMLIonRadioElement and HTMLElement are both missing `checked`)
|
||||
@HostListener('ionSelect', ['$event.target'])
|
||||
_handleIonSelect(el: any): void {
|
||||
/**
|
||||
* The `el` type is any to access the `checked` state property
|
||||
* that is not exposed on the type interface.
|
||||
*/
|
||||
this.handleValueChange(el, el.checked);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { ElementRef, Injector, Directive, HostListener } from '@angular/core';
|
||||
import { NG_VALUE_ACCESSOR } from '@angular/forms';
|
||||
|
||||
import { ValueAccessor } from './value-accessor';
|
||||
import { ValueAccessor } from '@ionic/angular/common';
|
||||
|
||||
@Directive({
|
||||
/* tslint:disable-next-line:directive-selector */
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { ElementRef, Injector, Directive, HostListener } from '@angular/core';
|
||||
import { NG_VALUE_ACCESSOR } from '@angular/forms';
|
||||
|
||||
import { ValueAccessor } from './value-accessor';
|
||||
import { ValueAccessor } from '@ionic/angular/common';
|
||||
|
||||
@Directive({
|
||||
selector: 'ion-input:not([type=number]),ion-textarea,ion-searchbar',
|
||||
|
||||
86
packages/angular/standalone/src/directives/checkbox.ts
Normal file
86
packages/angular/standalone/src/directives/checkbox.ts
Normal file
@@ -0,0 +1,86 @@
|
||||
import {
|
||||
ChangeDetectionStrategy,
|
||||
ChangeDetectorRef,
|
||||
Component,
|
||||
ElementRef,
|
||||
EventEmitter,
|
||||
HostListener,
|
||||
Injector,
|
||||
NgZone,
|
||||
} from '@angular/core';
|
||||
import { NG_VALUE_ACCESSOR } from '@angular/forms';
|
||||
import { ValueAccessor, setIonicClasses } from '@ionic/angular/common';
|
||||
import type { CheckboxChangeEventDetail, Components } from '@ionic/core/components';
|
||||
import { defineCustomElement } from '@ionic/core/components/ion-checkbox.js';
|
||||
|
||||
import { ProxyCmp, proxyOutputs } from './angular-component-lib/utils';
|
||||
|
||||
const CHECKBOX_INPUTS = [
|
||||
'checked',
|
||||
'color',
|
||||
'disabled',
|
||||
'indeterminate',
|
||||
'justify',
|
||||
'labelPlacement',
|
||||
'legacy',
|
||||
'mode',
|
||||
'name',
|
||||
'value',
|
||||
];
|
||||
|
||||
@ProxyCmp({
|
||||
defineCustomElementFn: defineCustomElement,
|
||||
inputs: CHECKBOX_INPUTS,
|
||||
})
|
||||
@Component({
|
||||
selector: 'ion-checkbox',
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
template: '<ng-content></ng-content>',
|
||||
// eslint-disable-next-line @angular-eslint/no-inputs-metadata-property
|
||||
inputs: CHECKBOX_INPUTS,
|
||||
providers: [
|
||||
{
|
||||
provide: NG_VALUE_ACCESSOR,
|
||||
useExisting: IonCheckbox,
|
||||
multi: true,
|
||||
},
|
||||
],
|
||||
standalone: true,
|
||||
})
|
||||
export class IonCheckbox extends ValueAccessor {
|
||||
protected el: HTMLElement;
|
||||
constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone, injector: Injector) {
|
||||
super(injector, r);
|
||||
c.detach();
|
||||
this.el = r.nativeElement;
|
||||
proxyOutputs(this, this.el, ['ionChange', 'ionFocus', 'ionBlur']);
|
||||
}
|
||||
|
||||
writeValue(value: boolean): void {
|
||||
this.elementRef.nativeElement.checked = this.lastValue = value;
|
||||
setIonicClasses(this.elementRef);
|
||||
}
|
||||
|
||||
@HostListener('ionChange', ['$event.target'])
|
||||
handleIonChange(el: HTMLIonCheckboxElement | HTMLIonToggleElement): void {
|
||||
this.handleValueChange(el, el.checked);
|
||||
}
|
||||
}
|
||||
|
||||
export declare interface IonCheckbox extends Components.IonCheckbox {
|
||||
/**
|
||||
* Emitted when the checked property has changed
|
||||
as a result of a user action such as a click.
|
||||
This event will not emit when programmatically
|
||||
setting the checked property.
|
||||
*/
|
||||
ionChange: EventEmitter<CustomEvent<CheckboxChangeEventDetail>>;
|
||||
/**
|
||||
* Emitted when the checkbox has focus.
|
||||
*/
|
||||
ionFocus: EventEmitter<CustomEvent<void>>;
|
||||
/**
|
||||
* Emitted when the checkbox loses focus.
|
||||
*/
|
||||
ionBlur: EventEmitter<CustomEvent<void>>;
|
||||
}
|
||||
103
packages/angular/standalone/src/directives/datetime.ts
Normal file
103
packages/angular/standalone/src/directives/datetime.ts
Normal file
@@ -0,0 +1,103 @@
|
||||
import {
|
||||
ChangeDetectionStrategy,
|
||||
ChangeDetectorRef,
|
||||
Component,
|
||||
ElementRef,
|
||||
EventEmitter,
|
||||
HostListener,
|
||||
Injector,
|
||||
NgZone,
|
||||
} from '@angular/core';
|
||||
import { NG_VALUE_ACCESSOR } from '@angular/forms';
|
||||
import { ValueAccessor } from '@ionic/angular/common';
|
||||
import type { DatetimeChangeEventDetail, Components } from '@ionic/core/components';
|
||||
import { defineCustomElement } from '@ionic/core/components/ion-datetime.js';
|
||||
|
||||
import { ProxyCmp, proxyOutputs } from './angular-component-lib/utils';
|
||||
|
||||
const DATETIME_INPUTS = [
|
||||
'cancelText',
|
||||
'clearText',
|
||||
'color',
|
||||
'dayValues',
|
||||
'disabled',
|
||||
'doneText',
|
||||
'firstDayOfWeek',
|
||||
'highlightedDates',
|
||||
'hourCycle',
|
||||
'hourValues',
|
||||
'isDateEnabled',
|
||||
'locale',
|
||||
'max',
|
||||
'min',
|
||||
'minuteValues',
|
||||
'mode',
|
||||
'monthValues',
|
||||
'multiple',
|
||||
'name',
|
||||
'preferWheel',
|
||||
'presentation',
|
||||
'readonly',
|
||||
'showClearButton',
|
||||
'showDefaultButtons',
|
||||
'showDefaultTimeLabel',
|
||||
'showDefaultTitle',
|
||||
'size',
|
||||
'titleSelectedDatesFormatter',
|
||||
'value',
|
||||
'yearValues',
|
||||
];
|
||||
|
||||
@ProxyCmp({
|
||||
defineCustomElementFn: defineCustomElement,
|
||||
inputs: DATETIME_INPUTS,
|
||||
methods: ['confirm', 'reset', 'cancel'],
|
||||
})
|
||||
@Component({
|
||||
selector: 'ion-datetime',
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
template: '<ng-content></ng-content>',
|
||||
// eslint-disable-next-line @angular-eslint/no-inputs-metadata-property
|
||||
inputs: DATETIME_INPUTS,
|
||||
providers: [
|
||||
{
|
||||
provide: NG_VALUE_ACCESSOR,
|
||||
useExisting: IonDatetime,
|
||||
multi: true,
|
||||
},
|
||||
],
|
||||
standalone: true,
|
||||
})
|
||||
export class IonDatetime extends ValueAccessor {
|
||||
protected el: HTMLElement;
|
||||
constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone, injector: Injector) {
|
||||
super(injector, r);
|
||||
c.detach();
|
||||
this.el = r.nativeElement;
|
||||
proxyOutputs(this, this.el, ['ionCancel', 'ionChange', 'ionFocus', 'ionBlur']);
|
||||
}
|
||||
|
||||
@HostListener('ionChange', ['$event.target'])
|
||||
handleIonChange(el: HTMLIonDatetimeElement): void {
|
||||
this.handleValueChange(el, el.value);
|
||||
}
|
||||
}
|
||||
|
||||
export declare interface IonDatetime extends Components.IonDatetime {
|
||||
/**
|
||||
* Emitted when the datetime selection was cancelled.
|
||||
*/
|
||||
ionCancel: EventEmitter<CustomEvent<void>>;
|
||||
/**
|
||||
* Emitted when the value (selected date) has changed.
|
||||
*/
|
||||
ionChange: EventEmitter<CustomEvent<DatetimeChangeEventDetail>>;
|
||||
/**
|
||||
* Emitted when the datetime has focus.
|
||||
*/
|
||||
ionFocus: EventEmitter<CustomEvent<void>>;
|
||||
/**
|
||||
* Emitted when the datetime loses focus.
|
||||
*/
|
||||
ionBlur: EventEmitter<CustomEvent<void>>;
|
||||
}
|
||||
12
packages/angular/standalone/src/directives/index.ts
Normal file
12
packages/angular/standalone/src/directives/index.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
export * from './checkbox';
|
||||
export * from './datetime';
|
||||
export * from './icon';
|
||||
export * from './input';
|
||||
export * from './radio-group';
|
||||
export * from './radio';
|
||||
export * from './range';
|
||||
export * from './searchbar';
|
||||
export * from './segment';
|
||||
export * from './select';
|
||||
export * from './textarea';
|
||||
export * from './toggle';
|
||||
145
packages/angular/standalone/src/directives/input.ts
Normal file
145
packages/angular/standalone/src/directives/input.ts
Normal file
@@ -0,0 +1,145 @@
|
||||
import {
|
||||
ChangeDetectionStrategy,
|
||||
ChangeDetectorRef,
|
||||
Component,
|
||||
ElementRef,
|
||||
EventEmitter,
|
||||
HostListener,
|
||||
Injector,
|
||||
NgZone,
|
||||
} from '@angular/core';
|
||||
import { NG_VALUE_ACCESSOR } from '@angular/forms';
|
||||
import { ValueAccessor } from '@ionic/angular/common';
|
||||
import type {
|
||||
InputInputEventDetail as IIonInputInputInputEventDetail,
|
||||
InputChangeEventDetail as IIonInputInputChangeEventDetail,
|
||||
Components,
|
||||
} from '@ionic/core/components';
|
||||
import { defineCustomElement } from '@ionic/core/components/ion-input.js';
|
||||
|
||||
import { ProxyCmp, proxyOutputs } from './angular-component-lib/utils';
|
||||
|
||||
const INPUT_INPUTS = [
|
||||
'accept',
|
||||
'autocapitalize',
|
||||
'autocomplete',
|
||||
'autocorrect',
|
||||
'autofocus',
|
||||
'clearInput',
|
||||
'clearOnEdit',
|
||||
'color',
|
||||
'counter',
|
||||
'counterFormatter',
|
||||
'debounce',
|
||||
'disabled',
|
||||
'enterkeyhint',
|
||||
'errorText',
|
||||
'fill',
|
||||
'helperText',
|
||||
'inputmode',
|
||||
'label',
|
||||
'labelPlacement',
|
||||
'legacy',
|
||||
'max',
|
||||
'maxlength',
|
||||
'min',
|
||||
'minlength',
|
||||
'mode',
|
||||
'multiple',
|
||||
'name',
|
||||
'pattern',
|
||||
'placeholder',
|
||||
'readonly',
|
||||
'required',
|
||||
'shape',
|
||||
'size',
|
||||
'spellcheck',
|
||||
'step',
|
||||
'type',
|
||||
'value',
|
||||
];
|
||||
|
||||
@ProxyCmp({
|
||||
defineCustomElementFn: defineCustomElement,
|
||||
inputs: INPUT_INPUTS,
|
||||
methods: ['setFocus', 'getInputElement'],
|
||||
})
|
||||
@Component({
|
||||
selector: 'ion-input',
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
template: '<ng-content></ng-content>',
|
||||
// eslint-disable-next-line @angular-eslint/no-inputs-metadata-property
|
||||
inputs: INPUT_INPUTS,
|
||||
providers: [
|
||||
{
|
||||
provide: NG_VALUE_ACCESSOR,
|
||||
useExisting: IonInput,
|
||||
multi: true,
|
||||
},
|
||||
],
|
||||
standalone: true,
|
||||
})
|
||||
export class IonInput extends ValueAccessor {
|
||||
protected el: HTMLElement;
|
||||
constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone, injector: Injector) {
|
||||
super(injector, r);
|
||||
c.detach();
|
||||
this.el = r.nativeElement;
|
||||
proxyOutputs(this, this.el, ['ionInput', 'ionChange', 'ionBlur', 'ionFocus']);
|
||||
}
|
||||
|
||||
@HostListener('ionInput', ['$event.target'])
|
||||
handleIonInput(el: HTMLIonInputElement): void {
|
||||
this.handleValueChange(el, el.value);
|
||||
}
|
||||
|
||||
registerOnChange(fn: (_: any) => void): void {
|
||||
super.registerOnChange((value: string) => {
|
||||
if (this.type === 'number') {
|
||||
/**
|
||||
* If the input type is `number`, we need to convert the value to a number
|
||||
* when the value is not empty. If the value is empty, we want to treat
|
||||
* the value as null.
|
||||
*/
|
||||
fn(value === '' ? null : parseFloat(value));
|
||||
} else {
|
||||
fn(value);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export declare interface IonInput extends Components.IonInput {
|
||||
/**
|
||||
* The `ionInput` event is fired each time the user modifies the input's value.
|
||||
Unlike the `ionChange` event, the `ionInput` event is fired for each alteration
|
||||
to the input's value. This typically happens for each keystroke as the user types.
|
||||
|
||||
For elements that accept text input (`type=text`, `type=tel`, etc.), the interface
|
||||
is [`InputEvent`](https://developer.mozilla.org/en-US/docs/Web/API/InputEvent); for others,
|
||||
the interface is [`Event`](https://developer.mozilla.org/en-US/docs/Web/API/Event). If
|
||||
the input is cleared on edit, the type is `null`.
|
||||
*/
|
||||
ionInput: EventEmitter<CustomEvent<IIonInputInputInputEventDetail>>;
|
||||
/**
|
||||
* The `ionChange` event is fired when the user modifies the input's value.
|
||||
Unlike the `ionInput` event, the `ionChange` event is only fired when changes
|
||||
are committed, not as the user types.
|
||||
|
||||
Depending on the way the users interacts with the element, the `ionChange`
|
||||
event fires at a different moment:
|
||||
- When the user commits the change explicitly (e.g. by selecting a date
|
||||
from a date picker for `<ion-input type="date">`, pressing the "Enter" key, etc.).
|
||||
- When the element loses focus after its value has changed: for elements
|
||||
where the user's interaction is typing.
|
||||
*/
|
||||
ionChange: EventEmitter<CustomEvent<IIonInputInputChangeEventDetail>>;
|
||||
/**
|
||||
* Emitted when the input loses focus.
|
||||
*/
|
||||
ionBlur: EventEmitter<CustomEvent<FocusEvent>>;
|
||||
/**
|
||||
* Emitted when the input has focus.
|
||||
*/
|
||||
ionFocus: EventEmitter<CustomEvent<FocusEvent>>;
|
||||
}
|
||||
@@ -23,11 +23,9 @@ import { defineCustomElement as defineIonCardContent } from '@ionic/core/compone
|
||||
import { defineCustomElement as defineIonCardHeader } from '@ionic/core/components/ion-card-header.js';
|
||||
import { defineCustomElement as defineIonCardSubtitle } from '@ionic/core/components/ion-card-subtitle.js';
|
||||
import { defineCustomElement as defineIonCardTitle } from '@ionic/core/components/ion-card-title.js';
|
||||
import { defineCustomElement as defineIonCheckbox } from '@ionic/core/components/ion-checkbox.js';
|
||||
import { defineCustomElement as defineIonChip } from '@ionic/core/components/ion-chip.js';
|
||||
import { defineCustomElement as defineIonCol } from '@ionic/core/components/ion-col.js';
|
||||
import { defineCustomElement as defineIonContent } from '@ionic/core/components/ion-content.js';
|
||||
import { defineCustomElement as defineIonDatetime } from '@ionic/core/components/ion-datetime.js';
|
||||
import { defineCustomElement as defineIonDatetimeButton } from '@ionic/core/components/ion-datetime-button.js';
|
||||
import { defineCustomElement as defineIonFab } from '@ionic/core/components/ion-fab.js';
|
||||
import { defineCustomElement as defineIonFabButton } from '@ionic/core/components/ion-fab-button.js';
|
||||
@@ -38,7 +36,6 @@ import { defineCustomElement as defineIonHeader } from '@ionic/core/components/i
|
||||
import { defineCustomElement as defineIonImg } from '@ionic/core/components/ion-img.js';
|
||||
import { defineCustomElement as defineIonInfiniteScroll } from '@ionic/core/components/ion-infinite-scroll.js';
|
||||
import { defineCustomElement as defineIonInfiniteScrollContent } from '@ionic/core/components/ion-infinite-scroll-content.js';
|
||||
import { defineCustomElement as defineIonInput } from '@ionic/core/components/ion-input.js';
|
||||
import { defineCustomElement as defineIonItem } from '@ionic/core/components/ion-item.js';
|
||||
import { defineCustomElement as defineIonItemDivider } from '@ionic/core/components/ion-item-divider.js';
|
||||
import { defineCustomElement as defineIonItemGroup } from '@ionic/core/components/ion-item-group.js';
|
||||
@@ -56,19 +53,13 @@ import { defineCustomElement as defineIonNavLink } from '@ionic/core/components/
|
||||
import { defineCustomElement as defineIonNote } from '@ionic/core/components/ion-note.js';
|
||||
import { defineCustomElement as defineIonPicker } from '@ionic/core/components/ion-picker.js';
|
||||
import { defineCustomElement as defineIonProgressBar } from '@ionic/core/components/ion-progress-bar.js';
|
||||
import { defineCustomElement as defineIonRadio } from '@ionic/core/components/ion-radio.js';
|
||||
import { defineCustomElement as defineIonRadioGroup } from '@ionic/core/components/ion-radio-group.js';
|
||||
import { defineCustomElement as defineIonRange } from '@ionic/core/components/ion-range.js';
|
||||
import { defineCustomElement as defineIonRefresher } from '@ionic/core/components/ion-refresher.js';
|
||||
import { defineCustomElement as defineIonRefresherContent } from '@ionic/core/components/ion-refresher-content.js';
|
||||
import { defineCustomElement as defineIonReorder } from '@ionic/core/components/ion-reorder.js';
|
||||
import { defineCustomElement as defineIonReorderGroup } from '@ionic/core/components/ion-reorder-group.js';
|
||||
import { defineCustomElement as defineIonRippleEffect } from '@ionic/core/components/ion-ripple-effect.js';
|
||||
import { defineCustomElement as defineIonRow } from '@ionic/core/components/ion-row.js';
|
||||
import { defineCustomElement as defineIonSearchbar } from '@ionic/core/components/ion-searchbar.js';
|
||||
import { defineCustomElement as defineIonSegment } from '@ionic/core/components/ion-segment.js';
|
||||
import { defineCustomElement as defineIonSegmentButton } from '@ionic/core/components/ion-segment-button.js';
|
||||
import { defineCustomElement as defineIonSelect } from '@ionic/core/components/ion-select.js';
|
||||
import { defineCustomElement as defineIonSelectOption } from '@ionic/core/components/ion-select-option.js';
|
||||
import { defineCustomElement as defineIonSkeletonText } from '@ionic/core/components/ion-skeleton-text.js';
|
||||
import { defineCustomElement as defineIonSpinner } from '@ionic/core/components/ion-spinner.js';
|
||||
@@ -76,11 +67,9 @@ import { defineCustomElement as defineIonSplitPane } from '@ionic/core/component
|
||||
import { defineCustomElement as defineIonTabBar } from '@ionic/core/components/ion-tab-bar.js';
|
||||
import { defineCustomElement as defineIonTabButton } from '@ionic/core/components/ion-tab-button.js';
|
||||
import { defineCustomElement as defineIonText } from '@ionic/core/components/ion-text.js';
|
||||
import { defineCustomElement as defineIonTextarea } from '@ionic/core/components/ion-textarea.js';
|
||||
import { defineCustomElement as defineIonThumbnail } from '@ionic/core/components/ion-thumbnail.js';
|
||||
import { defineCustomElement as defineIonTitle } from '@ionic/core/components/ion-title.js';
|
||||
import { defineCustomElement as defineIonToast } from '@ionic/core/components/ion-toast.js';
|
||||
import { defineCustomElement as defineIonToggle } from '@ionic/core/components/ion-toggle.js';
|
||||
import { defineCustomElement as defineIonToolbar } from '@ionic/core/components/ion-toolbar.js';
|
||||
@ProxyCmp({
|
||||
defineCustomElementFn: defineIonAccordion,
|
||||
@@ -747,69 +736,6 @@ export class IonCardTitle {
|
||||
|
||||
export declare interface IonCardTitle extends Components.IonCardTitle {}
|
||||
|
||||
@ProxyCmp({
|
||||
defineCustomElementFn: defineIonCheckbox,
|
||||
inputs: [
|
||||
'checked',
|
||||
'color',
|
||||
'disabled',
|
||||
'indeterminate',
|
||||
'justify',
|
||||
'labelPlacement',
|
||||
'legacy',
|
||||
'mode',
|
||||
'name',
|
||||
'value',
|
||||
],
|
||||
})
|
||||
@Component({
|
||||
selector: 'ion-checkbox',
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
template: '<ng-content></ng-content>',
|
||||
// eslint-disable-next-line @angular-eslint/no-inputs-metadata-property
|
||||
inputs: [
|
||||
'checked',
|
||||
'color',
|
||||
'disabled',
|
||||
'indeterminate',
|
||||
'justify',
|
||||
'labelPlacement',
|
||||
'legacy',
|
||||
'mode',
|
||||
'name',
|
||||
'value',
|
||||
],
|
||||
standalone: true,
|
||||
})
|
||||
export class IonCheckbox {
|
||||
protected el: HTMLElement;
|
||||
constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone) {
|
||||
c.detach();
|
||||
this.el = r.nativeElement;
|
||||
proxyOutputs(this, this.el, ['ionChange', 'ionFocus', 'ionBlur']);
|
||||
}
|
||||
}
|
||||
|
||||
import type { CheckboxChangeEventDetail as IIonCheckboxCheckboxChangeEventDetail } from '@ionic/core/components';
|
||||
|
||||
export declare interface IonCheckbox extends Components.IonCheckbox {
|
||||
/**
|
||||
* Emitted when the checked property has changed
|
||||
as a result of a user action such as a click.
|
||||
This event will not emit when programmatically
|
||||
setting the checked property.
|
||||
*/
|
||||
ionChange: EventEmitter<CustomEvent<IIonCheckboxCheckboxChangeEventDetail>>;
|
||||
/**
|
||||
* Emitted when the checkbox has focus.
|
||||
*/
|
||||
ionFocus: EventEmitter<CustomEvent<void>>;
|
||||
/**
|
||||
* Emitted when the checkbox loses focus.
|
||||
*/
|
||||
ionBlur: EventEmitter<CustomEvent<void>>;
|
||||
}
|
||||
|
||||
@ProxyCmp({
|
||||
defineCustomElementFn: defineIonChip,
|
||||
inputs: ['color', 'disabled', 'mode', 'outline'],
|
||||
@@ -947,111 +873,6 @@ Set `scrollEvents` to `true` to enable.
|
||||
ionScrollEnd: EventEmitter<CustomEvent<IIonContentScrollBaseDetail>>;
|
||||
}
|
||||
|
||||
@ProxyCmp({
|
||||
defineCustomElementFn: defineIonDatetime,
|
||||
inputs: [
|
||||
'cancelText',
|
||||
'clearText',
|
||||
'color',
|
||||
'dayValues',
|
||||
'disabled',
|
||||
'doneText',
|
||||
'firstDayOfWeek',
|
||||
'highlightedDates',
|
||||
'hourCycle',
|
||||
'hourValues',
|
||||
'isDateEnabled',
|
||||
'locale',
|
||||
'max',
|
||||
'min',
|
||||
'minuteValues',
|
||||
'mode',
|
||||
'monthValues',
|
||||
'multiple',
|
||||
'name',
|
||||
'preferWheel',
|
||||
'presentation',
|
||||
'readonly',
|
||||
'showClearButton',
|
||||
'showDefaultButtons',
|
||||
'showDefaultTimeLabel',
|
||||
'showDefaultTitle',
|
||||
'size',
|
||||
'titleSelectedDatesFormatter',
|
||||
'value',
|
||||
'yearValues',
|
||||
],
|
||||
methods: ['confirm', 'reset', 'cancel'],
|
||||
})
|
||||
@Component({
|
||||
selector: 'ion-datetime',
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
template: '<ng-content></ng-content>',
|
||||
// eslint-disable-next-line @angular-eslint/no-inputs-metadata-property
|
||||
inputs: [
|
||||
'cancelText',
|
||||
'clearText',
|
||||
'color',
|
||||
'dayValues',
|
||||
'disabled',
|
||||
'doneText',
|
||||
'firstDayOfWeek',
|
||||
'highlightedDates',
|
||||
'hourCycle',
|
||||
'hourValues',
|
||||
'isDateEnabled',
|
||||
'locale',
|
||||
'max',
|
||||
'min',
|
||||
'minuteValues',
|
||||
'mode',
|
||||
'monthValues',
|
||||
'multiple',
|
||||
'name',
|
||||
'preferWheel',
|
||||
'presentation',
|
||||
'readonly',
|
||||
'showClearButton',
|
||||
'showDefaultButtons',
|
||||
'showDefaultTimeLabel',
|
||||
'showDefaultTitle',
|
||||
'size',
|
||||
'titleSelectedDatesFormatter',
|
||||
'value',
|
||||
'yearValues',
|
||||
],
|
||||
standalone: true,
|
||||
})
|
||||
export class IonDatetime {
|
||||
protected el: HTMLElement;
|
||||
constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone) {
|
||||
c.detach();
|
||||
this.el = r.nativeElement;
|
||||
proxyOutputs(this, this.el, ['ionCancel', 'ionChange', 'ionFocus', 'ionBlur']);
|
||||
}
|
||||
}
|
||||
|
||||
import type { DatetimeChangeEventDetail as IIonDatetimeDatetimeChangeEventDetail } from '@ionic/core/components';
|
||||
|
||||
export declare interface IonDatetime extends Components.IonDatetime {
|
||||
/**
|
||||
* Emitted when the datetime selection was cancelled.
|
||||
*/
|
||||
ionCancel: EventEmitter<CustomEvent<void>>;
|
||||
/**
|
||||
* Emitted when the value (selected date) has changed.
|
||||
*/
|
||||
ionChange: EventEmitter<CustomEvent<IIonDatetimeDatetimeChangeEventDetail>>;
|
||||
/**
|
||||
* Emitted when the datetime has focus.
|
||||
*/
|
||||
ionFocus: EventEmitter<CustomEvent<void>>;
|
||||
/**
|
||||
* Emitted when the datetime loses focus.
|
||||
*/
|
||||
ionBlur: EventEmitter<CustomEvent<void>>;
|
||||
}
|
||||
|
||||
@ProxyCmp({
|
||||
defineCustomElementFn: defineIonDatetimeButton,
|
||||
inputs: ['color', 'datetime', 'disabled', 'mode'],
|
||||
@@ -1339,142 +1160,6 @@ export class IonInfiniteScrollContent {
|
||||
|
||||
export declare interface IonInfiniteScrollContent extends Components.IonInfiniteScrollContent {}
|
||||
|
||||
@ProxyCmp({
|
||||
defineCustomElementFn: defineIonInput,
|
||||
inputs: [
|
||||
'accept',
|
||||
'autocapitalize',
|
||||
'autocomplete',
|
||||
'autocorrect',
|
||||
'autofocus',
|
||||
'clearInput',
|
||||
'clearOnEdit',
|
||||
'color',
|
||||
'counter',
|
||||
'counterFormatter',
|
||||
'debounce',
|
||||
'disabled',
|
||||
'enterkeyhint',
|
||||
'errorText',
|
||||
'fill',
|
||||
'helperText',
|
||||
'inputmode',
|
||||
'label',
|
||||
'labelPlacement',
|
||||
'legacy',
|
||||
'max',
|
||||
'maxlength',
|
||||
'min',
|
||||
'minlength',
|
||||
'mode',
|
||||
'multiple',
|
||||
'name',
|
||||
'pattern',
|
||||
'placeholder',
|
||||
'readonly',
|
||||
'required',
|
||||
'shape',
|
||||
'size',
|
||||
'spellcheck',
|
||||
'step',
|
||||
'type',
|
||||
'value',
|
||||
],
|
||||
methods: ['setFocus', 'getInputElement'],
|
||||
})
|
||||
@Component({
|
||||
selector: 'ion-input',
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
template: '<ng-content></ng-content>',
|
||||
// eslint-disable-next-line @angular-eslint/no-inputs-metadata-property
|
||||
inputs: [
|
||||
'accept',
|
||||
'autocapitalize',
|
||||
'autocomplete',
|
||||
'autocorrect',
|
||||
'autofocus',
|
||||
'clearInput',
|
||||
'clearOnEdit',
|
||||
'color',
|
||||
'counter',
|
||||
'counterFormatter',
|
||||
'debounce',
|
||||
'disabled',
|
||||
'enterkeyhint',
|
||||
'errorText',
|
||||
'fill',
|
||||
'helperText',
|
||||
'inputmode',
|
||||
'label',
|
||||
'labelPlacement',
|
||||
'legacy',
|
||||
'max',
|
||||
'maxlength',
|
||||
'min',
|
||||
'minlength',
|
||||
'mode',
|
||||
'multiple',
|
||||
'name',
|
||||
'pattern',
|
||||
'placeholder',
|
||||
'readonly',
|
||||
'required',
|
||||
'shape',
|
||||
'size',
|
||||
'spellcheck',
|
||||
'step',
|
||||
'type',
|
||||
'value',
|
||||
],
|
||||
standalone: true,
|
||||
})
|
||||
export class IonInput {
|
||||
protected el: HTMLElement;
|
||||
constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone) {
|
||||
c.detach();
|
||||
this.el = r.nativeElement;
|
||||
proxyOutputs(this, this.el, ['ionInput', 'ionChange', 'ionBlur', 'ionFocus']);
|
||||
}
|
||||
}
|
||||
|
||||
import type { InputInputEventDetail as IIonInputInputInputEventDetail } from '@ionic/core/components';
|
||||
import type { InputChangeEventDetail as IIonInputInputChangeEventDetail } from '@ionic/core/components';
|
||||
|
||||
export declare interface IonInput extends Components.IonInput {
|
||||
/**
|
||||
* The `ionInput` event is fired each time the user modifies the input's value.
|
||||
Unlike the `ionChange` event, the `ionInput` event is fired for each alteration
|
||||
to the input's value. This typically happens for each keystroke as the user types.
|
||||
|
||||
For elements that accept text input (`type=text`, `type=tel`, etc.), the interface
|
||||
is [`InputEvent`](https://developer.mozilla.org/en-US/docs/Web/API/InputEvent); for others,
|
||||
the interface is [`Event`](https://developer.mozilla.org/en-US/docs/Web/API/Event). If
|
||||
the input is cleared on edit, the type is `null`.
|
||||
*/
|
||||
ionInput: EventEmitter<CustomEvent<IIonInputInputInputEventDetail>>;
|
||||
/**
|
||||
* The `ionChange` event is fired when the user modifies the input's value.
|
||||
Unlike the `ionInput` event, the `ionChange` event is only fired when changes
|
||||
are committed, not as the user types.
|
||||
|
||||
Depending on the way the users interacts with the element, the `ionChange`
|
||||
event fires at a different moment:
|
||||
- When the user commits the change explicitly (e.g. by selecting a date
|
||||
from a date picker for `<ion-input type="date">`, pressing the "Enter" key, etc.).
|
||||
- When the element loses focus after its value has changed: for elements
|
||||
where the user's interaction is typing.
|
||||
*/
|
||||
ionChange: EventEmitter<CustomEvent<IIonInputInputChangeEventDetail>>;
|
||||
/**
|
||||
* Emitted when the input loses focus.
|
||||
*/
|
||||
ionBlur: EventEmitter<CustomEvent<FocusEvent>>;
|
||||
/**
|
||||
* Emitted when the input has focus.
|
||||
*/
|
||||
ionFocus: EventEmitter<CustomEvent<FocusEvent>>;
|
||||
}
|
||||
|
||||
@ProxyCmp({
|
||||
defineCustomElementFn: defineIonItem,
|
||||
inputs: [
|
||||
@@ -2081,167 +1766,6 @@ export class IonProgressBar {
|
||||
|
||||
export declare interface IonProgressBar extends Components.IonProgressBar {}
|
||||
|
||||
@ProxyCmp({
|
||||
defineCustomElementFn: defineIonRadio,
|
||||
inputs: ['color', 'disabled', 'justify', 'labelPlacement', 'legacy', 'mode', 'name', 'value'],
|
||||
})
|
||||
@Component({
|
||||
selector: 'ion-radio',
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
template: '<ng-content></ng-content>',
|
||||
// eslint-disable-next-line @angular-eslint/no-inputs-metadata-property
|
||||
inputs: ['color', 'disabled', 'justify', 'labelPlacement', 'legacy', 'mode', 'name', 'value'],
|
||||
standalone: true,
|
||||
})
|
||||
export class IonRadio {
|
||||
protected el: HTMLElement;
|
||||
constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone) {
|
||||
c.detach();
|
||||
this.el = r.nativeElement;
|
||||
proxyOutputs(this, this.el, ['ionFocus', 'ionBlur']);
|
||||
}
|
||||
}
|
||||
|
||||
export declare interface IonRadio extends Components.IonRadio {
|
||||
/**
|
||||
* Emitted when the radio button has focus.
|
||||
*/
|
||||
ionFocus: EventEmitter<CustomEvent<void>>;
|
||||
/**
|
||||
* Emitted when the radio button loses focus.
|
||||
*/
|
||||
ionBlur: EventEmitter<CustomEvent<void>>;
|
||||
}
|
||||
|
||||
@ProxyCmp({
|
||||
defineCustomElementFn: defineIonRadioGroup,
|
||||
inputs: ['allowEmptySelection', 'name', 'value'],
|
||||
})
|
||||
@Component({
|
||||
selector: 'ion-radio-group',
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
template: '<ng-content></ng-content>',
|
||||
// eslint-disable-next-line @angular-eslint/no-inputs-metadata-property
|
||||
inputs: ['allowEmptySelection', 'name', 'value'],
|
||||
standalone: true,
|
||||
})
|
||||
export class IonRadioGroup {
|
||||
protected el: HTMLElement;
|
||||
constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone) {
|
||||
c.detach();
|
||||
this.el = r.nativeElement;
|
||||
proxyOutputs(this, this.el, ['ionChange']);
|
||||
}
|
||||
}
|
||||
|
||||
import type { RadioGroupChangeEventDetail as IIonRadioGroupRadioGroupChangeEventDetail } from '@ionic/core/components';
|
||||
|
||||
export declare interface IonRadioGroup extends Components.IonRadioGroup {
|
||||
/**
|
||||
* Emitted when the value has changed.
|
||||
*/
|
||||
ionChange: EventEmitter<CustomEvent<IIonRadioGroupRadioGroupChangeEventDetail>>;
|
||||
}
|
||||
|
||||
@ProxyCmp({
|
||||
defineCustomElementFn: defineIonRange,
|
||||
inputs: [
|
||||
'activeBarStart',
|
||||
'color',
|
||||
'debounce',
|
||||
'disabled',
|
||||
'dualKnobs',
|
||||
'label',
|
||||
'labelPlacement',
|
||||
'legacy',
|
||||
'max',
|
||||
'min',
|
||||
'mode',
|
||||
'name',
|
||||
'pin',
|
||||
'pinFormatter',
|
||||
'snaps',
|
||||
'step',
|
||||
'ticks',
|
||||
'value',
|
||||
],
|
||||
})
|
||||
@Component({
|
||||
selector: 'ion-range',
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
template: '<ng-content></ng-content>',
|
||||
// eslint-disable-next-line @angular-eslint/no-inputs-metadata-property
|
||||
inputs: [
|
||||
'activeBarStart',
|
||||
'color',
|
||||
'debounce',
|
||||
'disabled',
|
||||
'dualKnobs',
|
||||
'label',
|
||||
'labelPlacement',
|
||||
'legacy',
|
||||
'max',
|
||||
'min',
|
||||
'mode',
|
||||
'name',
|
||||
'pin',
|
||||
'pinFormatter',
|
||||
'snaps',
|
||||
'step',
|
||||
'ticks',
|
||||
'value',
|
||||
],
|
||||
standalone: true,
|
||||
})
|
||||
export class IonRange {
|
||||
protected el: HTMLElement;
|
||||
constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone) {
|
||||
c.detach();
|
||||
this.el = r.nativeElement;
|
||||
proxyOutputs(this, this.el, ['ionChange', 'ionInput', 'ionFocus', 'ionBlur', 'ionKnobMoveStart', 'ionKnobMoveEnd']);
|
||||
}
|
||||
}
|
||||
|
||||
import type { RangeChangeEventDetail as IIonRangeRangeChangeEventDetail } from '@ionic/core/components';
|
||||
import type { RangeKnobMoveStartEventDetail as IIonRangeRangeKnobMoveStartEventDetail } from '@ionic/core/components';
|
||||
import type { RangeKnobMoveEndEventDetail as IIonRangeRangeKnobMoveEndEventDetail } from '@ionic/core/components';
|
||||
|
||||
export declare interface IonRange extends Components.IonRange {
|
||||
/**
|
||||
* The `ionChange` event is fired for `<ion-range>` elements when the user
|
||||
modifies the element's value:
|
||||
- When the user releases the knob after dragging;
|
||||
- When the user moves the knob with keyboard arrows
|
||||
|
||||
`ionChange` is not fired when the value is changed programmatically.
|
||||
*/
|
||||
ionChange: EventEmitter<CustomEvent<IIonRangeRangeChangeEventDetail>>;
|
||||
/**
|
||||
* The `ionInput` event is fired for `<ion-range>` elements when the value
|
||||
is modified. Unlike `ionChange`, `ionInput` is fired continuously
|
||||
while the user is dragging the knob.
|
||||
*/
|
||||
ionInput: EventEmitter<CustomEvent<IIonRangeRangeChangeEventDetail>>;
|
||||
/**
|
||||
* Emitted when the range has focus.
|
||||
*/
|
||||
ionFocus: EventEmitter<CustomEvent<void>>;
|
||||
/**
|
||||
* Emitted when the range loses focus.
|
||||
*/
|
||||
ionBlur: EventEmitter<CustomEvent<void>>;
|
||||
/**
|
||||
* Emitted when the user starts moving the range knob, whether through
|
||||
mouse drag, touch gesture, or keyboard interaction.
|
||||
*/
|
||||
ionKnobMoveStart: EventEmitter<CustomEvent<IIonRangeRangeKnobMoveStartEventDetail>>;
|
||||
/**
|
||||
* Emitted when the user finishes moving the range knob, whether through
|
||||
mouse drag, touch gesture, or keyboard interaction.
|
||||
*/
|
||||
ionKnobMoveEnd: EventEmitter<CustomEvent<IIonRangeRangeKnobMoveEndEventDetail>>;
|
||||
}
|
||||
|
||||
@ProxyCmp({
|
||||
defineCustomElementFn: defineIonRefresher,
|
||||
inputs: ['closeDuration', 'disabled', 'pullFactor', 'pullMax', 'pullMin', 'snapbackDuration'],
|
||||
@@ -2404,138 +1928,6 @@ export class IonRow {
|
||||
|
||||
export declare interface IonRow extends Components.IonRow {}
|
||||
|
||||
@ProxyCmp({
|
||||
defineCustomElementFn: defineIonSearchbar,
|
||||
inputs: [
|
||||
'animated',
|
||||
'autocomplete',
|
||||
'autocorrect',
|
||||
'cancelButtonIcon',
|
||||
'cancelButtonText',
|
||||
'clearIcon',
|
||||
'color',
|
||||
'debounce',
|
||||
'disabled',
|
||||
'enterkeyhint',
|
||||
'inputmode',
|
||||
'mode',
|
||||
'name',
|
||||
'placeholder',
|
||||
'searchIcon',
|
||||
'showCancelButton',
|
||||
'showClearButton',
|
||||
'spellcheck',
|
||||
'type',
|
||||
'value',
|
||||
],
|
||||
methods: ['setFocus', 'getInputElement'],
|
||||
})
|
||||
@Component({
|
||||
selector: 'ion-searchbar',
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
template: '<ng-content></ng-content>',
|
||||
// eslint-disable-next-line @angular-eslint/no-inputs-metadata-property
|
||||
inputs: [
|
||||
'animated',
|
||||
'autocomplete',
|
||||
'autocorrect',
|
||||
'cancelButtonIcon',
|
||||
'cancelButtonText',
|
||||
'clearIcon',
|
||||
'color',
|
||||
'debounce',
|
||||
'disabled',
|
||||
'enterkeyhint',
|
||||
'inputmode',
|
||||
'mode',
|
||||
'name',
|
||||
'placeholder',
|
||||
'searchIcon',
|
||||
'showCancelButton',
|
||||
'showClearButton',
|
||||
'spellcheck',
|
||||
'type',
|
||||
'value',
|
||||
],
|
||||
standalone: true,
|
||||
})
|
||||
export class IonSearchbar {
|
||||
protected el: HTMLElement;
|
||||
constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone) {
|
||||
c.detach();
|
||||
this.el = r.nativeElement;
|
||||
proxyOutputs(this, this.el, ['ionInput', 'ionChange', 'ionCancel', 'ionClear', 'ionBlur', 'ionFocus']);
|
||||
}
|
||||
}
|
||||
|
||||
import type { SearchbarInputEventDetail as IIonSearchbarSearchbarInputEventDetail } from '@ionic/core/components';
|
||||
import type { SearchbarChangeEventDetail as IIonSearchbarSearchbarChangeEventDetail } from '@ionic/core/components';
|
||||
|
||||
export declare interface IonSearchbar extends Components.IonSearchbar {
|
||||
/**
|
||||
* Emitted when the `value` of the `ion-searchbar` element has changed.
|
||||
*/
|
||||
ionInput: EventEmitter<CustomEvent<IIonSearchbarSearchbarInputEventDetail>>;
|
||||
/**
|
||||
* The `ionChange` event is fired for `<ion-searchbar>` elements when the user
|
||||
modifies the element's value. Unlike the `ionInput` event, the `ionChange`
|
||||
event is not necessarily fired for each alteration to an element's value.
|
||||
|
||||
The `ionChange` event is fired when the value has been committed
|
||||
by the user. This can happen when the element loses focus or
|
||||
when the "Enter" key is pressed. `ionChange` can also fire
|
||||
when clicking the clear or cancel buttons.
|
||||
*/
|
||||
ionChange: EventEmitter<CustomEvent<IIonSearchbarSearchbarChangeEventDetail>>;
|
||||
/**
|
||||
* Emitted when the cancel button is clicked.
|
||||
*/
|
||||
ionCancel: EventEmitter<CustomEvent<void>>;
|
||||
/**
|
||||
* Emitted when the clear input button is clicked.
|
||||
*/
|
||||
ionClear: EventEmitter<CustomEvent<void>>;
|
||||
/**
|
||||
* Emitted when the input loses focus.
|
||||
*/
|
||||
ionBlur: EventEmitter<CustomEvent<void>>;
|
||||
/**
|
||||
* Emitted when the input has focus.
|
||||
*/
|
||||
ionFocus: EventEmitter<CustomEvent<void>>;
|
||||
}
|
||||
|
||||
@ProxyCmp({
|
||||
defineCustomElementFn: defineIonSegment,
|
||||
inputs: ['color', 'disabled', 'mode', 'scrollable', 'selectOnFocus', 'swipeGesture', 'value'],
|
||||
})
|
||||
@Component({
|
||||
selector: 'ion-segment',
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
template: '<ng-content></ng-content>',
|
||||
// eslint-disable-next-line @angular-eslint/no-inputs-metadata-property
|
||||
inputs: ['color', 'disabled', 'mode', 'scrollable', 'selectOnFocus', 'swipeGesture', 'value'],
|
||||
standalone: true,
|
||||
})
|
||||
export class IonSegment {
|
||||
protected el: HTMLElement;
|
||||
constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone) {
|
||||
c.detach();
|
||||
this.el = r.nativeElement;
|
||||
proxyOutputs(this, this.el, ['ionChange']);
|
||||
}
|
||||
}
|
||||
|
||||
import type { SegmentChangeEventDetail as IIonSegmentSegmentChangeEventDetail } from '@ionic/core/components';
|
||||
|
||||
export declare interface IonSegment extends Components.IonSegment {
|
||||
/**
|
||||
* Emitted when the value property has changed and any
|
||||
dragging pointer has been released from `ion-segment`.
|
||||
*/
|
||||
ionChange: EventEmitter<CustomEvent<IIonSegmentSegmentChangeEventDetail>>;
|
||||
}
|
||||
|
||||
@ProxyCmp({
|
||||
defineCustomElementFn: defineIonSegmentButton,
|
||||
inputs: ['disabled', 'layout', 'mode', 'type', 'value'],
|
||||
@@ -2558,97 +1950,6 @@ export class IonSegmentButton {
|
||||
|
||||
export declare interface IonSegmentButton extends Components.IonSegmentButton {}
|
||||
|
||||
@ProxyCmp({
|
||||
defineCustomElementFn: defineIonSelect,
|
||||
inputs: [
|
||||
'cancelText',
|
||||
'color',
|
||||
'compareWith',
|
||||
'disabled',
|
||||
'expandedIcon',
|
||||
'fill',
|
||||
'interface',
|
||||
'interfaceOptions',
|
||||
'justify',
|
||||
'label',
|
||||
'labelPlacement',
|
||||
'legacy',
|
||||
'mode',
|
||||
'multiple',
|
||||
'name',
|
||||
'okText',
|
||||
'placeholder',
|
||||
'selectedText',
|
||||
'shape',
|
||||
'toggleIcon',
|
||||
'value',
|
||||
],
|
||||
methods: ['open'],
|
||||
})
|
||||
@Component({
|
||||
selector: 'ion-select',
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
template: '<ng-content></ng-content>',
|
||||
// eslint-disable-next-line @angular-eslint/no-inputs-metadata-property
|
||||
inputs: [
|
||||
'cancelText',
|
||||
'color',
|
||||
'compareWith',
|
||||
'disabled',
|
||||
'expandedIcon',
|
||||
'fill',
|
||||
'interface',
|
||||
'interfaceOptions',
|
||||
'justify',
|
||||
'label',
|
||||
'labelPlacement',
|
||||
'legacy',
|
||||
'mode',
|
||||
'multiple',
|
||||
'name',
|
||||
'okText',
|
||||
'placeholder',
|
||||
'selectedText',
|
||||
'shape',
|
||||
'toggleIcon',
|
||||
'value',
|
||||
],
|
||||
standalone: true,
|
||||
})
|
||||
export class IonSelect {
|
||||
protected el: HTMLElement;
|
||||
constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone) {
|
||||
c.detach();
|
||||
this.el = r.nativeElement;
|
||||
proxyOutputs(this, this.el, ['ionChange', 'ionCancel', 'ionDismiss', 'ionFocus', 'ionBlur']);
|
||||
}
|
||||
}
|
||||
|
||||
import type { SelectChangeEventDetail as IIonSelectSelectChangeEventDetail } from '@ionic/core/components';
|
||||
|
||||
export declare interface IonSelect extends Components.IonSelect {
|
||||
/**
|
||||
* Emitted when the value has changed.
|
||||
*/
|
||||
ionChange: EventEmitter<CustomEvent<IIonSelectSelectChangeEventDetail>>;
|
||||
/**
|
||||
* Emitted when the selection is cancelled.
|
||||
*/
|
||||
ionCancel: EventEmitter<CustomEvent<void>>;
|
||||
/**
|
||||
* Emitted when the overlay is dismissed.
|
||||
*/
|
||||
ionDismiss: EventEmitter<CustomEvent<void>>;
|
||||
/**
|
||||
* Emitted when the select has focus.
|
||||
*/
|
||||
ionFocus: EventEmitter<CustomEvent<void>>;
|
||||
/**
|
||||
* Emitted when the select loses focus.
|
||||
*/
|
||||
ionBlur: EventEmitter<CustomEvent<void>>;
|
||||
}
|
||||
|
||||
@ProxyCmp({
|
||||
defineCustomElementFn: defineIonSelectOption,
|
||||
inputs: ['disabled', 'value'],
|
||||
@@ -2809,119 +2110,6 @@ export class IonText {
|
||||
|
||||
export declare interface IonText extends Components.IonText {}
|
||||
|
||||
@ProxyCmp({
|
||||
defineCustomElementFn: defineIonTextarea,
|
||||
inputs: [
|
||||
'autoGrow',
|
||||
'autocapitalize',
|
||||
'autofocus',
|
||||
'clearOnEdit',
|
||||
'color',
|
||||
'cols',
|
||||
'counter',
|
||||
'counterFormatter',
|
||||
'debounce',
|
||||
'disabled',
|
||||
'enterkeyhint',
|
||||
'errorText',
|
||||
'fill',
|
||||
'helperText',
|
||||
'inputmode',
|
||||
'label',
|
||||
'labelPlacement',
|
||||
'legacy',
|
||||
'maxlength',
|
||||
'minlength',
|
||||
'mode',
|
||||
'name',
|
||||
'placeholder',
|
||||
'readonly',
|
||||
'required',
|
||||
'rows',
|
||||
'shape',
|
||||
'spellcheck',
|
||||
'value',
|
||||
'wrap',
|
||||
],
|
||||
methods: ['setFocus', 'getInputElement'],
|
||||
})
|
||||
@Component({
|
||||
selector: 'ion-textarea',
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
template: '<ng-content></ng-content>',
|
||||
// eslint-disable-next-line @angular-eslint/no-inputs-metadata-property
|
||||
inputs: [
|
||||
'autoGrow',
|
||||
'autocapitalize',
|
||||
'autofocus',
|
||||
'clearOnEdit',
|
||||
'color',
|
||||
'cols',
|
||||
'counter',
|
||||
'counterFormatter',
|
||||
'debounce',
|
||||
'disabled',
|
||||
'enterkeyhint',
|
||||
'errorText',
|
||||
'fill',
|
||||
'helperText',
|
||||
'inputmode',
|
||||
'label',
|
||||
'labelPlacement',
|
||||
'legacy',
|
||||
'maxlength',
|
||||
'minlength',
|
||||
'mode',
|
||||
'name',
|
||||
'placeholder',
|
||||
'readonly',
|
||||
'required',
|
||||
'rows',
|
||||
'shape',
|
||||
'spellcheck',
|
||||
'value',
|
||||
'wrap',
|
||||
],
|
||||
standalone: true,
|
||||
})
|
||||
export class IonTextarea {
|
||||
protected el: HTMLElement;
|
||||
constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone) {
|
||||
c.detach();
|
||||
this.el = r.nativeElement;
|
||||
proxyOutputs(this, this.el, ['ionChange', 'ionInput', 'ionBlur', 'ionFocus']);
|
||||
}
|
||||
}
|
||||
|
||||
import type { TextareaChangeEventDetail as IIonTextareaTextareaChangeEventDetail } from '@ionic/core/components';
|
||||
import type { TextareaInputEventDetail as IIonTextareaTextareaInputEventDetail } from '@ionic/core/components';
|
||||
|
||||
export declare interface IonTextarea extends Components.IonTextarea {
|
||||
/**
|
||||
* The `ionChange` event is fired when the user modifies the textarea's value.
|
||||
Unlike the `ionInput` event, the `ionChange` event is fired when
|
||||
the element loses focus after its value has been modified.
|
||||
*/
|
||||
ionChange: EventEmitter<CustomEvent<IIonTextareaTextareaChangeEventDetail>>;
|
||||
/**
|
||||
* The `ionInput` event is fired each time the user modifies the textarea's value.
|
||||
Unlike the `ionChange` event, the `ionInput` event is fired for each alteration
|
||||
to the textarea's value. This typically happens for each keystroke as the user types.
|
||||
|
||||
When `clearOnEdit` is enabled, the `ionInput` event will be fired when
|
||||
the user clears the textarea by performing a keydown event.
|
||||
*/
|
||||
ionInput: EventEmitter<CustomEvent<IIonTextareaTextareaInputEventDetail>>;
|
||||
/**
|
||||
* Emitted when the input loses focus.
|
||||
*/
|
||||
ionBlur: EventEmitter<CustomEvent<FocusEvent>>;
|
||||
/**
|
||||
* Emitted when the input has focus.
|
||||
*/
|
||||
ionFocus: EventEmitter<CustomEvent<FocusEvent>>;
|
||||
}
|
||||
|
||||
@ProxyCmp({
|
||||
defineCustomElementFn: defineIonThumbnail,
|
||||
})
|
||||
@@ -3075,67 +2263,6 @@ Shorthand for ionToastDidDismiss.
|
||||
didDismiss: EventEmitter<CustomEvent<IIonToastOverlayEventDetail>>;
|
||||
}
|
||||
|
||||
@ProxyCmp({
|
||||
defineCustomElementFn: defineIonToggle,
|
||||
inputs: [
|
||||
'checked',
|
||||
'color',
|
||||
'disabled',
|
||||
'enableOnOffLabels',
|
||||
'justify',
|
||||
'labelPlacement',
|
||||
'legacy',
|
||||
'mode',
|
||||
'name',
|
||||
'value',
|
||||
],
|
||||
})
|
||||
@Component({
|
||||
selector: 'ion-toggle',
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
template: '<ng-content></ng-content>',
|
||||
// eslint-disable-next-line @angular-eslint/no-inputs-metadata-property
|
||||
inputs: [
|
||||
'checked',
|
||||
'color',
|
||||
'disabled',
|
||||
'enableOnOffLabels',
|
||||
'justify',
|
||||
'labelPlacement',
|
||||
'legacy',
|
||||
'mode',
|
||||
'name',
|
||||
'value',
|
||||
],
|
||||
standalone: true,
|
||||
})
|
||||
export class IonToggle {
|
||||
protected el: HTMLElement;
|
||||
constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone) {
|
||||
c.detach();
|
||||
this.el = r.nativeElement;
|
||||
proxyOutputs(this, this.el, ['ionChange', 'ionFocus', 'ionBlur']);
|
||||
}
|
||||
}
|
||||
|
||||
import type { ToggleChangeEventDetail as IIonToggleToggleChangeEventDetail } from '@ionic/core/components';
|
||||
|
||||
export declare interface IonToggle extends Components.IonToggle {
|
||||
/**
|
||||
* Emitted when the user switches the toggle on or off. Does not emit
|
||||
when programmatically changing the value of the `checked` property.
|
||||
*/
|
||||
ionChange: EventEmitter<CustomEvent<IIonToggleToggleChangeEventDetail>>;
|
||||
/**
|
||||
* Emitted when the toggle has focus.
|
||||
*/
|
||||
ionFocus: EventEmitter<CustomEvent<void>>;
|
||||
/**
|
||||
* Emitted when the toggle loses focus.
|
||||
*/
|
||||
ionBlur: EventEmitter<CustomEvent<void>>;
|
||||
}
|
||||
|
||||
@ProxyCmp({
|
||||
defineCustomElementFn: defineIonToolbar,
|
||||
inputs: ['color', 'mode'],
|
||||
|
||||
59
packages/angular/standalone/src/directives/radio-group.ts
Normal file
59
packages/angular/standalone/src/directives/radio-group.ts
Normal file
@@ -0,0 +1,59 @@
|
||||
import {
|
||||
ChangeDetectionStrategy,
|
||||
ChangeDetectorRef,
|
||||
Component,
|
||||
ElementRef,
|
||||
EventEmitter,
|
||||
HostListener,
|
||||
Injector,
|
||||
NgZone,
|
||||
} from '@angular/core';
|
||||
import { NG_VALUE_ACCESSOR } from '@angular/forms';
|
||||
import { ValueAccessor } from '@ionic/angular/common';
|
||||
import type { RadioGroupChangeEventDetail, Components } from '@ionic/core/components';
|
||||
import { defineCustomElement } from '@ionic/core/components/ion-radio-group.js';
|
||||
|
||||
import { ProxyCmp, proxyOutputs } from './angular-component-lib/utils';
|
||||
|
||||
const RADIO_GROUP_INPUTS = ['allowEmptySelection', 'name', 'value'];
|
||||
|
||||
@ProxyCmp({
|
||||
defineCustomElementFn: defineCustomElement,
|
||||
inputs: RADIO_GROUP_INPUTS,
|
||||
})
|
||||
@Component({
|
||||
selector: 'ion-radio-group',
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
template: '<ng-content></ng-content>',
|
||||
// eslint-disable-next-line @angular-eslint/no-inputs-metadata-property
|
||||
inputs: RADIO_GROUP_INPUTS,
|
||||
providers: [
|
||||
{
|
||||
provide: NG_VALUE_ACCESSOR,
|
||||
useExisting: IonRadioGroup,
|
||||
multi: true,
|
||||
},
|
||||
],
|
||||
standalone: true,
|
||||
})
|
||||
export class IonRadioGroup extends ValueAccessor {
|
||||
protected el: HTMLElement;
|
||||
constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone, injector: Injector) {
|
||||
super(injector, r);
|
||||
c.detach();
|
||||
this.el = r.nativeElement;
|
||||
proxyOutputs(this, this.el, ['ionChange']);
|
||||
}
|
||||
|
||||
@HostListener('ionChange', ['$event.target'])
|
||||
handleIonChange(el: HTMLIonRadioGroupElement): void {
|
||||
this.handleValueChange(el, el.value);
|
||||
}
|
||||
}
|
||||
|
||||
export declare interface IonRadioGroup extends Components.IonRadioGroup {
|
||||
/**
|
||||
* Emitted when the value has changed.
|
||||
*/
|
||||
ionChange: EventEmitter<CustomEvent<RadioGroupChangeEventDetail>>;
|
||||
}
|
||||
67
packages/angular/standalone/src/directives/radio.ts
Normal file
67
packages/angular/standalone/src/directives/radio.ts
Normal file
@@ -0,0 +1,67 @@
|
||||
import {
|
||||
ChangeDetectionStrategy,
|
||||
ChangeDetectorRef,
|
||||
Component,
|
||||
ElementRef,
|
||||
EventEmitter,
|
||||
HostListener,
|
||||
Injector,
|
||||
NgZone,
|
||||
} from '@angular/core';
|
||||
import { NG_VALUE_ACCESSOR } from '@angular/forms';
|
||||
import { ValueAccessor } from '@ionic/angular/common';
|
||||
import type { Components } from '@ionic/core/components';
|
||||
import { defineCustomElement } from '@ionic/core/components/ion-radio.js';
|
||||
|
||||
import { ProxyCmp, proxyOutputs } from './angular-component-lib/utils';
|
||||
|
||||
const RADIO_INPUTS = ['color', 'disabled', 'justify', 'labelPlacement', 'legacy', 'mode', 'name', 'value'];
|
||||
|
||||
@ProxyCmp({
|
||||
defineCustomElementFn: defineCustomElement,
|
||||
inputs: RADIO_INPUTS,
|
||||
})
|
||||
@Component({
|
||||
selector: 'ion-radio',
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
template: '<ng-content></ng-content>',
|
||||
// eslint-disable-next-line @angular-eslint/no-inputs-metadata-property
|
||||
inputs: RADIO_INPUTS,
|
||||
providers: [
|
||||
{
|
||||
provide: NG_VALUE_ACCESSOR,
|
||||
useExisting: IonRadio,
|
||||
multi: true,
|
||||
},
|
||||
],
|
||||
standalone: true,
|
||||
})
|
||||
export class IonRadio extends ValueAccessor {
|
||||
protected el: HTMLElement;
|
||||
constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone, injector: Injector) {
|
||||
super(injector, r);
|
||||
c.detach();
|
||||
this.el = r.nativeElement;
|
||||
proxyOutputs(this, this.el, ['ionFocus', 'ionBlur']);
|
||||
}
|
||||
|
||||
@HostListener('ionSelect', ['$event.target'])
|
||||
handleIonSelect(el: any): void {
|
||||
/**
|
||||
* The `el` type is any to access the `checked` state property
|
||||
* that is not exposed on the type interface.
|
||||
*/
|
||||
this.handleValueChange(el, el.checked);
|
||||
}
|
||||
}
|
||||
|
||||
export declare interface IonRadio extends Components.IonRadio {
|
||||
/**
|
||||
* Emitted when the radio button has focus.
|
||||
*/
|
||||
ionFocus: EventEmitter<CustomEvent<void>>;
|
||||
/**
|
||||
* Emitted when the radio button loses focus.
|
||||
*/
|
||||
ionBlur: EventEmitter<CustomEvent<void>>;
|
||||
}
|
||||
112
packages/angular/standalone/src/directives/range.ts
Normal file
112
packages/angular/standalone/src/directives/range.ts
Normal file
@@ -0,0 +1,112 @@
|
||||
import {
|
||||
ChangeDetectionStrategy,
|
||||
ChangeDetectorRef,
|
||||
Component,
|
||||
ElementRef,
|
||||
EventEmitter,
|
||||
HostListener,
|
||||
Injector,
|
||||
NgZone,
|
||||
} from '@angular/core';
|
||||
import { NG_VALUE_ACCESSOR } from '@angular/forms';
|
||||
import { ValueAccessor } from '@ionic/angular/common';
|
||||
import type {
|
||||
RangeChangeEventDetail,
|
||||
RangeKnobMoveStartEventDetail,
|
||||
RangeKnobMoveEndEventDetail,
|
||||
Components,
|
||||
} from '@ionic/core/components';
|
||||
import { defineCustomElement } from '@ionic/core/components/ion-range.js';
|
||||
|
||||
import { ProxyCmp, proxyOutputs } from './angular-component-lib/utils';
|
||||
|
||||
const RANGE_INPUTS = [
|
||||
'activeBarStart',
|
||||
'color',
|
||||
'debounce',
|
||||
'disabled',
|
||||
'dualKnobs',
|
||||
'label',
|
||||
'labelPlacement',
|
||||
'legacy',
|
||||
'max',
|
||||
'min',
|
||||
'mode',
|
||||
'name',
|
||||
'pin',
|
||||
'pinFormatter',
|
||||
'snaps',
|
||||
'step',
|
||||
'ticks',
|
||||
'value',
|
||||
];
|
||||
|
||||
@ProxyCmp({
|
||||
defineCustomElementFn: defineCustomElement,
|
||||
inputs: RANGE_INPUTS,
|
||||
})
|
||||
@Component({
|
||||
selector: 'ion-range',
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
template: '<ng-content></ng-content>',
|
||||
// eslint-disable-next-line @angular-eslint/no-inputs-metadata-property
|
||||
inputs: RANGE_INPUTS,
|
||||
providers: [
|
||||
{
|
||||
provide: NG_VALUE_ACCESSOR,
|
||||
useExisting: IonRange,
|
||||
multi: true,
|
||||
},
|
||||
],
|
||||
standalone: true,
|
||||
})
|
||||
export class IonRange extends ValueAccessor {
|
||||
protected el: HTMLElement;
|
||||
constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone, injector: Injector) {
|
||||
super(injector, r);
|
||||
c.detach();
|
||||
this.el = r.nativeElement;
|
||||
proxyOutputs(this, this.el, ['ionChange', 'ionInput', 'ionFocus', 'ionBlur', 'ionKnobMoveStart', 'ionKnobMoveEnd']);
|
||||
}
|
||||
|
||||
@HostListener('ionChange', ['$event.target'])
|
||||
handleIonChange(el: HTMLIonRangeElement): void {
|
||||
this.handleValueChange(el, el.value);
|
||||
}
|
||||
}
|
||||
|
||||
export declare interface IonRange extends Components.IonRange {
|
||||
/**
|
||||
* The `ionChange` event is fired for `<ion-range>` elements when the user
|
||||
modifies the element's value:
|
||||
- When the user releases the knob after dragging;
|
||||
- When the user moves the knob with keyboard arrows
|
||||
|
||||
`ionChange` is not fired when the value is changed programmatically.
|
||||
*/
|
||||
ionChange: EventEmitter<CustomEvent<RangeChangeEventDetail>>;
|
||||
/**
|
||||
* The `ionInput` event is fired for `<ion-range>` elements when the value
|
||||
is modified. Unlike `ionChange`, `ionInput` is fired continuously
|
||||
while the user is dragging the knob.
|
||||
*/
|
||||
ionInput: EventEmitter<CustomEvent<RangeChangeEventDetail>>;
|
||||
/**
|
||||
* Emitted when the range has focus.
|
||||
*/
|
||||
ionFocus: EventEmitter<CustomEvent<void>>;
|
||||
/**
|
||||
* Emitted when the range loses focus.
|
||||
*/
|
||||
ionBlur: EventEmitter<CustomEvent<void>>;
|
||||
/**
|
||||
* Emitted when the user starts moving the range knob, whether through
|
||||
mouse drag, touch gesture, or keyboard interaction.
|
||||
*/
|
||||
ionKnobMoveStart: EventEmitter<CustomEvent<RangeKnobMoveStartEventDetail>>;
|
||||
/**
|
||||
* Emitted when the user finishes moving the range knob, whether through
|
||||
mouse drag, touch gesture, or keyboard interaction.
|
||||
*/
|
||||
ionKnobMoveEnd: EventEmitter<CustomEvent<RangeKnobMoveEndEventDetail>>;
|
||||
}
|
||||
108
packages/angular/standalone/src/directives/searchbar.ts
Normal file
108
packages/angular/standalone/src/directives/searchbar.ts
Normal file
@@ -0,0 +1,108 @@
|
||||
import {
|
||||
ChangeDetectionStrategy,
|
||||
ChangeDetectorRef,
|
||||
Component,
|
||||
ElementRef,
|
||||
EventEmitter,
|
||||
HostListener,
|
||||
Injector,
|
||||
NgZone,
|
||||
} from '@angular/core';
|
||||
import { NG_VALUE_ACCESSOR } from '@angular/forms';
|
||||
import { ValueAccessor } from '@ionic/angular/common';
|
||||
import type { SearchbarInputEventDetail, SearchbarChangeEventDetail, Components } from '@ionic/core/components';
|
||||
import { defineCustomElement } from '@ionic/core/components/ion-searchbar.js';
|
||||
|
||||
import { ProxyCmp, proxyOutputs } from './angular-component-lib/utils';
|
||||
|
||||
const SEARCHBAR_INPUTS = [
|
||||
'animated',
|
||||
'autocomplete',
|
||||
'autocorrect',
|
||||
'cancelButtonIcon',
|
||||
'cancelButtonText',
|
||||
'clearIcon',
|
||||
'color',
|
||||
'debounce',
|
||||
'disabled',
|
||||
'enterkeyhint',
|
||||
'inputmode',
|
||||
'mode',
|
||||
'name',
|
||||
'placeholder',
|
||||
'searchIcon',
|
||||
'showCancelButton',
|
||||
'showClearButton',
|
||||
'spellcheck',
|
||||
'type',
|
||||
'value',
|
||||
];
|
||||
|
||||
@ProxyCmp({
|
||||
defineCustomElementFn: defineCustomElement,
|
||||
inputs: SEARCHBAR_INPUTS,
|
||||
methods: ['setFocus', 'getInputElement'],
|
||||
})
|
||||
@Component({
|
||||
selector: 'ion-searchbar',
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
template: '<ng-content></ng-content>',
|
||||
// eslint-disable-next-line @angular-eslint/no-inputs-metadata-property
|
||||
inputs: SEARCHBAR_INPUTS,
|
||||
providers: [
|
||||
{
|
||||
provide: NG_VALUE_ACCESSOR,
|
||||
useExisting: IonSearchbar,
|
||||
multi: true,
|
||||
},
|
||||
],
|
||||
standalone: true,
|
||||
})
|
||||
export class IonSearchbar extends ValueAccessor {
|
||||
protected el: HTMLElement;
|
||||
constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone, injector: Injector) {
|
||||
super(injector, r);
|
||||
c.detach();
|
||||
this.el = r.nativeElement;
|
||||
proxyOutputs(this, this.el, ['ionInput', 'ionChange', 'ionCancel', 'ionClear', 'ionBlur', 'ionFocus']);
|
||||
}
|
||||
|
||||
@HostListener('ionInput', ['$event.target'])
|
||||
handleIonInput(el: HTMLIonSearchbarElement): void {
|
||||
this.handleValueChange(el, el.value);
|
||||
}
|
||||
}
|
||||
|
||||
export declare interface IonSearchbar extends Components.IonSearchbar {
|
||||
/**
|
||||
* Emitted when the `value` of the `ion-searchbar` element has changed.
|
||||
*/
|
||||
ionInput: EventEmitter<CustomEvent<SearchbarInputEventDetail>>;
|
||||
/**
|
||||
* The `ionChange` event is fired for `<ion-searchbar>` elements when the user
|
||||
modifies the element's value. Unlike the `ionInput` event, the `ionChange`
|
||||
event is not necessarily fired for each alteration to an element's value.
|
||||
|
||||
The `ionChange` event is fired when the value has been committed
|
||||
by the user. This can happen when the element loses focus or
|
||||
when the "Enter" key is pressed. `ionChange` can also fire
|
||||
when clicking the clear or cancel buttons.
|
||||
*/
|
||||
ionChange: EventEmitter<CustomEvent<SearchbarChangeEventDetail>>;
|
||||
/**
|
||||
* Emitted when the cancel button is clicked.
|
||||
*/
|
||||
ionCancel: EventEmitter<CustomEvent<void>>;
|
||||
/**
|
||||
* Emitted when the clear input button is clicked.
|
||||
*/
|
||||
ionClear: EventEmitter<CustomEvent<void>>;
|
||||
/**
|
||||
* Emitted when the input loses focus.
|
||||
*/
|
||||
ionBlur: EventEmitter<CustomEvent<void>>;
|
||||
/**
|
||||
* Emitted when the input has focus.
|
||||
*/
|
||||
ionFocus: EventEmitter<CustomEvent<void>>;
|
||||
}
|
||||
60
packages/angular/standalone/src/directives/segment.ts
Normal file
60
packages/angular/standalone/src/directives/segment.ts
Normal file
@@ -0,0 +1,60 @@
|
||||
import {
|
||||
ChangeDetectionStrategy,
|
||||
ChangeDetectorRef,
|
||||
Component,
|
||||
ElementRef,
|
||||
EventEmitter,
|
||||
HostListener,
|
||||
Injector,
|
||||
NgZone,
|
||||
} from '@angular/core';
|
||||
import { NG_VALUE_ACCESSOR } from '@angular/forms';
|
||||
import { ValueAccessor } from '@ionic/angular/common';
|
||||
import type { SegmentChangeEventDetail, Components } from '@ionic/core/components';
|
||||
import { defineCustomElement } from '@ionic/core/components/ion-segment.js';
|
||||
|
||||
import { ProxyCmp, proxyOutputs } from './angular-component-lib/utils';
|
||||
|
||||
const SEGMENT_INPUTS = ['color', 'disabled', 'mode', 'scrollable', 'selectOnFocus', 'swipeGesture', 'value'];
|
||||
|
||||
@ProxyCmp({
|
||||
defineCustomElementFn: defineCustomElement,
|
||||
inputs: SEGMENT_INPUTS,
|
||||
})
|
||||
@Component({
|
||||
selector: 'ion-segment',
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
template: '<ng-content></ng-content>',
|
||||
// eslint-disable-next-line @angular-eslint/no-inputs-metadata-property
|
||||
inputs: SEGMENT_INPUTS,
|
||||
providers: [
|
||||
{
|
||||
provide: NG_VALUE_ACCESSOR,
|
||||
useExisting: IonSegment,
|
||||
multi: true,
|
||||
},
|
||||
],
|
||||
standalone: true,
|
||||
})
|
||||
export class IonSegment extends ValueAccessor {
|
||||
protected el: HTMLElement;
|
||||
constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone, injector: Injector) {
|
||||
super(injector, r);
|
||||
c.detach();
|
||||
this.el = r.nativeElement;
|
||||
proxyOutputs(this, this.el, ['ionChange']);
|
||||
}
|
||||
|
||||
@HostListener('ionChange', ['$event.target'])
|
||||
handleIonChange(el: HTMLIonSegmentElement): void {
|
||||
this.handleValueChange(el, el.value);
|
||||
}
|
||||
}
|
||||
|
||||
export declare interface IonSegment extends Components.IonSegment {
|
||||
/**
|
||||
* Emitted when the value property has changed and any
|
||||
dragging pointer has been released from `ion-segment`.
|
||||
*/
|
||||
ionChange: EventEmitter<CustomEvent<SegmentChangeEventDetail>>;
|
||||
}
|
||||
98
packages/angular/standalone/src/directives/select.ts
Normal file
98
packages/angular/standalone/src/directives/select.ts
Normal file
@@ -0,0 +1,98 @@
|
||||
import {
|
||||
ChangeDetectionStrategy,
|
||||
ChangeDetectorRef,
|
||||
Component,
|
||||
ElementRef,
|
||||
EventEmitter,
|
||||
HostListener,
|
||||
Injector,
|
||||
NgZone,
|
||||
} from '@angular/core';
|
||||
import { NG_VALUE_ACCESSOR } from '@angular/forms';
|
||||
import { ValueAccessor } from '@ionic/angular/common';
|
||||
import type { SelectChangeEventDetail, Components } from '@ionic/core/components';
|
||||
import { defineCustomElement } from '@ionic/core/components/ion-select.js';
|
||||
|
||||
import { ProxyCmp, proxyOutputs } from './angular-component-lib/utils';
|
||||
|
||||
const SELECT_INPUTS = [
|
||||
'cancelText',
|
||||
'color',
|
||||
'compareWith',
|
||||
'disabled',
|
||||
'expandedIcon',
|
||||
'fill',
|
||||
'interface',
|
||||
'interfaceOptions',
|
||||
'justify',
|
||||
'label',
|
||||
'labelPlacement',
|
||||
'legacy',
|
||||
'mode',
|
||||
'multiple',
|
||||
'name',
|
||||
'okText',
|
||||
'placeholder',
|
||||
'selectedText',
|
||||
'shape',
|
||||
'toggleIcon',
|
||||
'value',
|
||||
];
|
||||
|
||||
@ProxyCmp({
|
||||
defineCustomElementFn: defineCustomElement,
|
||||
inputs: SELECT_INPUTS,
|
||||
methods: ['open'],
|
||||
})
|
||||
@Component({
|
||||
selector: 'ion-select',
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
template: '<ng-content></ng-content>',
|
||||
// eslint-disable-next-line @angular-eslint/no-inputs-metadata-property
|
||||
inputs: SELECT_INPUTS,
|
||||
providers: [
|
||||
{
|
||||
provide: NG_VALUE_ACCESSOR,
|
||||
useExisting: IonSelect,
|
||||
multi: true,
|
||||
},
|
||||
],
|
||||
standalone: true,
|
||||
})
|
||||
export class IonSelect extends ValueAccessor {
|
||||
protected el: HTMLElement;
|
||||
constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone, injector: Injector) {
|
||||
super(injector, r);
|
||||
c.detach();
|
||||
this.el = r.nativeElement;
|
||||
proxyOutputs(this, this.el, ['ionChange', 'ionCancel', 'ionDismiss', 'ionFocus', 'ionBlur']);
|
||||
}
|
||||
|
||||
@HostListener('ionChange', ['$event.target'])
|
||||
handleIonChange(el: HTMLIonSelectElement): void {
|
||||
this.handleValueChange(el, el.value);
|
||||
}
|
||||
}
|
||||
|
||||
export declare interface IonSelect extends Components.IonSelect {
|
||||
/**
|
||||
* Emitted when the value has changed.
|
||||
*/
|
||||
ionChange: EventEmitter<CustomEvent<SelectChangeEventDetail>>;
|
||||
/**
|
||||
* Emitted when the selection is cancelled.
|
||||
*/
|
||||
ionCancel: EventEmitter<CustomEvent<void>>;
|
||||
/**
|
||||
* Emitted when the overlay is dismissed.
|
||||
*/
|
||||
ionDismiss: EventEmitter<CustomEvent<void>>;
|
||||
/**
|
||||
* Emitted when the select has focus.
|
||||
*/
|
||||
ionFocus: EventEmitter<CustomEvent<void>>;
|
||||
/**
|
||||
* Emitted when the select loses focus.
|
||||
*/
|
||||
ionBlur: EventEmitter<CustomEvent<void>>;
|
||||
}
|
||||
110
packages/angular/standalone/src/directives/textarea.ts
Normal file
110
packages/angular/standalone/src/directives/textarea.ts
Normal file
@@ -0,0 +1,110 @@
|
||||
import {
|
||||
ChangeDetectionStrategy,
|
||||
ChangeDetectorRef,
|
||||
Component,
|
||||
ElementRef,
|
||||
EventEmitter,
|
||||
HostListener,
|
||||
Injector,
|
||||
NgZone,
|
||||
} from '@angular/core';
|
||||
import { NG_VALUE_ACCESSOR } from '@angular/forms';
|
||||
import { ValueAccessor } from '@ionic/angular/common';
|
||||
import type { TextareaChangeEventDetail, TextareaInputEventDetail, Components } from '@ionic/core/components';
|
||||
import { defineCustomElement } from '@ionic/core/components/ion-textarea.js';
|
||||
|
||||
import { ProxyCmp, proxyOutputs } from './angular-component-lib/utils';
|
||||
|
||||
const TEXTAREA_INPUTS = [
|
||||
'autoGrow',
|
||||
'autocapitalize',
|
||||
'autofocus',
|
||||
'clearOnEdit',
|
||||
'color',
|
||||
'cols',
|
||||
'counter',
|
||||
'counterFormatter',
|
||||
'debounce',
|
||||
'disabled',
|
||||
'enterkeyhint',
|
||||
'errorText',
|
||||
'fill',
|
||||
'helperText',
|
||||
'inputmode',
|
||||
'label',
|
||||
'labelPlacement',
|
||||
'legacy',
|
||||
'maxlength',
|
||||
'minlength',
|
||||
'mode',
|
||||
'name',
|
||||
'placeholder',
|
||||
'readonly',
|
||||
'required',
|
||||
'rows',
|
||||
'shape',
|
||||
'spellcheck',
|
||||
'value',
|
||||
'wrap',
|
||||
];
|
||||
|
||||
@ProxyCmp({
|
||||
defineCustomElementFn: defineCustomElement,
|
||||
inputs: TEXTAREA_INPUTS,
|
||||
methods: ['setFocus', 'getInputElement'],
|
||||
})
|
||||
@Component({
|
||||
selector: 'ion-textarea',
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
template: '<ng-content></ng-content>',
|
||||
// eslint-disable-next-line @angular-eslint/no-inputs-metadata-property
|
||||
inputs: TEXTAREA_INPUTS,
|
||||
providers: [
|
||||
{
|
||||
provide: NG_VALUE_ACCESSOR,
|
||||
useExisting: IonTextarea,
|
||||
multi: true,
|
||||
},
|
||||
],
|
||||
standalone: true,
|
||||
})
|
||||
export class IonTextarea extends ValueAccessor {
|
||||
protected el: HTMLElement;
|
||||
constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone, injector: Injector) {
|
||||
super(injector, r);
|
||||
c.detach();
|
||||
this.el = r.nativeElement;
|
||||
proxyOutputs(this, this.el, ['ionChange', 'ionInput', 'ionBlur', 'ionFocus']);
|
||||
}
|
||||
|
||||
@HostListener('ionInput', ['$event.target'])
|
||||
handleIonInput(el: HTMLIonTextareaElement): void {
|
||||
this.handleValueChange(el, el.value);
|
||||
}
|
||||
}
|
||||
|
||||
export declare interface IonTextarea extends Components.IonTextarea {
|
||||
/**
|
||||
* The `ionChange` event is fired when the user modifies the textarea's value.
|
||||
Unlike the `ionInput` event, the `ionChange` event is fired when
|
||||
the element loses focus after its value has been modified.
|
||||
*/
|
||||
ionChange: EventEmitter<CustomEvent<TextareaChangeEventDetail>>;
|
||||
/**
|
||||
* The `ionInput` event is fired each time the user modifies the textarea's value.
|
||||
Unlike the `ionChange` event, the `ionInput` event is fired for each alteration
|
||||
to the textarea's value. This typically happens for each keystroke as the user types.
|
||||
|
||||
When `clearOnEdit` is enabled, the `ionInput` event will be fired when
|
||||
the user clears the textarea by performing a keydown event.
|
||||
*/
|
||||
ionInput: EventEmitter<CustomEvent<TextareaInputEventDetail>>;
|
||||
/**
|
||||
* Emitted when the input loses focus.
|
||||
*/
|
||||
ionBlur: EventEmitter<CustomEvent<FocusEvent>>;
|
||||
/**
|
||||
* Emitted when the input has focus.
|
||||
*/
|
||||
ionFocus: EventEmitter<CustomEvent<FocusEvent>>;
|
||||
}
|
||||
84
packages/angular/standalone/src/directives/toggle.ts
Normal file
84
packages/angular/standalone/src/directives/toggle.ts
Normal file
@@ -0,0 +1,84 @@
|
||||
import {
|
||||
ChangeDetectionStrategy,
|
||||
ChangeDetectorRef,
|
||||
Component,
|
||||
ElementRef,
|
||||
EventEmitter,
|
||||
HostListener,
|
||||
Injector,
|
||||
NgZone,
|
||||
} from '@angular/core';
|
||||
import { NG_VALUE_ACCESSOR } from '@angular/forms';
|
||||
import { ValueAccessor, setIonicClasses } from '@ionic/angular/common';
|
||||
import type { ToggleChangeEventDetail, Components } from '@ionic/core/components';
|
||||
import { defineCustomElement } from '@ionic/core/components/ion-toggle.js';
|
||||
|
||||
import { ProxyCmp, proxyOutputs } from './angular-component-lib/utils';
|
||||
|
||||
const TOGGLE_INPUTS = [
|
||||
'checked',
|
||||
'color',
|
||||
'disabled',
|
||||
'enableOnOffLabels',
|
||||
'justify',
|
||||
'labelPlacement',
|
||||
'legacy',
|
||||
'mode',
|
||||
'name',
|
||||
'value',
|
||||
];
|
||||
|
||||
@ProxyCmp({
|
||||
defineCustomElementFn: defineCustomElement,
|
||||
inputs: TOGGLE_INPUTS,
|
||||
})
|
||||
@Component({
|
||||
selector: 'ion-toggle',
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
template: '<ng-content></ng-content>',
|
||||
// eslint-disable-next-line @angular-eslint/no-inputs-metadata-property
|
||||
inputs: TOGGLE_INPUTS,
|
||||
providers: [
|
||||
{
|
||||
provide: NG_VALUE_ACCESSOR,
|
||||
useExisting: IonToggle,
|
||||
multi: true,
|
||||
},
|
||||
],
|
||||
standalone: true,
|
||||
})
|
||||
export class IonToggle extends ValueAccessor {
|
||||
protected el: HTMLElement;
|
||||
constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone, injector: Injector) {
|
||||
super(injector, r);
|
||||
c.detach();
|
||||
this.el = r.nativeElement;
|
||||
proxyOutputs(this, this.el, ['ionChange', 'ionFocus', 'ionBlur']);
|
||||
}
|
||||
|
||||
writeValue(value: boolean): void {
|
||||
this.elementRef.nativeElement.checked = this.lastValue = value;
|
||||
setIonicClasses(this.elementRef);
|
||||
}
|
||||
|
||||
@HostListener('ionChange', ['$event.target'])
|
||||
handleIonChange(el: HTMLIonToggleElement): void {
|
||||
this.handleValueChange(el, el.checked);
|
||||
}
|
||||
}
|
||||
|
||||
export declare interface IonToggle extends Components.IonToggle {
|
||||
/**
|
||||
* Emitted when the user switches the toggle on or off. Does not emit
|
||||
when programmatically changing the value of the `checked` property.
|
||||
*/
|
||||
ionChange: EventEmitter<CustomEvent<ToggleChangeEventDetail>>;
|
||||
/**
|
||||
* Emitted when the toggle has focus.
|
||||
*/
|
||||
ionFocus: EventEmitter<CustomEvent<void>>;
|
||||
/**
|
||||
* Emitted when the toggle loses focus.
|
||||
*/
|
||||
ionBlur: EventEmitter<CustomEvent<void>>;
|
||||
}
|
||||
@@ -24,7 +24,20 @@ export {
|
||||
IonicRouteStrategy,
|
||||
} from '@ionic/angular/common';
|
||||
export { IonNav } from './navigation/nav';
|
||||
export { IonIcon } from './directives/icon';
|
||||
export {
|
||||
IonCheckbox,
|
||||
IonDatetime,
|
||||
IonInput,
|
||||
IonIcon,
|
||||
IonRadioGroup,
|
||||
IonRadio,
|
||||
IonRange,
|
||||
IonSearchbar,
|
||||
IonSegment,
|
||||
IonSelect,
|
||||
IonTextarea,
|
||||
IonToggle,
|
||||
} from './directives';
|
||||
export * from './directives/proxies';
|
||||
|
||||
export {
|
||||
|
||||
@@ -0,0 +1,132 @@
|
||||
|
||||
describe('Value Accessors', () => {
|
||||
|
||||
describe('Checkbox', () => {
|
||||
beforeEach(() => cy.visit('/standalone/value-accessors/checkbox'));
|
||||
|
||||
it('should update the form value', () => {
|
||||
cy.get('#formValue').should('have.text', JSON.stringify({ checkbox: false }, null, 2));
|
||||
cy.get('ion-checkbox').should('have.class', 'ion-pristine');
|
||||
|
||||
cy.get('ion-checkbox').click();
|
||||
|
||||
cy.get('#formValue').should('have.text', JSON.stringify({ checkbox: true }, null, 2));
|
||||
cy.get('ion-checkbox').should('have.class', 'ion-dirty');
|
||||
cy.get('ion-checkbox').should('have.class', 'ion-valid');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Input', () => {
|
||||
beforeEach(() => cy.visit('/standalone/value-accessors/input'));
|
||||
|
||||
it('should update the form value', () => {
|
||||
cy.get('#formValue').should('have.text', JSON.stringify({
|
||||
inputString: '',
|
||||
inputNumber: ''
|
||||
}, null, 2));
|
||||
cy.get('ion-input[formControlName="inputString"]').should('have.class', 'ion-pristine');
|
||||
cy.get('ion-input[formControlName="inputNumber"]').should('have.class', 'ion-pristine');
|
||||
|
||||
cy.get('ion-input[formControlName="inputString"]').should('have.class', 'ion-invalid');
|
||||
cy.get('ion-input[formControlName="inputNumber"]').should('have.class', 'ion-invalid');
|
||||
|
||||
cy.get('ion-input[formControlName="inputString"] input').type('test');
|
||||
cy.get('ion-input[formControlName="inputString"] input').blur();
|
||||
|
||||
cy.get('ion-input[formControlName="inputNumber"] input').type(1);
|
||||
cy.get('ion-input[formControlName="inputNumber"] input').blur();
|
||||
|
||||
cy.get('#formValue').should('have.text', JSON.stringify({
|
||||
inputString: 'test',
|
||||
inputNumber: 1
|
||||
}, null, 2));
|
||||
|
||||
cy.get('ion-input[formControlName="inputString"]').should('have.class', 'ion-dirty');
|
||||
cy.get('ion-input[formControlName="inputNumber"]').should('have.class', 'ion-dirty');
|
||||
|
||||
cy.get('ion-input[formControlName="inputString"]').should('have.class', 'ion-valid');
|
||||
cy.get('ion-input[formControlName="inputNumber"]').should('have.class', 'ion-valid');
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
describe('Radio Group', () => {
|
||||
beforeEach(() => cy.visit('/standalone/value-accessors/radio-group'));
|
||||
|
||||
it('should update the form value', () => {
|
||||
cy.get('#formValue').should('have.text', JSON.stringify({ radioGroup: '1' }, null, 2));
|
||||
cy.get('ion-radio-group').should('have.class', 'ion-pristine');
|
||||
|
||||
cy.get('ion-radio').contains('Two').click();
|
||||
|
||||
cy.get('#formValue').should('have.text', JSON.stringify({ radioGroup: '2' }, null, 2));
|
||||
cy.get('ion-radio-group').should('have.class', 'ion-dirty');
|
||||
cy.get('ion-radio-group').should('have.class', 'ion-valid');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Searchbar', () => {
|
||||
beforeEach(() => cy.visit('/standalone/value-accessors/searchbar'));
|
||||
|
||||
it('should update the form value', () => {
|
||||
cy.get('#formValue').should('have.text', JSON.stringify({ searchbar: '' }, null, 2));
|
||||
cy.get('ion-searchbar').should('have.class', 'ion-pristine');
|
||||
cy.get('ion-searchbar').should('have.class', 'ion-invalid');
|
||||
|
||||
cy.get('ion-searchbar').type('test');
|
||||
cy.get('ion-searchbar input').blur();
|
||||
|
||||
cy.get('#formValue').should('have.text', JSON.stringify({ searchbar: 'test' }, null, 2));
|
||||
cy.get('ion-searchbar').should('have.class', 'ion-dirty');
|
||||
cy.get('ion-searchbar').should('have.class', 'ion-valid');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Segment', () => {
|
||||
beforeEach(() => cy.visit('/standalone/value-accessors/segment'));
|
||||
|
||||
it('should update the form value', () => {
|
||||
cy.get('#formValue').should('have.text', JSON.stringify({ segment: 'Paid' }, null, 2));
|
||||
cy.get('ion-segment').should('have.class', 'ion-pristine');
|
||||
|
||||
cy.get('ion-segment-button').eq(1).click();
|
||||
|
||||
cy.get('#formValue').should('have.text', JSON.stringify({ segment: 'Free' }, null, 2));
|
||||
cy.get('ion-segment').should('have.class', 'ion-dirty');
|
||||
cy.get('ion-segment').should('have.class', 'ion-valid');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Textarea', () => {
|
||||
beforeEach(() => cy.visit('/standalone/value-accessors/textarea'));
|
||||
|
||||
it('should update the form value', () => {
|
||||
cy.get('#formValue').should('have.text', JSON.stringify({ textarea: '' }, null, 2));
|
||||
cy.get('ion-textarea').should('have.class', 'ion-pristine');
|
||||
cy.get('ion-textarea').should('have.class', 'ion-invalid');
|
||||
|
||||
cy.get('ion-textarea').click();
|
||||
cy.get('ion-textarea').type('test');
|
||||
|
||||
cy.get('#formValue').should('have.text', JSON.stringify({ textarea: 'test' }, null, 2));
|
||||
cy.get('ion-textarea').should('have.class', 'ion-dirty');
|
||||
cy.get('ion-textarea').should('have.class', 'ion-valid');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Toggle', () => {
|
||||
beforeEach(() => cy.visit('/standalone/value-accessors/toggle'));
|
||||
|
||||
it('should update the form value', () => {
|
||||
cy.get('#formValue').should('have.text', JSON.stringify({ toggle: false }, null, 2));
|
||||
cy.get('ion-toggle').should('have.class', 'ion-pristine');
|
||||
|
||||
cy.get('ion-toggle').click();
|
||||
|
||||
cy.get('#formValue').should('have.text', JSON.stringify({ toggle: true }, null, 2));
|
||||
cy.get('ion-toggle').should('have.class', 'ion-dirty');
|
||||
cy.get('ion-toggle').should('have.class', 'ion-valid');
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
@@ -26,6 +26,21 @@ export const routes: Routes = [
|
||||
{ path: 'tab-three', loadComponent: () => import('../tabs/tab3.component').then(c => c.TabThreeComponent) }
|
||||
]
|
||||
},
|
||||
{
|
||||
path: 'value-accessors',
|
||||
children: [
|
||||
{ path: 'checkbox', loadComponent: () => import('../value-accessors/checkbox/checkbox.component').then(c => c.CheckboxComponent) },
|
||||
{ path: 'datetime', loadComponent: () => import('../value-accessors/datetime/datetime.component').then(c => c.DatetimeComponent) },
|
||||
{ path: 'input', loadComponent: () => import('../value-accessors/input/input.component').then(c => c.InputComponent) },
|
||||
{ path: 'radio-group', loadComponent: () => import('../value-accessors/radio-group/radio-group.component').then(c => c.RadioGroupComponent) },
|
||||
{ path: 'range', loadComponent: () => import('../value-accessors/range/range.component').then(c => c.RangeComponent) },
|
||||
{ path: 'searchbar', loadComponent: () => import('../value-accessors/searchbar/searchbar.component').then(c => c.SearchbarComponent) },
|
||||
{ path: 'segment', loadComponent: () => import('../value-accessors/segment/segment.component').then(c => c.SegmentComponent) },
|
||||
{ path: 'textarea', loadComponent: () => import('../value-accessors/textarea/textarea.component').then(c => c.TextareaComponent) },
|
||||
{ path: 'toggle', loadComponent: () => import('../value-accessors/toggle/toggle.component').then(c => c.ToggleComponent) },
|
||||
{ path: '**', redirectTo: 'checkbox' }
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
];
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
<div>
|
||||
<h1>IonCheckbox Value Accessors</h1>
|
||||
<p>
|
||||
This test checks the form integrations with ion-checkbox to make sure values are correctly assigned to the form
|
||||
group.
|
||||
</p>
|
||||
|
||||
<app-value-accessor-test [formGroup]="form">
|
||||
<ion-checkbox formControlName="checkbox"></ion-checkbox>
|
||||
</app-value-accessor-test>
|
||||
</div>
|
||||
@@ -0,0 +1,25 @@
|
||||
import { Component } from "@angular/core";
|
||||
import { FormBuilder, FormsModule, ReactiveFormsModule, Validators } from "@angular/forms";
|
||||
import { IonCheckbox } from "@ionic/angular/standalone";
|
||||
import { ValueAccessorTestComponent } from "../value-accessor-test/value-accessor-test.component";
|
||||
|
||||
@Component({
|
||||
selector: 'app-checkbox',
|
||||
templateUrl: 'checkbox.component.html',
|
||||
standalone: true,
|
||||
imports: [
|
||||
IonCheckbox,
|
||||
ReactiveFormsModule,
|
||||
FormsModule,
|
||||
ValueAccessorTestComponent
|
||||
]
|
||||
})
|
||||
export class CheckboxComponent {
|
||||
|
||||
form = this.fb.group({
|
||||
checkbox: [false, Validators.required],
|
||||
});
|
||||
|
||||
constructor(private fb: FormBuilder) { }
|
||||
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
<div>
|
||||
<h1>IonDatetime Value Accessors</h1>
|
||||
<p>
|
||||
This test checks the form integrations with ion-datetime to make sure values are correctly assigned to the form
|
||||
group.
|
||||
</p>
|
||||
|
||||
<app-value-accessor-test [formGroup]="form">
|
||||
<ion-datetime formControlName="datetime"></ion-datetime>
|
||||
</app-value-accessor-test>
|
||||
</div>
|
||||
@@ -0,0 +1,25 @@
|
||||
import { Component } from "@angular/core";
|
||||
import { FormBuilder, FormsModule, ReactiveFormsModule, Validators } from "@angular/forms";
|
||||
import { IonDatetime } from "@ionic/angular/standalone";
|
||||
import { ValueAccessorTestComponent } from "../value-accessor-test/value-accessor-test.component";
|
||||
|
||||
@Component({
|
||||
selector: 'app-datetime',
|
||||
templateUrl: 'datetime.component.html',
|
||||
standalone: true,
|
||||
imports: [
|
||||
IonDatetime,
|
||||
ReactiveFormsModule,
|
||||
FormsModule,
|
||||
ValueAccessorTestComponent
|
||||
]
|
||||
})
|
||||
export class DatetimeComponent {
|
||||
|
||||
form = this.fb.group({
|
||||
datetime: ['2023-05-10T04:00:00', Validators.required],
|
||||
});
|
||||
|
||||
constructor(private fb: FormBuilder) { }
|
||||
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
<div>
|
||||
<h1>IonInput Value Accessors</h1>
|
||||
<p>
|
||||
This test checks the form integrations with ion-input to make sure values are correctly assigned to the form group.
|
||||
</p>
|
||||
|
||||
<app-value-accessor-test [formGroup]="form">
|
||||
<ion-input label="String" formControlName="inputString"></ion-input>
|
||||
<ion-input label="Number" type="number" formControlName="inputNumber"></ion-input>
|
||||
</app-value-accessor-test>
|
||||
</div>
|
||||
@@ -0,0 +1,26 @@
|
||||
import { Component } from "@angular/core";
|
||||
import { FormBuilder, FormsModule, ReactiveFormsModule, Validators } from "@angular/forms";
|
||||
import { IonInput } from "@ionic/angular/standalone";
|
||||
import { ValueAccessorTestComponent } from "../value-accessor-test/value-accessor-test.component";
|
||||
|
||||
@Component({
|
||||
selector: 'app-input',
|
||||
templateUrl: 'input.component.html',
|
||||
standalone: true,
|
||||
imports: [
|
||||
IonInput,
|
||||
ReactiveFormsModule,
|
||||
FormsModule,
|
||||
ValueAccessorTestComponent,
|
||||
]
|
||||
})
|
||||
export class InputComponent {
|
||||
|
||||
form = this.fb.group({
|
||||
inputString: ['', Validators.required],
|
||||
inputNumber: ['', Validators.required],
|
||||
});
|
||||
|
||||
constructor(private fb: FormBuilder) { }
|
||||
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
<div>
|
||||
<h1>IonRadioGroup Value Accessors</h1>
|
||||
<p>
|
||||
This test checks the form integrations with ion-radio-group to make sure values are correctly assigned to the form
|
||||
group.
|
||||
</p>
|
||||
|
||||
<app-value-accessor-test [formGroup]="form">
|
||||
<ion-radio-group formControlName="radioGroup">
|
||||
<ion-radio value="1">One</ion-radio>
|
||||
<ion-radio value="2">Two</ion-radio>
|
||||
</ion-radio-group>
|
||||
</app-value-accessor-test>
|
||||
</div>
|
||||
@@ -0,0 +1,26 @@
|
||||
import { Component } from "@angular/core";
|
||||
import { FormBuilder, FormsModule, ReactiveFormsModule, Validators } from "@angular/forms";
|
||||
import { IonRadioGroup, IonRadio } from "@ionic/angular/standalone";
|
||||
import { ValueAccessorTestComponent } from "../value-accessor-test/value-accessor-test.component";
|
||||
|
||||
@Component({
|
||||
selector: 'app-radio-group',
|
||||
templateUrl: 'radio-group.component.html',
|
||||
standalone: true,
|
||||
imports: [
|
||||
IonRadioGroup,
|
||||
IonRadio,
|
||||
ReactiveFormsModule,
|
||||
FormsModule,
|
||||
ValueAccessorTestComponent
|
||||
]
|
||||
})
|
||||
export class RadioGroupComponent {
|
||||
|
||||
form = this.fb.group({
|
||||
radioGroup: ['1', Validators.required],
|
||||
});
|
||||
|
||||
constructor(private fb: FormBuilder) { }
|
||||
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
<div>
|
||||
<h1>IonRange Value Accessors</h1>
|
||||
<p>
|
||||
This test checks the form integrations with ion-range to make sure values are correctly assigned to the form group.
|
||||
</p>
|
||||
<app-value-accessor-test [formGroup]="form">
|
||||
<ion-range formControlName="range"></ion-range>
|
||||
</app-value-accessor-test>
|
||||
</div>
|
||||
@@ -0,0 +1,25 @@
|
||||
import { Component } from "@angular/core";
|
||||
import { FormBuilder, FormsModule, ReactiveFormsModule, Validators } from "@angular/forms";
|
||||
import { IonRange } from "@ionic/angular/standalone";
|
||||
import { ValueAccessorTestComponent } from "../value-accessor-test/value-accessor-test.component";
|
||||
|
||||
@Component({
|
||||
selector: 'app-range',
|
||||
templateUrl: 'range.component.html',
|
||||
standalone: true,
|
||||
imports: [
|
||||
IonRange,
|
||||
ReactiveFormsModule,
|
||||
FormsModule,
|
||||
ValueAccessorTestComponent
|
||||
]
|
||||
})
|
||||
export class RangeComponent {
|
||||
|
||||
form = this.fb.group({
|
||||
range: [0, Validators.required],
|
||||
});
|
||||
|
||||
constructor(private fb: FormBuilder) { }
|
||||
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
<div>
|
||||
<h1>IonSearchbar Value Accessors</h1>
|
||||
<p>
|
||||
This test checks the form integrations with ion-searchbar to make sure values are correctly assigned to the form
|
||||
group.
|
||||
</p>
|
||||
<app-value-accessor-test [formGroup]="form">
|
||||
<ion-searchbar label="String" formControlName="searchbar"></ion-searchbar>
|
||||
</app-value-accessor-test>
|
||||
</div>
|
||||
@@ -0,0 +1,25 @@
|
||||
import { Component } from "@angular/core";
|
||||
import { FormBuilder, FormsModule, ReactiveFormsModule, Validators } from "@angular/forms";
|
||||
import { IonSearchbar } from "@ionic/angular/standalone";
|
||||
import { ValueAccessorTestComponent } from "../value-accessor-test/value-accessor-test.component";
|
||||
|
||||
@Component({
|
||||
selector: 'app-searchbar',
|
||||
templateUrl: 'searchbar.component.html',
|
||||
standalone: true,
|
||||
imports: [
|
||||
IonSearchbar,
|
||||
ReactiveFormsModule,
|
||||
FormsModule,
|
||||
ValueAccessorTestComponent
|
||||
]
|
||||
})
|
||||
export class SearchbarComponent {
|
||||
|
||||
form = this.fb.group({
|
||||
searchbar: ['', Validators.required],
|
||||
});
|
||||
|
||||
constructor(private fb: FormBuilder) { }
|
||||
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
<div>
|
||||
<h1>IonSegment Value Accessors</h1>
|
||||
<p>
|
||||
This test checks the form integrations with ion-segment to make sure values are correctly assigned to the form
|
||||
group.
|
||||
</p>
|
||||
|
||||
<app-value-accessor-test [formGroup]="form">
|
||||
<ion-segment formControlName="segment">
|
||||
<ion-segment-button value="Paid">
|
||||
<ion-label>Paid</ion-label>
|
||||
</ion-segment-button>
|
||||
<ion-segment-button value="Free">
|
||||
<ion-label>Free</ion-label>
|
||||
</ion-segment-button>
|
||||
</ion-segment>
|
||||
</app-value-accessor-test>
|
||||
</div>
|
||||
@@ -0,0 +1,27 @@
|
||||
import { Component } from "@angular/core";
|
||||
import { FormBuilder, FormsModule, ReactiveFormsModule, Validators } from "@angular/forms";
|
||||
import { IonSegment, IonSegmentButton, IonLabel } from "@ionic/angular/standalone";
|
||||
import { ValueAccessorTestComponent } from "../value-accessor-test/value-accessor-test.component";
|
||||
|
||||
@Component({
|
||||
selector: 'app-segment',
|
||||
templateUrl: 'segment.component.html',
|
||||
standalone: true,
|
||||
imports: [
|
||||
IonSegment,
|
||||
IonSegmentButton,
|
||||
IonLabel,
|
||||
ReactiveFormsModule,
|
||||
FormsModule,
|
||||
ValueAccessorTestComponent
|
||||
]
|
||||
})
|
||||
export class SegmentComponent {
|
||||
|
||||
form = this.fb.group({
|
||||
segment: ['Paid', Validators.required],
|
||||
});
|
||||
|
||||
constructor(private fb: FormBuilder) { }
|
||||
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
<div>
|
||||
<h1>IonTextarea Value Accessors</h1>
|
||||
<p>
|
||||
This test checks the form integrations with ion-textarea to make sure values are correctly assigned to the form
|
||||
group.
|
||||
</p>
|
||||
|
||||
<app-value-accessor-test [formGroup]="form">
|
||||
<ion-textarea label="String" formControlName="textarea"></ion-textarea>
|
||||
</app-value-accessor-test>
|
||||
</div>
|
||||
@@ -0,0 +1,25 @@
|
||||
import { Component } from "@angular/core";
|
||||
import { FormBuilder, FormsModule, ReactiveFormsModule, Validators } from "@angular/forms";
|
||||
import { IonTextarea } from "@ionic/angular/standalone";
|
||||
import { ValueAccessorTestComponent } from "../value-accessor-test/value-accessor-test.component";
|
||||
|
||||
@Component({
|
||||
selector: 'app-textarea',
|
||||
templateUrl: 'textarea.component.html',
|
||||
standalone: true,
|
||||
imports: [
|
||||
IonTextarea,
|
||||
ReactiveFormsModule,
|
||||
FormsModule,
|
||||
ValueAccessorTestComponent
|
||||
]
|
||||
})
|
||||
export class TextareaComponent {
|
||||
|
||||
form = this.fb.group({
|
||||
textarea: ['', Validators.required],
|
||||
});
|
||||
|
||||
constructor(private fb: FormBuilder) { }
|
||||
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
<div>
|
||||
<h1>IonToggle Value Accessors</h1>
|
||||
<p>
|
||||
This test checks the form integrations with ion-toggle to make sure values are correctly assigned to the form group.
|
||||
</p>
|
||||
|
||||
<app-value-accessor-test [formGroup]="form">
|
||||
<ion-toggle formControlName="toggle"></ion-toggle>
|
||||
</app-value-accessor-test>
|
||||
</div>
|
||||
@@ -0,0 +1,25 @@
|
||||
import { Component } from "@angular/core";
|
||||
import { FormBuilder, FormsModule, ReactiveFormsModule, Validators } from "@angular/forms";
|
||||
import { IonToggle } from "@ionic/angular/standalone";
|
||||
import { ValueAccessorTestComponent } from "../value-accessor-test/value-accessor-test.component";
|
||||
|
||||
@Component({
|
||||
selector: 'app-toggle',
|
||||
templateUrl: 'toggle.component.html',
|
||||
standalone: true,
|
||||
imports: [
|
||||
IonToggle,
|
||||
ReactiveFormsModule,
|
||||
FormsModule,
|
||||
ValueAccessorTestComponent
|
||||
]
|
||||
})
|
||||
export class ToggleComponent {
|
||||
|
||||
form = this.fb.group({
|
||||
toggle: [false, Validators.required],
|
||||
});
|
||||
|
||||
constructor(private fb: FormBuilder) { }
|
||||
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
<form [formGroup]="formGroup">
|
||||
<div>
|
||||
<ng-content></ng-content>
|
||||
</div>
|
||||
|
||||
<input type="submit" value="Submit" [disabled]="!formGroup.valid" />
|
||||
|
||||
<h3>Form Value</h3>
|
||||
<pre id="formValue">{{ formGroup.value | json }}</pre>
|
||||
|
||||
<h3>Form Validity</h3>
|
||||
<ul id="formValidity">
|
||||
<li>Form valid: {{ formGroup.valid }}</li>
|
||||
<li *ngFor="let control of formGroup.controls | keyvalue">{{ control.key }} valid: {{ control.value.valid }}</li>
|
||||
</ul>
|
||||
</form>
|
||||
@@ -0,0 +1,27 @@
|
||||
import { CommonModule, JsonPipe, KeyValuePipe } from "@angular/common";
|
||||
import { Component, Input } from "@angular/core";
|
||||
import { FormGroup, FormsModule, ReactiveFormsModule } from "@angular/forms";
|
||||
|
||||
@Component({
|
||||
selector: 'app-value-accessor-test',
|
||||
templateUrl: 'value-accessor-test.component.html',
|
||||
standalone: true,
|
||||
imports: [
|
||||
ReactiveFormsModule,
|
||||
FormsModule,
|
||||
JsonPipe,
|
||||
KeyValuePipe,
|
||||
/**
|
||||
* NgFor directive is not available until Angular 15.
|
||||
* We import the CommonModule for now.
|
||||
*
|
||||
* TODO: FW-5197 Replace with NgFor when dropping Angular 14 support.
|
||||
*/
|
||||
CommonModule
|
||||
]
|
||||
})
|
||||
export class ValueAccessorTestComponent {
|
||||
|
||||
@Input() formGroup!: FormGroup;
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user