mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2025-08-18 19:21:34 +08:00
Merge branch 'main' into rebase-next-after-8.4-release
This commit is contained in:
@ -5,7 +5,7 @@ import type { NotchController } from '@utils/forms';
|
||||
import { compareOptions, createNotchController, isOptionSelected } from '@utils/forms';
|
||||
import { focusVisibleElement, renderHiddenInput, inheritAttributes } from '@utils/helpers';
|
||||
import type { Attributes } from '@utils/helpers';
|
||||
import { actionSheetController, alertController, popoverController } from '@utils/overlays';
|
||||
import { actionSheetController, alertController, popoverController, modalController } from '@utils/overlays';
|
||||
import type { OverlaySelect } from '@utils/overlays-interface';
|
||||
import { isRTL } from '@utils/rtl';
|
||||
import { createColorClasses, hostContext } from '@utils/theme';
|
||||
@ -21,6 +21,7 @@ import type {
|
||||
CssClassMap,
|
||||
PopoverOptions,
|
||||
StyleEventDetail,
|
||||
ModalOptions,
|
||||
} from '../../interface';
|
||||
import type { ActionSheetButton } from '../action-sheet/action-sheet-interface';
|
||||
import type { AlertInput } from '../alert/alert-interface';
|
||||
@ -102,15 +103,15 @@ export class Select implements ComponentInterface {
|
||||
@Prop() fill?: 'outline' | 'solid';
|
||||
|
||||
/**
|
||||
* The interface the select should use: `action-sheet`, `popover` or `alert`.
|
||||
* The interface the select should use: `action-sheet`, `popover`, `alert`, or `modal`.
|
||||
*/
|
||||
@Prop() interface: SelectInterface = 'alert';
|
||||
|
||||
/**
|
||||
* Any additional options that the `alert`, `action-sheet` or `popover` interface
|
||||
* can take. See the [ion-alert docs](./alert), the
|
||||
* [ion-action-sheet docs](./action-sheet) and the
|
||||
* [ion-popover docs](./popover) for the
|
||||
* [ion-action-sheet docs](./action-sheet), the
|
||||
* [ion-popover docs](./popover), and the [ion-modal docs](./modal) for the
|
||||
* create options for each interface.
|
||||
*
|
||||
* Note: `interfaceOptions` will not override `inputs` or `buttons` with the `alert` interface.
|
||||
@ -322,9 +323,9 @@ export class Select implements ComponentInterface {
|
||||
|
||||
await overlay.present();
|
||||
|
||||
// focus selected option for popovers
|
||||
if (this.interface === 'popover') {
|
||||
const indexOfSelected = this.childOpts.map((o) => o.value).indexOf(this.value);
|
||||
// focus selected option for popovers and modals
|
||||
if (this.interface === 'popover' || this.interface === 'modal') {
|
||||
const indexOfSelected = this.childOpts.findIndex((o) => o.value === this.value);
|
||||
|
||||
if (indexOfSelected > -1) {
|
||||
const selectedItem = overlay.querySelector<HTMLElement>(
|
||||
@ -332,8 +333,6 @@ export class Select implements ComponentInterface {
|
||||
);
|
||||
|
||||
if (selectedItem) {
|
||||
focusVisibleElement(selectedItem);
|
||||
|
||||
/**
|
||||
* Browsers such as Firefox do not
|
||||
* correctly delegate focus when manually
|
||||
@ -345,10 +344,17 @@ export class Select implements ComponentInterface {
|
||||
* we only need to worry about those two components
|
||||
* when focusing.
|
||||
*/
|
||||
const interactiveEl = selectedItem.querySelector<HTMLElement>('ion-radio, ion-checkbox');
|
||||
const interactiveEl = selectedItem.querySelector<HTMLElement>('ion-radio, ion-checkbox') as
|
||||
| HTMLIonRadioElement
|
||||
| HTMLIonCheckboxElement
|
||||
| null;
|
||||
if (interactiveEl) {
|
||||
interactiveEl.focus();
|
||||
// Needs to be called before `focusVisibleElement` to prevent issue with focus event bubbling
|
||||
// and removing `ion-focused` style
|
||||
interactiveEl.setFocus();
|
||||
}
|
||||
|
||||
focusVisibleElement(selectedItem);
|
||||
}
|
||||
} else {
|
||||
/**
|
||||
@ -356,14 +362,18 @@ export class Select implements ComponentInterface {
|
||||
*/
|
||||
const firstEnabledOption = overlay.querySelector<HTMLElement>(
|
||||
'ion-radio:not(.radio-disabled), ion-checkbox:not(.checkbox-disabled)'
|
||||
);
|
||||
if (firstEnabledOption) {
|
||||
focusVisibleElement(firstEnabledOption.closest('ion-item')!);
|
||||
) as HTMLIonRadioElement | HTMLIonCheckboxElement | null;
|
||||
|
||||
if (firstEnabledOption) {
|
||||
/**
|
||||
* Focus the option for the same reason as we do above.
|
||||
*
|
||||
* Needs to be called before `focusVisibleElement` to prevent issue with focus event bubbling
|
||||
* and removing `ion-focused` style
|
||||
*/
|
||||
firstEnabledOption.focus();
|
||||
firstEnabledOption.setFocus();
|
||||
|
||||
focusVisibleElement(firstEnabledOption.closest('ion-item')!);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -393,6 +403,9 @@ export class Select implements ComponentInterface {
|
||||
if (selectInterface === 'popover') {
|
||||
return this.openPopover(ev!);
|
||||
}
|
||||
if (selectInterface === 'modal') {
|
||||
return this.openModal();
|
||||
}
|
||||
return this.openAlert();
|
||||
}
|
||||
|
||||
@ -410,7 +423,13 @@ export class Select implements ComponentInterface {
|
||||
case 'popover':
|
||||
const popover = overlay.querySelector('ion-select-popover');
|
||||
if (popover) {
|
||||
popover.options = this.createPopoverOptions(childOpts, value);
|
||||
popover.options = this.createOverlaySelectOptions(childOpts, value);
|
||||
}
|
||||
break;
|
||||
case 'modal':
|
||||
const modal = overlay.querySelector('ion-select-modal');
|
||||
if (modal) {
|
||||
modal.options = this.createOverlaySelectOptions(childOpts, value);
|
||||
}
|
||||
break;
|
||||
case 'alert':
|
||||
@ -479,7 +498,7 @@ export class Select implements ComponentInterface {
|
||||
return alertInputs;
|
||||
}
|
||||
|
||||
private createPopoverOptions(data: HTMLIonSelectOptionElement[], selectValue: any): SelectPopoverOption[] {
|
||||
private createOverlaySelectOptions(data: HTMLIonSelectOptionElement[], selectValue: any): SelectPopoverOption[] {
|
||||
const popoverOptions = data.map((option) => {
|
||||
const value = getOptionValue(option);
|
||||
|
||||
@ -557,7 +576,7 @@ export class Select implements ComponentInterface {
|
||||
message: interfaceOptions.message,
|
||||
multiple,
|
||||
value,
|
||||
options: this.createPopoverOptions(this.childOpts, value),
|
||||
options: this.createOverlaySelectOptions(this.childOpts, value),
|
||||
},
|
||||
};
|
||||
|
||||
@ -651,6 +670,40 @@ export class Select implements ComponentInterface {
|
||||
return alertController.create(alertOpts);
|
||||
}
|
||||
|
||||
private openModal() {
|
||||
const { multiple, value, interfaceOptions } = this;
|
||||
const theme = getIonTheme(this);
|
||||
|
||||
const modalOpts: ModalOptions = {
|
||||
...interfaceOptions,
|
||||
mode: theme,
|
||||
|
||||
cssClass: ['select-modal', interfaceOptions.cssClass],
|
||||
component: 'ion-select-modal',
|
||||
componentProps: {
|
||||
header: interfaceOptions.header,
|
||||
multiple,
|
||||
value,
|
||||
options: this.createOverlaySelectOptions(this.childOpts, value),
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* Workaround for Stencil to autodefine
|
||||
* ion-select-modal and ion-modal when
|
||||
* using Custom Elements build.
|
||||
*/
|
||||
// eslint-disable-next-line
|
||||
if (false) {
|
||||
// eslint-disable-next-line
|
||||
// @ts-ignore
|
||||
document.createElement('ion-select-modal');
|
||||
document.createElement('ion-modal');
|
||||
}
|
||||
|
||||
return modalController.create(modalOpts);
|
||||
}
|
||||
|
||||
/**
|
||||
* Close the select interface.
|
||||
*/
|
||||
|
Reference in New Issue
Block a user