@3t-transform/threeteeui 0.2.97 → 0.2.99

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 (28) hide show
  1. package/dist/cjs/tttx-form.cjs.entry.js +114 -29
  2. package/dist/cjs/tttx-icon.cjs.entry.js +6 -1
  3. package/dist/cjs/tttx-standalone-input.cjs.entry.js +1 -1
  4. package/dist/collection/components/atoms/tttx-icon/tttx-icon.js +6 -1
  5. package/dist/collection/components/atoms/tttx-icon/tttx-icon.stories.js +1 -1
  6. package/dist/collection/components/atoms/tttx-loading-spinner/tttx-loading-spinner.stories.js +8 -1
  7. package/dist/collection/components/molecules/tttx-form/lib/setErrorState.js +2 -0
  8. package/dist/collection/components/molecules/tttx-form/lib/validityCheck.js +10 -5
  9. package/dist/collection/components/molecules/tttx-form/tttx-form.css +7 -0
  10. package/dist/collection/components/molecules/tttx-form/tttx-form.js +101 -23
  11. package/dist/collection/components/molecules/tttx-form/tttx-form.stories.js +126 -3
  12. package/dist/collection/components/molecules/tttx-standalone-input/tttx-standalone-input.css +7 -0
  13. package/dist/components/tttx-form.js +114 -29
  14. package/dist/components/tttx-icon2.js +6 -1
  15. package/dist/components/tttx-standalone-input2.js +1 -1
  16. package/dist/esm/tttx-form.entry.js +114 -29
  17. package/dist/esm/tttx-icon.entry.js +6 -1
  18. package/dist/esm/tttx-standalone-input.entry.js +1 -1
  19. package/dist/tttx/{p-93e63568.entry.js → p-6e0fac85.entry.js} +1 -1
  20. package/dist/tttx/p-b2b0e16d.entry.js +1 -0
  21. package/dist/tttx/p-bcf3844e.entry.js +1 -0
  22. package/dist/tttx/tttx.esm.js +1 -1
  23. package/dist/types/components/atoms/tttx-loading-spinner/tttx-loading-spinner.stories.d.ts +10 -1
  24. package/dist/types/components/molecules/tttx-form/tttx-form.d.ts +12 -0
  25. package/dist/types/components/molecules/tttx-form/tttx-form.stories.d.ts +110 -0
  26. package/package.json +2 -2
  27. package/dist/tttx/p-a94f7efc.entry.js +0 -1
  28. package/dist/tttx/p-feea36cb.entry.js +0 -1
@@ -38,6 +38,16 @@ function validityCheck(event) {
38
38
  // target.setCustomValidity('custom error!');
39
39
  // and cleared with
40
40
  // target.setCustomValidity('');
41
+ //handle whitespace-only input
42
+ if (target.value.length && !target.value.replace(/\s/g, '').length) {
43
+ errorMessage = 'This field cannot be left blank';
44
+ target.setCustomValidity(errorMessage);
45
+ }
46
+ else {
47
+ errorMessage = '';
48
+ if (target.setCustomValidity) // tests are dumb as a brick
49
+ target.setCustomValidity('');
50
+ }
41
51
  // Check the validity of the input field and set an error message if needed
42
52
  switch (true) {
43
53
  // The field is required, but has no value
@@ -65,11 +75,6 @@ function validityCheck(event) {
65
75
  default:
66
76
  hasError = false;
67
77
  }
68
- //handle whitespace-only input
69
- if (!target.value.replace(/\s/g, '').length && target.value.length !== 0) {
70
- errorMessage = 'Whitespace-only not allowed';
71
- hasError = true;
72
- }
73
78
  // Return the error state
74
79
  return { target, hasError, errorMessage };
75
80
  }
@@ -99,6 +104,8 @@ function setErrorState(target, hasError, errorMessage, parent = undefined) {
99
104
  errorBubble = target.parentElement.parentElement.querySelector('.errorBubble');
100
105
  }
101
106
  }
107
+ if (!errorBubble)
108
+ return;
102
109
  // If an error was detected, set the input field's class to "invalid" and display the error message in the error bubble
103
110
  if (hasError) {
104
111
  target.classList.add('invalid');
@@ -122,7 +129,7 @@ function setErrorState(target, hasError, errorMessage, parent = undefined) {
122
129
  }
123
130
  }
124
131
 
125
- const tttxFormCss = ".material-symbols-rounded{font-variation-settings:\"FILL\" 1, \"wght\" 400, \"GRAD\" 0, \"opsz\" 24}.material-symbols-rounded{font-variation-settings:\"FILL\" 1, \"wght\" 400, \"GRAD\" 0, \"opsz\" 24}label{font-weight:500;font-size:16px;line-height:19px;color:#212121}label .optional{color:#757575;font-weight:normal}label .outer-container{position:relative}label .outer-container .left-icons,label .outer-container .right-icons{display:flex;position:absolute;height:24px;gap:8px}label .outer-container .left-icons tttx-icon,label .outer-container .right-icons tttx-icon{height:24px;width:24px}label .outer-container .left-icons{left:8px}label .outer-container .right-icons{right:8px}label .outer-container input{color:#212121;box-sizing:border-box;border:1px solid #d5d5d5;border-radius:4px;padding:0;padding-left:16px;padding-right:16px;margin-top:4px;}label .outer-container input.has-input-icon{padding-left:40px}label .outer-container input.has-input-icon.has-left-icon{padding-left:72px}label .outer-container input.has-left-icon{padding-left:40px}label .outer-container input.has-right-icon{padding-right:40px}label .outer-container input.invalid{border:1px solid #dc0000}label .outer-container input:not([type=submit]){font-family:\"Roboto\", serif;width:100%;height:36px;font-size:16px;line-height:19px}label .outer-container input[type=date]{background:white;display:block;min-width:calc(100% - 18px);line-height:37px}label .outer-container input[readonly]{cursor:default;pointer-events:none;user-select:none;color:gray}label .outer-container input:focus{border-color:#1479c6}label .outer-container input:focus-visible{outline:none}label .outer-container.inputBlock{display:flex;align-items:center;line-height:21px}label .outer-container.inputBlock .left-icons,label .outer-container.inputBlock .right-icons{margin-top:4px}label .outer-container.inputBlock.readonly{pointer-events:none;user-select:none;color:gray}label .outer-container.inputInline{display:flex;white-space:nowrap;align-items:center;margin:0}label .outer-container.inputInline input{margin-top:0}label .secondarylabel{color:#757575;font-size:14px;line-height:16px;font-weight:normal;display:flex;margin-top:4px}label .errorBubble{position:relative;font-size:14px;line-height:16px;font-weight:normal;width:100%;font-family:\"Roboto\", sans-serif;color:#dc0000;display:flex;align-content:center;align-items:center;justify-items:center;margin-top:4px}label .errorBubble:not(.visible){display:none}label .errorBubble span{color:#dc0000;font-size:16px;margin-right:4px}.material-symbols-rounded{font-family:\"Material Symbols Rounded\", sans-serif;font-weight:400;font-style:normal;font-size:24px;line-height:1;letter-spacing:normal;text-transform:none;display:inline-block;white-space:nowrap;word-wrap:normal;direction:ltr;text-rendering:optimizeLegibility;-webkit-font-smoothing:antialiased;color:#9e9e9e}button{cursor:pointer}.button{font-family:Roboto, serif;box-sizing:border-box;height:36px;min-width:36px;padding:0;margin:0;background:transparent;color:#212121;border:1px solid #c8c8c8;border-radius:4px;text-transform:uppercase;display:flex;justify-content:left;align-items:center;font-size:14px;font-weight:500}.button-content{display:block;padding:0 16px}.icon-left,.icon-right{margin-top:4px}.iconleft{padding-left:8px}.iconleft .button-content{padding-left:4px}.iconright{padding-right:8px}.iconright .button-content{padding-right:4px}.notext{padding:0 6px}.button:active{background:rgba(17, 17, 17, 0.2);border:1px solid #d5d5d5}.primary{background:#1479c6;border:1px solid #1479c6;color:white}.primary:active{background:#1464a2;border:1px solid #1464a2}.borderless{background:transparent;border:none;color:#212121}.borderless:active{background:rgba(17, 17, 17, 0.2);border:none}.borderless-circle{background:transparent;border:none;color:#212121;border-radius:50%}.borderless-circle:active{border:none}.borderless-circle:focus{border-color:transparent}.danger{background:#dc0000;border:1px solid #dc0000;color:white}.danger:active{background:#b00000;border:1px solid #b00000}.disabled{background:#aeaeae;border:none;color:#4c4c4c;cursor:not-allowed}.disabled:active{background:#aeaeae;border:none;color:#4c4c4c;cursor:not-allowed}@media (hover: hover){.button:hover{background:rgba(17, 17, 17, 0.1);border:1px solid #d5d5d5}.primary:hover{background:#146eb3;border:1px solid #146eb3}.borderless:hover{background:rgba(17, 17, 17, 0.1);border:none}.borderless-circle:hover{background:rgba(17, 17, 17, 0.1);border:none}.danger:hover{background:#c60000;border:1px solid #c60000}.disabled:hover{background:#aeaeae;border:none;color:#4c4c4c;cursor:not-allowed}}:host{display:block}fieldset{margin:0;padding:0;border:none}label{display:block;position:relative;margin-bottom:16px}.inlineLabel{font-weight:400;display:inline-block;vertical-align:top;padding-top:4px}input[type=checkbox]{width:18px;height:18px}input~label{font-weight:400}select{font-family:\"Roboto\", serif;box-sizing:border-box;width:100%;height:36px;padding:0 16px;font-size:16px;border:1px solid #d5d5d5;border-radius:4px;margin-top:4px}.placeholder{color:#9e9e9e}.placeholder option{color:initial}select.invalid:invalid{border:1px solid #dc0000}select~.errorBubble{position:relative;font-size:14px;font-weight:normal;width:100%;font-family:\"Roboto\", sans-serif;color:#dc0000;display:flex;align-content:center;align-items:center;justify-items:center}select~.errorBubble:not(.visible){visibility:hidden}select~.errorBubble span{color:#dc0000;font-size:16px;margin-right:4px;height:16px}select.invalid:invalid~.errorBubble{position:relative;font-size:14px;font-weight:normal;width:100%;font-family:\"Roboto\", sans-serif;color:#dc0000;visibility:visible}select:focus{border-color:#1479c6}select:focus-visible{outline:none}.button{padding:0 16px}.footer{display:flex;gap:16px;flex-direction:row-reverse}";
132
+ const tttxFormCss = ".material-symbols-rounded{font-variation-settings:\"FILL\" 1, \"wght\" 400, \"GRAD\" 0, \"opsz\" 24}.material-symbols-rounded{font-variation-settings:\"FILL\" 1, \"wght\" 400, \"GRAD\" 0, \"opsz\" 24}label{font-weight:500;font-size:16px;line-height:19px;color:#212121}label .optional{color:#757575;font-weight:normal}label .outer-container{position:relative}label .outer-container .left-icons,label .outer-container .right-icons{display:flex;position:absolute;height:24px;gap:8px}label .outer-container .left-icons tttx-icon,label .outer-container .right-icons tttx-icon{height:24px;width:24px}label .outer-container .left-icons{left:8px}label .outer-container .right-icons{right:8px}label .outer-container input{color:#212121;box-sizing:border-box;border:1px solid #d5d5d5;border-radius:4px;padding:0;padding-left:16px;padding-right:16px;margin-top:4px;}label .outer-container input.has-input-icon{padding-left:40px}label .outer-container input.has-input-icon.has-left-icon{padding-left:72px}label .outer-container input.has-left-icon{padding-left:40px}label .outer-container input.has-right-icon{padding-right:40px}label .outer-container input.invalid{border:1px solid #dc0000}label .outer-container input:not([type=submit]){font-family:\"Roboto\", serif;width:100%;height:36px;font-size:16px;line-height:19px}label .outer-container input[type=radio]{width:20px;height:20px}label .outer-container input[type=date]{background:white;display:block;min-width:calc(100% - 18px);line-height:37px}label .outer-container input[readonly]{cursor:default;pointer-events:none;user-select:none;color:gray}label .outer-container input:focus{border-color:#1479c6}label .outer-container input:focus-visible{outline:none}label .outer-container.inputBlock{display:flex;align-items:center;line-height:21px}label .outer-container.inputBlock .left-icons,label .outer-container.inputBlock .right-icons{margin-top:4px}label .outer-container.inputBlock.readonly{pointer-events:none;user-select:none;color:gray}label .outer-container.inputBlock.radioBlock{display:block}label .outer-container.inputInline{display:flex;white-space:nowrap;align-items:center;margin:0}label .outer-container.inputInline input{margin-top:0}label .secondarylabel{color:#757575;font-size:14px;line-height:16px;font-weight:normal;display:flex;margin-top:4px}label .errorBubble{position:relative;font-size:14px;line-height:16px;font-weight:normal;width:100%;font-family:\"Roboto\", sans-serif;color:#dc0000;display:flex;align-content:center;align-items:center;justify-items:center;margin-top:4px}label .errorBubble:not(.visible){display:none}label .errorBubble span{color:#dc0000;font-size:16px;margin-right:4px}.material-symbols-rounded{font-family:\"Material Symbols Rounded\", sans-serif;font-weight:400;font-style:normal;font-size:24px;line-height:1;letter-spacing:normal;text-transform:none;display:inline-block;white-space:nowrap;word-wrap:normal;direction:ltr;text-rendering:optimizeLegibility;-webkit-font-smoothing:antialiased;color:#9e9e9e}button{cursor:pointer}.button{font-family:Roboto, serif;box-sizing:border-box;height:36px;min-width:36px;padding:0;margin:0;background:transparent;color:#212121;border:1px solid #c8c8c8;border-radius:4px;text-transform:uppercase;display:flex;justify-content:left;align-items:center;font-size:14px;font-weight:500}.button-content{display:block;padding:0 16px}.icon-left,.icon-right{margin-top:4px}.iconleft{padding-left:8px}.iconleft .button-content{padding-left:4px}.iconright{padding-right:8px}.iconright .button-content{padding-right:4px}.notext{padding:0 6px}.button:active{background:rgba(17, 17, 17, 0.2);border:1px solid #d5d5d5}.primary{background:#1479c6;border:1px solid #1479c6;color:white}.primary:active{background:#1464a2;border:1px solid #1464a2}.borderless{background:transparent;border:none;color:#212121}.borderless:active{background:rgba(17, 17, 17, 0.2);border:none}.borderless-circle{background:transparent;border:none;color:#212121;border-radius:50%}.borderless-circle:active{border:none}.borderless-circle:focus{border-color:transparent}.danger{background:#dc0000;border:1px solid #dc0000;color:white}.danger:active{background:#b00000;border:1px solid #b00000}.disabled{background:#aeaeae;border:none;color:#4c4c4c;cursor:not-allowed}.disabled:active{background:#aeaeae;border:none;color:#4c4c4c;cursor:not-allowed}@media (hover: hover){.button:hover{background:rgba(17, 17, 17, 0.1);border:1px solid #d5d5d5}.primary:hover{background:#146eb3;border:1px solid #146eb3}.borderless:hover{background:rgba(17, 17, 17, 0.1);border:none}.borderless-circle:hover{background:rgba(17, 17, 17, 0.1);border:none}.danger:hover{background:#c60000;border:1px solid #c60000}.disabled:hover{background:#aeaeae;border:none;color:#4c4c4c;cursor:not-allowed}}:host{display:block}fieldset{margin:0;padding:0;border:none}label{display:block;position:relative;margin-bottom:16px}.inlineLabel{font-weight:400;display:inline-block;vertical-align:top;padding-top:4px}input[type=checkbox]{width:18px;height:18px}input~label{font-weight:400}select{font-family:\"Roboto\", serif;box-sizing:border-box;width:100%;height:36px;padding:0 16px;font-size:16px;border:1px solid #d5d5d5;border-radius:4px;margin-top:4px}.placeholder{color:#9e9e9e}.placeholder option{color:initial}select.invalid:invalid{border:1px solid #dc0000}select~.errorBubble{position:relative;font-size:14px;font-weight:normal;width:100%;font-family:\"Roboto\", sans-serif;color:#dc0000;display:flex;align-content:center;align-items:center;justify-items:center}select~.errorBubble:not(.visible){visibility:hidden}select~.errorBubble span{color:#dc0000;font-size:16px;margin-right:4px;height:16px}select.invalid:invalid~.errorBubble{position:relative;font-size:14px;font-weight:normal;width:100%;font-family:\"Roboto\", sans-serif;color:#dc0000;visibility:visible}select:focus{border-color:#1479c6}select:focus-visible{outline:none}.button{padding:0 16px}.footer{display:flex;gap:16px;flex-direction:row-reverse}";
126
133
 
127
134
  const TttxForm = class {
128
135
  constructor(hostRef) {
@@ -195,6 +202,23 @@ const TttxForm = class {
195
202
  event.preventDefault();
196
203
  // create a new FormData object with the form data
197
204
  const formData = new FormData(event.target);
205
+ // get a list of checkboxes, if any, so we can manually set unchecked box data
206
+ const checkboxSchemaItems = [];
207
+ const formProperties = this._formSchema.properties;
208
+ for (const formKey of Object.keys(formProperties)) {
209
+ if (formProperties[formKey].form.type === 'checkbox') {
210
+ checkboxSchemaItems.push(formKey);
211
+ }
212
+ }
213
+ const allFormKeys = [];
214
+ for (const key of formData.keys()) {
215
+ allFormKeys.push(key);
216
+ }
217
+ for (const checkItem of checkboxSchemaItems) {
218
+ if (allFormKeys.indexOf(checkItem) === -1) {
219
+ formData.append(checkItem, 'off');
220
+ }
221
+ }
198
222
  // emit the form data through the `dataSubmitted` event
199
223
  this.dataSubmitted.emit(formData);
200
224
  }
@@ -379,17 +403,21 @@ const TttxForm = class {
379
403
  */
380
404
  createLabel(formProperties, input, errorBubble) {
381
405
  const outerContainer = document.createElement('div');
382
- outerContainer.className = 'outer-container inputBlock';
406
+ let outerContainerClassName = 'outer-container inputBlock';
383
407
  // Create a new <label> element with the "inputBlock" class and the specified text
384
408
  const label = document.createElement('label');
385
409
  label.innerText = formProperties.label;
386
410
  // If the form property has no validation object, add an "optional" span element to the label
387
- if (!formProperties.validation) {
411
+ if (!formProperties.validation && formProperties.label) {
388
412
  const optionalSpan = document.createElement('span');
389
413
  optionalSpan.className = 'optional';
390
414
  optionalSpan.innerHTML = '&nbsp;(optional)';
391
415
  label.appendChild(optionalSpan);
392
416
  }
417
+ if (formProperties.type === 'radio') {
418
+ outerContainerClassName += ' radioBlock';
419
+ }
420
+ outerContainer.className = outerContainerClassName;
393
421
  if (formProperties.readonly) {
394
422
  label.classList.add('readonly');
395
423
  }
@@ -407,6 +435,38 @@ const TttxForm = class {
407
435
  // Return the label element
408
436
  return label;
409
437
  }
438
+ /**
439
+ * Creates a new radio input with a set of options.
440
+ *
441
+ * @param {string} formKey - The name of the dropdown field, as specified in the form schema.
442
+ * @param {Object} formProperties - An object containing additional properties, such as the field type and options properties.
443
+ * @param {'radio'} formProperties.type - The type of form field. In this case, it will always be "radio".
444
+ * @param {Object} formProperties.validation - A set of validation rules for the field.
445
+ * @param {Object[]} formProperties.options - A list of properties to pass to the select options.
446
+ * @param {string} formProperties.options.label - The visible value of the option.
447
+ * @param {string} formProperties.options.value - The actual value of the option.
448
+ */
449
+ createRadio(formKey, formProperties) {
450
+ var _a, _b;
451
+ const fragment = new DocumentFragment();
452
+ for (const optionProperties of formProperties.options) {
453
+ // Create a new <input> element with the specified name and type
454
+ const input = document.createElement('input');
455
+ input.type = 'radio';
456
+ input.name = formKey;
457
+ input.value = optionProperties.value;
458
+ if ((_a = formProperties === null || formProperties === void 0 ? void 0 : formProperties.validation) === null || _a === void 0 ? void 0 : _a.required) {
459
+ input.setAttribute('required', 'required');
460
+ input.setAttribute('data-required', (_b = formProperties.validation.required.message) !== null && _b !== void 0 ? _b : '');
461
+ }
462
+ const span = document.createElement('span');
463
+ span.innerText = optionProperties.label;
464
+ fragment.appendChild(input);
465
+ fragment.appendChild(span);
466
+ fragment.appendChild(document.createElement('br'));
467
+ }
468
+ return fragment;
469
+ }
410
470
  /**
411
471
  * Populates the form template with input fields and labels based on the properties of the
412
472
  * current form schema. For each property in the schema, it creates an input element, applies
@@ -426,9 +486,19 @@ const TttxForm = class {
426
486
  propertyKeys.forEach(formKey => {
427
487
  const formItem = properties[formKey];
428
488
  const formProperties = formItem.form;
429
- const input = formProperties.type === 'select' ? this.createSelect(formKey, formProperties) : this.createInput(formKey, formProperties);
489
+ let input;
490
+ switch (formProperties.type) {
491
+ case 'select':
492
+ input = this.createSelect(formKey, formProperties);
493
+ break;
494
+ case 'radio':
495
+ input = this.createRadio(formKey, formProperties);
496
+ break;
497
+ default:
498
+ input = this.createInput(formKey, formProperties);
499
+ }
430
500
  // If the form property has validation, apply it to the input
431
- if (formProperties.validation) {
501
+ if (formProperties.validation && formProperties.type !== 'radio') {
432
502
  this.applyValidation(input, formProperties.validation);
433
503
  }
434
504
  // Create an error bubble and label element for the input
@@ -459,25 +529,40 @@ const TttxForm = class {
459
529
  const propertyKeys = Object.keys(properties);
460
530
  propertyKeys.forEach(formKey => {
461
531
  var _a, _b;
462
- const formInput = formItems.querySelector(`[name=${formKey}]`);
463
- // Bind events to form input elements
464
- formInput.oninvalid = this.validityCheckWrapper.bind(this);
465
- formInput.onblur = this.validityCheckWrapper.bind(this);
466
- formInput.onkeyup = this.fieldChanged.bind(this);
467
- formInput.onchange = this.fieldChanged.bind(this);
468
- if (((_a = this._data) === null || _a === void 0 ? void 0 : _a[formKey]) !== undefined && this._data[formKey] !== null) {
469
- formInput.value = this._data[formKey];
470
- }
471
- // If explicitly setting input as invalid, set invalid state and error message on render
472
- if ((_b = properties[formKey].form.validation) === null || _b === void 0 ? void 0 : _b.invalid) {
473
- const errorMessage = properties[formKey].form.validation.invalid.message;
474
- formInput.setCustomValidity(errorMessage); // Prevents the invalid styling from resetting on blur
475
- setErrorState(formInput, true, errorMessage);
476
- }
477
- if (properties[formKey].form.type === 'select' && formInput.classList.contains('placeholder')) {
478
- formInput.addEventListener('change', () => {
479
- formInput.classList.remove('placeholder');
480
- });
532
+ const formInputs = formItems.querySelectorAll(`[name=${formKey}]`);
533
+ for (const formInput of formInputs) {
534
+ // Bind events to form input elements
535
+ formInput.oninvalid = this.validityCheckWrapper.bind(this);
536
+ formInput.onblur = this.validityCheckWrapper.bind(this);
537
+ formInput.onkeyup = this.fieldChanged.bind(this);
538
+ formInput.onchange = this.fieldChanged.bind(this);
539
+ if ((_a = this._data) === null || _a === void 0 ? void 0 : _a[formKey]) {
540
+ switch (formInput.type) {
541
+ case 'checkbox':
542
+ if (this._data[formKey] === 'on') {
543
+ formInput.checked = true;
544
+ }
545
+ break;
546
+ case 'radio':
547
+ if (formInput.value === this._data[formKey]) {
548
+ formInput.checked = true;
549
+ }
550
+ break;
551
+ default:
552
+ formInput.value = this._data[formKey];
553
+ }
554
+ }
555
+ // If explicitly setting input as invalid, set invalid state and error message on render
556
+ if ((_b = properties[formKey].form.validation) === null || _b === void 0 ? void 0 : _b.invalid) {
557
+ const errorMessage = properties[formKey].form.validation.invalid.message;
558
+ formInput.setCustomValidity(errorMessage); // Prevents the invalid styling from resetting on blur
559
+ setErrorState(formInput, true, errorMessage);
560
+ }
561
+ if (properties[formKey].form.type === 'select' && formInput.classList.contains('placeholder')) {
562
+ formInput.addEventListener('change', () => {
563
+ formInput.classList.remove('placeholder');
564
+ });
565
+ }
481
566
  }
482
567
  });
483
568
  // Append the cloned form elements to the fieldset
@@ -13,7 +13,12 @@ const TttxIcon = class {
13
13
  this.color = 'grey';
14
14
  }
15
15
  render() {
16
- return (index.h(index.Host, null, index.h("span", { class: `material-symbols-rounded ${this.color ? this.color : ''}` }, this.icon)));
16
+ if (this.color.startsWith('#')) {
17
+ return (index.h(index.Host, null, index.h("span", { class: 'material-symbols-rounded', style: { color: this.color } }, this.icon)));
18
+ }
19
+ else {
20
+ return (index.h(index.Host, null, index.h("span", { class: `material-symbols-rounded ${this.color}` }, this.icon)));
21
+ }
17
22
  }
18
23
  };
19
24
  TttxIcon.style = tttxIconCss;
@@ -4,7 +4,7 @@ Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
5
  const index = require('./index-992a9fb3.js');
6
6
 
7
- const tttxStandaloneInputCss = ".material-symbols-rounded.sc-tttx-standalone-input{font-variation-settings:\"FILL\" 1, \"wght\" 400, \"GRAD\" 0, \"opsz\" 24}.material-symbols-rounded.sc-tttx-standalone-input{font-variation-settings:\"FILL\" 1, \"wght\" 400, \"GRAD\" 0, \"opsz\" 24}label.sc-tttx-standalone-input{font-weight:500;font-size:16px;line-height:19px;color:#212121}label.sc-tttx-standalone-input .optional.sc-tttx-standalone-input{color:#757575;font-weight:normal}label.sc-tttx-standalone-input .outer-container.sc-tttx-standalone-input{position:relative}label.sc-tttx-standalone-input .outer-container.sc-tttx-standalone-input .left-icons.sc-tttx-standalone-input,label.sc-tttx-standalone-input .outer-container.sc-tttx-standalone-input .right-icons.sc-tttx-standalone-input{display:flex;position:absolute;height:24px;gap:8px}label.sc-tttx-standalone-input .outer-container.sc-tttx-standalone-input .left-icons.sc-tttx-standalone-input tttx-icon.sc-tttx-standalone-input,label.sc-tttx-standalone-input .outer-container.sc-tttx-standalone-input .right-icons.sc-tttx-standalone-input tttx-icon.sc-tttx-standalone-input{height:24px;width:24px}label.sc-tttx-standalone-input .outer-container.sc-tttx-standalone-input .left-icons.sc-tttx-standalone-input{left:8px}label.sc-tttx-standalone-input .outer-container.sc-tttx-standalone-input .right-icons.sc-tttx-standalone-input{right:8px}label.sc-tttx-standalone-input .outer-container.sc-tttx-standalone-input input.sc-tttx-standalone-input{color:#212121;box-sizing:border-box;border:1px solid #d5d5d5;border-radius:4px;padding:0;padding-left:16px;padding-right:16px;margin-top:4px}label.sc-tttx-standalone-input .outer-container.sc-tttx-standalone-input input.has-input-icon.sc-tttx-standalone-input{padding-left:40px}label.sc-tttx-standalone-input .outer-container.sc-tttx-standalone-input input.has-input-icon.has-left-icon.sc-tttx-standalone-input{padding-left:72px}label.sc-tttx-standalone-input .outer-container.sc-tttx-standalone-input input.has-left-icon.sc-tttx-standalone-input{padding-left:40px}label.sc-tttx-standalone-input .outer-container.sc-tttx-standalone-input input.has-right-icon.sc-tttx-standalone-input{padding-right:40px}label.sc-tttx-standalone-input .outer-container.sc-tttx-standalone-input input.invalid.sc-tttx-standalone-input{border:1px solid #dc0000}label.sc-tttx-standalone-input .outer-container.sc-tttx-standalone-input input.sc-tttx-standalone-input:not([type=submit]){font-family:\"Roboto\", serif;width:100%;height:36px;font-size:16px;line-height:19px}label.sc-tttx-standalone-input .outer-container.sc-tttx-standalone-input input[type=date].sc-tttx-standalone-input{background:white;display:block;min-width:calc(100% - 18px);line-height:37px}label.sc-tttx-standalone-input .outer-container.sc-tttx-standalone-input input[readonly].sc-tttx-standalone-input{cursor:default;pointer-events:none;user-select:none;color:gray}label.sc-tttx-standalone-input .outer-container.sc-tttx-standalone-input input.sc-tttx-standalone-input:focus{border-color:#1479c6}label.sc-tttx-standalone-input .outer-container.sc-tttx-standalone-input input.sc-tttx-standalone-input:focus-visible{outline:none}label.sc-tttx-standalone-input .outer-container.inputBlock.sc-tttx-standalone-input{display:flex;align-items:center;line-height:21px}label.sc-tttx-standalone-input .outer-container.inputBlock.sc-tttx-standalone-input .left-icons.sc-tttx-standalone-input,label.sc-tttx-standalone-input .outer-container.inputBlock.sc-tttx-standalone-input .right-icons.sc-tttx-standalone-input{margin-top:4px}label.sc-tttx-standalone-input .outer-container.inputBlock.readonly.sc-tttx-standalone-input{pointer-events:none;user-select:none;color:gray}label.sc-tttx-standalone-input .outer-container.inputInline.sc-tttx-standalone-input{display:flex;white-space:nowrap;align-items:center;margin:0}label.sc-tttx-standalone-input .outer-container.inputInline.sc-tttx-standalone-input input.sc-tttx-standalone-input{margin-top:0}label.sc-tttx-standalone-input .secondarylabel.sc-tttx-standalone-input{color:#757575;font-size:14px;line-height:16px;font-weight:normal;display:flex;margin-top:4px}label.sc-tttx-standalone-input .errorBubble.sc-tttx-standalone-input{position:relative;font-size:14px;line-height:16px;font-weight:normal;width:100%;font-family:\"Roboto\", sans-serif;color:#dc0000;display:flex;align-content:center;align-items:center;justify-items:center;margin-top:4px}label.sc-tttx-standalone-input .errorBubble.sc-tttx-standalone-input:not(.visible){display:none}label.sc-tttx-standalone-input .errorBubble.sc-tttx-standalone-input span.sc-tttx-standalone-input{color:#dc0000;font-size:16px;margin-right:4px}.material-symbols-rounded.sc-tttx-standalone-input{font-family:\"Material Symbols Rounded\", sans-serif;font-weight:400;font-style:normal;font-size:24px;line-height:1;letter-spacing:normal;text-transform:none;display:inline-block;white-space:nowrap;word-wrap:normal;direction:ltr;text-rendering:optimizeLegibility;-webkit-font-smoothing:antialiased;color:#9e9e9e}.sc-tttx-standalone-input-h{display:block}";
7
+ const tttxStandaloneInputCss = ".material-symbols-rounded.sc-tttx-standalone-input{font-variation-settings:\"FILL\" 1, \"wght\" 400, \"GRAD\" 0, \"opsz\" 24}.material-symbols-rounded.sc-tttx-standalone-input{font-variation-settings:\"FILL\" 1, \"wght\" 400, \"GRAD\" 0, \"opsz\" 24}label.sc-tttx-standalone-input{font-weight:500;font-size:16px;line-height:19px;color:#212121}label.sc-tttx-standalone-input .optional.sc-tttx-standalone-input{color:#757575;font-weight:normal}label.sc-tttx-standalone-input .outer-container.sc-tttx-standalone-input{position:relative}label.sc-tttx-standalone-input .outer-container.sc-tttx-standalone-input .left-icons.sc-tttx-standalone-input,label.sc-tttx-standalone-input .outer-container.sc-tttx-standalone-input .right-icons.sc-tttx-standalone-input{display:flex;position:absolute;height:24px;gap:8px}label.sc-tttx-standalone-input .outer-container.sc-tttx-standalone-input .left-icons.sc-tttx-standalone-input tttx-icon.sc-tttx-standalone-input,label.sc-tttx-standalone-input .outer-container.sc-tttx-standalone-input .right-icons.sc-tttx-standalone-input tttx-icon.sc-tttx-standalone-input{height:24px;width:24px}label.sc-tttx-standalone-input .outer-container.sc-tttx-standalone-input .left-icons.sc-tttx-standalone-input{left:8px}label.sc-tttx-standalone-input .outer-container.sc-tttx-standalone-input .right-icons.sc-tttx-standalone-input{right:8px}label.sc-tttx-standalone-input .outer-container.sc-tttx-standalone-input input.sc-tttx-standalone-input{color:#212121;box-sizing:border-box;border:1px solid #d5d5d5;border-radius:4px;padding:0;padding-left:16px;padding-right:16px;margin-top:4px}label.sc-tttx-standalone-input .outer-container.sc-tttx-standalone-input input.has-input-icon.sc-tttx-standalone-input{padding-left:40px}label.sc-tttx-standalone-input .outer-container.sc-tttx-standalone-input input.has-input-icon.has-left-icon.sc-tttx-standalone-input{padding-left:72px}label.sc-tttx-standalone-input .outer-container.sc-tttx-standalone-input input.has-left-icon.sc-tttx-standalone-input{padding-left:40px}label.sc-tttx-standalone-input .outer-container.sc-tttx-standalone-input input.has-right-icon.sc-tttx-standalone-input{padding-right:40px}label.sc-tttx-standalone-input .outer-container.sc-tttx-standalone-input input.invalid.sc-tttx-standalone-input{border:1px solid #dc0000}label.sc-tttx-standalone-input .outer-container.sc-tttx-standalone-input input.sc-tttx-standalone-input:not([type=submit]){font-family:\"Roboto\", serif;width:100%;height:36px;font-size:16px;line-height:19px}label.sc-tttx-standalone-input .outer-container.sc-tttx-standalone-input input[type=radio].sc-tttx-standalone-input{width:20px;height:20px}label.sc-tttx-standalone-input .outer-container.sc-tttx-standalone-input input[type=date].sc-tttx-standalone-input{background:white;display:block;min-width:calc(100% - 18px);line-height:37px}label.sc-tttx-standalone-input .outer-container.sc-tttx-standalone-input input[readonly].sc-tttx-standalone-input{cursor:default;pointer-events:none;user-select:none;color:gray}label.sc-tttx-standalone-input .outer-container.sc-tttx-standalone-input input.sc-tttx-standalone-input:focus{border-color:#1479c6}label.sc-tttx-standalone-input .outer-container.sc-tttx-standalone-input input.sc-tttx-standalone-input:focus-visible{outline:none}label.sc-tttx-standalone-input .outer-container.inputBlock.sc-tttx-standalone-input{display:flex;align-items:center;line-height:21px}label.sc-tttx-standalone-input .outer-container.inputBlock.sc-tttx-standalone-input .left-icons.sc-tttx-standalone-input,label.sc-tttx-standalone-input .outer-container.inputBlock.sc-tttx-standalone-input .right-icons.sc-tttx-standalone-input{margin-top:4px}label.sc-tttx-standalone-input .outer-container.inputBlock.readonly.sc-tttx-standalone-input{pointer-events:none;user-select:none;color:gray}label.sc-tttx-standalone-input .outer-container.inputBlock.radioBlock.sc-tttx-standalone-input{display:block}label.sc-tttx-standalone-input .outer-container.inputInline.sc-tttx-standalone-input{display:flex;white-space:nowrap;align-items:center;margin:0}label.sc-tttx-standalone-input .outer-container.inputInline.sc-tttx-standalone-input input.sc-tttx-standalone-input{margin-top:0}label.sc-tttx-standalone-input .secondarylabel.sc-tttx-standalone-input{color:#757575;font-size:14px;line-height:16px;font-weight:normal;display:flex;margin-top:4px}label.sc-tttx-standalone-input .errorBubble.sc-tttx-standalone-input{position:relative;font-size:14px;line-height:16px;font-weight:normal;width:100%;font-family:\"Roboto\", sans-serif;color:#dc0000;display:flex;align-content:center;align-items:center;justify-items:center;margin-top:4px}label.sc-tttx-standalone-input .errorBubble.sc-tttx-standalone-input:not(.visible){display:none}label.sc-tttx-standalone-input .errorBubble.sc-tttx-standalone-input span.sc-tttx-standalone-input{color:#dc0000;font-size:16px;margin-right:4px}.material-symbols-rounded.sc-tttx-standalone-input{font-family:\"Material Symbols Rounded\", sans-serif;font-weight:400;font-style:normal;font-size:24px;line-height:1;letter-spacing:normal;text-transform:none;display:inline-block;white-space:nowrap;word-wrap:normal;direction:ltr;text-rendering:optimizeLegibility;-webkit-font-smoothing:antialiased;color:#9e9e9e}.sc-tttx-standalone-input-h{display:block}";
8
8
 
9
9
  const TttxInput = class {
10
10
  constructor(hostRef) {
@@ -5,7 +5,12 @@ export class TttxIcon {
5
5
  this.color = 'grey';
6
6
  }
7
7
  render() {
8
- return (h(Host, null, h("span", { class: `material-symbols-rounded ${this.color ? this.color : ''}` }, this.icon)));
8
+ if (this.color.startsWith('#')) {
9
+ return (h(Host, null, h("span", { class: 'material-symbols-rounded', style: { color: this.color } }, this.icon)));
10
+ }
11
+ else {
12
+ return (h(Host, null, h("span", { class: `material-symbols-rounded ${this.color}` }, this.icon)));
13
+ }
9
14
  }
10
15
  static get is() { return "tttx-icon"; }
11
16
  static get encapsulation() { return "shadow"; }
@@ -1,5 +1,5 @@
1
1
  import icons from './../../../icons';
2
- const colors = ['red', 'blue', 'black', 'white', 'gray', 'green', 'orange', 'darkred'];
2
+ const colors = ['red', 'blue', 'black', 'white', 'gray', 'green', 'orange', 'darkred', '#397A4C', '#FF00BB'];
3
3
  export default {
4
4
  title: 'Atoms/Icons',
5
5
  component: 'tttx-icon',
@@ -13,5 +13,12 @@ export default {
13
13
  };
14
14
  export const Example = args => {
15
15
  const size = args.size || 'large';
16
- return `<tttx-loading-spinner ${args.loadingMessage ? 'loading-message' : ''} size='${size}' />`;
16
+ return `<tttx-loading-spinner data-chromatic="ignore" ${args.loadingMessage ? 'loading-message' : ''} size='${size}' />`;
17
+ };
18
+ Example.parameters = {
19
+ chromatic: {
20
+ pauseAnimationAtEnd: true,
21
+ delay: 5000,
22
+ disableSnapshot: true
23
+ },
17
24
  };
@@ -23,6 +23,8 @@ function setErrorState(target, hasError, errorMessage, parent = undefined) {
23
23
  errorBubble = target.parentElement.parentElement.querySelector('.errorBubble');
24
24
  }
25
25
  }
26
+ if (!errorBubble)
27
+ return;
26
28
  // If an error was detected, set the input field's class to "invalid" and display the error message in the error bubble
27
29
  if (hasError) {
28
30
  target.classList.add('invalid');
@@ -30,6 +30,16 @@ function validityCheck(event) {
30
30
  // target.setCustomValidity('custom error!');
31
31
  // and cleared with
32
32
  // target.setCustomValidity('');
33
+ //handle whitespace-only input
34
+ if (target.value.length && !target.value.replace(/\s/g, '').length) {
35
+ errorMessage = 'This field cannot be left blank';
36
+ target.setCustomValidity(errorMessage);
37
+ }
38
+ else {
39
+ errorMessage = '';
40
+ if (target.setCustomValidity) // tests are dumb as a brick
41
+ target.setCustomValidity('');
42
+ }
33
43
  // Check the validity of the input field and set an error message if needed
34
44
  switch (true) {
35
45
  // The field is required, but has no value
@@ -57,11 +67,6 @@ function validityCheck(event) {
57
67
  default:
58
68
  hasError = false;
59
69
  }
60
- //handle whitespace-only input
61
- if (!target.value.replace(/\s/g, '').length && target.value.length !== 0) {
62
- errorMessage = 'Whitespace-only not allowed';
63
- hasError = true;
64
- }
65
70
  // Return the error state
66
71
  return { target, hasError, errorMessage };
67
72
  }
@@ -70,6 +70,10 @@ label .outer-container input:not([type=submit]) {
70
70
  font-size: 16px;
71
71
  line-height: 19px;
72
72
  }
73
+ label .outer-container input[type=radio] {
74
+ width: 20px;
75
+ height: 20px;
76
+ }
73
77
  label .outer-container input[type=date] {
74
78
  background: white;
75
79
  display: block;
@@ -102,6 +106,9 @@ label .outer-container.inputBlock.readonly {
102
106
  user-select: none;
103
107
  color: gray;
104
108
  }
109
+ label .outer-container.inputBlock.radioBlock {
110
+ display: block;
111
+ }
105
112
  label .outer-container.inputInline {
106
113
  display: flex;
107
114
  white-space: nowrap;
@@ -74,6 +74,23 @@ export class TttxForm {
74
74
  event.preventDefault();
75
75
  // create a new FormData object with the form data
76
76
  const formData = new FormData(event.target);
77
+ // get a list of checkboxes, if any, so we can manually set unchecked box data
78
+ const checkboxSchemaItems = [];
79
+ const formProperties = this._formSchema.properties;
80
+ for (const formKey of Object.keys(formProperties)) {
81
+ if (formProperties[formKey].form.type === 'checkbox') {
82
+ checkboxSchemaItems.push(formKey);
83
+ }
84
+ }
85
+ const allFormKeys = [];
86
+ for (const key of formData.keys()) {
87
+ allFormKeys.push(key);
88
+ }
89
+ for (const checkItem of checkboxSchemaItems) {
90
+ if (allFormKeys.indexOf(checkItem) === -1) {
91
+ formData.append(checkItem, 'off');
92
+ }
93
+ }
77
94
  // emit the form data through the `dataSubmitted` event
78
95
  this.dataSubmitted.emit(formData);
79
96
  }
@@ -258,17 +275,21 @@ export class TttxForm {
258
275
  */
259
276
  createLabel(formProperties, input, errorBubble) {
260
277
  const outerContainer = document.createElement('div');
261
- outerContainer.className = 'outer-container inputBlock';
278
+ let outerContainerClassName = 'outer-container inputBlock';
262
279
  // Create a new <label> element with the "inputBlock" class and the specified text
263
280
  const label = document.createElement('label');
264
281
  label.innerText = formProperties.label;
265
282
  // If the form property has no validation object, add an "optional" span element to the label
266
- if (!formProperties.validation) {
283
+ if (!formProperties.validation && formProperties.label) {
267
284
  const optionalSpan = document.createElement('span');
268
285
  optionalSpan.className = 'optional';
269
286
  optionalSpan.innerHTML = '&nbsp;(optional)';
270
287
  label.appendChild(optionalSpan);
271
288
  }
289
+ if (formProperties.type === 'radio') {
290
+ outerContainerClassName += ' radioBlock';
291
+ }
292
+ outerContainer.className = outerContainerClassName;
272
293
  if (formProperties.readonly) {
273
294
  label.classList.add('readonly');
274
295
  }
@@ -286,6 +307,38 @@ export class TttxForm {
286
307
  // Return the label element
287
308
  return label;
288
309
  }
310
+ /**
311
+ * Creates a new radio input with a set of options.
312
+ *
313
+ * @param {string} formKey - The name of the dropdown field, as specified in the form schema.
314
+ * @param {Object} formProperties - An object containing additional properties, such as the field type and options properties.
315
+ * @param {'radio'} formProperties.type - The type of form field. In this case, it will always be "radio".
316
+ * @param {Object} formProperties.validation - A set of validation rules for the field.
317
+ * @param {Object[]} formProperties.options - A list of properties to pass to the select options.
318
+ * @param {string} formProperties.options.label - The visible value of the option.
319
+ * @param {string} formProperties.options.value - The actual value of the option.
320
+ */
321
+ createRadio(formKey, formProperties) {
322
+ var _a, _b;
323
+ const fragment = new DocumentFragment();
324
+ for (const optionProperties of formProperties.options) {
325
+ // Create a new <input> element with the specified name and type
326
+ const input = document.createElement('input');
327
+ input.type = 'radio';
328
+ input.name = formKey;
329
+ input.value = optionProperties.value;
330
+ if ((_a = formProperties === null || formProperties === void 0 ? void 0 : formProperties.validation) === null || _a === void 0 ? void 0 : _a.required) {
331
+ input.setAttribute('required', 'required');
332
+ input.setAttribute('data-required', (_b = formProperties.validation.required.message) !== null && _b !== void 0 ? _b : '');
333
+ }
334
+ const span = document.createElement('span');
335
+ span.innerText = optionProperties.label;
336
+ fragment.appendChild(input);
337
+ fragment.appendChild(span);
338
+ fragment.appendChild(document.createElement('br'));
339
+ }
340
+ return fragment;
341
+ }
289
342
  /**
290
343
  * Populates the form template with input fields and labels based on the properties of the
291
344
  * current form schema. For each property in the schema, it creates an input element, applies
@@ -305,9 +358,19 @@ export class TttxForm {
305
358
  propertyKeys.forEach(formKey => {
306
359
  const formItem = properties[formKey];
307
360
  const formProperties = formItem.form;
308
- const input = formProperties.type === 'select' ? this.createSelect(formKey, formProperties) : this.createInput(formKey, formProperties);
361
+ let input;
362
+ switch (formProperties.type) {
363
+ case 'select':
364
+ input = this.createSelect(formKey, formProperties);
365
+ break;
366
+ case 'radio':
367
+ input = this.createRadio(formKey, formProperties);
368
+ break;
369
+ default:
370
+ input = this.createInput(formKey, formProperties);
371
+ }
309
372
  // If the form property has validation, apply it to the input
310
- if (formProperties.validation) {
373
+ if (formProperties.validation && formProperties.type !== 'radio') {
311
374
  this.applyValidation(input, formProperties.validation);
312
375
  }
313
376
  // Create an error bubble and label element for the input
@@ -338,25 +401,40 @@ export class TttxForm {
338
401
  const propertyKeys = Object.keys(properties);
339
402
  propertyKeys.forEach(formKey => {
340
403
  var _a, _b;
341
- const formInput = formItems.querySelector(`[name=${formKey}]`);
342
- // Bind events to form input elements
343
- formInput.oninvalid = this.validityCheckWrapper.bind(this);
344
- formInput.onblur = this.validityCheckWrapper.bind(this);
345
- formInput.onkeyup = this.fieldChanged.bind(this);
346
- formInput.onchange = this.fieldChanged.bind(this);
347
- if (((_a = this._data) === null || _a === void 0 ? void 0 : _a[formKey]) !== undefined && this._data[formKey] !== null) {
348
- formInput.value = this._data[formKey];
349
- }
350
- // If explicitly setting input as invalid, set invalid state and error message on render
351
- if ((_b = properties[formKey].form.validation) === null || _b === void 0 ? void 0 : _b.invalid) {
352
- const errorMessage = properties[formKey].form.validation.invalid.message;
353
- formInput.setCustomValidity(errorMessage); // Prevents the invalid styling from resetting on blur
354
- setErrorState(formInput, true, errorMessage);
355
- }
356
- if (properties[formKey].form.type === 'select' && formInput.classList.contains('placeholder')) {
357
- formInput.addEventListener('change', () => {
358
- formInput.classList.remove('placeholder');
359
- });
404
+ const formInputs = formItems.querySelectorAll(`[name=${formKey}]`);
405
+ for (const formInput of formInputs) {
406
+ // Bind events to form input elements
407
+ formInput.oninvalid = this.validityCheckWrapper.bind(this);
408
+ formInput.onblur = this.validityCheckWrapper.bind(this);
409
+ formInput.onkeyup = this.fieldChanged.bind(this);
410
+ formInput.onchange = this.fieldChanged.bind(this);
411
+ if ((_a = this._data) === null || _a === void 0 ? void 0 : _a[formKey]) {
412
+ switch (formInput.type) {
413
+ case 'checkbox':
414
+ if (this._data[formKey] === 'on') {
415
+ formInput.checked = true;
416
+ }
417
+ break;
418
+ case 'radio':
419
+ if (formInput.value === this._data[formKey]) {
420
+ formInput.checked = true;
421
+ }
422
+ break;
423
+ default:
424
+ formInput.value = this._data[formKey];
425
+ }
426
+ }
427
+ // If explicitly setting input as invalid, set invalid state and error message on render
428
+ if ((_b = properties[formKey].form.validation) === null || _b === void 0 ? void 0 : _b.invalid) {
429
+ const errorMessage = properties[formKey].form.validation.invalid.message;
430
+ formInput.setCustomValidity(errorMessage); // Prevents the invalid styling from resetting on blur
431
+ setErrorState(formInput, true, errorMessage);
432
+ }
433
+ if (properties[formKey].form.type === 'select' && formInput.classList.contains('placeholder')) {
434
+ formInput.addEventListener('change', () => {
435
+ formInput.classList.remove('placeholder');
436
+ });
437
+ }
360
438
  }
361
439
  });
362
440
  // Append the cloned form elements to the fieldset