@ionic/core 8.7.4-dev.11755809082.11a7702b → 8.7.4-nightly.20250822
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-input.js +8 -63
- package/components/ion-textarea.js +7 -62
- package/dist/cjs/ion-input.cjs.entry.js +8 -62
- package/dist/cjs/ion-textarea.cjs.entry.js +7 -61
- package/dist/cjs/ionic.cjs.js +1 -1
- package/dist/cjs/loader.cjs.js +1 -1
- package/dist/collection/components/input/input.js +9 -64
- package/dist/collection/components/textarea/textarea.js +8 -63
- package/dist/docs.json +1 -1
- package/dist/esm/ion-input.entry.js +8 -62
- package/dist/esm/ion-textarea.entry.js +7 -61
- package/dist/esm/ionic.js +1 -1
- package/dist/esm/loader.js +1 -1
- package/dist/ionic/ionic.esm.js +1 -1
- package/dist/ionic/p-1488b7cc.entry.js +4 -0
- package/dist/ionic/p-c5210d3e.entry.js +4 -0
- package/dist/types/components/input/input.d.ts +0 -9
- package/dist/types/components/textarea/textarea.d.ts +0 -9
- package/hydrate/index.js +15 -93
- package/hydrate/index.mjs +15 -93
- package/package.json +1 -1
- package/dist/ionic/p-1d65c601.entry.js +0 -4
- package/dist/ionic/p-33017191.entry.js +0 -4
package/components/ion-input.js
CHANGED
|
@@ -43,10 +43,6 @@ const Input = /*@__PURE__*/ proxyCustomElement(class Input extends HTMLElement {
|
|
|
43
43
|
* is applied in both cases.
|
|
44
44
|
*/
|
|
45
45
|
this.hasFocus = false;
|
|
46
|
-
/**
|
|
47
|
-
* Track validation state for proper aria-live announcements
|
|
48
|
-
*/
|
|
49
|
-
this.isInvalid = false;
|
|
50
46
|
/**
|
|
51
47
|
* Indicates whether and how the text value should be automatically capitalized as it is entered/edited by the user.
|
|
52
48
|
* Available options: `"off"`, `"none"`, `"on"`, `"sentences"`, `"words"`, `"characters"`.
|
|
@@ -132,19 +128,6 @@ const Input = /*@__PURE__*/ proxyCustomElement(class Input extends HTMLElement {
|
|
|
132
128
|
}
|
|
133
129
|
this.didInputClearOnEdit = false;
|
|
134
130
|
this.ionBlur.emit(ev);
|
|
135
|
-
/**
|
|
136
|
-
* Check validation state after blur to handle framework-managed classes.
|
|
137
|
-
* Frameworks like Angular update classes asynchronously, often using
|
|
138
|
-
* requestAnimationFrame or promises. Using setTimeout ensures we check
|
|
139
|
-
* after all microtasks and animation frames have completed.
|
|
140
|
-
*/
|
|
141
|
-
setTimeout(() => {
|
|
142
|
-
const newIsInvalid = this.checkValidationState();
|
|
143
|
-
if (this.isInvalid !== newIsInvalid) {
|
|
144
|
-
this.isInvalid = newIsInvalid;
|
|
145
|
-
forceUpdate(this);
|
|
146
|
-
}
|
|
147
|
-
}, 100);
|
|
148
131
|
};
|
|
149
132
|
this.onFocus = (ev) => {
|
|
150
133
|
this.hasFocus = true;
|
|
@@ -244,42 +227,10 @@ const Input = /*@__PURE__*/ proxyCustomElement(class Input extends HTMLElement {
|
|
|
244
227
|
componentWillLoad() {
|
|
245
228
|
this.inheritedAttributes = Object.assign(Object.assign({}, inheritAriaAttributes(this.el)), inheritAttributes(this.el, ['tabindex', 'title', 'data-form-type', 'dir']));
|
|
246
229
|
}
|
|
247
|
-
/**
|
|
248
|
-
* Checks if the input is in an invalid state based on validation classes
|
|
249
|
-
*/
|
|
250
|
-
checkValidationState() {
|
|
251
|
-
// Check for both Ionic and Angular validation classes on the element itself
|
|
252
|
-
// Angular applies ng-touched/ng-invalid directly to the host element with ngModel
|
|
253
|
-
const hasIonTouched = this.el.classList.contains('ion-touched');
|
|
254
|
-
const hasIonInvalid = this.el.classList.contains('ion-invalid');
|
|
255
|
-
const hasNgTouched = this.el.classList.contains('ng-touched');
|
|
256
|
-
const hasNgInvalid = this.el.classList.contains('ng-invalid');
|
|
257
|
-
// Return true if we have both touched and invalid states from either framework
|
|
258
|
-
const isTouched = hasIonTouched || hasNgTouched;
|
|
259
|
-
const isInvalid = hasIonInvalid || hasNgInvalid;
|
|
260
|
-
return isTouched && isInvalid;
|
|
261
|
-
}
|
|
262
230
|
connectedCallback() {
|
|
263
231
|
const { el } = this;
|
|
264
232
|
this.slotMutationController = createSlotMutationController(el, ['label', 'start', 'end'], () => forceUpdate(this));
|
|
265
233
|
this.notchController = createNotchController(el, () => this.notchSpacerEl, () => this.labelSlot);
|
|
266
|
-
// Watch for class changes to update validation state
|
|
267
|
-
if (Build.isBrowser && typeof MutationObserver !== 'undefined') {
|
|
268
|
-
this.validationObserver = new MutationObserver(() => {
|
|
269
|
-
const newIsInvalid = this.checkValidationState();
|
|
270
|
-
if (this.isInvalid !== newIsInvalid) {
|
|
271
|
-
this.isInvalid = newIsInvalid;
|
|
272
|
-
// Force a re-render to update aria-describedby immediately
|
|
273
|
-
forceUpdate(this);
|
|
274
|
-
}
|
|
275
|
-
});
|
|
276
|
-
this.validationObserver.observe(el, {
|
|
277
|
-
attributes: true,
|
|
278
|
-
attributeFilter: ['class'],
|
|
279
|
-
});
|
|
280
|
-
}
|
|
281
|
-
// Always set initial state
|
|
282
|
-
this.isInvalid = this.checkValidationState();
|
|
283
234
|
this.debounceChanged();
|
|
284
235
|
if (Build.isBrowser) {
|
|
285
236
|
document.dispatchEvent(new CustomEvent('ionInputDidLoad', {
|
|
@@ -316,11 +267,6 @@ const Input = /*@__PURE__*/ proxyCustomElement(class Input extends HTMLElement {
|
|
|
316
267
|
this.notchController.destroy();
|
|
317
268
|
this.notchController = undefined;
|
|
318
269
|
}
|
|
319
|
-
// Clean up validation observer to prevent memory leaks
|
|
320
|
-
if (this.validationObserver) {
|
|
321
|
-
this.validationObserver.disconnect();
|
|
322
|
-
this.validationObserver = undefined;
|
|
323
|
-
}
|
|
324
270
|
}
|
|
325
271
|
/**
|
|
326
272
|
* Sets focus on the native `input` in `ion-input`. Use this method instead of the global
|
|
@@ -422,15 +368,15 @@ const Input = /*@__PURE__*/ proxyCustomElement(class Input extends HTMLElement {
|
|
|
422
368
|
* Renders the helper text or error text values
|
|
423
369
|
*/
|
|
424
370
|
renderHintText() {
|
|
425
|
-
const { helperText, errorText, helperTextId, errorTextId
|
|
371
|
+
const { helperText, errorText, helperTextId, errorTextId } = this;
|
|
426
372
|
return [
|
|
427
|
-
|
|
428
|
-
|
|
373
|
+
h("div", { id: helperTextId, class: "helper-text" }, helperText),
|
|
374
|
+
h("div", { id: errorTextId, class: "error-text" }, errorText),
|
|
429
375
|
];
|
|
430
376
|
}
|
|
431
377
|
getHintTextID() {
|
|
432
|
-
const {
|
|
433
|
-
if (
|
|
378
|
+
const { el, helperText, errorText, helperTextId, errorTextId } = this;
|
|
379
|
+
if (el.classList.contains('ion-touched') && el.classList.contains('ion-invalid') && errorText) {
|
|
434
380
|
return errorTextId;
|
|
435
381
|
}
|
|
436
382
|
if (helperText) {
|
|
@@ -543,7 +489,7 @@ const Input = /*@__PURE__*/ proxyCustomElement(class Input extends HTMLElement {
|
|
|
543
489
|
* TODO(FW-5592): Remove hasStartEndSlots condition
|
|
544
490
|
*/
|
|
545
491
|
const labelShouldFloat = labelPlacement === 'stacked' || (labelPlacement === 'floating' && (hasValue || hasFocus || hasStartEndSlots));
|
|
546
|
-
return (h(Host, { key: '
|
|
492
|
+
return (h(Host, { key: '41b2526627e7d2773a80f011b123284203a71ca0', class: createColorClasses(this.color, {
|
|
547
493
|
[mode]: true,
|
|
548
494
|
'has-value': hasValue,
|
|
549
495
|
'has-focus': hasFocus,
|
|
@@ -554,14 +500,14 @@ const Input = /*@__PURE__*/ proxyCustomElement(class Input extends HTMLElement {
|
|
|
554
500
|
'in-item': inItem,
|
|
555
501
|
'in-item-color': hostContext('ion-item.ion-color', this.el),
|
|
556
502
|
'input-disabled': disabled,
|
|
557
|
-
}) }, h("label", { key: '
|
|
503
|
+
}) }, h("label", { key: '9ab078363e32528102b441ad1791d83f86fdcbdc', class: "input-wrapper", htmlFor: inputId, onClick: this.onLabelClick }, this.renderLabelContainer(), h("div", { key: 'e34b594980ec62e4c618e827fadf7669a39ad0d8', class: "native-wrapper", onClick: this.onLabelClick }, h("slot", { key: '12dc04ead5502e9e5736240e918bf9331bf7b5d9', name: "start" }), h("input", Object.assign({ key: 'df356eb4ced23109b2c0242f36dc043aba8782d6', class: "native-input", ref: (input) => (this.nativeInput = input), id: inputId, disabled: disabled, autoCapitalize: this.autocapitalize, autoComplete: this.autocomplete, autoCorrect: this.autocorrect, autoFocus: this.autofocus, enterKeyHint: this.enterkeyhint, inputMode: this.inputmode, min: this.min, max: this.max, minLength: this.minlength, maxLength: this.maxlength, multiple: this.multiple, name: this.name, pattern: this.pattern, placeholder: this.placeholder || '', readOnly: readonly, required: this.required, spellcheck: this.spellcheck, step: this.step, type: this.type, value: value, onInput: this.onInput, onChange: this.onChange, onBlur: this.onBlur, onFocus: this.onFocus, onKeyDown: this.onKeydown, onCompositionstart: this.onCompositionStart, onCompositionend: this.onCompositionEnd, "aria-describedby": this.getHintTextID(), "aria-invalid": this.getHintTextID() === this.errorTextId }, this.inheritedAttributes)), this.clearInput && !readonly && !disabled && (h("button", { key: 'f79f68cabcd4ea99419331174a377827db0c0741', "aria-label": "reset", type: "button", class: "input-clear-icon", onPointerDown: (ev) => {
|
|
558
504
|
/**
|
|
559
505
|
* This prevents mobile browsers from
|
|
560
506
|
* blurring the input when the clear
|
|
561
507
|
* button is activated.
|
|
562
508
|
*/
|
|
563
509
|
ev.preventDefault();
|
|
564
|
-
}, onClick: this.clearTextInput }, h("ion-icon", { key: '
|
|
510
|
+
}, onClick: this.clearTextInput }, h("ion-icon", { key: '237ec07ec2e10f08818a332bb596578c2c49f770', "aria-hidden": "true", icon: clearIconData }))), h("slot", { key: '1f0a3624aa3e8dc3c307a6762230ab698768a5e5', name: "end" })), shouldRenderHighlight && h("div", { key: '8a8cbb82695a722a0010b53dd0b1f1f97534a20b', class: "input-highlight" })), this.renderBottomContent()));
|
|
565
511
|
}
|
|
566
512
|
get el() { return this; }
|
|
567
513
|
static get watchers() { return {
|
|
@@ -610,7 +556,6 @@ const Input = /*@__PURE__*/ proxyCustomElement(class Input extends HTMLElement {
|
|
|
610
556
|
"type": [1],
|
|
611
557
|
"value": [1032],
|
|
612
558
|
"hasFocus": [32],
|
|
613
|
-
"isInvalid": [32],
|
|
614
559
|
"setFocus": [64],
|
|
615
560
|
"getInputElement": [64]
|
|
616
561
|
}, [[2, "click", "onClickCapture"]], {
|
|
@@ -40,10 +40,6 @@ const Textarea = /*@__PURE__*/ proxyCustomElement(class Textarea extends HTMLEle
|
|
|
40
40
|
* is applied in both cases.
|
|
41
41
|
*/
|
|
42
42
|
this.hasFocus = false;
|
|
43
|
-
/**
|
|
44
|
-
* Track validation state for proper aria-live announcements
|
|
45
|
-
*/
|
|
46
|
-
this.isInvalid = false;
|
|
47
43
|
/**
|
|
48
44
|
* Indicates whether and how the text value should be automatically capitalized as it is entered/edited by the user.
|
|
49
45
|
* Available options: `"off"`, `"none"`, `"on"`, `"sentences"`, `"words"`, `"characters"`.
|
|
@@ -133,19 +129,6 @@ const Textarea = /*@__PURE__*/ proxyCustomElement(class Textarea extends HTMLEle
|
|
|
133
129
|
}
|
|
134
130
|
this.didTextareaClearOnEdit = false;
|
|
135
131
|
this.ionBlur.emit(ev);
|
|
136
|
-
/**
|
|
137
|
-
* Check validation state after blur to handle framework-managed classes.
|
|
138
|
-
* Frameworks like Angular update classes asynchronously, often using
|
|
139
|
-
* requestAnimationFrame or promises. Using setTimeout ensures we check
|
|
140
|
-
* after all microtasks and animation frames have completed.
|
|
141
|
-
*/
|
|
142
|
-
setTimeout(() => {
|
|
143
|
-
const newIsInvalid = this.checkValidationState();
|
|
144
|
-
if (this.isInvalid !== newIsInvalid) {
|
|
145
|
-
this.isInvalid = newIsInvalid;
|
|
146
|
-
forceUpdate(this);
|
|
147
|
-
}
|
|
148
|
-
}, 100);
|
|
149
132
|
};
|
|
150
133
|
this.onKeyDown = (ev) => {
|
|
151
134
|
this.checkClearOnEdit(ev);
|
|
@@ -203,42 +186,10 @@ const Textarea = /*@__PURE__*/ proxyCustomElement(class Textarea extends HTMLEle
|
|
|
203
186
|
this.el.click();
|
|
204
187
|
}
|
|
205
188
|
}
|
|
206
|
-
/**
|
|
207
|
-
* Checks if the textarea is in an invalid state based on validation classes
|
|
208
|
-
*/
|
|
209
|
-
checkValidationState() {
|
|
210
|
-
// Check for both Ionic and Angular validation classes on the element itself
|
|
211
|
-
// Angular applies ng-touched/ng-invalid directly to the host element with ngModel
|
|
212
|
-
const hasIonTouched = this.el.classList.contains('ion-touched');
|
|
213
|
-
const hasIonInvalid = this.el.classList.contains('ion-invalid');
|
|
214
|
-
const hasNgTouched = this.el.classList.contains('ng-touched');
|
|
215
|
-
const hasNgInvalid = this.el.classList.contains('ng-invalid');
|
|
216
|
-
// Return true if we have both touched and invalid states from either framework
|
|
217
|
-
const isTouched = hasIonTouched || hasNgTouched;
|
|
218
|
-
const isInvalid = hasIonInvalid || hasNgInvalid;
|
|
219
|
-
return isTouched && isInvalid;
|
|
220
|
-
}
|
|
221
189
|
connectedCallback() {
|
|
222
190
|
const { el } = this;
|
|
223
191
|
this.slotMutationController = createSlotMutationController(el, ['label', 'start', 'end'], () => forceUpdate(this));
|
|
224
192
|
this.notchController = createNotchController(el, () => this.notchSpacerEl, () => this.labelSlot);
|
|
225
|
-
// Watch for class changes to update validation state
|
|
226
|
-
if (Build.isBrowser && typeof MutationObserver !== 'undefined') {
|
|
227
|
-
this.validationObserver = new MutationObserver(() => {
|
|
228
|
-
const newIsInvalid = this.checkValidationState();
|
|
229
|
-
if (this.isInvalid !== newIsInvalid) {
|
|
230
|
-
this.isInvalid = newIsInvalid;
|
|
231
|
-
// Force a re-render to update aria-describedby immediately
|
|
232
|
-
forceUpdate(this);
|
|
233
|
-
}
|
|
234
|
-
});
|
|
235
|
-
this.validationObserver.observe(el, {
|
|
236
|
-
attributes: true,
|
|
237
|
-
attributeFilter: ['class'],
|
|
238
|
-
});
|
|
239
|
-
}
|
|
240
|
-
// Always set initial state
|
|
241
|
-
this.isInvalid = this.checkValidationState();
|
|
242
193
|
this.debounceChanged();
|
|
243
194
|
if (Build.isBrowser) {
|
|
244
195
|
document.dispatchEvent(new CustomEvent('ionInputDidLoad', {
|
|
@@ -260,11 +211,6 @@ const Textarea = /*@__PURE__*/ proxyCustomElement(class Textarea extends HTMLEle
|
|
|
260
211
|
this.notchController.destroy();
|
|
261
212
|
this.notchController = undefined;
|
|
262
213
|
}
|
|
263
|
-
// Clean up validation observer to prevent memory leaks
|
|
264
|
-
if (this.validationObserver) {
|
|
265
|
-
this.validationObserver.disconnect();
|
|
266
|
-
this.validationObserver = undefined;
|
|
267
|
-
}
|
|
268
214
|
}
|
|
269
215
|
componentWillLoad() {
|
|
270
216
|
this.inheritedAttributes = Object.assign(Object.assign({}, inheritAriaAttributes(this.el)), inheritAttributes(this.el, ['data-form-type', 'title', 'tabindex', 'dir']));
|
|
@@ -435,15 +381,15 @@ const Textarea = /*@__PURE__*/ proxyCustomElement(class Textarea extends HTMLEle
|
|
|
435
381
|
* Renders the helper text or error text values
|
|
436
382
|
*/
|
|
437
383
|
renderHintText() {
|
|
438
|
-
const { helperText, errorText, helperTextId, errorTextId
|
|
384
|
+
const { helperText, errorText, helperTextId, errorTextId } = this;
|
|
439
385
|
return [
|
|
440
|
-
|
|
441
|
-
|
|
386
|
+
h("div", { id: helperTextId, class: "helper-text" }, helperText),
|
|
387
|
+
h("div", { id: errorTextId, class: "error-text" }, errorText),
|
|
442
388
|
];
|
|
443
389
|
}
|
|
444
390
|
getHintTextID() {
|
|
445
|
-
const {
|
|
446
|
-
if (
|
|
391
|
+
const { el, helperText, errorText, helperTextId, errorTextId } = this;
|
|
392
|
+
if (el.classList.contains('ion-touched') && el.classList.contains('ion-invalid') && errorText) {
|
|
447
393
|
return errorTextId;
|
|
448
394
|
}
|
|
449
395
|
if (helperText) {
|
|
@@ -502,7 +448,7 @@ const Textarea = /*@__PURE__*/ proxyCustomElement(class Textarea extends HTMLEle
|
|
|
502
448
|
* TODO(FW-5592): Remove hasStartEndSlots condition
|
|
503
449
|
*/
|
|
504
450
|
const labelShouldFloat = labelPlacement === 'stacked' || (labelPlacement === 'floating' && (hasValue || hasFocus || hasStartEndSlots));
|
|
505
|
-
return (h(Host, { key: '
|
|
451
|
+
return (h(Host, { key: 'd9f2ede0107987fc42c99e310cd2336bad5a5755', class: createColorClasses(this.color, {
|
|
506
452
|
[mode]: true,
|
|
507
453
|
'has-value': hasValue,
|
|
508
454
|
'has-focus': hasFocus,
|
|
@@ -511,7 +457,7 @@ const Textarea = /*@__PURE__*/ proxyCustomElement(class Textarea extends HTMLEle
|
|
|
511
457
|
[`textarea-shape-${shape}`]: shape !== undefined,
|
|
512
458
|
[`textarea-label-placement-${labelPlacement}`]: true,
|
|
513
459
|
'textarea-disabled': disabled,
|
|
514
|
-
}) }, h("label", { key: '
|
|
460
|
+
}) }, h("label", { key: '9de598b95237462bb3bccffaefe83afbb43554b8', class: "textarea-wrapper", htmlFor: inputId, onClick: this.onLabelClick }, this.renderLabelContainer(), h("div", { key: 'e33c426c6541d723ccc246bb404c03687726ff83', class: "textarea-wrapper-inner" }, h("div", { key: '521e11af9d54d281b0a2b1c25bcfc6f742c18296', class: "start-slot-wrapper" }, h("slot", { key: '515523f6ca3ce0e5dd08f3275c21a190fb1ca177', name: "start" })), h("div", { key: '916e01e00de8400ae00ef06bc1fb62d8be2eee08', class: "native-wrapper", ref: (el) => (this.textareaWrapper = el) }, h("textarea", Object.assign({ key: '810271e6532d90e27dab1fcb26546113c1ce9cb0', class: "native-textarea", ref: (el) => (this.nativeInput = el), id: inputId, disabled: disabled, autoCapitalize: this.autocapitalize, autoFocus: this.autofocus, enterKeyHint: this.enterkeyhint, inputMode: this.inputmode, minLength: this.minlength, maxLength: this.maxlength, name: this.name, placeholder: this.placeholder || '', readOnly: this.readonly, required: this.required, spellcheck: this.spellcheck, cols: this.cols, rows: this.rows, wrap: this.wrap, onInput: this.onInput, onChange: this.onChange, onBlur: this.onBlur, onFocus: this.onFocus, onKeyDown: this.onKeyDown, "aria-describedby": this.getHintTextID(), "aria-invalid": this.getHintTextID() === this.errorTextId }, this.inheritedAttributes), value)), h("div", { key: '80aca9ea9546dca9d38efd291a6b0be384bb6978', class: "end-slot-wrapper" }, h("slot", { key: '407fab16c66a9f4a542369bfecc0d9afa0065977', name: "end" }))), shouldRenderHighlight && h("div", { key: 'f00523a6698fac8a1996e04303487bef01d10f25', class: "textarea-highlight" })), this.renderBottomContent()));
|
|
515
461
|
}
|
|
516
462
|
get el() { return this; }
|
|
517
463
|
static get watchers() { return {
|
|
@@ -553,7 +499,6 @@ const Textarea = /*@__PURE__*/ proxyCustomElement(class Textarea extends HTMLEle
|
|
|
553
499
|
"labelPlacement": [1, "label-placement"],
|
|
554
500
|
"shape": [1],
|
|
555
501
|
"hasFocus": [32],
|
|
556
|
-
"isInvalid": [32],
|
|
557
502
|
"setFocus": [64],
|
|
558
503
|
"getInputElement": [64]
|
|
559
504
|
}, [[2, "click", "onClickCapture"]], {
|
|
@@ -44,10 +44,6 @@ const Input = class {
|
|
|
44
44
|
* is applied in both cases.
|
|
45
45
|
*/
|
|
46
46
|
this.hasFocus = false;
|
|
47
|
-
/**
|
|
48
|
-
* Track validation state for proper aria-live announcements
|
|
49
|
-
*/
|
|
50
|
-
this.isInvalid = false;
|
|
51
47
|
/**
|
|
52
48
|
* Indicates whether and how the text value should be automatically capitalized as it is entered/edited by the user.
|
|
53
49
|
* Available options: `"off"`, `"none"`, `"on"`, `"sentences"`, `"words"`, `"characters"`.
|
|
@@ -133,19 +129,6 @@ const Input = class {
|
|
|
133
129
|
}
|
|
134
130
|
this.didInputClearOnEdit = false;
|
|
135
131
|
this.ionBlur.emit(ev);
|
|
136
|
-
/**
|
|
137
|
-
* Check validation state after blur to handle framework-managed classes.
|
|
138
|
-
* Frameworks like Angular update classes asynchronously, often using
|
|
139
|
-
* requestAnimationFrame or promises. Using setTimeout ensures we check
|
|
140
|
-
* after all microtasks and animation frames have completed.
|
|
141
|
-
*/
|
|
142
|
-
setTimeout(() => {
|
|
143
|
-
const newIsInvalid = this.checkValidationState();
|
|
144
|
-
if (this.isInvalid !== newIsInvalid) {
|
|
145
|
-
this.isInvalid = newIsInvalid;
|
|
146
|
-
index.forceUpdate(this);
|
|
147
|
-
}
|
|
148
|
-
}, 100);
|
|
149
132
|
};
|
|
150
133
|
this.onFocus = (ev) => {
|
|
151
134
|
this.hasFocus = true;
|
|
@@ -245,42 +228,10 @@ const Input = class {
|
|
|
245
228
|
componentWillLoad() {
|
|
246
229
|
this.inheritedAttributes = Object.assign(Object.assign({}, helpers.inheritAriaAttributes(this.el)), helpers.inheritAttributes(this.el, ['tabindex', 'title', 'data-form-type', 'dir']));
|
|
247
230
|
}
|
|
248
|
-
/**
|
|
249
|
-
* Checks if the input is in an invalid state based on validation classes
|
|
250
|
-
*/
|
|
251
|
-
checkValidationState() {
|
|
252
|
-
// Check for both Ionic and Angular validation classes on the element itself
|
|
253
|
-
// Angular applies ng-touched/ng-invalid directly to the host element with ngModel
|
|
254
|
-
const hasIonTouched = this.el.classList.contains('ion-touched');
|
|
255
|
-
const hasIonInvalid = this.el.classList.contains('ion-invalid');
|
|
256
|
-
const hasNgTouched = this.el.classList.contains('ng-touched');
|
|
257
|
-
const hasNgInvalid = this.el.classList.contains('ng-invalid');
|
|
258
|
-
// Return true if we have both touched and invalid states from either framework
|
|
259
|
-
const isTouched = hasIonTouched || hasNgTouched;
|
|
260
|
-
const isInvalid = hasIonInvalid || hasNgInvalid;
|
|
261
|
-
return isTouched && isInvalid;
|
|
262
|
-
}
|
|
263
231
|
connectedCallback() {
|
|
264
232
|
const { el } = this;
|
|
265
233
|
this.slotMutationController = input_utils.createSlotMutationController(el, ['label', 'start', 'end'], () => index.forceUpdate(this));
|
|
266
234
|
this.notchController = notchController.createNotchController(el, () => this.notchSpacerEl, () => this.labelSlot);
|
|
267
|
-
// Watch for class changes to update validation state
|
|
268
|
-
if (typeof MutationObserver !== 'undefined') {
|
|
269
|
-
this.validationObserver = new MutationObserver(() => {
|
|
270
|
-
const newIsInvalid = this.checkValidationState();
|
|
271
|
-
if (this.isInvalid !== newIsInvalid) {
|
|
272
|
-
this.isInvalid = newIsInvalid;
|
|
273
|
-
// Force a re-render to update aria-describedby immediately
|
|
274
|
-
index.forceUpdate(this);
|
|
275
|
-
}
|
|
276
|
-
});
|
|
277
|
-
this.validationObserver.observe(el, {
|
|
278
|
-
attributes: true,
|
|
279
|
-
attributeFilter: ['class'],
|
|
280
|
-
});
|
|
281
|
-
}
|
|
282
|
-
// Always set initial state
|
|
283
|
-
this.isInvalid = this.checkValidationState();
|
|
284
235
|
this.debounceChanged();
|
|
285
236
|
{
|
|
286
237
|
document.dispatchEvent(new CustomEvent('ionInputDidLoad', {
|
|
@@ -317,11 +268,6 @@ const Input = class {
|
|
|
317
268
|
this.notchController.destroy();
|
|
318
269
|
this.notchController = undefined;
|
|
319
270
|
}
|
|
320
|
-
// Clean up validation observer to prevent memory leaks
|
|
321
|
-
if (this.validationObserver) {
|
|
322
|
-
this.validationObserver.disconnect();
|
|
323
|
-
this.validationObserver = undefined;
|
|
324
|
-
}
|
|
325
271
|
}
|
|
326
272
|
/**
|
|
327
273
|
* Sets focus on the native `input` in `ion-input`. Use this method instead of the global
|
|
@@ -423,15 +369,15 @@ const Input = class {
|
|
|
423
369
|
* Renders the helper text or error text values
|
|
424
370
|
*/
|
|
425
371
|
renderHintText() {
|
|
426
|
-
const { helperText, errorText, helperTextId, errorTextId
|
|
372
|
+
const { helperText, errorText, helperTextId, errorTextId } = this;
|
|
427
373
|
return [
|
|
428
|
-
|
|
429
|
-
|
|
374
|
+
index.h("div", { id: helperTextId, class: "helper-text" }, helperText),
|
|
375
|
+
index.h("div", { id: errorTextId, class: "error-text" }, errorText),
|
|
430
376
|
];
|
|
431
377
|
}
|
|
432
378
|
getHintTextID() {
|
|
433
|
-
const {
|
|
434
|
-
if (
|
|
379
|
+
const { el, helperText, errorText, helperTextId, errorTextId } = this;
|
|
380
|
+
if (el.classList.contains('ion-touched') && el.classList.contains('ion-invalid') && errorText) {
|
|
435
381
|
return errorTextId;
|
|
436
382
|
}
|
|
437
383
|
if (helperText) {
|
|
@@ -544,7 +490,7 @@ const Input = class {
|
|
|
544
490
|
* TODO(FW-5592): Remove hasStartEndSlots condition
|
|
545
491
|
*/
|
|
546
492
|
const labelShouldFloat = labelPlacement === 'stacked' || (labelPlacement === 'floating' && (hasValue || hasFocus || hasStartEndSlots));
|
|
547
|
-
return (index.h(index.Host, { key: '
|
|
493
|
+
return (index.h(index.Host, { key: '41b2526627e7d2773a80f011b123284203a71ca0', class: theme.createColorClasses(this.color, {
|
|
548
494
|
[mode]: true,
|
|
549
495
|
'has-value': hasValue,
|
|
550
496
|
'has-focus': hasFocus,
|
|
@@ -555,14 +501,14 @@ const Input = class {
|
|
|
555
501
|
'in-item': inItem,
|
|
556
502
|
'in-item-color': theme.hostContext('ion-item.ion-color', this.el),
|
|
557
503
|
'input-disabled': disabled,
|
|
558
|
-
}) }, index.h("label", { key: '
|
|
504
|
+
}) }, index.h("label", { key: '9ab078363e32528102b441ad1791d83f86fdcbdc', class: "input-wrapper", htmlFor: inputId, onClick: this.onLabelClick }, this.renderLabelContainer(), index.h("div", { key: 'e34b594980ec62e4c618e827fadf7669a39ad0d8', class: "native-wrapper", onClick: this.onLabelClick }, index.h("slot", { key: '12dc04ead5502e9e5736240e918bf9331bf7b5d9', name: "start" }), index.h("input", Object.assign({ key: 'df356eb4ced23109b2c0242f36dc043aba8782d6', class: "native-input", ref: (input) => (this.nativeInput = input), id: inputId, disabled: disabled, autoCapitalize: this.autocapitalize, autoComplete: this.autocomplete, autoCorrect: this.autocorrect, autoFocus: this.autofocus, enterKeyHint: this.enterkeyhint, inputMode: this.inputmode, min: this.min, max: this.max, minLength: this.minlength, maxLength: this.maxlength, multiple: this.multiple, name: this.name, pattern: this.pattern, placeholder: this.placeholder || '', readOnly: readonly, required: this.required, spellcheck: this.spellcheck, step: this.step, type: this.type, value: value, onInput: this.onInput, onChange: this.onChange, onBlur: this.onBlur, onFocus: this.onFocus, onKeyDown: this.onKeydown, onCompositionstart: this.onCompositionStart, onCompositionend: this.onCompositionEnd, "aria-describedby": this.getHintTextID(), "aria-invalid": this.getHintTextID() === this.errorTextId }, this.inheritedAttributes)), this.clearInput && !readonly && !disabled && (index.h("button", { key: 'f79f68cabcd4ea99419331174a377827db0c0741', "aria-label": "reset", type: "button", class: "input-clear-icon", onPointerDown: (ev) => {
|
|
559
505
|
/**
|
|
560
506
|
* This prevents mobile browsers from
|
|
561
507
|
* blurring the input when the clear
|
|
562
508
|
* button is activated.
|
|
563
509
|
*/
|
|
564
510
|
ev.preventDefault();
|
|
565
|
-
}, onClick: this.clearTextInput }, index.h("ion-icon", { key: '
|
|
511
|
+
}, onClick: this.clearTextInput }, index.h("ion-icon", { key: '237ec07ec2e10f08818a332bb596578c2c49f770', "aria-hidden": "true", icon: clearIconData }))), index.h("slot", { key: '1f0a3624aa3e8dc3c307a6762230ab698768a5e5', name: "end" })), shouldRenderHighlight && index.h("div", { key: '8a8cbb82695a722a0010b53dd0b1f1f97534a20b', class: "input-highlight" })), this.renderBottomContent()));
|
|
566
512
|
}
|
|
567
513
|
get el() { return index.getElement(this); }
|
|
568
514
|
static get watchers() { return {
|
|
@@ -42,10 +42,6 @@ const Textarea = class {
|
|
|
42
42
|
* is applied in both cases.
|
|
43
43
|
*/
|
|
44
44
|
this.hasFocus = false;
|
|
45
|
-
/**
|
|
46
|
-
* Track validation state for proper aria-live announcements
|
|
47
|
-
*/
|
|
48
|
-
this.isInvalid = false;
|
|
49
45
|
/**
|
|
50
46
|
* Indicates whether and how the text value should be automatically capitalized as it is entered/edited by the user.
|
|
51
47
|
* Available options: `"off"`, `"none"`, `"on"`, `"sentences"`, `"words"`, `"characters"`.
|
|
@@ -135,19 +131,6 @@ const Textarea = class {
|
|
|
135
131
|
}
|
|
136
132
|
this.didTextareaClearOnEdit = false;
|
|
137
133
|
this.ionBlur.emit(ev);
|
|
138
|
-
/**
|
|
139
|
-
* Check validation state after blur to handle framework-managed classes.
|
|
140
|
-
* Frameworks like Angular update classes asynchronously, often using
|
|
141
|
-
* requestAnimationFrame or promises. Using setTimeout ensures we check
|
|
142
|
-
* after all microtasks and animation frames have completed.
|
|
143
|
-
*/
|
|
144
|
-
setTimeout(() => {
|
|
145
|
-
const newIsInvalid = this.checkValidationState();
|
|
146
|
-
if (this.isInvalid !== newIsInvalid) {
|
|
147
|
-
this.isInvalid = newIsInvalid;
|
|
148
|
-
index.forceUpdate(this);
|
|
149
|
-
}
|
|
150
|
-
}, 100);
|
|
151
134
|
};
|
|
152
135
|
this.onKeyDown = (ev) => {
|
|
153
136
|
this.checkClearOnEdit(ev);
|
|
@@ -205,42 +188,10 @@ const Textarea = class {
|
|
|
205
188
|
this.el.click();
|
|
206
189
|
}
|
|
207
190
|
}
|
|
208
|
-
/**
|
|
209
|
-
* Checks if the textarea is in an invalid state based on validation classes
|
|
210
|
-
*/
|
|
211
|
-
checkValidationState() {
|
|
212
|
-
// Check for both Ionic and Angular validation classes on the element itself
|
|
213
|
-
// Angular applies ng-touched/ng-invalid directly to the host element with ngModel
|
|
214
|
-
const hasIonTouched = this.el.classList.contains('ion-touched');
|
|
215
|
-
const hasIonInvalid = this.el.classList.contains('ion-invalid');
|
|
216
|
-
const hasNgTouched = this.el.classList.contains('ng-touched');
|
|
217
|
-
const hasNgInvalid = this.el.classList.contains('ng-invalid');
|
|
218
|
-
// Return true if we have both touched and invalid states from either framework
|
|
219
|
-
const isTouched = hasIonTouched || hasNgTouched;
|
|
220
|
-
const isInvalid = hasIonInvalid || hasNgInvalid;
|
|
221
|
-
return isTouched && isInvalid;
|
|
222
|
-
}
|
|
223
191
|
connectedCallback() {
|
|
224
192
|
const { el } = this;
|
|
225
193
|
this.slotMutationController = input_utils.createSlotMutationController(el, ['label', 'start', 'end'], () => index.forceUpdate(this));
|
|
226
194
|
this.notchController = notchController.createNotchController(el, () => this.notchSpacerEl, () => this.labelSlot);
|
|
227
|
-
// Watch for class changes to update validation state
|
|
228
|
-
if (typeof MutationObserver !== 'undefined') {
|
|
229
|
-
this.validationObserver = new MutationObserver(() => {
|
|
230
|
-
const newIsInvalid = this.checkValidationState();
|
|
231
|
-
if (this.isInvalid !== newIsInvalid) {
|
|
232
|
-
this.isInvalid = newIsInvalid;
|
|
233
|
-
// Force a re-render to update aria-describedby immediately
|
|
234
|
-
index.forceUpdate(this);
|
|
235
|
-
}
|
|
236
|
-
});
|
|
237
|
-
this.validationObserver.observe(el, {
|
|
238
|
-
attributes: true,
|
|
239
|
-
attributeFilter: ['class'],
|
|
240
|
-
});
|
|
241
|
-
}
|
|
242
|
-
// Always set initial state
|
|
243
|
-
this.isInvalid = this.checkValidationState();
|
|
244
195
|
this.debounceChanged();
|
|
245
196
|
{
|
|
246
197
|
document.dispatchEvent(new CustomEvent('ionInputDidLoad', {
|
|
@@ -262,11 +213,6 @@ const Textarea = class {
|
|
|
262
213
|
this.notchController.destroy();
|
|
263
214
|
this.notchController = undefined;
|
|
264
215
|
}
|
|
265
|
-
// Clean up validation observer to prevent memory leaks
|
|
266
|
-
if (this.validationObserver) {
|
|
267
|
-
this.validationObserver.disconnect();
|
|
268
|
-
this.validationObserver = undefined;
|
|
269
|
-
}
|
|
270
216
|
}
|
|
271
217
|
componentWillLoad() {
|
|
272
218
|
this.inheritedAttributes = Object.assign(Object.assign({}, helpers.inheritAriaAttributes(this.el)), helpers.inheritAttributes(this.el, ['data-form-type', 'title', 'tabindex', 'dir']));
|
|
@@ -437,15 +383,15 @@ const Textarea = class {
|
|
|
437
383
|
* Renders the helper text or error text values
|
|
438
384
|
*/
|
|
439
385
|
renderHintText() {
|
|
440
|
-
const { helperText, errorText, helperTextId, errorTextId
|
|
386
|
+
const { helperText, errorText, helperTextId, errorTextId } = this;
|
|
441
387
|
return [
|
|
442
|
-
|
|
443
|
-
|
|
388
|
+
index.h("div", { id: helperTextId, class: "helper-text" }, helperText),
|
|
389
|
+
index.h("div", { id: errorTextId, class: "error-text" }, errorText),
|
|
444
390
|
];
|
|
445
391
|
}
|
|
446
392
|
getHintTextID() {
|
|
447
|
-
const {
|
|
448
|
-
if (
|
|
393
|
+
const { el, helperText, errorText, helperTextId, errorTextId } = this;
|
|
394
|
+
if (el.classList.contains('ion-touched') && el.classList.contains('ion-invalid') && errorText) {
|
|
449
395
|
return errorTextId;
|
|
450
396
|
}
|
|
451
397
|
if (helperText) {
|
|
@@ -504,7 +450,7 @@ const Textarea = class {
|
|
|
504
450
|
* TODO(FW-5592): Remove hasStartEndSlots condition
|
|
505
451
|
*/
|
|
506
452
|
const labelShouldFloat = labelPlacement === 'stacked' || (labelPlacement === 'floating' && (hasValue || hasFocus || hasStartEndSlots));
|
|
507
|
-
return (index.h(index.Host, { key: '
|
|
453
|
+
return (index.h(index.Host, { key: 'd9f2ede0107987fc42c99e310cd2336bad5a5755', class: theme.createColorClasses(this.color, {
|
|
508
454
|
[mode]: true,
|
|
509
455
|
'has-value': hasValue,
|
|
510
456
|
'has-focus': hasFocus,
|
|
@@ -513,7 +459,7 @@ const Textarea = class {
|
|
|
513
459
|
[`textarea-shape-${shape}`]: shape !== undefined,
|
|
514
460
|
[`textarea-label-placement-${labelPlacement}`]: true,
|
|
515
461
|
'textarea-disabled': disabled,
|
|
516
|
-
}) }, index.h("label", { key: '
|
|
462
|
+
}) }, index.h("label", { key: '9de598b95237462bb3bccffaefe83afbb43554b8', class: "textarea-wrapper", htmlFor: inputId, onClick: this.onLabelClick }, this.renderLabelContainer(), index.h("div", { key: 'e33c426c6541d723ccc246bb404c03687726ff83', class: "textarea-wrapper-inner" }, index.h("div", { key: '521e11af9d54d281b0a2b1c25bcfc6f742c18296', class: "start-slot-wrapper" }, index.h("slot", { key: '515523f6ca3ce0e5dd08f3275c21a190fb1ca177', name: "start" })), index.h("div", { key: '916e01e00de8400ae00ef06bc1fb62d8be2eee08', class: "native-wrapper", ref: (el) => (this.textareaWrapper = el) }, index.h("textarea", Object.assign({ key: '810271e6532d90e27dab1fcb26546113c1ce9cb0', class: "native-textarea", ref: (el) => (this.nativeInput = el), id: inputId, disabled: disabled, autoCapitalize: this.autocapitalize, autoFocus: this.autofocus, enterKeyHint: this.enterkeyhint, inputMode: this.inputmode, minLength: this.minlength, maxLength: this.maxlength, name: this.name, placeholder: this.placeholder || '', readOnly: this.readonly, required: this.required, spellcheck: this.spellcheck, cols: this.cols, rows: this.rows, wrap: this.wrap, onInput: this.onInput, onChange: this.onChange, onBlur: this.onBlur, onFocus: this.onFocus, onKeyDown: this.onKeyDown, "aria-describedby": this.getHintTextID(), "aria-invalid": this.getHintTextID() === this.errorTextId }, this.inheritedAttributes), value)), index.h("div", { key: '80aca9ea9546dca9d38efd291a6b0be384bb6978', class: "end-slot-wrapper" }, index.h("slot", { key: '407fab16c66a9f4a542369bfecc0d9afa0065977', name: "end" }))), shouldRenderHighlight && index.h("div", { key: 'f00523a6698fac8a1996e04303487bef01d10f25', class: "textarea-highlight" })), this.renderBottomContent()));
|
|
517
463
|
}
|
|
518
464
|
get el() { return index.getElement(this); }
|
|
519
465
|
static get watchers() { return {
|