@aurodesignsystem/auro-formkit 2.0.0-beta.15 → 2.0.0-beta.16

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 (136) hide show
  1. package/.turbo/cache/000950e13093845f-meta.json +1 -1
  2. package/.turbo/cache/000950e13093845f.tar.zst +0 -0
  3. package/.turbo/cache/02ee016619902665-meta.json +1 -0
  4. package/.turbo/cache/02ee016619902665.tar.zst +0 -0
  5. package/.turbo/cache/1b97859030a6d60b-meta.json +1 -1
  6. package/.turbo/cache/1e855b2163b7c980-meta.json +1 -0
  7. package/.turbo/cache/1e855b2163b7c980.tar.zst +0 -0
  8. package/.turbo/cache/1f6653e2ed4c0087-meta.json +1 -1
  9. package/.turbo/cache/1f6653e2ed4c0087.tar.zst +0 -0
  10. package/.turbo/cache/2059bc724ac24519-meta.json +1 -1
  11. package/.turbo/cache/2059bc724ac24519.tar.zst +0 -0
  12. package/.turbo/cache/23b3a2fea223679d-meta.json +1 -1
  13. package/.turbo/cache/23b3a2fea223679d.tar.zst +0 -0
  14. package/.turbo/cache/2c0d681132c153dd-meta.json +1 -1
  15. package/.turbo/cache/31bd5687f46c7eba-meta.json +1 -1
  16. package/.turbo/cache/3488cff10dd06acd-meta.json +1 -1
  17. package/.turbo/cache/3488cff10dd06acd.tar.zst +0 -0
  18. package/.turbo/cache/3c8718d2ba6d3fe5-meta.json +1 -0
  19. package/.turbo/cache/3c8718d2ba6d3fe5.tar.zst +0 -0
  20. package/.turbo/cache/4006a206400d5c7b-meta.json +1 -1
  21. package/.turbo/cache/43693504bf6e7c4c-meta.json +1 -1
  22. package/.turbo/cache/45cd32cd38eacbcb-meta.json +1 -1
  23. package/.turbo/cache/492dda333b8d15f1-meta.json +1 -1
  24. package/.turbo/cache/50cd7dcfc9f820c5-meta.json +1 -1
  25. package/.turbo/cache/51eaa58d5c167de8-meta.json +1 -1
  26. package/.turbo/cache/56f2745125fdd552-meta.json +1 -1
  27. package/.turbo/cache/56f2745125fdd552.tar.zst +0 -0
  28. package/.turbo/cache/5f43911cbacf7df1-meta.json +1 -1
  29. package/.turbo/cache/5f43911cbacf7df1.tar.zst +0 -0
  30. package/.turbo/cache/6081837e8943b62e-meta.json +1 -1
  31. package/.turbo/cache/60ad74320c682a2b-meta.json +1 -1
  32. package/.turbo/cache/61e218aba69cff58-meta.json +1 -1
  33. package/.turbo/cache/6951c2a52e5ab5f7-meta.json +1 -1
  34. package/.turbo/cache/6951c2a52e5ab5f7.tar.zst +0 -0
  35. package/.turbo/cache/77da375a012de9d0-meta.json +1 -1
  36. package/.turbo/cache/78418f9089673a21-meta.json +1 -1
  37. package/.turbo/cache/7964d1656e9e702a-meta.json +1 -1
  38. package/.turbo/cache/7bf2b06a479d0b30-meta.json +1 -1
  39. package/.turbo/cache/7c9ca6163e61285c-meta.json +1 -1
  40. package/.turbo/cache/80410f4b5990ab83-meta.json +1 -1
  41. package/.turbo/cache/876b8fa390c8ec81-meta.json +1 -1
  42. package/.turbo/cache/8bb856bd31b5b479-meta.json +1 -1
  43. package/.turbo/cache/8f287fd5d33579ae-meta.json +1 -1
  44. package/.turbo/cache/8f287fd5d33579ae.tar.zst +0 -0
  45. package/.turbo/cache/8f5fef3e04a6fcfa-meta.json +1 -1
  46. package/.turbo/cache/8fcce3ba8357be57-meta.json +1 -0
  47. package/.turbo/cache/8fcce3ba8357be57.tar.zst +0 -0
  48. package/.turbo/cache/9b5868be65819fdf-meta.json +1 -0
  49. package/.turbo/cache/9b5868be65819fdf.tar.zst +0 -0
  50. package/.turbo/cache/b5e6dc7fb9ae1a2f-meta.json +1 -1
  51. package/.turbo/cache/b6a202cc85cb61a0-meta.json +1 -1
  52. package/.turbo/cache/ba270a0ef147f2e5-meta.json +1 -1
  53. package/.turbo/cache/be0b95293ea517cc-meta.json +1 -1
  54. package/.turbo/cache/c03a12ff38ba1e02-meta.json +1 -1
  55. package/.turbo/cache/c1312c6f8c051461-meta.json +1 -1
  56. package/.turbo/cache/c1312c6f8c051461.tar.zst +0 -0
  57. package/.turbo/cache/c3b2cc1b044a4135-meta.json +1 -0
  58. package/.turbo/cache/c3b2cc1b044a4135.tar.zst +0 -0
  59. package/.turbo/cache/c6c6411199b68170-meta.json +1 -1
  60. package/.turbo/cache/c6dbc49c3038946d-meta.json +1 -1
  61. package/.turbo/cache/cae7586c45bed13e-meta.json +1 -1
  62. package/.turbo/cache/cae7586c45bed13e.tar.zst +0 -0
  63. package/.turbo/cache/cf143eb1a55684db-meta.json +1 -1
  64. package/.turbo/cache/cf143eb1a55684db.tar.zst +0 -0
  65. package/.turbo/cache/d5b85352bc667f19-meta.json +1 -1
  66. package/.turbo/cache/d5b85352bc667f19.tar.zst +0 -0
  67. package/.turbo/cache/d5db503b2eaf239c-meta.json +1 -1
  68. package/.turbo/cache/d775555355d6b8fc-meta.json +1 -1
  69. package/.turbo/cache/d7c3007be148d2a1-meta.json +1 -1
  70. package/.turbo/cache/dad3d78b33edd9e4-meta.json +1 -1
  71. package/.turbo/cache/db5e65d819bfe66b-meta.json +1 -1
  72. package/.turbo/cache/dc597b3ea4f61ec8-meta.json +1 -1
  73. package/.turbo/cache/e392fe0927e4af23-meta.json +1 -0
  74. package/.turbo/cache/e392fe0927e4af23.tar.zst +0 -0
  75. package/.turbo/cache/e62cfee068e3ef36-meta.json +1 -1
  76. package/.turbo/cache/e9e36823f6c98f07-meta.json +1 -1
  77. package/.turbo/cache/ea8dd91dba19ddee-meta.json +1 -1
  78. package/.turbo/cache/ea8dd91dba19ddee.tar.zst +0 -0
  79. package/.turbo/cache/f2913bf19939d840-meta.json +1 -1
  80. package/.turbo/cache/f2913bf19939d840.tar.zst +0 -0
  81. package/.turbo/cache/f86b28e5ea2c66fe-meta.json +1 -1
  82. package/CHANGELOG.md +15 -0
  83. package/components/checkbox/.turbo/turbo-build.log +3 -3
  84. package/components/checkbox/.turbo/turbo-bundler.log +3 -3
  85. package/components/checkbox/README.md +1 -1
  86. package/components/combobox/.turbo/turbo-build.log +3 -3
  87. package/components/combobox/README.md +4 -4
  88. package/components/counter/.turbo/turbo-build.log +3 -3
  89. package/components/counter/.turbo/turbo-bundler.log +3 -3
  90. package/components/counter/README.md +1 -1
  91. package/components/datepicker/.turbo/turbo-build.log +3 -3
  92. package/components/datepicker/README.md +4 -4
  93. package/components/dropdown/.turbo/turbo-build.log +3 -3
  94. package/components/dropdown/.turbo/turbo-bundler.log +2 -2
  95. package/components/dropdown/README.md +1 -1
  96. package/components/form/.turbo/turbo-build.log +3 -3
  97. package/components/form/.turbo/turbo-bundler.log +3 -3
  98. package/components/form/README.md +11 -9
  99. package/components/form/demo/api.min.js +187 -22
  100. package/components/form/demo/index.min.js +187 -22
  101. package/components/form/demo/registerDemoDeps.js +5 -0
  102. package/components/form/demo/working.html +86 -0
  103. package/components/form/dist/auro-form.d.ts +83 -2
  104. package/components/form/dist/auro-form.d.ts.map +1 -1
  105. package/components/form/dist/index.js +187 -22
  106. package/components/form/package.json +3 -1
  107. package/components/form/src/auro-form.js +187 -25
  108. package/components/form/src/styles/style-css.js +1 -1
  109. package/components/form/src/styles/style.css +8 -0
  110. package/components/form/src/styles/style.scss +10 -0
  111. package/components/input/.turbo/turbo-build.log +3 -3
  112. package/components/input/.turbo/turbo-bundler.log +3 -3
  113. package/components/input/README.md +1 -1
  114. package/components/menu/.turbo/turbo-build.log +3 -3
  115. package/components/menu/.turbo/turbo-bundler.log +3 -3
  116. package/components/menu/README.md +1 -1
  117. package/components/radio/.turbo/turbo-build.log +3 -3
  118. package/components/radio/.turbo/turbo-bundler.log +3 -3
  119. package/components/radio/README.md +1 -1
  120. package/components/select/.turbo/turbo-build.log +3 -3
  121. package/components/select/README.md +3 -3
  122. package/package.json +1 -1
  123. package/.turbo/cache/07028b4d43bdf4e1-meta.json +0 -1
  124. package/.turbo/cache/07028b4d43bdf4e1.tar.zst +0 -0
  125. package/.turbo/cache/0c8124a987c1cc05-meta.json +0 -1
  126. package/.turbo/cache/0c8124a987c1cc05.tar.zst +0 -0
  127. package/.turbo/cache/2690f6279766d11d-meta.json +0 -1
  128. package/.turbo/cache/2690f6279766d11d.tar.zst +0 -0
  129. package/.turbo/cache/50993de942ec15a9-meta.json +0 -1
  130. package/.turbo/cache/50993de942ec15a9.tar.zst +0 -0
  131. package/.turbo/cache/6830a9e37c9d391f-meta.json +0 -1
  132. package/.turbo/cache/6830a9e37c9d391f.tar.zst +0 -0
  133. package/.turbo/cache/9ae99e8e7bd83d06-meta.json +0 -1
  134. package/.turbo/cache/9ae99e8e7bd83d06.tar.zst +0 -0
  135. package/.turbo/cache/bfefe028012c089a-meta.json +0 -1
  136. package/.turbo/cache/bfefe028012c089a.tar.zst +0 -0
@@ -75,10 +75,11 @@ import "@aurodesignsystem/auro-formkit/auro-form";
75
75
  <!-- The below code snippet is automatically added from ./apiExamples/basic.html -->
76
76
 
77
77
  ```html
78
- <auro-form>Hello World</auro-form>
79
- <auro-input>
80
- <span slot="label">Hello World</span>
81
- </auro-input>
78
+ <auro-form>
79
+ <auro-input>
80
+ <span slot="label">Hello World</span>
81
+ </auro-input>
82
+ </auro-form>
82
83
  ```
83
84
  <!-- AURO-GENERATED-CONTENT:END -->
84
85
 
@@ -97,7 +98,7 @@ In cases where the project is not able to process JS assets, there are pre-proce
97
98
  <!-- The below content is automatically added from ../../docs/templates/componentBundleUseModBrowsers.md -->
98
99
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@aurodesignsystem/design-tokens@4.12.1/dist/tokens/CSSCustomProperties.css" />
99
100
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@aurodesignsystem/webcorestylesheets@5.1.2/dist/bundled/essentials.css" />
100
- <script src="https://cdn.jsdelivr.net/npm/@aurodesignsystem/auro-formkit/auro-form@2.0.0-beta.14/dist/auro-form__bundled.js" type="module"></script>
101
+ <script src="https://cdn.jsdelivr.net/npm/@aurodesignsystem/auro-formkit/auro-form@2.0.0-beta.15/dist/auro-form__bundled.js" type="module"></script>
101
102
  <!-- AURO-GENERATED-CONTENT:END -->
102
103
 
103
104
  ## auro-form use cases
@@ -119,10 +120,11 @@ The `<auro-form>` element should be used in situations where users may:
119
120
  <!-- The below code snippet is automatically added from ./apiExamples/basic.html -->
120
121
 
121
122
  ```html
122
- <auro-form>Hello World</auro-form>
123
- <auro-input>
124
- <span slot="label">Hello World</span>
125
- </auro-input>
123
+ <auro-form>
124
+ <auro-input>
125
+ <span slot="label">Hello World</span>
126
+ </auro-input>
127
+ </auro-form>
126
128
  ```
127
129
  <!-- AURO-GENERATED-CONTENT:END -->
128
130
 
@@ -24,7 +24,7 @@ const t=globalThis,i$1=t.trustedTypes,s=i$1?i$1.createPolicy("lit-html",{createH
24
24
  * SPDX-License-Identifier: BSD-3-Clause
25
25
  */class r extends b{constructor(){super(...arguments),this.renderOptions={host:this},this._$Do=undefined;}createRenderRoot(){const t=super.createRenderRoot();return this.renderOptions.renderBefore??=t.firstChild,t}update(t){const s=this.render();this.hasUpdated||(this.renderOptions.isConnected=this.isConnected),super.update(t),this._$Do=B(s,this.renderRoot,this.renderOptions);}connectedCallback(){super.connectedCallback(),this._$Do?.setConnected(true);}disconnectedCallback(){super.disconnectedCallback(),this._$Do?.setConnected(false);}render(){return T}}r._$litElement$=true,r["finalized"]=true,globalThis.litElementHydrateSupport?.({LitElement:r});const i=globalThis.litElementPolyfillSupport;i?.({LitElement:r});(globalThis.litElementVersions??=[]).push("4.1.1");
26
26
 
27
- var styleCss = i$3`*,*:before,*:after{box-sizing:border-box}@media(prefers-reduced-motion: reduce){*,*:before,*:after{animation-duration:.01ms !important;animation-iteration-count:1 !important;transition-duration:.01ms !important}}*:focus-visible{outline:0}*:focus-visible{outline:0}:focus:not(:focus-visible){outline:3px solid transparent}.testClass{display:inline-block;padding:var(--auro-text-body-size-default);border:1px solid var(--auro-color-border-error-on-light);color:var(--auro-color-border-error-on-light)}:focus-visible{background-color:var(--auro-color-border-error-on-light);color:var(--auro-color-base-white)}`;
27
+ var styleCss = i$3`*,*:before,*:after{box-sizing:border-box}@media(prefers-reduced-motion: reduce){*,*:before,*:after{animation-duration:.01ms !important;animation-iteration-count:1 !important;transition-duration:.01ms !important}}*:focus-visible{outline:0}*:focus-visible{outline:0}:focus:not(:focus-visible){outline:3px solid transparent}.testClass{display:inline-block;padding:var(--auro-text-body-size-default);border:1px solid var(--auro-color-border-error-on-light);color:var(--auro-color-border-error-on-light)}:focus-visible{background-color:var(--auro-color-border-error-on-light);color:var(--auro-color-base-white)}:host form{display:block;width:100%;padding:1rem;border:1px solid #2a2a2a;border-radius:1rem}`;
28
28
 
29
29
  // Copyright (c) Alaska Air. All right reserved. Licensed under the Apache-2.0 license
30
30
  // See LICENSE in the project root for license information.
@@ -96,8 +96,20 @@ class AuroLibraryRuntimeUtils {
96
96
  }
97
97
  }
98
98
 
99
- // Copyright (c) 2024 Alaska Airlines. All right reserved. Licensed under the Apache-2.0 license
100
- // See LICENSE in the project root for license information.
99
+ /* eslint-disable no-underscore-dangle */
100
+
101
+
102
+ /**
103
+ * @typedef {Object} FormStateMember - The form state member.
104
+ * @property {string | number | boolean | string[] | null} value - The value of the form element.
105
+ * @property {ValidityState} validity - The validity state of the form element, stored when fired from the form element.
106
+ * @property {boolean} required - Whether the form element is required or not.
107
+ * @property {HTMLElement} element - Whether the form element is required or not.
108
+ */
109
+
110
+ /**
111
+ * @typedef {Object.<string, FormStateMember>} FormState - The form state.
112
+ */
101
113
 
102
114
 
103
115
  // See https://git.io/JJ6SJ for "How to document your components using JSDoc"
@@ -110,26 +122,138 @@ class AuroLibraryRuntimeUtils {
110
122
 
111
123
  // build the component class
112
124
  class AuroForm extends r {
113
- // constructor() {
114
- // super();
115
- // }
116
-
117
- // This function is to define props used within the scope of this component
118
- // Be sure to review https://lit.dev/docs/components/properties/
119
- // to understand how to use reflected attributes with your property settings.
120
125
  static get properties() {
121
126
  return {
122
- // ...super.properties,
123
-
124
- // this property is DEMO ONLY! Please delete.
125
- cssClass: { type: String }
127
+ _formState: { attribute: false },
126
128
  };
127
129
  }
128
130
 
131
+ constructor() {
132
+ super();
133
+
134
+ /** @type {FormState} */
135
+ this._formState = {};
136
+ }
137
+
138
+ // Note: button is NOT considered a form element in this context
139
+ // as it does not have a .value property.
140
+ static get formElementTags() {
141
+ return [
142
+ 'auro-input',
143
+ 'auro-select',
144
+ 'auro-datepicker',
145
+ 'auro-checkbox-group',
146
+ ];
147
+ }
148
+
149
+ /**
150
+ * Shared code for determining if an element is something we care about (submit, form element, etc.).
151
+ * @param {string[]} collection - The array to use for tag name search.
152
+ * @param {HTMLElement} element - The element to compare against the master list.
153
+ * @returns boolean
154
+ * @private
155
+ */
156
+ _isInElementCollection(collection, element) {
157
+ return collection.some((elementTag) => element.tagName.toLowerCase() === elementTag || element.hasAttribute(elementTag.toLowerCase()));
158
+ }
159
+
160
+ /**
161
+ * Check if the tag name is a form element.
162
+ * @param {HTMLElement} element - The element to check (attr or tag name).
163
+ * @returns {boolean}
164
+ */
165
+ isFormElement(element) {
166
+ return this._isInElementCollection(AuroForm.formElementTags, element);
167
+ }
168
+
169
+ static get submitElementTags() {
170
+ return [
171
+ 'button',
172
+ 'auro-button',
173
+ ];
174
+ }
175
+
176
+ /**
177
+ * Check if the tag name is a submit element.
178
+ * @param {HTMLElement} element - The element to check.
179
+ * @returns {boolean}
180
+ */
181
+ isSubmitElement(element) {
182
+ return this._isInElementCollection(AuroForm.submitElementTags, element);
183
+ }
184
+
129
185
  static get styles() {
130
186
  return [styleCss];
131
187
  }
132
188
 
189
+ /**
190
+ * Reduce the form value into a key-value pair.
191
+ *
192
+ * NOTE: form keys use `name` first, and `id` second if `name` is not available.
193
+ * This follows standard HTML5 form behavior - submission uses `name` by default when creating
194
+ * the FormData object.
195
+ *
196
+ * @returns {Record<string, string | number | boolean | string[] | null>} The form value.
197
+ */
198
+ get value() {
199
+ return Object.keys(this._formState).reduce((acc, key) => {
200
+ acc[key] = this._formState[key].value;
201
+ return acc;
202
+ }, {});
203
+ }
204
+
205
+ get validity() {
206
+ // go through validity states and return the first invalid state (if any)
207
+ const invalidKey = Object.keys(this._formState).
208
+ find((key) => {
209
+ const formKey = this._formState[key];
210
+
211
+ // these are NOT extra :(
212
+ // eslint-disable-next-line no-extra-parens
213
+ return (formKey.validity !== 'valid' && formKey.required) || (formKey.validity !== 'valid' && formKey.value !== null);
214
+ });
215
+
216
+ return invalidKey ? 'invalid' : 'valid';
217
+ }
218
+
219
+ // Below is not implemented yet
220
+ get isInitialState() {
221
+ const anyTainted = Object.keys(this._formState).some((key) => this._formState[key].validity !== null);
222
+
223
+ return !anyTainted;
224
+ }
225
+
226
+ getSubmitFunction() {
227
+ // We return an arrow function here to ensure that the `this` context points at this same AuroForm context.
228
+ // Otherwise, submission tries to read `this.value` on the button element.
229
+ return (event) => {
230
+ event.preventDefault();
231
+
232
+ this.dispatchEvent(new CustomEvent('submit', {
233
+ detail: {
234
+ value: this.value
235
+ }
236
+ }));
237
+ };
238
+ }
239
+
240
+ /**
241
+ * Construct the query strings from elements, append them together, execute, and return the NodeList.
242
+ * @returns {NodeList}
243
+ */
244
+ queryAuroElements() {
245
+ const formElementQuery = AuroForm.formElementTags.map((tag) => `${tag}[name]`).join(',');
246
+ const submitterQuery = AuroForm.submitElementTags.map((tag) => `${tag}[type=submit]`).join(',');
247
+
248
+ // Alternatively, for renamed components...
249
+ const renamedFormElementQuery = AuroForm.formElementTags.map((tag) => `[${tag}][name]`).join(',');
250
+ const renamedSubmitterQuery = AuroForm.formElementTags.map((tag) => `[${tag}][type=submit]`).join(',');
251
+
252
+ const unifiedElementQuery = `${formElementQuery},${submitterQuery},${renamedFormElementQuery},${renamedSubmitterQuery}`;
253
+
254
+ return this.querySelectorAll(unifiedElementQuery);
255
+ }
256
+
133
257
  /**
134
258
  * This will register this element with the browser.
135
259
  * @param {string} [name="auro-form"] - The name of element that you want to register to.
@@ -142,17 +266,58 @@ class AuroForm extends r {
142
266
  AuroLibraryRuntimeUtils.prototype.registerComponent(name, AuroForm);
143
267
  }
144
268
 
145
- // When using auroElement, use the following attribute and function when hiding content from screen readers.
146
- // aria-hidden="${this.hideAudible(this.hiddenAudible)}"
269
+ firstUpdated(_changedProperties) {
270
+ super.firstUpdated(_changedProperties);
271
+
272
+ const slot = this.shadowRoot.querySelector('slot');
273
+
274
+ // Update the form state when a form element is detected
275
+ slot.addEventListener('input', (event) => {
276
+
277
+ /** @type {HTMLInputElement} */
278
+ const eventTarget = event.target;
279
+ if (this.isFormElement(eventTarget)) {
280
+ this._formState[eventTarget.getAttribute("name")].value = eventTarget.value;
281
+ }
282
+ });
283
+
284
+ slot.addEventListener('auroFormElement-validated', (event) => {
285
+ const oldValue = this._formState;
286
+
287
+ this._formState[event.target.getAttribute("name")].validity = event.detail.validity;
288
+ this.requestUpdate('formState', oldValue);
289
+ });
290
+ }
291
+
292
+ onSlotChange() {
293
+ this._formState = {};
147
294
 
148
- // function that renders the HTML and CSS into the scope of the component
295
+ this.queryAuroElements().forEach((element) => {
296
+ if (this.isFormElement(element)) {
297
+ this._formState[element.getAttribute('name')] = {
298
+ value: element.getAttribute('value'),
299
+ validity: element.getAttribute('validity'),
300
+ required: element.hasAttribute('required'),
301
+ element
302
+ };
303
+ }
304
+
305
+ if (this.isSubmitElement(element) && element.getAttribute('type') === 'submit') {
306
+ element.removeEventListener('click', this.getSubmitFunction());
307
+ element.addEventListener('click', this.getSubmitFunction());
308
+ }
309
+ });
310
+ }
311
+
312
+ // function that renders the HTML and CSS into the scope of the component
149
313
  render() {
150
314
  return x`
151
-
152
- <!-- this is demo code, DO NOT USE IN YOUR ELEMENT -->
153
- <div class=${this.cssClass} tabindex="0">
154
- <slot></slot>
155
- </div>
315
+ <form>
316
+ <p>Value: ${JSON.stringify(this.value)}</p>
317
+ <p>Validity: ${this.validity}</p>
318
+ <h3>Auro form example</h3>
319
+ <slot @slotchange="${this.onSlotChange}"></slot>
320
+ </form>
156
321
  `;
157
322
  }
158
323
  }
@@ -24,7 +24,7 @@ const t=globalThis,i$1=t.trustedTypes,s=i$1?i$1.createPolicy("lit-html",{createH
24
24
  * SPDX-License-Identifier: BSD-3-Clause
25
25
  */class r extends b{constructor(){super(...arguments),this.renderOptions={host:this},this._$Do=undefined;}createRenderRoot(){const t=super.createRenderRoot();return this.renderOptions.renderBefore??=t.firstChild,t}update(t){const s=this.render();this.hasUpdated||(this.renderOptions.isConnected=this.isConnected),super.update(t),this._$Do=B(s,this.renderRoot,this.renderOptions);}connectedCallback(){super.connectedCallback(),this._$Do?.setConnected(true);}disconnectedCallback(){super.disconnectedCallback(),this._$Do?.setConnected(false);}render(){return T}}r._$litElement$=true,r["finalized"]=true,globalThis.litElementHydrateSupport?.({LitElement:r});const i=globalThis.litElementPolyfillSupport;i?.({LitElement:r});(globalThis.litElementVersions??=[]).push("4.1.1");
26
26
 
27
- var styleCss = i$3`*,*:before,*:after{box-sizing:border-box}@media(prefers-reduced-motion: reduce){*,*:before,*:after{animation-duration:.01ms !important;animation-iteration-count:1 !important;transition-duration:.01ms !important}}*:focus-visible{outline:0}*:focus-visible{outline:0}:focus:not(:focus-visible){outline:3px solid transparent}.testClass{display:inline-block;padding:var(--auro-text-body-size-default);border:1px solid var(--auro-color-border-error-on-light);color:var(--auro-color-border-error-on-light)}:focus-visible{background-color:var(--auro-color-border-error-on-light);color:var(--auro-color-base-white)}`;
27
+ var styleCss = i$3`*,*:before,*:after{box-sizing:border-box}@media(prefers-reduced-motion: reduce){*,*:before,*:after{animation-duration:.01ms !important;animation-iteration-count:1 !important;transition-duration:.01ms !important}}*:focus-visible{outline:0}*:focus-visible{outline:0}:focus:not(:focus-visible){outline:3px solid transparent}.testClass{display:inline-block;padding:var(--auro-text-body-size-default);border:1px solid var(--auro-color-border-error-on-light);color:var(--auro-color-border-error-on-light)}:focus-visible{background-color:var(--auro-color-border-error-on-light);color:var(--auro-color-base-white)}:host form{display:block;width:100%;padding:1rem;border:1px solid #2a2a2a;border-radius:1rem}`;
28
28
 
29
29
  // Copyright (c) Alaska Air. All right reserved. Licensed under the Apache-2.0 license
30
30
  // See LICENSE in the project root for license information.
@@ -96,8 +96,20 @@ class AuroLibraryRuntimeUtils {
96
96
  }
97
97
  }
98
98
 
99
- // Copyright (c) 2024 Alaska Airlines. All right reserved. Licensed under the Apache-2.0 license
100
- // See LICENSE in the project root for license information.
99
+ /* eslint-disable no-underscore-dangle */
100
+
101
+
102
+ /**
103
+ * @typedef {Object} FormStateMember - The form state member.
104
+ * @property {string | number | boolean | string[] | null} value - The value of the form element.
105
+ * @property {ValidityState} validity - The validity state of the form element, stored when fired from the form element.
106
+ * @property {boolean} required - Whether the form element is required or not.
107
+ * @property {HTMLElement} element - Whether the form element is required or not.
108
+ */
109
+
110
+ /**
111
+ * @typedef {Object.<string, FormStateMember>} FormState - The form state.
112
+ */
101
113
 
102
114
 
103
115
  // See https://git.io/JJ6SJ for "How to document your components using JSDoc"
@@ -110,26 +122,138 @@ class AuroLibraryRuntimeUtils {
110
122
 
111
123
  // build the component class
112
124
  class AuroForm extends r {
113
- // constructor() {
114
- // super();
115
- // }
116
-
117
- // This function is to define props used within the scope of this component
118
- // Be sure to review https://lit.dev/docs/components/properties/
119
- // to understand how to use reflected attributes with your property settings.
120
125
  static get properties() {
121
126
  return {
122
- // ...super.properties,
123
-
124
- // this property is DEMO ONLY! Please delete.
125
- cssClass: { type: String }
127
+ _formState: { attribute: false },
126
128
  };
127
129
  }
128
130
 
131
+ constructor() {
132
+ super();
133
+
134
+ /** @type {FormState} */
135
+ this._formState = {};
136
+ }
137
+
138
+ // Note: button is NOT considered a form element in this context
139
+ // as it does not have a .value property.
140
+ static get formElementTags() {
141
+ return [
142
+ 'auro-input',
143
+ 'auro-select',
144
+ 'auro-datepicker',
145
+ 'auro-checkbox-group',
146
+ ];
147
+ }
148
+
149
+ /**
150
+ * Shared code for determining if an element is something we care about (submit, form element, etc.).
151
+ * @param {string[]} collection - The array to use for tag name search.
152
+ * @param {HTMLElement} element - The element to compare against the master list.
153
+ * @returns boolean
154
+ * @private
155
+ */
156
+ _isInElementCollection(collection, element) {
157
+ return collection.some((elementTag) => element.tagName.toLowerCase() === elementTag || element.hasAttribute(elementTag.toLowerCase()));
158
+ }
159
+
160
+ /**
161
+ * Check if the tag name is a form element.
162
+ * @param {HTMLElement} element - The element to check (attr or tag name).
163
+ * @returns {boolean}
164
+ */
165
+ isFormElement(element) {
166
+ return this._isInElementCollection(AuroForm.formElementTags, element);
167
+ }
168
+
169
+ static get submitElementTags() {
170
+ return [
171
+ 'button',
172
+ 'auro-button',
173
+ ];
174
+ }
175
+
176
+ /**
177
+ * Check if the tag name is a submit element.
178
+ * @param {HTMLElement} element - The element to check.
179
+ * @returns {boolean}
180
+ */
181
+ isSubmitElement(element) {
182
+ return this._isInElementCollection(AuroForm.submitElementTags, element);
183
+ }
184
+
129
185
  static get styles() {
130
186
  return [styleCss];
131
187
  }
132
188
 
189
+ /**
190
+ * Reduce the form value into a key-value pair.
191
+ *
192
+ * NOTE: form keys use `name` first, and `id` second if `name` is not available.
193
+ * This follows standard HTML5 form behavior - submission uses `name` by default when creating
194
+ * the FormData object.
195
+ *
196
+ * @returns {Record<string, string | number | boolean | string[] | null>} The form value.
197
+ */
198
+ get value() {
199
+ return Object.keys(this._formState).reduce((acc, key) => {
200
+ acc[key] = this._formState[key].value;
201
+ return acc;
202
+ }, {});
203
+ }
204
+
205
+ get validity() {
206
+ // go through validity states and return the first invalid state (if any)
207
+ const invalidKey = Object.keys(this._formState).
208
+ find((key) => {
209
+ const formKey = this._formState[key];
210
+
211
+ // these are NOT extra :(
212
+ // eslint-disable-next-line no-extra-parens
213
+ return (formKey.validity !== 'valid' && formKey.required) || (formKey.validity !== 'valid' && formKey.value !== null);
214
+ });
215
+
216
+ return invalidKey ? 'invalid' : 'valid';
217
+ }
218
+
219
+ // Below is not implemented yet
220
+ get isInitialState() {
221
+ const anyTainted = Object.keys(this._formState).some((key) => this._formState[key].validity !== null);
222
+
223
+ return !anyTainted;
224
+ }
225
+
226
+ getSubmitFunction() {
227
+ // We return an arrow function here to ensure that the `this` context points at this same AuroForm context.
228
+ // Otherwise, submission tries to read `this.value` on the button element.
229
+ return (event) => {
230
+ event.preventDefault();
231
+
232
+ this.dispatchEvent(new CustomEvent('submit', {
233
+ detail: {
234
+ value: this.value
235
+ }
236
+ }));
237
+ };
238
+ }
239
+
240
+ /**
241
+ * Construct the query strings from elements, append them together, execute, and return the NodeList.
242
+ * @returns {NodeList}
243
+ */
244
+ queryAuroElements() {
245
+ const formElementQuery = AuroForm.formElementTags.map((tag) => `${tag}[name]`).join(',');
246
+ const submitterQuery = AuroForm.submitElementTags.map((tag) => `${tag}[type=submit]`).join(',');
247
+
248
+ // Alternatively, for renamed components...
249
+ const renamedFormElementQuery = AuroForm.formElementTags.map((tag) => `[${tag}][name]`).join(',');
250
+ const renamedSubmitterQuery = AuroForm.formElementTags.map((tag) => `[${tag}][type=submit]`).join(',');
251
+
252
+ const unifiedElementQuery = `${formElementQuery},${submitterQuery},${renamedFormElementQuery},${renamedSubmitterQuery}`;
253
+
254
+ return this.querySelectorAll(unifiedElementQuery);
255
+ }
256
+
133
257
  /**
134
258
  * This will register this element with the browser.
135
259
  * @param {string} [name="auro-form"] - The name of element that you want to register to.
@@ -142,17 +266,58 @@ class AuroForm extends r {
142
266
  AuroLibraryRuntimeUtils.prototype.registerComponent(name, AuroForm);
143
267
  }
144
268
 
145
- // When using auroElement, use the following attribute and function when hiding content from screen readers.
146
- // aria-hidden="${this.hideAudible(this.hiddenAudible)}"
269
+ firstUpdated(_changedProperties) {
270
+ super.firstUpdated(_changedProperties);
271
+
272
+ const slot = this.shadowRoot.querySelector('slot');
273
+
274
+ // Update the form state when a form element is detected
275
+ slot.addEventListener('input', (event) => {
276
+
277
+ /** @type {HTMLInputElement} */
278
+ const eventTarget = event.target;
279
+ if (this.isFormElement(eventTarget)) {
280
+ this._formState[eventTarget.getAttribute("name")].value = eventTarget.value;
281
+ }
282
+ });
283
+
284
+ slot.addEventListener('auroFormElement-validated', (event) => {
285
+ const oldValue = this._formState;
286
+
287
+ this._formState[event.target.getAttribute("name")].validity = event.detail.validity;
288
+ this.requestUpdate('formState', oldValue);
289
+ });
290
+ }
291
+
292
+ onSlotChange() {
293
+ this._formState = {};
147
294
 
148
- // function that renders the HTML and CSS into the scope of the component
295
+ this.queryAuroElements().forEach((element) => {
296
+ if (this.isFormElement(element)) {
297
+ this._formState[element.getAttribute('name')] = {
298
+ value: element.getAttribute('value'),
299
+ validity: element.getAttribute('validity'),
300
+ required: element.hasAttribute('required'),
301
+ element
302
+ };
303
+ }
304
+
305
+ if (this.isSubmitElement(element) && element.getAttribute('type') === 'submit') {
306
+ element.removeEventListener('click', this.getSubmitFunction());
307
+ element.addEventListener('click', this.getSubmitFunction());
308
+ }
309
+ });
310
+ }
311
+
312
+ // function that renders the HTML and CSS into the scope of the component
149
313
  render() {
150
314
  return x`
151
-
152
- <!-- this is demo code, DO NOT USE IN YOUR ELEMENT -->
153
- <div class=${this.cssClass} tabindex="0">
154
- <slot></slot>
155
- </div>
315
+ <form>
316
+ <p>Value: ${JSON.stringify(this.value)}</p>
317
+ <p>Validity: ${this.validity}</p>
318
+ <h3>Auro form example</h3>
319
+ <slot @slotchange="${this.onSlotChange}"></slot>
320
+ </form>
156
321
  `;
157
322
  }
158
323
  }
@@ -0,0 +1,5 @@
1
+ import {AuroInput} from "@auro-formkit/auro-input";
2
+ import {AuroDatePicker} from "@auro-formkit/auro-datepicker";
3
+
4
+ AuroInput.register();
5
+ AuroDatePicker.register();
@@ -0,0 +1,86 @@
1
+ <!--
2
+ Copyright (c) Alaska Air. All right reserved. Licensed under the Apache-2.0 license
3
+ See LICENSE in the project root for license information.
4
+
5
+ HTML in this document is standardized and NOT to be edited.
6
+ All demo code should be added/edited in ./demo/index.md
7
+
8
+ With the exception of adding custom elements if needed for the demo.
9
+
10
+ ----------------------- DO NOT EDIT -----------------------------
11
+
12
+ -->
13
+
14
+ <!DOCTYPE html>
15
+ <html lang="en">
16
+ <head>
17
+ <meta charset="UTF-8" />
18
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
19
+ <title>Auro Web Component Generator | auro-form custom element</title>
20
+ <link
21
+ rel="stylesheet"
22
+ type="text/css"
23
+ href="https://cdn.jsdelivr.net/npm/prismjs@1.24.1/themes/prism.css"
24
+ />
25
+ <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@aurodesignsystem/design-tokens@latest/dist/tokens/CSSCustomProperties.css">
26
+ <link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/@aurodesignsystem/webcorestylesheets@latest/dist/demoWrapper.css" />
27
+ <link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/@aurodesignsystem/webcorestylesheets@latest/dist/elementDemoStyles.css" />
28
+ <script src="https://cdn.jsdelivr.net/npm/@aurodesignsystem/auro-button@8.2.0/dist/auro-button__bundled.js" type="module"></script>
29
+ </head>
30
+ <body class="auro-markdown">
31
+ <main>
32
+ <style>
33
+ .submitBlock {
34
+ margin-top: 1rem;
35
+ display: flex;
36
+ justify-content: flex-end;
37
+ gap: 1rem;
38
+ }
39
+
40
+ .datepickerBlock {
41
+ margin-top: 1rem;
42
+ }
43
+ </style>
44
+
45
+ <h2>auro-form testing</h2>
46
+ <auro-form>
47
+ <auro-input id="first-name" name="firstName" required>
48
+ <span slot="label">First Name</span>
49
+ </auro-input>
50
+ <div>
51
+ <auro-input id="last-name" name="lastName" required>
52
+ <span slot="label">Last Name</span>
53
+ </auro-input>
54
+ </div>
55
+ <div>
56
+ <div>
57
+ <auro-input id="occupation" name="occupation" required>
58
+ <span slot="label">Occupation</span>
59
+ </auro-input>
60
+ </div>
61
+ </div>
62
+
63
+ <div class="datepickerBlock">
64
+ <h4>Pick a cool date</h4>
65
+ <auro-datepicker id="date-example" name="dateExample" required>
66
+ <span slot="fromLabel">Choose a date</span>
67
+ <span slot="mobileDateLabel">Choose a date</span>
68
+ </auro-datepicker>
69
+ </div>
70
+
71
+ <div class="submitBlock">
72
+ <auro-button type="reset">Reset</auro-button>
73
+ <auro-button type="submit">Submit</auro-button>
74
+ </div>
75
+ </auro-form>
76
+ </main>
77
+
78
+ <script type="module" data-demo-script="true" src="./index.js"></script>
79
+ <script type="module" data-demo-script="true" src="./registerDemoDeps.js"></script>
80
+ <!--<script type="module" data-demo-script="true" src="~@auro-formkit/auro-input/dist/index.min.js"></script>-->
81
+
82
+ <!-- If additional elements are needed for the demo, add them here. -->
83
+ <script src="https://cdn.jsdelivr.net/npm/@aurodesignsystem/auro-accordion@latest/dist/auro-accordion__bundled.js" type="module"></script>
84
+ <script src="https://cdn.jsdelivr.net/npm/@aurodesignsystem/auro-accordion@latest/dist/auro-accordion__bundled.js" type="module"></script>
85
+ </body>
86
+ </html>