@aurodesignsystem-dev/auro-formkit 0.0.0-pr1452.2 → 0.0.0-pr1452.4

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.
Files changed (72) hide show
  1. package/components/checkbox/demo/api.html +5 -24
  2. package/components/checkbox/demo/index.min.js +1 -1
  3. package/components/checkbox/dist/index.js +1 -1
  4. package/components/checkbox/dist/registered.js +1 -1
  5. package/components/combobox/demo/api.html +4 -22
  6. package/components/combobox/demo/index.min.js +15 -1
  7. package/components/combobox/demo/registered.min.js +3 -3
  8. package/components/combobox/dist/index.js +3 -3
  9. package/components/combobox/dist/registered.js +3 -3
  10. package/components/counter/demo/api.html +6 -27
  11. package/components/counter/demo/index.min.js +8392 -1
  12. package/components/counter/dist/index.js +240 -13
  13. package/components/counter/dist/registered.js +1 -1
  14. package/components/datepicker/demo/api.html +5 -27
  15. package/components/datepicker/demo/index.min.js +24612 -1
  16. package/components/datepicker/demo/readme.html +2 -10
  17. package/components/datepicker/dist/index.js +3 -3
  18. package/components/datepicker/dist/registered.js +3 -3
  19. package/components/dropdown/demo/api.html +6 -29
  20. package/components/dropdown/demo/index.min.js +5097 -1
  21. package/components/dropdown/dist/index.js +1 -1
  22. package/components/dropdown/dist/registered.js +1 -1
  23. package/components/form/demo/api.html +6 -25
  24. package/components/form/demo/index.min.js +716 -2
  25. package/components/form/demo/registerDemoDeps.min.js +257 -257
  26. package/components/input/demo/accessibility.md +1 -1
  27. package/components/input/demo/api.html +18 -26
  28. package/components/input/demo/auro-input.min.js +1 -1
  29. package/components/input/demo/readme.html +2 -10
  30. package/components/input/dist/index.js +1 -1
  31. package/components/input/dist/registered.js +1 -1
  32. package/components/menu/demo/api.html +6 -28
  33. package/components/menu/demo/index.min.js +2287 -1
  34. package/components/radio/demo/api.html +8 -26
  35. package/components/radio/demo/index.min.js +1 -1
  36. package/components/radio/dist/index.js +1 -1
  37. package/components/radio/dist/registered.js +1 -1
  38. package/components/select/demo/api.html +6 -40
  39. package/components/select/demo/getting-started.min.js +31 -1
  40. package/components/select/demo/registered.min.js +2 -2
  41. package/components/select/dist/index.js +2 -2
  42. package/components/select/dist/registered.js +2 -2
  43. package/custom-elements.json +1444 -1444
  44. package/package.json +1 -1
  45. package/components/checkbox/demo/api.js +0 -17
  46. package/components/checkbox/demo/api.min.js +0 -26
  47. package/components/combobox/demo/api.js +0 -39
  48. package/components/combobox/demo/api.min.js +0 -106
  49. package/components/combobox/demo/swap-value.min.js +0 -16
  50. package/components/counter/demo/api.js +0 -24
  51. package/components/counter/demo/api.min.js +0 -52
  52. package/components/counter/demo/auro-counter-group.min.js +0 -8394
  53. package/components/datepicker/demo/api.js +0 -37
  54. package/components/datepicker/demo/api.min.js +0 -300
  55. package/components/datepicker/demo/auro-datepicker.min.js +0 -24614
  56. package/components/dropdown/demo/api.js +0 -26
  57. package/components/dropdown/demo/api.min.js +0 -109
  58. package/components/dropdown/demo/auro-dropdown.min.js +0 -5099
  59. package/components/form/demo/api.js +0 -5
  60. package/components/form/demo/api.min.js +0 -8
  61. package/components/form/demo/auro-form.min.js +0 -718
  62. package/components/form/demo/autocomplete.html +0 -31
  63. package/components/input/demo/api.js +0 -8
  64. package/components/input/demo/api.min.js +0 -9
  65. package/components/menu/demo/api.js +0 -29
  66. package/components/menu/demo/api.min.js +0 -121
  67. package/components/menu/demo/auro-menuoption.min.js +0 -2289
  68. package/components/radio/demo/api.js +0 -19
  69. package/components/radio/demo/api.min.js +0 -44
  70. package/components/select/demo/api.js +0 -39
  71. package/components/select/demo/api.min.js +0 -83
  72. package/components/select/demo/update-active-option.min.js +0 -32
@@ -1,8 +1,722 @@
1
- import { A as AuroForm } from './auro-form.min.js';
2
- import './registerDemoDeps.min.js';
1
+ import { i, a as i$1, b as b$1 } from './registerDemoDeps.min.js';
3
2
  import '@aurodesignsystem/auro-datepicker';
4
3
  import '@aurodesignsystem/auro-combobox';
5
4
  import '@aurodesignsystem/auro-select';
6
5
 
6
+ var styleCss = i``;
7
+
8
+ // Copyright (c) Alaska Air. All right reserved. Licensed under the Apache-2.0 license
9
+ // See LICENSE in the project root for license information.
10
+
11
+ // ---------------------------------------------------------------------
12
+
13
+ /* eslint-disable line-comment-position, no-inline-comments, no-confusing-arrow, no-nested-ternary, implicit-arrow-linebreak */
14
+
15
+ class AuroLibraryRuntimeUtils {
16
+
17
+ /* eslint-disable jsdoc/require-param */
18
+
19
+ /**
20
+ * This will register a new custom element with the browser.
21
+ * @param {String} name - The name of the custom element.
22
+ * @param {Object} componentClass - The class to register as a custom element.
23
+ * @returns {void}
24
+ */
25
+ registerComponent(name, componentClass) {
26
+ if (!customElements.get(name)) {
27
+ customElements.define(name, class extends componentClass {});
28
+ }
29
+ }
30
+
31
+ /**
32
+ * Finds and returns the closest HTML Element based on a selector.
33
+ * @returns {void}
34
+ */
35
+ closestElement(
36
+ selector, // selector like in .closest()
37
+ base = this, // extra functionality to skip a parent
38
+ __Closest = (el, found = el && el.closest(selector)) =>
39
+ !el || el === document || el === window
40
+ ? null // standard .closest() returns null for non-found selectors also
41
+ : found
42
+ ? found // found a selector INside this element
43
+ : __Closest(el.getRootNode().host) // recursion!! break out to parent DOM
44
+ ) {
45
+ return __Closest(base);
46
+ }
47
+ /* eslint-enable jsdoc/require-param */
48
+
49
+ /**
50
+ * If the element passed is registered with a different tag name than what is passed in, the tag name is added as an attribute to the element.
51
+ * @param {Object} elem - The element to check.
52
+ * @param {String} tagName - The name of the Auro component to check for or add as an attribute.
53
+ * @returns {void}
54
+ */
55
+ handleComponentTagRename(elem, tagName) {
56
+ const tag = tagName.toLowerCase();
57
+ const elemTag = elem.tagName.toLowerCase();
58
+
59
+ if (elemTag !== tag) {
60
+ elem.setAttribute(tag, true);
61
+ }
62
+ }
63
+
64
+ /**
65
+ * Validates if an element is a specific Auro component.
66
+ * @param {Object} elem - The element to validate.
67
+ * @param {String} tagName - The name of the Auro component to check against.
68
+ * @returns {Boolean} - Returns true if the element is the specified Auro component.
69
+ */
70
+ elementMatch(elem, tagName) {
71
+ const tag = tagName.toLowerCase();
72
+ const elemTag = elem.tagName.toLowerCase();
73
+
74
+ return elemTag === tag || elem.hasAttribute(tag);
75
+ }
76
+
77
+ /**
78
+ * Gets the text content of a named slot.
79
+ * @returns {String}
80
+ * @private
81
+ */
82
+ getSlotText(elem, name) {
83
+ const slot = elem.shadowRoot?.querySelector(`slot[name="${name}"]`);
84
+ const nodes = slot?.assignedNodes({ flatten: true }) || [];
85
+ const text = nodes.map(n => n.textContent?.trim()).join(' ').trim();
86
+
87
+ return text || null;
88
+ }
89
+ }
90
+
91
+ /* eslint-disable no-underscore-dangle, max-lines, object-property-newline */
92
+
93
+
94
+ /**
95
+ * @typedef {Object} FormStateMember - The form state member.
96
+ * @property {string | number | boolean | string[] | null} value - The value of the form element.
97
+ * @property {ValidityState} validity - The validity state of the form element, stored when fired from the form element.
98
+ * @property {boolean} required - Whether the form element is required or not.
99
+ * @property {HTMLElement} element - Whether the form element is required or not.
100
+ */
101
+
102
+ /**
103
+ * @typedef {Object.<string, FormStateMember>} FormState - The form state.
104
+ */
105
+
106
+ /**
107
+ * The `auro-form` element provides users a way to create and manage forms in a consistent manner.
108
+ * @customElement auro-form
109
+ *
110
+ * @slot default - The default slot for form elements.
111
+ *
112
+ * @event input - Fires when a child form element receives user input.
113
+ * @event change - Fires when a child form element's value changes or the form is initialized.
114
+ * @event reset - Fires when the form is reset. The event detail contains the previous value of the form before reset.
115
+ * @event submit - Fires when the form is submitted. The event detail contains the current value of the form.
116
+ */
117
+ class AuroForm extends i$1 {
118
+ static get properties() {
119
+ return {
120
+
121
+ /** @private */
122
+ formState: { type: Object, attribute: false },
123
+
124
+ /** @private */
125
+ _validity: { type: Object, attribute: false },
126
+
127
+ /** @private */
128
+ _isInitialState: { type: Boolean, attribute: false },
129
+
130
+ /** @private */
131
+ _elements: { type: Array, attribute: false },
132
+
133
+ /** @private */
134
+ _submitElements: { type: Array, attribute: false },
135
+
136
+ /** @private */
137
+ _resetElements: { type: Array, attribute: false },
138
+ };
139
+ }
140
+
141
+ constructor() {
142
+ super();
143
+
144
+ /**
145
+ * @type {FormState}
146
+ * @private
147
+ */
148
+ this.formState = {};
149
+
150
+ /**
151
+ * @type {"valid" | "invalid" | null}
152
+ * @private
153
+ */
154
+ this._validity = null;
155
+
156
+ /** @private */
157
+ this._isInitialState = true;
158
+
159
+ /**
160
+ * @type {(HTMLElement & {reset: () => void})[]}
161
+ * @private
162
+ */
163
+ this._elements = [];
164
+
165
+ /**
166
+ * @type {HTMLButtonElement[]}
167
+ * @private
168
+ */
169
+ this._submitElements = [];
170
+
171
+ /**
172
+ * @type {HTMLButtonElement[]}
173
+ * @private
174
+ */
175
+ this._resetElements = [];
176
+
177
+ /**
178
+ * @private
179
+ * @type {MutationObserver[]}
180
+ */
181
+ this.mutationObservers = [];
182
+
183
+ // Bind listeners
184
+ /** @private */
185
+ this.reset = this.reset.bind(this);
186
+
187
+ /** @private */
188
+ this.submit = this.submit.bind(this);
189
+
190
+ /** @private */
191
+ this.sharedInputListener = this.sharedInputListener.bind(this);
192
+
193
+ /** @private */
194
+ this.sharedValidationListener = this.sharedValidationListener.bind(this);
195
+
196
+ /** @private */
197
+ this.mutationEventListener = this.mutationEventListener.bind(this);
198
+
199
+ /** @private */
200
+ this.handleKeyDown = this.handleKeyDown.bind(this);
201
+ }
202
+
203
+ // Note: button is NOT considered a form element in this context
204
+ // as it does not have a .value property.
205
+ static get formElementTags() {
206
+ return [
207
+ 'auro-input',
208
+ 'auro-select',
209
+ 'auro-datepicker',
210
+ 'auro-combobox',
211
+ // checkbox and radio are grouped elements
212
+ 'auro-checkbox-group',
213
+ 'auro-radio-group',
214
+ // while counter is groupable, the group is for min/max values and not for grouped values
215
+ 'auro-counter-group'
216
+ ];
217
+ }
218
+
219
+ /**
220
+ * Compare tag name with element to identify it (for API purposes).
221
+ * @param {string} elementTag - The HTML tag name like `auro-datepicker`.
222
+ * @param {HTMLElement} element - The actual HTML element to compare.
223
+ * @returns {boolean}
224
+ * @private
225
+ */
226
+ _isElementTag(elementTag, element) {
227
+ return element.tagName.toLowerCase() === elementTag || element.hasAttribute(elementTag.toLowerCase());
228
+ }
229
+
230
+ /**
231
+ * Shared code for determining if an element is something we care about (submit, form element, etc.).
232
+ * @param {string[]} collection - The array to use for tag name search.
233
+ * @param {HTMLElement} element - The element to compare against the master list.
234
+ * @returns {boolean}
235
+ * @private
236
+ */
237
+ _isInElementCollection(collection, element) {
238
+ return collection.some((elementTag) => this._isElementTag(elementTag, element));
239
+ }
240
+
241
+ /**
242
+ * Check if the tag name is a form element.
243
+ * @param {HTMLElement} element - The element to check (attr or tag name).
244
+ * @returns {boolean}
245
+ * @private
246
+ */
247
+ isFormElement(element) {
248
+ return this._isInElementCollection(AuroForm.formElementTags, element);
249
+ }
250
+
251
+ /**
252
+ * Validates if an event is from a valid form element with a name.
253
+ * @param {Event} event - The event to validate.
254
+ * @returns {boolean} - True if event is valid for processing.
255
+ * @private
256
+ */
257
+ _eventIsValidFormEvent(event) {
258
+ const targetName = event.target.getAttribute("name");
259
+ return this.isFormElement(event.target) && targetName;
260
+ }
261
+
262
+
263
+ static get buttonElementTags() {
264
+ return [
265
+ 'button',
266
+ 'auro-button',
267
+ ];
268
+ }
269
+
270
+ /**
271
+ * Check if the tag name is a button element.
272
+ * @param {HTMLElement} element - The element to check.
273
+ * @returns {boolean}
274
+ * @private
275
+ */
276
+ isButtonElement(element) {
277
+ return this._isInElementCollection(AuroForm.buttonElementTags, element);
278
+ }
279
+
280
+ static get styles() {
281
+ return [styleCss];
282
+ }
283
+
284
+ /**
285
+ * Returns the current values of all named form elements as a key-value object, keyed by each element's `name` attribute.
286
+ * @returns {Record<string, string | number | boolean | string[] | null>} The current form values.
287
+ */
288
+ get value() {
289
+ return Object.keys(this.formState).reduce((acc, key) => {
290
+ acc[key] = this.formState[key].value;
291
+ return acc;
292
+ }, {});
293
+ }
294
+
295
+ /**
296
+ * Getter for internal _submitElements.
297
+ * @returns {HTMLButtonElement[]}
298
+ * @private
299
+ */
300
+ get submitElements() {
301
+ return this._submitElements;
302
+ }
303
+
304
+ /**
305
+ * Returns a collection of elements that will reset the form.
306
+ * @returns {HTMLButtonElement[]}
307
+ * @private
308
+ */
309
+ get resetElements() {
310
+ return this._resetElements;
311
+ }
312
+
313
+ /**
314
+ * Infer validity status based on current formState.
315
+ * @private
316
+ */
317
+ _calculateValidity() {
318
+ if (this.isInitialState) {
319
+ this._validity = null;
320
+ } else {
321
+ // go through validity states and return the first invalid state (if any)
322
+ const invalidKey = Object.keys(this.formState).
323
+ find((key) => {
324
+ const formKey = this.formState[key];
325
+ // these are NOT extra parens
326
+ // eslint-disable-next-line no-extra-parens
327
+ return (formKey.validity !== 'valid' && formKey.required) || (formKey.validity !== 'valid' && formKey.value !== null);
328
+ });
329
+ this._validity = invalidKey ? 'invalid' : 'valid';
330
+ }
331
+ }
332
+
333
+ /**
334
+ * Returns `'valid'` if all required and interacted-with form elements are valid, `'invalid'` if any are not, or `null` if the form has not been interacted with yet.
335
+ * @returns {"valid" | "invalid" | null}
336
+ */
337
+ get validity() {
338
+ // Force calculate, as sometimes validity won't reflect
339
+ // the latest value while in-between renders.
340
+ this._calculateValidity();
341
+ return this._validity;
342
+ }
343
+
344
+ /**
345
+ * Determines whether the form is in its initial (untouched) state and updates `_isInitialState` accordingly.
346
+ * @returns {void}
347
+ * @private
348
+ */
349
+ _setInitialState() {
350
+ const anyTainted = Object.keys(this.formState).some((key) => this.formState[key].validity !== null || this.formState[key].value !== null);
351
+
352
+ this._isInitialState = !anyTainted;
353
+
354
+ this._resetElements.forEach((resetElement) => {
355
+ if (resetElement.hasAttribute("disabled")) {
356
+ resetElement.removeAttribute("disabled");
357
+ }
358
+ });
359
+ }
360
+
361
+ /**
362
+ * Returns `true` if no form element has been interacted with or had its value changed since the form was initialized or last reset.
363
+ * @returns {boolean}
364
+ */
365
+ get isInitialState() {
366
+ return this._isInitialState;
367
+ }
368
+
369
+ /**
370
+ * Enables or disables submit and reset buttons based on the current form state and validity.
371
+ * @returns {void}
372
+ * @private
373
+ */
374
+ setDisabledStateOnButtons() {
375
+ this._resetElements.forEach((element) => {
376
+ if (this.isInitialState) {
377
+ element.setAttribute("disabled", "");
378
+ } else {
379
+ element.removeAttribute("disabled");
380
+ }
381
+ });
382
+
383
+ this._submitElements.forEach((element) => {
384
+ if (this.isInitialState || this.validity !== "valid") {
385
+ element.setAttribute("disabled", "");
386
+ } else {
387
+ element.removeAttribute("disabled");
388
+ }
389
+ });
390
+ }
391
+
392
+ /**
393
+ * Construct the query strings from elements, append them together, execute, and return the NodeList.
394
+ * @returns {NodeList}
395
+ * @private
396
+ */
397
+ queryAuroElements() {
398
+ const queries = [
399
+ [
400
+ AuroForm.formElementTags,
401
+ '[name]'
402
+ ],
403
+ [
404
+ AuroForm.buttonElementTags,
405
+ '[type=submit]'
406
+ ],
407
+ [
408
+ AuroForm.buttonElementTags,
409
+ '[type=reset]'
410
+ ]
411
+ ];
412
+
413
+ return this.querySelectorAll(queries.flatMap(([
414
+ tags,
415
+ extraAttributes
416
+ ]) => tags.map((tag) => `${tag}${extraAttributes}, [${tag}]${extraAttributes}`)).join(', '));
417
+ }
418
+
419
+ /**
420
+ * Store an element in state and on the _elements array.
421
+ * @param {HTMLElement} element - The element to add to our state.
422
+ * @private
423
+ */
424
+ _addElementToState(element) {
425
+ const targetName = element.getAttribute('name');
426
+ if (this.formState[targetName]) {
427
+ return;
428
+ }
429
+
430
+ this.formState[targetName] = {
431
+ value: element.value || element.getAttribute('value'),
432
+ validity: element.validity || null,
433
+ required: element.hasAttribute('required'),
434
+ // element
435
+ };
436
+
437
+ this._elements.push(element);
438
+ }
439
+
440
+ /**
441
+ * Initialize (or reinitialize) the form state.
442
+ * @returns {void}
443
+ * @private
444
+ */
445
+ initializeState() {
446
+ this.formState = {};
447
+ this._submitElements = [];
448
+ this._resetElements = [];
449
+ this._elements = [];
450
+
451
+ this.queryAuroElements().forEach((element) => {
452
+ if (this.isFormElement(element)) {
453
+ this._addElementToState(element);
454
+ }
455
+
456
+ if (this.isButtonElement(element) && element.getAttribute('type') === 'submit') {
457
+ element.removeEventListener('click', this.submit);
458
+ element.addEventListener('click', this.submit);
459
+
460
+ // Keep record of this element, so we can enable/disable as needed
461
+ this._submitElements.push(element);
462
+ }
463
+
464
+ if (this.isButtonElement(element) && element.getAttribute('type') === 'reset') {
465
+ // Keep record of this element, so we can enable/disable as needed
466
+ element.removeEventListener('click', this.reset);
467
+ element.addEventListener('click', this.reset);
468
+
469
+ this._resetElements.push(element);
470
+ }
471
+ });
472
+
473
+ this.dispatchEvent(new Event('change', {
474
+ bubbles: true,
475
+ composed: true,
476
+ cancelable: true
477
+ }));
478
+
479
+ // Set enabled/disabled states on buttons
480
+ this.setDisabledStateOnButtons();
481
+ }
482
+
483
+ /**
484
+ * Resets all form elements to their initial state and fires a `reset` event. The event's `detail.previousValue` contains the form values captured immediately before the reset.
485
+ * @returns {void}
486
+ */
487
+ reset() {
488
+ const previousValue = this.value;
489
+ this._elements.forEach((element) => element.reset());
490
+
491
+ this.updateComplete.then(() => {
492
+ this.initializeState();
493
+ // Initial state must come first - validity can only be null if initial state is true
494
+ this._setInitialState();
495
+ this._calculateValidity();
496
+
497
+ // Wait for the above changes to run through, then disable submit/reset
498
+ this.updateComplete.then(() => {
499
+ this.setDisabledStateOnButtons();
500
+
501
+ this.dispatchEvent(new CustomEvent('reset', {
502
+ bubbles: true,
503
+ composed: true,
504
+ detail: {
505
+ previousValue
506
+ }
507
+ }));
508
+ });
509
+ });
510
+ }
511
+
512
+ /**
513
+ * Validates all form elements. If all are valid, fires a `submit` event with `detail.value` containing the current form values. If any element is invalid, its error state is surfaced and the `submit` event is not fired.
514
+ * @returns {Promise<void>}
515
+ */
516
+ async submit() {
517
+ // Force validation on ALL elements
518
+ this._elements.forEach((element) => {
519
+ element.validate(true);
520
+ });
521
+
522
+ // Wait for validation to complete and formState to update
523
+ await this.updateComplete;
524
+
525
+ // Only dispatch submit event if form is valid
526
+ if (this.validity === 'valid') {
527
+ this.dispatchEvent(new CustomEvent('submit', {
528
+ bubbles: true,
529
+ composed: true,
530
+ detail: {
531
+ value: this.value
532
+ }
533
+ }));
534
+ }
535
+ }
536
+
537
+ /**
538
+ * Registers the `auro-form` custom element with the browser under a given tag name.
539
+ * @param {string} [name="auro-form"] - The custom element tag name to register.
540
+ *
541
+ * @example
542
+ * AuroForm.register("custom-form") // registers as <custom-form>
543
+ */
544
+ static register(name = "auro-form") {
545
+ AuroLibraryRuntimeUtils.prototype.registerComponent(name, AuroForm);
546
+ }
547
+
548
+ /**
549
+ * Shared input listener for all form elements.
550
+ * @param {Event} event - The event that is fired from the form element.
551
+ * @private
552
+ */
553
+ sharedInputListener(event) {
554
+ const targetName = event.target.getAttribute("name");
555
+
556
+ // This should only happen if some bubble-up event is fired from inside a form element.
557
+ if (!this._eventIsValidFormEvent(event)) {
558
+ return;
559
+ }
560
+
561
+ // Occasionally, a form element will emit their event before the form can read data about the form element.
562
+ if (!this.formState[targetName] && this.isFormElement(event.target)) {
563
+ this._addElementToState(event.target);
564
+ }
565
+
566
+ // Check special input types and handle their edge cases
567
+ if (this._isElementTag('auro-datepicker', event.target) && event.target.hasAttribute('range')) {
568
+ this.formState[targetName].value = event.target.values;
569
+ } else {
570
+ this.formState[targetName].value = event.target.value;
571
+ }
572
+
573
+ this.requestUpdate('formState');
574
+ this.dispatchEvent(new CustomEvent('change', {
575
+ bubbles: true,
576
+ composed: true,
577
+ cancelable: true
578
+ }));
579
+ }
580
+
581
+ /**
582
+ * Shared validation listener for all form elements.
583
+ * @param {Event} event - The event that is fired from the form element.
584
+ * @private
585
+ */
586
+ sharedValidationListener(event) {
587
+ const targetName = event.target.getAttribute("name");
588
+ if (!this._eventIsValidFormEvent(event)) {
589
+ return;
590
+ }
591
+
592
+ if (!this.formState[targetName]) {
593
+ this._addElementToState(event.target);
594
+ }
595
+
596
+ this.formState[targetName].validity = event.detail.validity;
597
+ this._calculateValidity();
598
+ this.requestUpdate('formState');
599
+ }
600
+
601
+ /**
602
+ * Handle Enter key press on form elements.
603
+ * @param {KeyboardEvent} event - The keyboard event.
604
+ * @private
605
+ */
606
+ handleKeyDown(event) {
607
+ if (event.key === 'Enter' && this.isFormElement(event.target)) {
608
+ // Don't submit if it's a textarea (allow new lines)
609
+ if (event.target.tagName.toLowerCase() === 'textarea' ||
610
+ event.target.hasAttribute('textarea')) {
611
+ return;
612
+ }
613
+
614
+ event.preventDefault();
615
+ this.submit();
616
+ }
617
+ }
618
+
619
+ /**
620
+ * Attaches input, validation, and keydown listeners to all tracked form and button elements.
621
+ * Removes existing listeners first to avoid duplicates on re-initialization.
622
+ * @returns {void}
623
+ * @private
624
+ */
625
+ _attachEventListeners() {
626
+ this.queryAuroElements().forEach((element) => {
627
+ // remove any existing event listeners (in case of re-initialization)
628
+ element.removeEventListener('input', this.sharedInputListener);
629
+ element.removeEventListener('auroFormElement-validated', this.sharedValidationListener);
630
+ element.removeEventListener('keydown', this.handleKeyDown);
631
+
632
+ // add new event listeners
633
+ element.addEventListener('input', this.sharedInputListener);
634
+ element.addEventListener('auroFormElement-validated', this.sharedValidationListener);
635
+ element.addEventListener('keydown', this.handleKeyDown);
636
+ });
637
+ }
638
+
639
+ /**
640
+ * @param {import('lit').PropertyValues} _changedProperties - Map of changed properties with their previous values.
641
+ * @returns {void}
642
+ */
643
+ firstUpdated(_changedProperties) {
644
+ super.firstUpdated(_changedProperties);
645
+
646
+ this._attachEventListeners();
647
+ }
648
+
649
+ /**
650
+ * @param {import('lit').PropertyValues} _changedProperties - Map of changed properties with their previous values.
651
+ * @returns {void}
652
+ */
653
+ updated(_changedProperties) {
654
+ super.updated(_changedProperties);
655
+
656
+ if (_changedProperties.has("formState")) {
657
+ this._setInitialState();
658
+
659
+ // Automatically infer disabled state now
660
+ this.setDisabledStateOnButtons();
661
+ }
662
+
663
+ if (_changedProperties.has("_validity")) {
664
+ this._setInitialState();
665
+ }
666
+ }
667
+
668
+ /**
669
+ * Mutation observer for form elements. Slot change does not trigger unless
670
+ * root-level elements are added/removed. This is a workaround to ensure
671
+ * nested form elements are also observed.
672
+ *
673
+ * @returns {void}
674
+ * @private
675
+ */
676
+ mutationEventListener() {
677
+ this.initializeState();
678
+ this._attachEventListeners();
679
+ }
680
+
681
+ /**
682
+ * Slot change event listener. This is the main entry point for the form element.
683
+ * @param {Event} event - The slot change event.
684
+ * @returns {void}
685
+ * @private
686
+ */
687
+ onSlotChange(event) {
688
+ this.initializeState();
689
+ // Safe to call as we remove and re-add event listeners
690
+ this._attachEventListeners();
691
+
692
+ // Get rid of old observers - we'll create new ones in a moment
693
+ this.mutationObservers.forEach((mo) => mo.disconnect());
694
+ this.mutationObservers = [];
695
+
696
+ const slotNodes = event.currentTarget.assignedNodes();
697
+ slotNodes.forEach((node) => {
698
+ if (node.tagName && !this.isFormElement(node)) {
699
+ const mo = new MutationObserver(this.mutationEventListener);
700
+ mo.observe(node, {
701
+ subtree: true,
702
+ childList: true
703
+ });
704
+ this.mutationObservers.push(mo);
705
+ }
706
+ });
707
+ }
708
+
709
+ /**
710
+ * @returns {import('lit').TemplateResult}
711
+ */
712
+ render() {
713
+ return b$1`
714
+ <form>
715
+ <slot @slotchange="${this.onSlotChange}"></slot>
716
+ </form>
717
+ `;
718
+ }
719
+ }
720
+
7
721
  AuroForm.register();
8
722
  AuroForm.register('custom-form');