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