@ionic/core 8.8.5-dev.11776871786.1e73ab78 → 8.8.5-dev.11777476461.1fd79771
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/components/ion-modal.js +1 -1
- package/components/ion-radio-group.js +1 -1
- package/components/ion-select-modal.js +1 -1
- package/components/ion-select-popover.js +1 -1
- package/components/ion-select.js +1 -1
- package/components/p-BlNv564p.js +4 -0
- package/components/p-D-cP12ZN.js +4 -0
- package/components/{p-BZfgPT2N.js → p-DUqnmRFi.js} +1 -1
- package/components/{p-Bk2zuNWT.js → p-DvOO1fxp.js} +1 -1
- package/dist/cjs/ion-modal.cjs.entry.js +10 -4
- package/dist/cjs/ion-radio_2.cjs.entry.js +13 -1
- package/dist/cjs/ion-select-modal.cjs.entry.js +18 -7
- package/dist/cjs/ion-select_3.cjs.entry.js +18 -7
- package/dist/collection/components/content/content.css +0 -6
- package/dist/collection/components/modal/modal.js +10 -4
- package/dist/collection/components/radio-group/radio-group.js +13 -1
- package/dist/collection/components/radio-group/test/fixtures.js +2 -2
- package/dist/collection/components/select-modal/select-modal.js +18 -7
- package/dist/collection/components/select-modal/test/fixtures.js +4 -0
- package/dist/collection/components/select-popover/select-popover.js +18 -7
- package/dist/collection/components/select-popover/test/fixtures.js +4 -0
- package/dist/docs.json +1 -6
- package/dist/esm/ion-modal.entry.js +10 -4
- package/dist/esm/ion-radio_2.entry.js +13 -1
- package/dist/esm/ion-select-modal.entry.js +18 -7
- package/dist/esm/ion-select_3.entry.js +18 -7
- package/dist/ionic/ionic.esm.js +1 -1
- package/dist/ionic/p-28a9e720.entry.js +4 -0
- package/dist/ionic/{p-4dd5e8e0.entry.js → p-8fda6a62.entry.js} +1 -1
- package/dist/ionic/{p-9eac4eb1.entry.js → p-aa812c4b.entry.js} +1 -1
- package/dist/ionic/{p-4eedd78a.entry.js → p-e0287f41.entry.js} +1 -1
- package/dist/types/components/modal/modal.d.ts +6 -0
- package/dist/types/components/radio-group/test/fixtures.d.ts +1 -1
- package/dist/types/components/select-modal/select-modal.d.ts +1 -0
- package/dist/types/components/select-modal/test/fixtures.d.ts +1 -0
- package/dist/types/components/select-popover/select-popover.d.ts +1 -0
- package/dist/types/components/select-popover/test/fixtures.d.ts +1 -0
- package/hydrate/index.js +59 -19
- package/hydrate/index.mjs +59 -19
- package/package.json +1 -1
- package/components/p-EK4xUz-q.js +0 -4
- package/components/p-MlJRD6E1.js +0 -4
- package/dist/ionic/p-e6c5f060.entry.js +0 -4
|
@@ -2890,6 +2890,12 @@ const Modal = class {
|
|
|
2890
2890
|
* when no footer is present, so ion-content's .inner-scroll includes
|
|
2891
2891
|
* safe-area-bottom in its scroll padding. This keeps the modal background
|
|
2892
2892
|
* edge-to-edge while ensuring content scrolls clear of the system nav bar.
|
|
2893
|
+
*
|
|
2894
|
+
* --ion-content-safe-area-padding-bottom is an internal CSS property used
|
|
2895
|
+
* only by this code path. It is not part of ion-content's public API and
|
|
2896
|
+
* should not be set by consumers. The default of 0px makes it a no-op
|
|
2897
|
+
* when unset, which is the expected state for ion-content used outside of
|
|
2898
|
+
* a fullscreen modal without a footer.
|
|
2893
2899
|
*/
|
|
2894
2900
|
applyFullscreenSafeAreaTo(contentEl, hasFooter) {
|
|
2895
2901
|
// Only apply for standard Ionic layouts (has ion-content but no
|
|
@@ -2954,20 +2960,20 @@ const Modal = class {
|
|
|
2954
2960
|
const isCardModal = presentingElement !== undefined && mode === 'ios';
|
|
2955
2961
|
const isHandleCycle = handleBehavior === 'cycle';
|
|
2956
2962
|
const isSheetModalWithHandle = isSheetModal && showHandle;
|
|
2957
|
-
return (index$3.h(index$3.Host, Object.assign({ key: '
|
|
2963
|
+
return (index$3.h(index$3.Host, Object.assign({ key: '4bf38aa67df9a3f977163bba5423960bbafd16de', "no-router": true,
|
|
2958
2964
|
// Allow the modal to be navigable when the handle is focusable
|
|
2959
2965
|
tabIndex: isHandleCycle && isSheetModalWithHandle ? 0 : -1 }, htmlAttributes, { style: {
|
|
2960
2966
|
zIndex: `${20000 + this.overlayIndex}`,
|
|
2961
|
-
}, class: Object.assign({ [mode]: true, ['modal-default']: !isCardModal && !isSheetModal, [`modal-card`]: isCardModal, [`modal-sheet`]: isSheetModal, [`modal-no-expand-scroll`]: isSheetModal && !expandToScroll, 'overlay-hidden': true, [overlays.FOCUS_TRAP_DISABLE_CLASS]: focusTrap === false }, theme.getClassMap(this.cssClass)), onIonBackdropTap: this.onBackdropTap, onIonModalDidPresent: this.onLifecycle, onIonModalWillPresent: this.onLifecycle, onIonModalWillDismiss: this.onLifecycle, onIonModalDidDismiss: this.onLifecycle, onFocus: this.onModalFocus }), index$3.h("ion-backdrop", { key: '
|
|
2967
|
+
}, class: Object.assign({ [mode]: true, ['modal-default']: !isCardModal && !isSheetModal, [`modal-card`]: isCardModal, [`modal-sheet`]: isSheetModal, [`modal-no-expand-scroll`]: isSheetModal && !expandToScroll, 'overlay-hidden': true, [overlays.FOCUS_TRAP_DISABLE_CLASS]: focusTrap === false }, theme.getClassMap(this.cssClass)), onIonBackdropTap: this.onBackdropTap, onIonModalDidPresent: this.onLifecycle, onIonModalWillPresent: this.onLifecycle, onIonModalWillDismiss: this.onLifecycle, onIonModalDidDismiss: this.onLifecycle, onFocus: this.onModalFocus }), index$3.h("ion-backdrop", { key: '866da40cc5fc8d3e36637098fb3066a5bc9f4e0f', ref: (el) => (this.backdropEl = el), visible: this.showBackdrop, tappable: this.backdropDismiss, part: "backdrop" }), mode === 'ios' && index$3.h("div", { key: '5a2a05514ea8592c8feb0465e504aa7c7af17963', class: "modal-shadow" }), index$3.h("div", Object.assign({ key: '4d327115306451f57d190b06ab8cbb6191a6f1d7',
|
|
2962
2968
|
/*
|
|
2963
2969
|
role and aria-modal must be used on the
|
|
2964
2970
|
same element. They must also be set inside the
|
|
2965
2971
|
shadow DOM otherwise ion-button will not be highlighted
|
|
2966
2972
|
when using VoiceOver: https://bugs.webkit.org/show_bug.cgi?id=247134
|
|
2967
2973
|
*/
|
|
2968
|
-
role: "dialog" }, inheritedAttributes, { "aria-modal": "true", class: "modal-wrapper ion-overlay-wrapper", part: "content", ref: (el) => (this.wrapperEl = el) }), showHandle && (index$3.h("button", { key: '
|
|
2974
|
+
role: "dialog" }, inheritedAttributes, { "aria-modal": "true", class: "modal-wrapper ion-overlay-wrapper", part: "content", ref: (el) => (this.wrapperEl = el) }), showHandle && (index$3.h("button", { key: 'd1882835cc049232c0d957e3ba1e79676a07d179', class: "modal-handle",
|
|
2969
2975
|
// Prevents the handle from receiving keyboard focus when it does not cycle
|
|
2970
|
-
tabIndex: !isHandleCycle ? -1 : 0, "aria-label": "Activate to adjust the size of the dialog overlaying the screen", onClick: isHandleCycle ? this.onHandleClick : undefined, part: "handle", ref: (el) => (this.dragHandleEl = el) })), index$3.h("slot", { key: '
|
|
2976
|
+
tabIndex: !isHandleCycle ? -1 : 0, "aria-label": "Activate to adjust the size of the dialog overlaying the screen", onClick: isHandleCycle ? this.onHandleClick : undefined, part: "handle", ref: (el) => (this.dragHandleEl = el) })), index$3.h("slot", { key: '81dc58b09cf7d7022b04cd170f53113604364d5e', onSlotchange: this.onSlotChange }))));
|
|
2971
2977
|
}
|
|
2972
2978
|
get el() { return index$3.getElement(this); }
|
|
2973
2979
|
static get watchers() { return {
|
|
@@ -375,6 +375,18 @@ const RadioGroup = class {
|
|
|
375
375
|
// to the bottom of the screen
|
|
376
376
|
ev.preventDefault();
|
|
377
377
|
}
|
|
378
|
+
// Inside a select interface, Enter commits the focused radio
|
|
379
|
+
// value (matching native <select>). The !ev.repeat guard stops
|
|
380
|
+
// a held Enter on the triggering ion-select from re-committing
|
|
381
|
+
// once focus lands in the opened popover/modal.
|
|
382
|
+
if (ev.key === 'Enter' && inSelectInterface && !ev.repeat) {
|
|
383
|
+
const previousValue = this.value;
|
|
384
|
+
this.value = current.value;
|
|
385
|
+
if (previousValue !== this.value) {
|
|
386
|
+
this.emitValueChange(ev);
|
|
387
|
+
}
|
|
388
|
+
ev.preventDefault();
|
|
389
|
+
}
|
|
378
390
|
}
|
|
379
391
|
}
|
|
380
392
|
/** @internal */
|
|
@@ -407,7 +419,7 @@ const RadioGroup = class {
|
|
|
407
419
|
const { label, labelId, el, name, value } = this;
|
|
408
420
|
const mode = ionicGlobal.getIonMode(this);
|
|
409
421
|
helpers.renderHiddenInput(true, el, name, value, false);
|
|
410
|
-
return (index.h(index.Host, { key: '
|
|
422
|
+
return (index.h(index.Host, { key: '377e4aa3a656cc84b742f9d7a7d4be65d20c69f5', role: "radiogroup", "aria-labelledby": label ? labelId : null, "aria-describedby": this.hintTextId, "aria-invalid": this.isInvalid ? 'true' : undefined, onClick: this.onClick, class: mode }, this.renderHintText(), index.h("slot", { key: 'c3187a2497773b4f15cea3b413b036502bcec8c0' })));
|
|
411
423
|
}
|
|
412
424
|
get el() { return index.getElement(this); }
|
|
413
425
|
static get watchers() { return {
|
|
@@ -22,6 +22,10 @@ const selectModalMdCss = () => `.sc-ion-select-modal-md-h{height:100%}ion-list.s
|
|
|
22
22
|
const SelectModal = class {
|
|
23
23
|
constructor(hostRef) {
|
|
24
24
|
index.registerInstance(this, hostRef);
|
|
25
|
+
// Tracks the option that received Enter-keydown so keyup only
|
|
26
|
+
// dismisses when the press started on the same option. Prevents
|
|
27
|
+
// Enter on the triggering ion-select from auto-dismissing.
|
|
28
|
+
this.pendingEnterTarget = null;
|
|
25
29
|
/**
|
|
26
30
|
* The text to display on the cancel button.
|
|
27
31
|
*/
|
|
@@ -71,15 +75,22 @@ const SelectModal = class {
|
|
|
71
75
|
return (index.h("ion-radio-group", { value: checked, onIonChange: (ev) => this.callOptionHandler(ev) }, this.options.map((option) => (index.h("ion-item", { lines: "none", class: Object.assign({
|
|
72
76
|
// TODO FW-4784
|
|
73
77
|
'item-radio-checked': option.value === checked
|
|
74
|
-
}, theme.getClassMap(option.cssClass)) }, index.h("ion-radio", { value: option.value, disabled: option.disabled, justify: "start", labelPlacement: "end", onClick: () => this.closeModal(),
|
|
78
|
+
}, theme.getClassMap(option.cssClass)) }, index.h("ion-radio", { value: option.value, disabled: option.disabled, justify: "start", labelPlacement: "end", onClick: () => this.closeModal(), onKeyDown: (ev) => {
|
|
79
|
+
if (ev.key === 'Enter' && !ev.repeat) {
|
|
80
|
+
this.pendingEnterTarget = ev.currentTarget;
|
|
81
|
+
}
|
|
82
|
+
}, onKeyUp: (ev) => {
|
|
75
83
|
if (ev.key === ' ') {
|
|
76
|
-
|
|
77
|
-
* Selecting a radio option with keyboard navigation,
|
|
78
|
-
* either through the Enter or Space keys, should
|
|
79
|
-
* dismiss the modal.
|
|
80
|
-
*/
|
|
84
|
+
// Space selects and dismisses in one press.
|
|
81
85
|
this.closeModal();
|
|
82
86
|
}
|
|
87
|
+
else if (ev.key === 'Enter') {
|
|
88
|
+
const shouldClose = this.pendingEnterTarget === ev.currentTarget;
|
|
89
|
+
this.pendingEnterTarget = null;
|
|
90
|
+
if (shouldClose) {
|
|
91
|
+
this.closeModal();
|
|
92
|
+
}
|
|
93
|
+
}
|
|
83
94
|
} }, option.text))))));
|
|
84
95
|
}
|
|
85
96
|
renderCheckboxOptions() {
|
|
@@ -94,7 +105,7 @@ const SelectModal = class {
|
|
|
94
105
|
} }, option.text))));
|
|
95
106
|
}
|
|
96
107
|
render() {
|
|
97
|
-
return (index.h(index.Host, { key: '
|
|
108
|
+
return (index.h(index.Host, { key: 'fda0bf6f93cd5ec9f3c64f88a52de849e0e140a2', class: ionicGlobal.getIonMode(this) }, index.h("ion-header", { key: '27c0b17175a53db9ff159feeeb96451a3f011dab' }, index.h("ion-toolbar", { key: '91a4155ebc317fbc9f1bb3e26a7e94754b953c9b' }, this.header !== undefined && index.h("ion-title", { key: 'f6dae8e4e381f322cc90efefd9bb6ef81d4d2f3e' }, this.header), index.h("ion-buttons", { key: 'e7760532fb2e7e7385ed6e62097d92d96ff20148', slot: "end" }, index.h("ion-button", { key: '4999b6fc46cba138186546dca67b7950855e6fb7', onClick: () => this.closeModal() }, this.cancelText)))), index.h("ion-content", { key: 'c73f80a4bc25b9061ea65cf11e5d811c1a4d8704' }, index.h("ion-list", { key: 'b21905d15b36ad5eb45845e768918d2763cf48b1' }, this.multiple === true ? this.renderCheckboxOptions() : this.renderRadioOptions()))));
|
|
98
109
|
}
|
|
99
110
|
get el() { return index.getElement(this); }
|
|
100
111
|
};
|
|
@@ -892,6 +892,10 @@ const selectPopoverMdCss = () => `.sc-ion-select-popover-md-h ion-list.sc-ion-se
|
|
|
892
892
|
const SelectPopover = class {
|
|
893
893
|
constructor(hostRef) {
|
|
894
894
|
index.registerInstance(this, hostRef);
|
|
895
|
+
// Tracks the option that received Enter-keydown so keyup only
|
|
896
|
+
// dismisses when the press started on the same option. Prevents
|
|
897
|
+
// Enter on the triggering ion-select from auto-dismissing.
|
|
898
|
+
this.pendingEnterTarget = null;
|
|
895
899
|
/**
|
|
896
900
|
* An array of options for the popover
|
|
897
901
|
*/
|
|
@@ -969,21 +973,28 @@ const SelectPopover = class {
|
|
|
969
973
|
return (index.h("ion-radio-group", { value: checked, onIonChange: (ev) => this.callOptionHandler(ev) }, options.map((option) => (index.h("ion-item", { class: Object.assign({
|
|
970
974
|
// TODO FW-4784
|
|
971
975
|
'item-radio-checked': option.value === checked
|
|
972
|
-
}, theme.getClassMap(option.cssClass)) }, index.h("ion-radio", { value: option.value, disabled: option.disabled, onClick: () => this.dismissParentPopover(),
|
|
976
|
+
}, theme.getClassMap(option.cssClass)) }, index.h("ion-radio", { value: option.value, disabled: option.disabled, onClick: () => this.dismissParentPopover(), onKeyDown: (ev) => {
|
|
977
|
+
if (ev.key === 'Enter' && !ev.repeat) {
|
|
978
|
+
this.pendingEnterTarget = ev.currentTarget;
|
|
979
|
+
}
|
|
980
|
+
}, onKeyUp: (ev) => {
|
|
973
981
|
if (ev.key === ' ') {
|
|
974
|
-
|
|
975
|
-
* Selecting a radio option with keyboard navigation,
|
|
976
|
-
* either through the Enter or Space keys, should
|
|
977
|
-
* dismiss the popover.
|
|
978
|
-
*/
|
|
982
|
+
// Space selects and dismisses in one press.
|
|
979
983
|
this.dismissParentPopover();
|
|
980
984
|
}
|
|
985
|
+
else if (ev.key === 'Enter') {
|
|
986
|
+
const shouldDismiss = this.pendingEnterTarget === ev.currentTarget;
|
|
987
|
+
this.pendingEnterTarget = null;
|
|
988
|
+
if (shouldDismiss) {
|
|
989
|
+
this.dismissParentPopover();
|
|
990
|
+
}
|
|
991
|
+
}
|
|
981
992
|
} }, option.text))))));
|
|
982
993
|
}
|
|
983
994
|
render() {
|
|
984
995
|
const { header, message, options, subHeader } = this;
|
|
985
996
|
const hasSubHeaderOrMessage = subHeader !== undefined || message !== undefined;
|
|
986
|
-
return (index.h(index.Host, { key: '
|
|
997
|
+
return (index.h(index.Host, { key: 'e7449a1ecfcdbf45a79f8e26a00253c4e146448a', class: ionicGlobal.getIonMode(this) }, index.h("ion-list", { key: '52abdfc8668c3429a0dcefef8ddedb6647fdd894' }, header !== undefined && index.h("ion-list-header", { key: '978e5c03728756feafcc60a0e10e6ec59bf2ae11' }, header), hasSubHeaderOrMessage && (index.h("ion-item", { key: 'e93c44e7f07a76def16e4b11f0fb4780d84ed402' }, index.h("ion-label", { key: 'bba1aac43b0bc7f4f00978dd8301985233f3725c', class: "ion-text-wrap" }, subHeader !== undefined && index.h("h3", { key: 'ad96f6017cf2cc5219540bded2c4f1ca3b532de2' }, subHeader), message !== undefined && index.h("p", { key: '3fd038921dc40c4d0c29734433984b279ccaeec3' }, message)))), this.renderOptions(options))));
|
|
987
998
|
}
|
|
988
999
|
get el() { return index.getElement(this); }
|
|
989
1000
|
};
|
|
@@ -106,12 +106,6 @@
|
|
|
106
106
|
background: var(--background);
|
|
107
107
|
}
|
|
108
108
|
|
|
109
|
-
/**
|
|
110
|
-
* --ion-content-safe-area-padding-bottom is an internal property set by
|
|
111
|
-
* modal.tsx for fullscreen modals without an ion-footer. This decouples
|
|
112
|
-
* safe-area-bottom scroll padding from --padding-bottom (which is a
|
|
113
|
-
* public property consumers may override).
|
|
114
|
-
*/
|
|
115
109
|
.inner-scroll {
|
|
116
110
|
left: 0px;
|
|
117
111
|
right: 0px;
|
|
@@ -1092,6 +1092,12 @@ export class Modal {
|
|
|
1092
1092
|
* when no footer is present, so ion-content's .inner-scroll includes
|
|
1093
1093
|
* safe-area-bottom in its scroll padding. This keeps the modal background
|
|
1094
1094
|
* edge-to-edge while ensuring content scrolls clear of the system nav bar.
|
|
1095
|
+
*
|
|
1096
|
+
* --ion-content-safe-area-padding-bottom is an internal CSS property used
|
|
1097
|
+
* only by this code path. It is not part of ion-content's public API and
|
|
1098
|
+
* should not be set by consumers. The default of 0px makes it a no-op
|
|
1099
|
+
* when unset, which is the expected state for ion-content used outside of
|
|
1100
|
+
* a fullscreen modal without a footer.
|
|
1095
1101
|
*/
|
|
1096
1102
|
applyFullscreenSafeAreaTo(contentEl, hasFooter) {
|
|
1097
1103
|
// Only apply for standard Ionic layouts (has ion-content but no
|
|
@@ -1156,20 +1162,20 @@ export class Modal {
|
|
|
1156
1162
|
const isCardModal = presentingElement !== undefined && mode === 'ios';
|
|
1157
1163
|
const isHandleCycle = handleBehavior === 'cycle';
|
|
1158
1164
|
const isSheetModalWithHandle = isSheetModal && showHandle;
|
|
1159
|
-
return (h(Host, Object.assign({ key: '
|
|
1165
|
+
return (h(Host, Object.assign({ key: '4bf38aa67df9a3f977163bba5423960bbafd16de', "no-router": true,
|
|
1160
1166
|
// Allow the modal to be navigable when the handle is focusable
|
|
1161
1167
|
tabIndex: isHandleCycle && isSheetModalWithHandle ? 0 : -1 }, htmlAttributes, { style: {
|
|
1162
1168
|
zIndex: `${20000 + this.overlayIndex}`,
|
|
1163
|
-
}, class: Object.assign({ [mode]: true, ['modal-default']: !isCardModal && !isSheetModal, [`modal-card`]: isCardModal, [`modal-sheet`]: isSheetModal, [`modal-no-expand-scroll`]: isSheetModal && !expandToScroll, 'overlay-hidden': true, [FOCUS_TRAP_DISABLE_CLASS]: focusTrap === false }, getClassMap(this.cssClass)), onIonBackdropTap: this.onBackdropTap, onIonModalDidPresent: this.onLifecycle, onIonModalWillPresent: this.onLifecycle, onIonModalWillDismiss: this.onLifecycle, onIonModalDidDismiss: this.onLifecycle, onFocus: this.onModalFocus }), h("ion-backdrop", { key: '
|
|
1169
|
+
}, class: Object.assign({ [mode]: true, ['modal-default']: !isCardModal && !isSheetModal, [`modal-card`]: isCardModal, [`modal-sheet`]: isSheetModal, [`modal-no-expand-scroll`]: isSheetModal && !expandToScroll, 'overlay-hidden': true, [FOCUS_TRAP_DISABLE_CLASS]: focusTrap === false }, getClassMap(this.cssClass)), onIonBackdropTap: this.onBackdropTap, onIonModalDidPresent: this.onLifecycle, onIonModalWillPresent: this.onLifecycle, onIonModalWillDismiss: this.onLifecycle, onIonModalDidDismiss: this.onLifecycle, onFocus: this.onModalFocus }), h("ion-backdrop", { key: '866da40cc5fc8d3e36637098fb3066a5bc9f4e0f', ref: (el) => (this.backdropEl = el), visible: this.showBackdrop, tappable: this.backdropDismiss, part: "backdrop" }), mode === 'ios' && h("div", { key: '5a2a05514ea8592c8feb0465e504aa7c7af17963', class: "modal-shadow" }), h("div", Object.assign({ key: '4d327115306451f57d190b06ab8cbb6191a6f1d7',
|
|
1164
1170
|
/*
|
|
1165
1171
|
role and aria-modal must be used on the
|
|
1166
1172
|
same element. They must also be set inside the
|
|
1167
1173
|
shadow DOM otherwise ion-button will not be highlighted
|
|
1168
1174
|
when using VoiceOver: https://bugs.webkit.org/show_bug.cgi?id=247134
|
|
1169
1175
|
*/
|
|
1170
|
-
role: "dialog" }, inheritedAttributes, { "aria-modal": "true", class: "modal-wrapper ion-overlay-wrapper", part: "content", ref: (el) => (this.wrapperEl = el) }), showHandle && (h("button", { key: '
|
|
1176
|
+
role: "dialog" }, inheritedAttributes, { "aria-modal": "true", class: "modal-wrapper ion-overlay-wrapper", part: "content", ref: (el) => (this.wrapperEl = el) }), showHandle && (h("button", { key: 'd1882835cc049232c0d957e3ba1e79676a07d179', class: "modal-handle",
|
|
1171
1177
|
// Prevents the handle from receiving keyboard focus when it does not cycle
|
|
1172
|
-
tabIndex: !isHandleCycle ? -1 : 0, "aria-label": "Activate to adjust the size of the dialog overlaying the screen", onClick: isHandleCycle ? this.onHandleClick : undefined, part: "handle", ref: (el) => (this.dragHandleEl = el) })), h("slot", { key: '
|
|
1178
|
+
tabIndex: !isHandleCycle ? -1 : 0, "aria-label": "Activate to adjust the size of the dialog overlaying the screen", onClick: isHandleCycle ? this.onHandleClick : undefined, part: "handle", ref: (el) => (this.dragHandleEl = el) })), h("slot", { key: '81dc58b09cf7d7022b04cd170f53113604364d5e', onSlotchange: this.onSlotChange }))));
|
|
1173
1179
|
}
|
|
1174
1180
|
static get is() { return "ion-modal"; }
|
|
1175
1181
|
static get encapsulation() { return "shadow"; }
|
|
@@ -206,6 +206,18 @@ export class RadioGroup {
|
|
|
206
206
|
// to the bottom of the screen
|
|
207
207
|
ev.preventDefault();
|
|
208
208
|
}
|
|
209
|
+
// Inside a select interface, Enter commits the focused radio
|
|
210
|
+
// value (matching native <select>). The !ev.repeat guard stops
|
|
211
|
+
// a held Enter on the triggering ion-select from re-committing
|
|
212
|
+
// once focus lands in the opened popover/modal.
|
|
213
|
+
if (ev.key === 'Enter' && inSelectInterface && !ev.repeat) {
|
|
214
|
+
const previousValue = this.value;
|
|
215
|
+
this.value = current.value;
|
|
216
|
+
if (previousValue !== this.value) {
|
|
217
|
+
this.emitValueChange(ev);
|
|
218
|
+
}
|
|
219
|
+
ev.preventDefault();
|
|
220
|
+
}
|
|
209
221
|
}
|
|
210
222
|
}
|
|
211
223
|
/** @internal */
|
|
@@ -238,7 +250,7 @@ export class RadioGroup {
|
|
|
238
250
|
const { label, labelId, el, name, value } = this;
|
|
239
251
|
const mode = getIonMode(this);
|
|
240
252
|
renderHiddenInput(true, el, name, value, false);
|
|
241
|
-
return (h(Host, { key: '
|
|
253
|
+
return (h(Host, { key: '377e4aa3a656cc84b742f9d7a7d4be65d20c69f5', role: "radiogroup", "aria-labelledby": label ? labelId : null, "aria-describedby": this.hintTextId, "aria-invalid": this.isInvalid ? 'true' : undefined, onClick: this.onClick, class: mode }, this.renderHintText(), h("slot", { key: 'c3187a2497773b4f15cea3b413b036502bcec8c0' })));
|
|
242
254
|
}
|
|
243
255
|
static get is() { return "ion-radio-group"; }
|
|
244
256
|
static get originalStyleUrls() {
|
|
@@ -6,12 +6,12 @@ export class RadioFixture {
|
|
|
6
6
|
constructor(page) {
|
|
7
7
|
this.page = page;
|
|
8
8
|
}
|
|
9
|
-
async checkRadio(method, selector = 'ion-radio') {
|
|
9
|
+
async checkRadio(method, selector = 'ion-radio', key = 'Space') {
|
|
10
10
|
const { page } = this;
|
|
11
11
|
const radio = (this.radio = page.locator(selector));
|
|
12
12
|
if (method === 'keyboard') {
|
|
13
13
|
await radio.focus();
|
|
14
|
-
await page.keyboard.press(
|
|
14
|
+
await page.keyboard.press(key);
|
|
15
15
|
}
|
|
16
16
|
else {
|
|
17
17
|
await radio.click();
|
|
@@ -7,6 +7,10 @@ import { safeCall } from "../../utils/overlays";
|
|
|
7
7
|
import { getClassMap } from "../../utils/theme";
|
|
8
8
|
export class SelectModal {
|
|
9
9
|
constructor() {
|
|
10
|
+
// Tracks the option that received Enter-keydown so keyup only
|
|
11
|
+
// dismisses when the press started on the same option. Prevents
|
|
12
|
+
// Enter on the triggering ion-select from auto-dismissing.
|
|
13
|
+
this.pendingEnterTarget = null;
|
|
10
14
|
/**
|
|
11
15
|
* The text to display on the cancel button.
|
|
12
16
|
*/
|
|
@@ -56,15 +60,22 @@ export class SelectModal {
|
|
|
56
60
|
return (h("ion-radio-group", { value: checked, onIonChange: (ev) => this.callOptionHandler(ev) }, this.options.map((option) => (h("ion-item", { lines: "none", class: Object.assign({
|
|
57
61
|
// TODO FW-4784
|
|
58
62
|
'item-radio-checked': option.value === checked
|
|
59
|
-
}, getClassMap(option.cssClass)) }, h("ion-radio", { value: option.value, disabled: option.disabled, justify: "start", labelPlacement: "end", onClick: () => this.closeModal(),
|
|
63
|
+
}, getClassMap(option.cssClass)) }, h("ion-radio", { value: option.value, disabled: option.disabled, justify: "start", labelPlacement: "end", onClick: () => this.closeModal(), onKeyDown: (ev) => {
|
|
64
|
+
if (ev.key === 'Enter' && !ev.repeat) {
|
|
65
|
+
this.pendingEnterTarget = ev.currentTarget;
|
|
66
|
+
}
|
|
67
|
+
}, onKeyUp: (ev) => {
|
|
60
68
|
if (ev.key === ' ') {
|
|
61
|
-
|
|
62
|
-
* Selecting a radio option with keyboard navigation,
|
|
63
|
-
* either through the Enter or Space keys, should
|
|
64
|
-
* dismiss the modal.
|
|
65
|
-
*/
|
|
69
|
+
// Space selects and dismisses in one press.
|
|
66
70
|
this.closeModal();
|
|
67
71
|
}
|
|
72
|
+
else if (ev.key === 'Enter') {
|
|
73
|
+
const shouldClose = this.pendingEnterTarget === ev.currentTarget;
|
|
74
|
+
this.pendingEnterTarget = null;
|
|
75
|
+
if (shouldClose) {
|
|
76
|
+
this.closeModal();
|
|
77
|
+
}
|
|
78
|
+
}
|
|
68
79
|
} }, option.text))))));
|
|
69
80
|
}
|
|
70
81
|
renderCheckboxOptions() {
|
|
@@ -79,7 +90,7 @@ export class SelectModal {
|
|
|
79
90
|
} }, option.text))));
|
|
80
91
|
}
|
|
81
92
|
render() {
|
|
82
|
-
return (h(Host, { key: '
|
|
93
|
+
return (h(Host, { key: 'fda0bf6f93cd5ec9f3c64f88a52de849e0e140a2', class: getIonMode(this) }, h("ion-header", { key: '27c0b17175a53db9ff159feeeb96451a3f011dab' }, h("ion-toolbar", { key: '91a4155ebc317fbc9f1bb3e26a7e94754b953c9b' }, this.header !== undefined && h("ion-title", { key: 'f6dae8e4e381f322cc90efefd9bb6ef81d4d2f3e' }, this.header), h("ion-buttons", { key: 'e7760532fb2e7e7385ed6e62097d92d96ff20148', slot: "end" }, h("ion-button", { key: '4999b6fc46cba138186546dca67b7950855e6fb7', onClick: () => this.closeModal() }, this.cancelText)))), h("ion-content", { key: 'c73f80a4bc25b9061ea65cf11e5d811c1a4d8704' }, h("ion-list", { key: 'b21905d15b36ad5eb45845e768918d2763cf48b1' }, this.multiple === true ? this.renderCheckboxOptions() : this.renderRadioOptions()))));
|
|
83
94
|
}
|
|
84
95
|
static get is() { return "ion-select-modal"; }
|
|
85
96
|
static get encapsulation() { return "scoped"; }
|
|
@@ -39,6 +39,10 @@ export class SelectModalPage {
|
|
|
39
39
|
const option = this.getOption(value);
|
|
40
40
|
await option.press('Space');
|
|
41
41
|
}
|
|
42
|
+
async pressEnterOnOption(value) {
|
|
43
|
+
const option = this.getOption(value);
|
|
44
|
+
await option.press('Enter');
|
|
45
|
+
}
|
|
42
46
|
getOption(value) {
|
|
43
47
|
const { multiple, selectModal } = this;
|
|
44
48
|
const selector = multiple ? 'ion-checkbox' : 'ion-radio';
|
|
@@ -10,6 +10,10 @@ import { getIonMode } from "../../global/ionic-global";
|
|
|
10
10
|
*/
|
|
11
11
|
export class SelectPopover {
|
|
12
12
|
constructor() {
|
|
13
|
+
// Tracks the option that received Enter-keydown so keyup only
|
|
14
|
+
// dismisses when the press started on the same option. Prevents
|
|
15
|
+
// Enter on the triggering ion-select from auto-dismissing.
|
|
16
|
+
this.pendingEnterTarget = null;
|
|
13
17
|
/**
|
|
14
18
|
* An array of options for the popover
|
|
15
19
|
*/
|
|
@@ -87,21 +91,28 @@ export class SelectPopover {
|
|
|
87
91
|
return (h("ion-radio-group", { value: checked, onIonChange: (ev) => this.callOptionHandler(ev) }, options.map((option) => (h("ion-item", { class: Object.assign({
|
|
88
92
|
// TODO FW-4784
|
|
89
93
|
'item-radio-checked': option.value === checked
|
|
90
|
-
}, getClassMap(option.cssClass)) }, h("ion-radio", { value: option.value, disabled: option.disabled, onClick: () => this.dismissParentPopover(),
|
|
94
|
+
}, getClassMap(option.cssClass)) }, h("ion-radio", { value: option.value, disabled: option.disabled, onClick: () => this.dismissParentPopover(), onKeyDown: (ev) => {
|
|
95
|
+
if (ev.key === 'Enter' && !ev.repeat) {
|
|
96
|
+
this.pendingEnterTarget = ev.currentTarget;
|
|
97
|
+
}
|
|
98
|
+
}, onKeyUp: (ev) => {
|
|
91
99
|
if (ev.key === ' ') {
|
|
92
|
-
|
|
93
|
-
* Selecting a radio option with keyboard navigation,
|
|
94
|
-
* either through the Enter or Space keys, should
|
|
95
|
-
* dismiss the popover.
|
|
96
|
-
*/
|
|
100
|
+
// Space selects and dismisses in one press.
|
|
97
101
|
this.dismissParentPopover();
|
|
98
102
|
}
|
|
103
|
+
else if (ev.key === 'Enter') {
|
|
104
|
+
const shouldDismiss = this.pendingEnterTarget === ev.currentTarget;
|
|
105
|
+
this.pendingEnterTarget = null;
|
|
106
|
+
if (shouldDismiss) {
|
|
107
|
+
this.dismissParentPopover();
|
|
108
|
+
}
|
|
109
|
+
}
|
|
99
110
|
} }, option.text))))));
|
|
100
111
|
}
|
|
101
112
|
render() {
|
|
102
113
|
const { header, message, options, subHeader } = this;
|
|
103
114
|
const hasSubHeaderOrMessage = subHeader !== undefined || message !== undefined;
|
|
104
|
-
return (h(Host, { key: '
|
|
115
|
+
return (h(Host, { key: 'e7449a1ecfcdbf45a79f8e26a00253c4e146448a', class: getIonMode(this) }, h("ion-list", { key: '52abdfc8668c3429a0dcefef8ddedb6647fdd894' }, header !== undefined && h("ion-list-header", { key: '978e5c03728756feafcc60a0e10e6ec59bf2ae11' }, header), hasSubHeaderOrMessage && (h("ion-item", { key: 'e93c44e7f07a76def16e4b11f0fb4780d84ed402' }, h("ion-label", { key: 'bba1aac43b0bc7f4f00978dd8301985233f3725c', class: "ion-text-wrap" }, subHeader !== undefined && h("h3", { key: 'ad96f6017cf2cc5219540bded2c4f1ca3b532de2' }, subHeader), message !== undefined && h("p", { key: '3fd038921dc40c4d0c29734433984b279ccaeec3' }, message)))), this.renderOptions(options))));
|
|
105
116
|
}
|
|
106
117
|
static get is() { return "ion-select-popover"; }
|
|
107
118
|
static get encapsulation() { return "scoped"; }
|
|
@@ -39,6 +39,10 @@ export class SelectPopoverPage {
|
|
|
39
39
|
const option = this.getOption(value);
|
|
40
40
|
await option.press('Space');
|
|
41
41
|
}
|
|
42
|
+
async pressEnterOnOption(value) {
|
|
43
|
+
const option = this.getOption(value);
|
|
44
|
+
await option.press('Enter');
|
|
45
|
+
}
|
|
42
46
|
getOption(value) {
|
|
43
47
|
const { multiple, selectPopover } = this;
|
|
44
48
|
const selector = multiple ? 'ion-checkbox' : 'ion-radio';
|
package/dist/docs.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"timestamp": "2026-04-
|
|
2
|
+
"timestamp": "2026-04-29T15:29:39",
|
|
3
3
|
"compiler": {
|
|
4
4
|
"name": "@stencil/core",
|
|
5
5
|
"version": "4.43.0",
|
|
@@ -8379,11 +8379,6 @@
|
|
|
8379
8379
|
"annotation": "prop",
|
|
8380
8380
|
"docs": "Color of the content"
|
|
8381
8381
|
},
|
|
8382
|
-
{
|
|
8383
|
-
"name": "--ion-content-safe-area-padding-bottom is an internal property set by modal.tsx for fullscreen modals without an ion-footer. This decouples safe-area-bottom scroll padding from --padding-bottom (which is a public property consumers may override).",
|
|
8384
|
-
"annotation": "prop",
|
|
8385
|
-
"docs": ""
|
|
8386
|
-
},
|
|
8387
8382
|
{
|
|
8388
8383
|
"name": "--keyboard-offset",
|
|
8389
8384
|
"annotation": "prop",
|
|
@@ -2888,6 +2888,12 @@ const Modal = class {
|
|
|
2888
2888
|
* when no footer is present, so ion-content's .inner-scroll includes
|
|
2889
2889
|
* safe-area-bottom in its scroll padding. This keeps the modal background
|
|
2890
2890
|
* edge-to-edge while ensuring content scrolls clear of the system nav bar.
|
|
2891
|
+
*
|
|
2892
|
+
* --ion-content-safe-area-padding-bottom is an internal CSS property used
|
|
2893
|
+
* only by this code path. It is not part of ion-content's public API and
|
|
2894
|
+
* should not be set by consumers. The default of 0px makes it a no-op
|
|
2895
|
+
* when unset, which is the expected state for ion-content used outside of
|
|
2896
|
+
* a fullscreen modal without a footer.
|
|
2891
2897
|
*/
|
|
2892
2898
|
applyFullscreenSafeAreaTo(contentEl, hasFooter) {
|
|
2893
2899
|
// Only apply for standard Ionic layouts (has ion-content but no
|
|
@@ -2952,20 +2958,20 @@ const Modal = class {
|
|
|
2952
2958
|
const isCardModal = presentingElement !== undefined && mode === 'ios';
|
|
2953
2959
|
const isHandleCycle = handleBehavior === 'cycle';
|
|
2954
2960
|
const isSheetModalWithHandle = isSheetModal && showHandle;
|
|
2955
|
-
return (h(Host, Object.assign({ key: '
|
|
2961
|
+
return (h(Host, Object.assign({ key: '4bf38aa67df9a3f977163bba5423960bbafd16de', "no-router": true,
|
|
2956
2962
|
// Allow the modal to be navigable when the handle is focusable
|
|
2957
2963
|
tabIndex: isHandleCycle && isSheetModalWithHandle ? 0 : -1 }, htmlAttributes, { style: {
|
|
2958
2964
|
zIndex: `${20000 + this.overlayIndex}`,
|
|
2959
|
-
}, class: Object.assign({ [mode]: true, ['modal-default']: !isCardModal && !isSheetModal, [`modal-card`]: isCardModal, [`modal-sheet`]: isSheetModal, [`modal-no-expand-scroll`]: isSheetModal && !expandToScroll, 'overlay-hidden': true, [FOCUS_TRAP_DISABLE_CLASS]: focusTrap === false }, getClassMap(this.cssClass)), onIonBackdropTap: this.onBackdropTap, onIonModalDidPresent: this.onLifecycle, onIonModalWillPresent: this.onLifecycle, onIonModalWillDismiss: this.onLifecycle, onIonModalDidDismiss: this.onLifecycle, onFocus: this.onModalFocus }), h("ion-backdrop", { key: '
|
|
2965
|
+
}, class: Object.assign({ [mode]: true, ['modal-default']: !isCardModal && !isSheetModal, [`modal-card`]: isCardModal, [`modal-sheet`]: isSheetModal, [`modal-no-expand-scroll`]: isSheetModal && !expandToScroll, 'overlay-hidden': true, [FOCUS_TRAP_DISABLE_CLASS]: focusTrap === false }, getClassMap(this.cssClass)), onIonBackdropTap: this.onBackdropTap, onIonModalDidPresent: this.onLifecycle, onIonModalWillPresent: this.onLifecycle, onIonModalWillDismiss: this.onLifecycle, onIonModalDidDismiss: this.onLifecycle, onFocus: this.onModalFocus }), h("ion-backdrop", { key: '866da40cc5fc8d3e36637098fb3066a5bc9f4e0f', ref: (el) => (this.backdropEl = el), visible: this.showBackdrop, tappable: this.backdropDismiss, part: "backdrop" }), mode === 'ios' && h("div", { key: '5a2a05514ea8592c8feb0465e504aa7c7af17963', class: "modal-shadow" }), h("div", Object.assign({ key: '4d327115306451f57d190b06ab8cbb6191a6f1d7',
|
|
2960
2966
|
/*
|
|
2961
2967
|
role and aria-modal must be used on the
|
|
2962
2968
|
same element. They must also be set inside the
|
|
2963
2969
|
shadow DOM otherwise ion-button will not be highlighted
|
|
2964
2970
|
when using VoiceOver: https://bugs.webkit.org/show_bug.cgi?id=247134
|
|
2965
2971
|
*/
|
|
2966
|
-
role: "dialog" }, inheritedAttributes, { "aria-modal": "true", class: "modal-wrapper ion-overlay-wrapper", part: "content", ref: (el) => (this.wrapperEl = el) }), showHandle && (h("button", { key: '
|
|
2972
|
+
role: "dialog" }, inheritedAttributes, { "aria-modal": "true", class: "modal-wrapper ion-overlay-wrapper", part: "content", ref: (el) => (this.wrapperEl = el) }), showHandle && (h("button", { key: 'd1882835cc049232c0d957e3ba1e79676a07d179', class: "modal-handle",
|
|
2967
2973
|
// Prevents the handle from receiving keyboard focus when it does not cycle
|
|
2968
|
-
tabIndex: !isHandleCycle ? -1 : 0, "aria-label": "Activate to adjust the size of the dialog overlaying the screen", onClick: isHandleCycle ? this.onHandleClick : undefined, part: "handle", ref: (el) => (this.dragHandleEl = el) })), h("slot", { key: '
|
|
2974
|
+
tabIndex: !isHandleCycle ? -1 : 0, "aria-label": "Activate to adjust the size of the dialog overlaying the screen", onClick: isHandleCycle ? this.onHandleClick : undefined, part: "handle", ref: (el) => (this.dragHandleEl = el) })), h("slot", { key: '81dc58b09cf7d7022b04cd170f53113604364d5e', onSlotchange: this.onSlotChange }))));
|
|
2969
2975
|
}
|
|
2970
2976
|
get el() { return getElement(this); }
|
|
2971
2977
|
static get watchers() { return {
|
|
@@ -373,6 +373,18 @@ const RadioGroup = class {
|
|
|
373
373
|
// to the bottom of the screen
|
|
374
374
|
ev.preventDefault();
|
|
375
375
|
}
|
|
376
|
+
// Inside a select interface, Enter commits the focused radio
|
|
377
|
+
// value (matching native <select>). The !ev.repeat guard stops
|
|
378
|
+
// a held Enter on the triggering ion-select from re-committing
|
|
379
|
+
// once focus lands in the opened popover/modal.
|
|
380
|
+
if (ev.key === 'Enter' && inSelectInterface && !ev.repeat) {
|
|
381
|
+
const previousValue = this.value;
|
|
382
|
+
this.value = current.value;
|
|
383
|
+
if (previousValue !== this.value) {
|
|
384
|
+
this.emitValueChange(ev);
|
|
385
|
+
}
|
|
386
|
+
ev.preventDefault();
|
|
387
|
+
}
|
|
376
388
|
}
|
|
377
389
|
}
|
|
378
390
|
/** @internal */
|
|
@@ -405,7 +417,7 @@ const RadioGroup = class {
|
|
|
405
417
|
const { label, labelId, el, name, value } = this;
|
|
406
418
|
const mode = getIonMode(this);
|
|
407
419
|
renderHiddenInput(true, el, name, value, false);
|
|
408
|
-
return (h(Host, { key: '
|
|
420
|
+
return (h(Host, { key: '377e4aa3a656cc84b742f9d7a7d4be65d20c69f5', role: "radiogroup", "aria-labelledby": label ? labelId : null, "aria-describedby": this.hintTextId, "aria-invalid": this.isInvalid ? 'true' : undefined, onClick: this.onClick, class: mode }, this.renderHintText(), h("slot", { key: 'c3187a2497773b4f15cea3b413b036502bcec8c0' })));
|
|
409
421
|
}
|
|
410
422
|
get el() { return getElement(this); }
|
|
411
423
|
static get watchers() { return {
|
|
@@ -20,6 +20,10 @@ const selectModalMdCss = () => `.sc-ion-select-modal-md-h{height:100%}ion-list.s
|
|
|
20
20
|
const SelectModal = class {
|
|
21
21
|
constructor(hostRef) {
|
|
22
22
|
registerInstance(this, hostRef);
|
|
23
|
+
// Tracks the option that received Enter-keydown so keyup only
|
|
24
|
+
// dismisses when the press started on the same option. Prevents
|
|
25
|
+
// Enter on the triggering ion-select from auto-dismissing.
|
|
26
|
+
this.pendingEnterTarget = null;
|
|
23
27
|
/**
|
|
24
28
|
* The text to display on the cancel button.
|
|
25
29
|
*/
|
|
@@ -69,15 +73,22 @@ const SelectModal = class {
|
|
|
69
73
|
return (h("ion-radio-group", { value: checked, onIonChange: (ev) => this.callOptionHandler(ev) }, this.options.map((option) => (h("ion-item", { lines: "none", class: Object.assign({
|
|
70
74
|
// TODO FW-4784
|
|
71
75
|
'item-radio-checked': option.value === checked
|
|
72
|
-
}, getClassMap(option.cssClass)) }, h("ion-radio", { value: option.value, disabled: option.disabled, justify: "start", labelPlacement: "end", onClick: () => this.closeModal(),
|
|
76
|
+
}, getClassMap(option.cssClass)) }, h("ion-radio", { value: option.value, disabled: option.disabled, justify: "start", labelPlacement: "end", onClick: () => this.closeModal(), onKeyDown: (ev) => {
|
|
77
|
+
if (ev.key === 'Enter' && !ev.repeat) {
|
|
78
|
+
this.pendingEnterTarget = ev.currentTarget;
|
|
79
|
+
}
|
|
80
|
+
}, onKeyUp: (ev) => {
|
|
73
81
|
if (ev.key === ' ') {
|
|
74
|
-
|
|
75
|
-
* Selecting a radio option with keyboard navigation,
|
|
76
|
-
* either through the Enter or Space keys, should
|
|
77
|
-
* dismiss the modal.
|
|
78
|
-
*/
|
|
82
|
+
// Space selects and dismisses in one press.
|
|
79
83
|
this.closeModal();
|
|
80
84
|
}
|
|
85
|
+
else if (ev.key === 'Enter') {
|
|
86
|
+
const shouldClose = this.pendingEnterTarget === ev.currentTarget;
|
|
87
|
+
this.pendingEnterTarget = null;
|
|
88
|
+
if (shouldClose) {
|
|
89
|
+
this.closeModal();
|
|
90
|
+
}
|
|
91
|
+
}
|
|
81
92
|
} }, option.text))))));
|
|
82
93
|
}
|
|
83
94
|
renderCheckboxOptions() {
|
|
@@ -92,7 +103,7 @@ const SelectModal = class {
|
|
|
92
103
|
} }, option.text))));
|
|
93
104
|
}
|
|
94
105
|
render() {
|
|
95
|
-
return (h(Host, { key: '
|
|
106
|
+
return (h(Host, { key: 'fda0bf6f93cd5ec9f3c64f88a52de849e0e140a2', class: getIonMode(this) }, h("ion-header", { key: '27c0b17175a53db9ff159feeeb96451a3f011dab' }, h("ion-toolbar", { key: '91a4155ebc317fbc9f1bb3e26a7e94754b953c9b' }, this.header !== undefined && h("ion-title", { key: 'f6dae8e4e381f322cc90efefd9bb6ef81d4d2f3e' }, this.header), h("ion-buttons", { key: 'e7760532fb2e7e7385ed6e62097d92d96ff20148', slot: "end" }, h("ion-button", { key: '4999b6fc46cba138186546dca67b7950855e6fb7', onClick: () => this.closeModal() }, this.cancelText)))), h("ion-content", { key: 'c73f80a4bc25b9061ea65cf11e5d811c1a4d8704' }, h("ion-list", { key: 'b21905d15b36ad5eb45845e768918d2763cf48b1' }, this.multiple === true ? this.renderCheckboxOptions() : this.renderRadioOptions()))));
|
|
96
107
|
}
|
|
97
108
|
get el() { return getElement(this); }
|
|
98
109
|
};
|
|
@@ -890,6 +890,10 @@ const selectPopoverMdCss = () => `.sc-ion-select-popover-md-h ion-list.sc-ion-se
|
|
|
890
890
|
const SelectPopover = class {
|
|
891
891
|
constructor(hostRef) {
|
|
892
892
|
registerInstance(this, hostRef);
|
|
893
|
+
// Tracks the option that received Enter-keydown so keyup only
|
|
894
|
+
// dismisses when the press started on the same option. Prevents
|
|
895
|
+
// Enter on the triggering ion-select from auto-dismissing.
|
|
896
|
+
this.pendingEnterTarget = null;
|
|
893
897
|
/**
|
|
894
898
|
* An array of options for the popover
|
|
895
899
|
*/
|
|
@@ -967,21 +971,28 @@ const SelectPopover = class {
|
|
|
967
971
|
return (h("ion-radio-group", { value: checked, onIonChange: (ev) => this.callOptionHandler(ev) }, options.map((option) => (h("ion-item", { class: Object.assign({
|
|
968
972
|
// TODO FW-4784
|
|
969
973
|
'item-radio-checked': option.value === checked
|
|
970
|
-
}, getClassMap(option.cssClass)) }, h("ion-radio", { value: option.value, disabled: option.disabled, onClick: () => this.dismissParentPopover(),
|
|
974
|
+
}, getClassMap(option.cssClass)) }, h("ion-radio", { value: option.value, disabled: option.disabled, onClick: () => this.dismissParentPopover(), onKeyDown: (ev) => {
|
|
975
|
+
if (ev.key === 'Enter' && !ev.repeat) {
|
|
976
|
+
this.pendingEnterTarget = ev.currentTarget;
|
|
977
|
+
}
|
|
978
|
+
}, onKeyUp: (ev) => {
|
|
971
979
|
if (ev.key === ' ') {
|
|
972
|
-
|
|
973
|
-
* Selecting a radio option with keyboard navigation,
|
|
974
|
-
* either through the Enter or Space keys, should
|
|
975
|
-
* dismiss the popover.
|
|
976
|
-
*/
|
|
980
|
+
// Space selects and dismisses in one press.
|
|
977
981
|
this.dismissParentPopover();
|
|
978
982
|
}
|
|
983
|
+
else if (ev.key === 'Enter') {
|
|
984
|
+
const shouldDismiss = this.pendingEnterTarget === ev.currentTarget;
|
|
985
|
+
this.pendingEnterTarget = null;
|
|
986
|
+
if (shouldDismiss) {
|
|
987
|
+
this.dismissParentPopover();
|
|
988
|
+
}
|
|
989
|
+
}
|
|
979
990
|
} }, option.text))))));
|
|
980
991
|
}
|
|
981
992
|
render() {
|
|
982
993
|
const { header, message, options, subHeader } = this;
|
|
983
994
|
const hasSubHeaderOrMessage = subHeader !== undefined || message !== undefined;
|
|
984
|
-
return (h(Host, { key: '
|
|
995
|
+
return (h(Host, { key: 'e7449a1ecfcdbf45a79f8e26a00253c4e146448a', class: getIonMode(this) }, h("ion-list", { key: '52abdfc8668c3429a0dcefef8ddedb6647fdd894' }, header !== undefined && h("ion-list-header", { key: '978e5c03728756feafcc60a0e10e6ec59bf2ae11' }, header), hasSubHeaderOrMessage && (h("ion-item", { key: 'e93c44e7f07a76def16e4b11f0fb4780d84ed402' }, h("ion-label", { key: 'bba1aac43b0bc7f4f00978dd8301985233f3725c', class: "ion-text-wrap" }, subHeader !== undefined && h("h3", { key: 'ad96f6017cf2cc5219540bded2c4f1ca3b532de2' }, subHeader), message !== undefined && h("p", { key: '3fd038921dc40c4d0c29734433984b279ccaeec3' }, message)))), this.renderOptions(options))));
|
|
985
996
|
}
|
|
986
997
|
get el() { return getElement(this); }
|
|
987
998
|
};
|