@3t-transform/threeteeui 0.0.16 → 0.0.18

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 (125) hide show
  1. package/dist/cjs/{index-864b7110.js → index-bf777121.js} +3 -0
  2. package/dist/cjs/loader.cjs.js +2 -2
  3. package/dist/cjs/tttx-button.cjs.entry.js +25 -19
  4. package/dist/cjs/tttx-checkbox.cjs.entry.js +18 -18
  5. package/dist/cjs/tttx-form.cjs.entry.js +365 -365
  6. package/dist/cjs/tttx-icon.cjs.entry.js +14 -1856
  7. package/dist/cjs/tttx-input-calendar.cjs.entry.js +125 -0
  8. package/dist/cjs/tttx-keyvalue-block.cjs.entry.js +33 -0
  9. package/dist/cjs/tttx-list.cjs.entry.js +188 -188
  10. package/dist/cjs/tttx-loading-spinner.cjs.entry.js +16 -16
  11. package/dist/cjs/tttx-popover-content.cjs.entry.js +13 -13
  12. package/dist/cjs/tttx-standalone-input.cjs.entry.js +127 -127
  13. package/dist/cjs/tttx-table.cjs.entry.js +50 -50
  14. package/dist/cjs/tttx.cjs.js +2 -2
  15. package/dist/collection/collection-manifest.json +2 -0
  16. package/dist/collection/components/atoms/tttx-button/tttx-button.css +94 -18
  17. package/dist/collection/components/atoms/tttx-button/tttx-button.js +110 -93
  18. package/dist/collection/components/atoms/tttx-button/tttx-button.stories.js +74 -27
  19. package/dist/collection/components/atoms/tttx-icon/tttx-icon.css +8 -5
  20. package/dist/collection/components/atoms/tttx-icon/tttx-icon.js +62 -116
  21. package/dist/collection/components/atoms/tttx-icon/tttx-icon.stories.js +22 -47
  22. package/dist/collection/components/atoms/tttx-keyvalue-block/tttx-keyvalue-block.css +26 -0
  23. package/dist/collection/components/atoms/tttx-keyvalue-block/tttx-keyvalue-block.js +56 -0
  24. package/dist/collection/components/atoms/tttx-keyvalue-block/tttx-keyvalue-block.stories.js +12 -0
  25. package/dist/collection/components/atoms/tttx-loading-spinner/tttx-loading-spinner.js +67 -66
  26. package/dist/collection/components/atoms/tttx-loading-spinner/tttx-loading-spinner.stories.js +17 -17
  27. package/dist/collection/components/atoms/tttx-popover-content/tttx-popover-content.js +97 -96
  28. package/dist/collection/components/atoms/tttx-popover-content/tttx-popover-content.stories.js +23 -23
  29. package/dist/collection/components/atoms/ttx-checkbox/tttx-checkbox.js +102 -101
  30. package/dist/collection/components/atoms/ttx-checkbox/tttx-checkbox.stories.js +9 -9
  31. package/dist/collection/components/molecules/tttx-form/tttx-form.css +95 -11
  32. package/dist/collection/components/molecules/tttx-form/tttx-form.js +452 -451
  33. package/dist/collection/components/molecules/tttx-form/tttx-form.stories.js +109 -109
  34. package/dist/collection/components/molecules/tttx-input-calendar/tttx-input-calendar.css +93 -0
  35. package/dist/collection/components/molecules/tttx-input-calendar/tttx-input-calendar.js +174 -0
  36. package/dist/collection/components/molecules/tttx-input-calendar/tttx-input-calendar.stories.js +27 -0
  37. package/dist/collection/components/molecules/tttx-list/tttx-list.js +312 -311
  38. package/dist/collection/components/molecules/tttx-list/tttx-list.stories.js +14 -14
  39. package/dist/collection/components/molecules/tttx-standalone-input/tttx-standalone-input.js +570 -569
  40. package/dist/collection/components/molecules/tttx-standalone-input/tttx-standalone-input.stories.js +134 -134
  41. package/dist/collection/components/molecules/tttx-table/tttx-table.js +174 -177
  42. package/dist/collection/components/molecules/tttx-table/tttx-table.stories.js +65 -65
  43. package/dist/collection/components/palette.stories.js +7 -7
  44. package/dist/collection/docs/gettingstarted-developer.stories.js +5 -5
  45. package/dist/collection/icons.js +2838 -2838
  46. package/dist/collection/index.js +1 -1
  47. package/dist/components/index.d.ts +2 -0
  48. package/dist/components/index.js +2 -0
  49. package/dist/components/tttx-button.js +1 -40
  50. package/dist/components/tttx-button2.js +56 -0
  51. package/dist/components/tttx-checkbox.js +36 -36
  52. package/dist/components/tttx-form.js +382 -382
  53. package/dist/components/tttx-icon2.js +32 -1874
  54. package/dist/components/tttx-input-calendar.d.ts +11 -0
  55. package/dist/components/tttx-input-calendar.js +157 -0
  56. package/dist/components/tttx-keyvalue-block.d.ts +11 -0
  57. package/dist/components/tttx-keyvalue-block.js +49 -0
  58. package/dist/components/tttx-list.js +214 -214
  59. package/dist/components/tttx-loading-spinner2.js +33 -33
  60. package/dist/components/tttx-popover-content2.js +32 -32
  61. package/dist/components/tttx-standalone-input.js +168 -169
  62. package/dist/components/tttx-table.js +79 -79
  63. package/dist/esm/{index-232e347b.js → index-a05bd606.js} +3 -0
  64. package/dist/esm/loader.js +3 -3
  65. package/dist/esm/polyfills/core-js.js +0 -0
  66. package/dist/esm/polyfills/dom.js +0 -0
  67. package/dist/esm/polyfills/es5-html-element.js +0 -0
  68. package/dist/esm/polyfills/index.js +0 -0
  69. package/dist/esm/polyfills/system.js +0 -0
  70. package/dist/esm/tttx-button.entry.js +25 -19
  71. package/dist/esm/tttx-checkbox.entry.js +18 -18
  72. package/dist/esm/tttx-form.entry.js +365 -365
  73. package/dist/esm/tttx-icon.entry.js +14 -1856
  74. package/dist/esm/tttx-input-calendar.entry.js +121 -0
  75. package/dist/esm/tttx-keyvalue-block.entry.js +29 -0
  76. package/dist/esm/tttx-list.entry.js +188 -188
  77. package/dist/esm/tttx-loading-spinner.entry.js +16 -16
  78. package/dist/esm/tttx-popover-content.entry.js +13 -13
  79. package/dist/esm/tttx-standalone-input.entry.js +127 -127
  80. package/dist/esm/tttx-table.entry.js +50 -50
  81. package/dist/esm/tttx.js +3 -3
  82. package/dist/tttx/{p-3973b7dd.entry.js → p-037d286f.entry.js} +1 -1
  83. package/dist/tttx/{p-184c4fae.js → p-07b134af.js} +1 -1
  84. package/dist/tttx/{p-125f06b3.entry.js → p-1b63f16a.entry.js} +1 -1
  85. package/dist/tttx/p-45afb84c.entry.js +1 -0
  86. package/dist/tttx/p-68ff0f39.entry.js +1 -0
  87. package/dist/tttx/{p-6828fe6f.entry.js → p-93763d3c.entry.js} +1 -1
  88. package/dist/tttx/p-a5808741.entry.js +1 -0
  89. package/dist/tttx/{p-fe4c70b2.entry.js → p-a92ca87e.entry.js} +1 -1
  90. package/dist/tttx/{p-5ce1ba22.entry.js → p-a96ca037.entry.js} +1 -1
  91. package/dist/tttx/p-dc087fd8.entry.js +1 -0
  92. package/dist/tttx/p-e3cc75bb.entry.js +1 -0
  93. package/dist/tttx/{p-01e1894e.entry.js → p-f579ed1e.entry.js} +1 -1
  94. package/dist/tttx/tttx.esm.js +1 -1
  95. package/dist/types/components/atoms/tttx-button/tttx-button.d.ts +10 -9
  96. package/dist/types/components/atoms/tttx-button/tttx-button.stories.d.ts +10 -20
  97. package/dist/types/components/atoms/tttx-icon/tttx-icon.d.ts +5 -14
  98. package/dist/types/components/atoms/tttx-icon/tttx-icon.stories.d.ts +20 -21
  99. package/dist/types/components/atoms/tttx-keyvalue-block/tttx-keyvalue-block.d.ts +4 -0
  100. package/dist/types/components/atoms/tttx-keyvalue-block/tttx-keyvalue-block.stories.d.ts +6 -0
  101. package/dist/types/components/atoms/tttx-loading-spinner/tttx-loading-spinner.d.ts +6 -6
  102. package/dist/types/components/atoms/tttx-loading-spinner/tttx-loading-spinner.stories.d.ts +17 -17
  103. package/dist/types/components/atoms/tttx-popover-content/tttx-popover-content.d.ts +7 -7
  104. package/dist/types/components/atoms/tttx-popover-content/tttx-popover-content.stories.d.ts +18 -18
  105. package/dist/types/components/atoms/ttx-checkbox/tttx-checkbox.d.ts +9 -9
  106. package/dist/types/components/atoms/ttx-checkbox/tttx-checkbox.stories.d.ts +6 -6
  107. package/dist/types/components/molecules/tttx-form/tttx-form.d.ts +134 -134
  108. package/dist/types/components/molecules/tttx-form/tttx-form.stories.d.ts +12 -12
  109. package/dist/types/components/molecules/tttx-input-calendar/tttx-input-calendar.d.ts +19 -0
  110. package/dist/types/components/molecules/tttx-input-calendar/tttx-input-calendar.stories.d.ts +26 -0
  111. package/dist/types/components/molecules/tttx-list/tttx-list.d.ts +51 -51
  112. package/dist/types/components/molecules/tttx-list/tttx-list.stories.d.ts +13 -13
  113. package/dist/types/components/molecules/tttx-standalone-input/tttx-standalone-input.d.ts +38 -38
  114. package/dist/types/components/molecules/tttx-standalone-input/tttx-standalone-input.stories.d.ts +106 -106
  115. package/dist/types/components/molecules/tttx-table/tttx-table.d.ts +15 -15
  116. package/dist/types/components/molecules/tttx-table/tttx-table.stories.d.ts +21 -21
  117. package/dist/types/components/palette.stories.d.ts +6 -6
  118. package/dist/types/components.d.ts +46 -15
  119. package/dist/types/docs/gettingstarted-developer.stories.d.ts +5 -5
  120. package/dist/types/icons.d.ts +2 -2
  121. package/dist/types/index.d.ts +1 -1
  122. package/package.json +4 -4
  123. package/dist/tttx/p-10316ff1.entry.js +0 -1
  124. package/dist/tttx/p-b1c22f5f.entry.js +0 -1
  125. package/dist/tttx/p-c7f9be65.entry.js +0 -1
@@ -1,451 +1,452 @@
1
- import { h, Host } from '@stencil/core';
2
- export class TttxForm {
3
- constructor() {
4
- // Create a new template element using the HTMLTemplateElement interface.
5
- this.template = document.createElement('template');
6
- this.formschema = undefined;
7
- this.submitValue = undefined;
8
- }
9
- // This method is called whenever the "formschema" property changes
10
- onFormSchemaChange(newValue) {
11
- // Check if the new value is a string, indicating that it needs to be parsed
12
- if (typeof newValue === 'string') {
13
- // Parse the string and set the "_formSchema" property
14
- this._formSchema = JSON.parse(newValue);
15
- }
16
- else {
17
- // If the new value is already an object, set the "_formSchema" property directly
18
- this._formSchema = newValue;
19
- }
20
- }
21
- /**
22
- * Handles the focus event for a form field and emits a "dataChanged" event
23
- * to the parent component with the field name and its new value.
24
- *
25
- * @param {FocusEvent} event - The focus event triggered by the field.
26
- * @return {void}
27
- */
28
- fieldChanged(event) {
29
- // Extract the name and value of the field from the event
30
- const fieldName = event.target.name;
31
- const fieldValue = event.target.value;
32
- // Emit an event to signal that the field's data has changed
33
- this.dataChanged.emit({ name: fieldName, value: fieldValue });
34
- }
35
- /**
36
- * Validates the input field on focusout event by checking its validity state,
37
- * sets an error message if there's an issue, and emits a "dataChanged" event to
38
- * the parent component with the field name and its new value.
39
- *
40
- * @param {FocusEvent} event - The focusout event triggered by the input field.
41
- * @return {void}
42
- */
43
- validityCheck(event) {
44
- var _a, _b, _c, _d;
45
- event.preventDefault();
46
- const target = event.target;
47
- let hasError = true;
48
- let errorMessage = '';
49
- // validity object on HTML5 inputs has the following options
50
- // badInput
51
- // customError
52
- // patternMismatch
53
- // rangeOverflow
54
- // rangeUnderflow
55
- // stepMismatch
56
- // tooLong
57
- // tooShort
58
- // typeMismatch
59
- // valid
60
- // valueMissing
61
- // customErrors can be set with
62
- // target.setCustomValidity('custom error!');
63
- // and cleared with
64
- // target.setCustomValidity('');
65
- // Check the validity of the input field and set an error message if needed
66
- switch (true) {
67
- // The field is required, but has no value
68
- case target.validity.valueMissing:
69
- errorMessage = (_a = target.dataset.required) !== null && _a !== void 0 ? _a : 'This field is required';
70
- break;
71
- // The field's value does not match the expected pattern
72
- case target.validity.patternMismatch:
73
- errorMessage = (_b = target.dataset.pattern) !== null && _b !== void 0 ? _b : 'Incorrect format';
74
- break;
75
- // The field's value is not of the correct input type
76
- case target.validity.badInput:
77
- // IE string in a number field
78
- errorMessage = (_c = target.dataset.badinput) !== null && _c !== void 0 ? _c : 'Wrong input type';
79
- break;
80
- // The field's value is above or below the range set in the "min" and "max" attributes
81
- case target.validity.rangeOverflow || target.validity.rangeUnderflow:
82
- // IE date or number is above or below value set in min or max tags
83
- errorMessage = (_d = target.dataset.range) !== null && _d !== void 0 ? _d : 'Invalid value';
84
- break;
85
- // No error detected
86
- default:
87
- hasError = false;
88
- }
89
- // Set the error state of the input field based on the error message and whether an error was detected
90
- this.setErrorState(target, hasError, errorMessage);
91
- }
92
- /**
93
- * Sets the error state of an input field by updating its class and error message.
94
- * If an error was detected, it sets the input field's class to "invalid" and
95
- * displays the error message in an error bubble. If no error was detected,
96
- * it removes the "invalid" class from the input field and clears the error bubble.
97
- *
98
- * @param {HTMLInputElement} target - The input field to update.
99
- * @param {boolean} hasError - Whether an error was detected in the field.
100
- * @param {string} errorMessage - The error message to display (if any).
101
- * @return {void}
102
- */
103
- setErrorState(target, hasError, errorMessage) {
104
- // Find the error bubble element for the input field
105
- const errorBubble = target.parentElement.querySelector('.errorBubble');
106
- // If an error was detected, set the input field's class to "invalid" and display the error message in the error bubble
107
- if (hasError) {
108
- target.className = 'invalid';
109
- const errorIcon = this.createErrorIcon();
110
- errorBubble.replaceChildren(errorIcon, errorMessage);
111
- }
112
- // If no error was detected, remove the "invalid" class from the input field and clear the error bubble
113
- else {
114
- target.className = '';
115
- errorBubble.replaceChildren();
116
- }
117
- }
118
- // Create an error icon element to display in the error bubble of an input field
119
- createErrorIcon() {
120
- // Create a new <span> element to serve as the error icon
121
- const errorIcon = document.createElement('span');
122
- // Set the class of the error icon to a pre-defined CSS class that specifies the icon's appearance
123
- errorIcon.className = 'material-symbols-rounded';
124
- // Set the text content of the error icon to the word "warning"
125
- errorIcon.textContent = 'warning';
126
- // Return the error icon element
127
- return errorIcon;
128
- }
129
- /**
130
- * Submits the form data to the server.
131
- *
132
- * @param {SubmitEvent} event - The event object for the form submission.
133
- * @returns {void}
134
- *
135
- * @example
136
- * const form = document.getElementById('myForm');
137
- * form.addEventListener('submit', (event) => {
138
- * doSubmit(event);
139
- * });
140
- */
141
- doSubmit(event) {
142
- // prevent the form from submitting normally
143
- event.preventDefault();
144
- // create a new FormData object with the form data
145
- const formData = new FormData(event.target);
146
- // emit the form data through the `dataSubmitted` event
147
- this.dataSubmitted.emit(formData);
148
- }
149
- // This method is called before the component is loaded into the DOM
150
- componentWillLoad() {
151
- // Initialize the form schema by calling the "onFormSchemaChange" method with the current "formschema" property
152
- this.onFormSchemaChange(this.formschema);
153
- }
154
- // This method is called before the component is rendered
155
- componentWillRender() {
156
- // Clear the template to account for a potential re-render scenario
157
- this.template = document.createElement('template');
158
- // Populate the form from the form schema
159
- this.populateFormFromSchema();
160
- }
161
- /**
162
- * Creates a new HTMLInputElement with the specified name, type, and placeholder (if any),
163
- * and sets its autocomplete and autocapitalization properties to off.
164
- *
165
- * @param {string} formKey - The name of the input field, as specified in the form schema.
166
- * @param {Object} formProperties - An object containing additional properties for the input field, such as its type and placeholder value.
167
- * @param {string} formProperties.type - The type of the input field (e.g., "text", "email", "number", etc.).
168
- * @param {string} [formProperties.placeholder] - An optional placeholder value to display in the input field.
169
- * @return {HTMLInputElement} - The new input element.
170
- */
171
- createInput(formKey, formProperties) {
172
- var _a;
173
- // Create a new <input> element with the specified name and type
174
- const input = document.createElement('input');
175
- input.name = formKey;
176
- input.type = formProperties.type;
177
- // Set the placeholder attribute to the specified value (if any)
178
- input.placeholder = (_a = formProperties.placeholder) !== null && _a !== void 0 ? _a : '';
179
- // Disable autocomplete and autocapitalization
180
- input.autocomplete = 'off';
181
- input.autocapitalize = 'off';
182
- // Return the input element
183
- return input;
184
- }
185
- /**
186
- * Applies validation attributes to an input element based on the specified validation object.
187
- * If a certain property is present in the object, it will set the corresponding attribute on
188
- * the input element (e.g., "required" will set the "required" and "data-required" attributes,
189
- * "pattern" will set the "pattern" and "data-pattern" attributes, etc.).
190
- *
191
- * @param {HTMLInputElement} input - The input element to apply validation attributes to.
192
- * @param {Object} validation - An object containing the validation rules for the input field.
193
- * @param {Object} [validation.required] - An object containing a "message" property to display if the field is required.
194
- * @param {Object} [validation.pattern] - An object containing a "pattern" property to match against the field value, and a "message" property to display if the pattern doesn't match.
195
- * @param {Object} [validation.badInput] - An object containing a "message" property to display if the field value is invalid.
196
- * @param {Object} [validation.minmax] - An object containing "min" and "max" properties to validate the field value against, and a "message" property to display if the value is out of range.
197
- * @param {string} [validation.maxlength] - The maximum length of the input field.
198
- * @return {void}
199
- */
200
- applyValidation(input, validation) {
201
- var _a, _b;
202
- // If the "required" property is present, add the "required" attribute to the input element and
203
- // set its "data-required" attribute to the specified message (if any)
204
- if (validation.required) {
205
- input.setAttribute('required', '');
206
- input.setAttribute('data-required', (_a = validation.required.message) !== null && _a !== void 0 ? _a : '');
207
- }
208
- // If the "pattern" property is present, add the "pattern" attribute to the input element and set
209
- // its "data-pattern" attribute to the specified message (if any)
210
- if (validation.pattern) {
211
- input.setAttribute('pattern', validation.pattern.pattern);
212
- input.setAttribute('data-pattern', (_b = validation.pattern.message) !== null && _b !== void 0 ? _b : '');
213
- }
214
- // If the "badInput" property is present, set the input element's "data-badinput" attribute to
215
- // the specified message
216
- if (validation.badInput) {
217
- input.setAttribute('data-badinput', validation.badInput.message);
218
- }
219
- // If the "minmax" property is present, add the "min" and "max" attributes to the input element
220
- // and set its "data-range" attribute to the specified message (if any)
221
- if (validation.minmax) {
222
- input.setAttribute('min', validation.minmax.min);
223
- input.setAttribute('max', validation.minmax.max);
224
- input.setAttribute('data-range', validation.minmax.message);
225
- }
226
- // If the "maxlength" property is present, add the "maxlength" attribute to the input element
227
- if (validation.maxlength) {
228
- input.setAttribute('maxlength', validation.maxlength);
229
- }
230
- }
231
- // Create a new error bubble element
232
- createErrorBubble() {
233
- // Create a new <div> element with the "errorBubble" class
234
- const errorBubble = document.createElement('div');
235
- errorBubble.className = 'errorBubble';
236
- // Return the error bubble element
237
- return errorBubble;
238
- }
239
- /**
240
- * Creates a new <label> element with the "inputBlock" class and the specified label text,
241
- * and appends the input element and error bubble element to it. If the form property has
242
- * no validation object, it adds an "optional" span element to the label.
243
- *
244
- * @param {Object} formProperties - An object containing properties for the form field, including its label text and validation rules.
245
- * @param {HTMLInputElement} input - The input element to associate with the label.
246
- * @param {HTMLDivElement} errorBubble - The error bubble element to display error messages in.
247
- * @return {HTMLLabelElement} - The new label element.
248
- */
249
- createLabel(formProperties, input, errorBubble) {
250
- // Create a new <label> element with the "inputBlock" class and the specified text
251
- const label = document.createElement('label');
252
- label.className = 'inputBlock';
253
- label.innerText = formProperties.label;
254
- // If the form property has no validation object, add an "optional" span element to the label
255
- if (!formProperties.validation) {
256
- const optionalSpan = document.createElement('span');
257
- optionalSpan.className = 'optional';
258
- optionalSpan.innerHTML = '&nbsp;(optional)';
259
- label.appendChild(optionalSpan);
260
- }
261
- // Append the input element and error bubble element to the label
262
- label.appendChild(input);
263
- label.appendChild(errorBubble);
264
- // Return the label element
265
- return label;
266
- }
267
- /**
268
- * Creates a new <input> element with the "submit" type, the "primary-blue" class, and the
269
- * specified label text (i.e., "Save" by default), and returns the new submit button element.
270
- *
271
- * @return {HTMLInputElement} - The new submit button element.
272
- */
273
- createSubmitButton() {
274
- // Create a new <input> element with the "submit" type and the specified class and value
275
- const submitButton = document.createElement('input');
276
- submitButton.type = 'submit';
277
- submitButton.className = 'button primary-blue';
278
- submitButton.value = 'Save';
279
- // Return the submit button element
280
- return submitButton;
281
- }
282
- /**
283
- * Populates the form template with input fields and labels based on the properties of the
284
- * current form schema. For each property in the schema, it creates an input element, applies
285
- * any validation rules to it, creates an error bubble and label element, and appends them
286
- * to the form template. Finally, it creates and appends a submit button element to the form.
287
- *
288
- * @return {void}
289
- */
290
- populateFormFromSchema() {
291
- // If there is no form schema, return early
292
- if (!this._formSchema)
293
- return;
294
- // Get the properties of the form schema and their keys
295
- const properties = this._formSchema.properties;
296
- const propertyKeys = Object.keys(properties);
297
- // Loop through each property key and create an input, label, and error bubble for it
298
- propertyKeys.forEach(formKey => {
299
- const formItem = properties[formKey];
300
- const formProperties = formItem.form;
301
- const input = this.createInput(formKey, formProperties);
302
- // If the form property has validation, apply it to the input
303
- if (formProperties.validation) {
304
- this.applyValidation(input, formProperties.validation);
305
- }
306
- // Create an error bubble and label element for the input
307
- const errorBubble = this.createErrorBubble();
308
- const label = this.createLabel(formProperties, input, errorBubble);
309
- // Append the label element to the form template
310
- this.template.content.append(label);
311
- });
312
- // Create and append a submit button element to the form template
313
- const submitButton = this.createSubmitButton();
314
- this.template.content.append(submitButton);
315
- }
316
- /**
317
- * Clones the form template and binds event listeners to its input elements. First, it checks if
318
- * there is a form schema present. If so, it clones the template's content, binds events to form
319
- * input elements, and appends the cloned form elements to the fieldset. The event listeners include
320
- * "oninvalid" (to check input validity on submit), "onblur" (to check input validity on blur),
321
- * "onkeyup" (to handle changes in input fields), and "onchange" (to handle changes in select fields).
322
- *
323
- * @return {void}
324
- */
325
- componentDidRender() {
326
- // If there's no form schema, return
327
- if (!this._formSchema) {
328
- return;
329
- }
330
- // Clone the template's content and store it in a variable
331
- const formItems = this.template.content.cloneNode(true);
332
- // Bind event listeners to form elements
333
- const properties = this._formSchema.properties;
334
- const propertyKeys = Object.keys(properties);
335
- propertyKeys.forEach((formKey) => {
336
- const formInput = formItems.querySelector(`[name=${formKey}]`);
337
- // Bind events to form input elements
338
- formInput.oninvalid = this.validityCheck.bind(this);
339
- formInput.onblur = this.validityCheck.bind(this);
340
- formInput.onkeyup = this.fieldChanged.bind(this);
341
- formInput.onchange = this.fieldChanged.bind(this);
342
- });
343
- // Append the cloned form elements to the fieldset
344
- this.fieldset.appendChild(formItems);
345
- }
346
- /**
347
- * Renders the component's template as a form element with a fieldset container. The form's
348
- * "onSubmit" event is bound to the "doSubmit" function, which handles the form submission
349
- * and emits a "dataSubmitted" event with the form data. The fieldset element is assigned
350
- * to the "fieldset" instance variable using a ref, so it can be populated with form elements
351
- * later on.
352
- *
353
- * @return {JSX.Element} - The rendered form template as a JSX element.
354
- */
355
- render() {
356
- return (h(Host, null, h("form", { onSubmit: this.doSubmit.bind(this) }, h("fieldset", { ref: el => (this.fieldset = el) }))));
357
- }
358
- static get is() { return "tttx-form"; }
359
- static get encapsulation() { return "shadow"; }
360
- static get originalStyleUrls() {
361
- return {
362
- "$": ["tttx-form.scss"]
363
- };
364
- }
365
- static get styleUrls() {
366
- return {
367
- "$": ["tttx-form.css"]
368
- };
369
- }
370
- static get properties() {
371
- return {
372
- "formschema": {
373
- "type": "any",
374
- "mutable": false,
375
- "complexType": {
376
- "original": "any",
377
- "resolved": "any",
378
- "references": {}
379
- },
380
- "required": false,
381
- "optional": false,
382
- "docs": {
383
- "tags": [],
384
- "text": ""
385
- },
386
- "attribute": "formschema",
387
- "reflect": false
388
- },
389
- "submitValue": {
390
- "type": "any",
391
- "mutable": false,
392
- "complexType": {
393
- "original": "any",
394
- "resolved": "any",
395
- "references": {}
396
- },
397
- "required": false,
398
- "optional": false,
399
- "docs": {
400
- "tags": [],
401
- "text": ""
402
- },
403
- "attribute": "submit-value",
404
- "reflect": false
405
- }
406
- };
407
- }
408
- static get events() {
409
- return [{
410
- "method": "dataSubmitted",
411
- "name": "dataSubmitted",
412
- "bubbles": true,
413
- "cancelable": true,
414
- "composed": true,
415
- "docs": {
416
- "tags": [],
417
- "text": ""
418
- },
419
- "complexType": {
420
- "original": "FormData",
421
- "resolved": "FormData",
422
- "references": {
423
- "FormData": {
424
- "location": "global"
425
- }
426
- }
427
- }
428
- }, {
429
- "method": "dataChanged",
430
- "name": "dataChanged",
431
- "bubbles": true,
432
- "cancelable": true,
433
- "composed": true,
434
- "docs": {
435
- "tags": [],
436
- "text": ""
437
- },
438
- "complexType": {
439
- "original": "{ name: string, value: any }",
440
- "resolved": "{ name: string; value: any; }",
441
- "references": {}
442
- }
443
- }];
444
- }
445
- static get watchers() {
446
- return [{
447
- "propName": "formschema",
448
- "methodName": "onFormSchemaChange"
449
- }];
450
- }
451
- }
1
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
2
+ import { h, Host } from '@stencil/core';
3
+ export class TttxForm {
4
+ constructor() {
5
+ // Create a new template element using the HTMLTemplateElement interface.
6
+ this.template = document.createElement('template');
7
+ this.formschema = undefined;
8
+ this.submitValue = undefined;
9
+ }
10
+ // This method is called whenever the "formschema" property changes
11
+ onFormSchemaChange(newValue) {
12
+ // Check if the new value is a string, indicating that it needs to be parsed
13
+ if (typeof newValue === 'string') {
14
+ // Parse the string and set the "_formSchema" property
15
+ this._formSchema = JSON.parse(newValue);
16
+ }
17
+ else {
18
+ // If the new value is already an object, set the "_formSchema" property directly
19
+ this._formSchema = newValue;
20
+ }
21
+ }
22
+ /**
23
+ * Handles the focus event for a form field and emits a "dataChanged" event
24
+ * to the parent component with the field name and its new value.
25
+ *
26
+ * @param {FocusEvent} event - The focus event triggered by the field.
27
+ * @return {void}
28
+ */
29
+ fieldChanged(event) {
30
+ // Extract the name and value of the field from the event
31
+ const fieldName = event.target.name;
32
+ const fieldValue = event.target.value;
33
+ // Emit an event to signal that the field's data has changed
34
+ this.dataChanged.emit({ name: fieldName, value: fieldValue });
35
+ }
36
+ /**
37
+ * Validates the input field on focusout event by checking its validity state,
38
+ * sets an error message if there's an issue, and emits a "dataChanged" event to
39
+ * the parent component with the field name and its new value.
40
+ *
41
+ * @param {FocusEvent} event - The focusout event triggered by the input field.
42
+ * @return {void}
43
+ */
44
+ validityCheck(event) {
45
+ var _a, _b, _c, _d;
46
+ event.preventDefault();
47
+ const target = event.target;
48
+ let hasError = true;
49
+ let errorMessage = '';
50
+ // validity object on HTML5 inputs has the following options
51
+ // badInput
52
+ // customError
53
+ // patternMismatch
54
+ // rangeOverflow
55
+ // rangeUnderflow
56
+ // stepMismatch
57
+ // tooLong
58
+ // tooShort
59
+ // typeMismatch
60
+ // valid
61
+ // valueMissing
62
+ // customErrors can be set with
63
+ // target.setCustomValidity('custom error!');
64
+ // and cleared with
65
+ // target.setCustomValidity('');
66
+ // Check the validity of the input field and set an error message if needed
67
+ switch (true) {
68
+ // The field is required, but has no value
69
+ case target.validity.valueMissing:
70
+ errorMessage = (_a = target.dataset.required) !== null && _a !== void 0 ? _a : 'This field is required';
71
+ break;
72
+ // The field's value does not match the expected pattern
73
+ case target.validity.patternMismatch:
74
+ errorMessage = (_b = target.dataset.pattern) !== null && _b !== void 0 ? _b : 'Incorrect format';
75
+ break;
76
+ // The field's value is not of the correct input type
77
+ case target.validity.badInput:
78
+ // IE string in a number field
79
+ errorMessage = (_c = target.dataset.badinput) !== null && _c !== void 0 ? _c : 'Wrong input type';
80
+ break;
81
+ // The field's value is above or below the range set in the "min" and "max" attributes
82
+ case target.validity.rangeOverflow || target.validity.rangeUnderflow:
83
+ // IE date or number is above or below value set in min or max tags
84
+ errorMessage = (_d = target.dataset.range) !== null && _d !== void 0 ? _d : 'Invalid value';
85
+ break;
86
+ // No error detected
87
+ default:
88
+ hasError = false;
89
+ }
90
+ // Set the error state of the input field based on the error message and whether an error was detected
91
+ this.setErrorState(target, hasError, errorMessage);
92
+ }
93
+ /**
94
+ * Sets the error state of an input field by updating its class and error message.
95
+ * If an error was detected, it sets the input field's class to "invalid" and
96
+ * displays the error message in an error bubble. If no error was detected,
97
+ * it removes the "invalid" class from the input field and clears the error bubble.
98
+ *
99
+ * @param {HTMLInputElement} target - The input field to update.
100
+ * @param {boolean} hasError - Whether an error was detected in the field.
101
+ * @param {string} errorMessage - The error message to display (if any).
102
+ * @return {void}
103
+ */
104
+ setErrorState(target, hasError, errorMessage) {
105
+ // Find the error bubble element for the input field
106
+ const errorBubble = target.parentElement.querySelector('.errorBubble');
107
+ // If an error was detected, set the input field's class to "invalid" and display the error message in the error bubble
108
+ if (hasError) {
109
+ target.className = 'invalid';
110
+ const errorIcon = this.createErrorIcon();
111
+ errorBubble.replaceChildren(errorIcon, errorMessage);
112
+ }
113
+ // If no error was detected, remove the "invalid" class from the input field and clear the error bubble
114
+ else {
115
+ target.className = '';
116
+ errorBubble.replaceChildren();
117
+ }
118
+ }
119
+ // Create an error icon element to display in the error bubble of an input field
120
+ createErrorIcon() {
121
+ // Create a new <span> element to serve as the error icon
122
+ const errorIcon = document.createElement('span');
123
+ // Set the class of the error icon to a pre-defined CSS class that specifies the icon's appearance
124
+ errorIcon.className = 'material-symbols-rounded';
125
+ // Set the text content of the error icon to the word "warning"
126
+ errorIcon.textContent = 'warning';
127
+ // Return the error icon element
128
+ return errorIcon;
129
+ }
130
+ /**
131
+ * Submits the form data to the server.
132
+ *
133
+ * @param {SubmitEvent} event - The event object for the form submission.
134
+ * @returns {void}
135
+ *
136
+ * @example
137
+ * const form = document.getElementById('myForm');
138
+ * form.addEventListener('submit', (event) => {
139
+ * doSubmit(event);
140
+ * });
141
+ */
142
+ doSubmit(event) {
143
+ // prevent the form from submitting normally
144
+ event.preventDefault();
145
+ // create a new FormData object with the form data
146
+ const formData = new FormData(event.target);
147
+ // emit the form data through the `dataSubmitted` event
148
+ this.dataSubmitted.emit(formData);
149
+ }
150
+ // This method is called before the component is loaded into the DOM
151
+ componentWillLoad() {
152
+ // Initialize the form schema by calling the "onFormSchemaChange" method with the current "formschema" property
153
+ this.onFormSchemaChange(this.formschema);
154
+ }
155
+ // This method is called before the component is rendered
156
+ componentWillRender() {
157
+ // Clear the template to account for a potential re-render scenario
158
+ this.template = document.createElement('template');
159
+ // Populate the form from the form schema
160
+ this.populateFormFromSchema();
161
+ }
162
+ /**
163
+ * Creates a new HTMLInputElement with the specified name, type, and placeholder (if any),
164
+ * and sets its autocomplete and autocapitalization properties to off.
165
+ *
166
+ * @param {string} formKey - The name of the input field, as specified in the form schema.
167
+ * @param {Object} formProperties - An object containing additional properties for the input field, such as its type and placeholder value.
168
+ * @param {string} formProperties.type - The type of the input field (e.g., "text", "email", "number", etc.).
169
+ * @param {string} [formProperties.placeholder] - An optional placeholder value to display in the input field.
170
+ * @return {HTMLInputElement} - The new input element.
171
+ */
172
+ createInput(formKey, formProperties) {
173
+ var _a;
174
+ // Create a new <input> element with the specified name and type
175
+ const input = document.createElement('input');
176
+ input.name = formKey;
177
+ input.type = formProperties.type;
178
+ // Set the placeholder attribute to the specified value (if any)
179
+ input.placeholder = (_a = formProperties.placeholder) !== null && _a !== void 0 ? _a : '';
180
+ // Disable autocomplete and autocapitalization
181
+ input.autocomplete = 'off';
182
+ input.autocapitalize = 'off';
183
+ // Return the input element
184
+ return input;
185
+ }
186
+ /**
187
+ * Applies validation attributes to an input element based on the specified validation object.
188
+ * If a certain property is present in the object, it will set the corresponding attribute on
189
+ * the input element (e.g., "required" will set the "required" and "data-required" attributes,
190
+ * "pattern" will set the "pattern" and "data-pattern" attributes, etc.).
191
+ *
192
+ * @param {HTMLInputElement} input - The input element to apply validation attributes to.
193
+ * @param {Object} validation - An object containing the validation rules for the input field.
194
+ * @param {Object} [validation.required] - An object containing a "message" property to display if the field is required.
195
+ * @param {Object} [validation.pattern] - An object containing a "pattern" property to match against the field value, and a "message" property to display if the pattern doesn't match.
196
+ * @param {Object} [validation.badInput] - An object containing a "message" property to display if the field value is invalid.
197
+ * @param {Object} [validation.minmax] - An object containing "min" and "max" properties to validate the field value against, and a "message" property to display if the value is out of range.
198
+ * @param {string} [validation.maxlength] - The maximum length of the input field.
199
+ * @return {void}
200
+ */
201
+ applyValidation(input, validation) {
202
+ var _a, _b;
203
+ // If the "required" property is present, add the "required" attribute to the input element and
204
+ // set its "data-required" attribute to the specified message (if any)
205
+ if (validation.required) {
206
+ input.setAttribute('required', '');
207
+ input.setAttribute('data-required', (_a = validation.required.message) !== null && _a !== void 0 ? _a : '');
208
+ }
209
+ // If the "pattern" property is present, add the "pattern" attribute to the input element and set
210
+ // its "data-pattern" attribute to the specified message (if any)
211
+ if (validation.pattern) {
212
+ input.setAttribute('pattern', validation.pattern.pattern);
213
+ input.setAttribute('data-pattern', (_b = validation.pattern.message) !== null && _b !== void 0 ? _b : '');
214
+ }
215
+ // If the "badInput" property is present, set the input element's "data-badinput" attribute to
216
+ // the specified message
217
+ if (validation.badInput) {
218
+ input.setAttribute('data-badinput', validation.badInput.message);
219
+ }
220
+ // If the "minmax" property is present, add the "min" and "max" attributes to the input element
221
+ // and set its "data-range" attribute to the specified message (if any)
222
+ if (validation.minmax) {
223
+ input.setAttribute('min', validation.minmax.min);
224
+ input.setAttribute('max', validation.minmax.max);
225
+ input.setAttribute('data-range', validation.minmax.message);
226
+ }
227
+ // If the "maxlength" property is present, add the "maxlength" attribute to the input element
228
+ if (validation.maxlength) {
229
+ input.setAttribute('maxlength', validation.maxlength);
230
+ }
231
+ }
232
+ // Create a new error bubble element
233
+ createErrorBubble() {
234
+ // Create a new <div> element with the "errorBubble" class
235
+ const errorBubble = document.createElement('div');
236
+ errorBubble.className = 'errorBubble';
237
+ // Return the error bubble element
238
+ return errorBubble;
239
+ }
240
+ /**
241
+ * Creates a new <label> element with the "inputBlock" class and the specified label text,
242
+ * and appends the input element and error bubble element to it. If the form property has
243
+ * no validation object, it adds an "optional" span element to the label.
244
+ *
245
+ * @param {Object} formProperties - An object containing properties for the form field, including its label text and validation rules.
246
+ * @param {HTMLInputElement} input - The input element to associate with the label.
247
+ * @param {HTMLDivElement} errorBubble - The error bubble element to display error messages in.
248
+ * @return {HTMLLabelElement} - The new label element.
249
+ */
250
+ createLabel(formProperties, input, errorBubble) {
251
+ // Create a new <label> element with the "inputBlock" class and the specified text
252
+ const label = document.createElement('label');
253
+ label.className = 'inputBlock';
254
+ label.innerText = formProperties.label;
255
+ // If the form property has no validation object, add an "optional" span element to the label
256
+ if (!formProperties.validation) {
257
+ const optionalSpan = document.createElement('span');
258
+ optionalSpan.className = 'optional';
259
+ optionalSpan.innerHTML = '&nbsp;(optional)';
260
+ label.appendChild(optionalSpan);
261
+ }
262
+ // Append the input element and error bubble element to the label
263
+ label.appendChild(input);
264
+ label.appendChild(errorBubble);
265
+ // Return the label element
266
+ return label;
267
+ }
268
+ /**
269
+ * Creates a new <input> element with the "submit" type, the "primary-blue" class, and the
270
+ * specified label text (i.e., "Save" by default), and returns the new submit button element.
271
+ *
272
+ * @return {HTMLInputElement} - The new submit button element.
273
+ */
274
+ createSubmitButton() {
275
+ // Create a new <input> element with the "submit" type and the specified class and value
276
+ const submitButton = document.createElement('input');
277
+ submitButton.type = 'submit';
278
+ submitButton.className = 'button primary-blue';
279
+ submitButton.value = 'Save';
280
+ // Return the submit button element
281
+ return submitButton;
282
+ }
283
+ /**
284
+ * Populates the form template with input fields and labels based on the properties of the
285
+ * current form schema. For each property in the schema, it creates an input element, applies
286
+ * any validation rules to it, creates an error bubble and label element, and appends them
287
+ * to the form template. Finally, it creates and appends a submit button element to the form.
288
+ *
289
+ * @return {void}
290
+ */
291
+ populateFormFromSchema() {
292
+ // If there is no form schema, return early
293
+ if (!this._formSchema)
294
+ return;
295
+ // Get the properties of the form schema and their keys
296
+ const properties = this._formSchema.properties;
297
+ const propertyKeys = Object.keys(properties);
298
+ // Loop through each property key and create an input, label, and error bubble for it
299
+ propertyKeys.forEach(formKey => {
300
+ const formItem = properties[formKey];
301
+ const formProperties = formItem.form;
302
+ const input = this.createInput(formKey, formProperties);
303
+ // If the form property has validation, apply it to the input
304
+ if (formProperties.validation) {
305
+ this.applyValidation(input, formProperties.validation);
306
+ }
307
+ // Create an error bubble and label element for the input
308
+ const errorBubble = this.createErrorBubble();
309
+ const label = this.createLabel(formProperties, input, errorBubble);
310
+ // Append the label element to the form template
311
+ this.template.content.append(label);
312
+ });
313
+ // Create and append a submit button element to the form template
314
+ const submitButton = this.createSubmitButton();
315
+ this.template.content.append(submitButton);
316
+ }
317
+ /**
318
+ * Clones the form template and binds event listeners to its input elements. First, it checks if
319
+ * there is a form schema present. If so, it clones the template's content, binds events to form
320
+ * input elements, and appends the cloned form elements to the fieldset. The event listeners include
321
+ * "oninvalid" (to check input validity on submit), "onblur" (to check input validity on blur),
322
+ * "onkeyup" (to handle changes in input fields), and "onchange" (to handle changes in select fields).
323
+ *
324
+ * @return {void}
325
+ */
326
+ componentDidRender() {
327
+ // If there's no form schema, return
328
+ if (!this._formSchema) {
329
+ return;
330
+ }
331
+ // Clone the template's content and store it in a variable
332
+ const formItems = this.template.content.cloneNode(true);
333
+ // Bind event listeners to form elements
334
+ const properties = this._formSchema.properties;
335
+ const propertyKeys = Object.keys(properties);
336
+ propertyKeys.forEach((formKey) => {
337
+ const formInput = formItems.querySelector(`[name=${formKey}]`);
338
+ // Bind events to form input elements
339
+ formInput.oninvalid = this.validityCheck.bind(this);
340
+ formInput.onblur = this.validityCheck.bind(this);
341
+ formInput.onkeyup = this.fieldChanged.bind(this);
342
+ formInput.onchange = this.fieldChanged.bind(this);
343
+ });
344
+ // Append the cloned form elements to the fieldset
345
+ this.fieldset.appendChild(formItems);
346
+ }
347
+ /**
348
+ * Renders the component's template as a form element with a fieldset container. The form's
349
+ * "onSubmit" event is bound to the "doSubmit" function, which handles the form submission
350
+ * and emits a "dataSubmitted" event with the form data. The fieldset element is assigned
351
+ * to the "fieldset" instance variable using a ref, so it can be populated with form elements
352
+ * later on.
353
+ *
354
+ * @return {JSX.Element} - The rendered form template as a JSX element.
355
+ */
356
+ render() {
357
+ return (h(Host, null, h("form", { onSubmit: this.doSubmit.bind(this) }, h("fieldset", { ref: el => (this.fieldset = el) }))));
358
+ }
359
+ static get is() { return "tttx-form"; }
360
+ static get encapsulation() { return "shadow"; }
361
+ static get originalStyleUrls() {
362
+ return {
363
+ "$": ["tttx-form.scss"]
364
+ };
365
+ }
366
+ static get styleUrls() {
367
+ return {
368
+ "$": ["tttx-form.css"]
369
+ };
370
+ }
371
+ static get properties() {
372
+ return {
373
+ "formschema": {
374
+ "type": "any",
375
+ "mutable": false,
376
+ "complexType": {
377
+ "original": "any",
378
+ "resolved": "any",
379
+ "references": {}
380
+ },
381
+ "required": false,
382
+ "optional": false,
383
+ "docs": {
384
+ "tags": [],
385
+ "text": ""
386
+ },
387
+ "attribute": "formschema",
388
+ "reflect": false
389
+ },
390
+ "submitValue": {
391
+ "type": "any",
392
+ "mutable": false,
393
+ "complexType": {
394
+ "original": "any",
395
+ "resolved": "any",
396
+ "references": {}
397
+ },
398
+ "required": false,
399
+ "optional": false,
400
+ "docs": {
401
+ "tags": [],
402
+ "text": ""
403
+ },
404
+ "attribute": "submit-value",
405
+ "reflect": false
406
+ }
407
+ };
408
+ }
409
+ static get events() {
410
+ return [{
411
+ "method": "dataSubmitted",
412
+ "name": "dataSubmitted",
413
+ "bubbles": true,
414
+ "cancelable": true,
415
+ "composed": true,
416
+ "docs": {
417
+ "tags": [],
418
+ "text": ""
419
+ },
420
+ "complexType": {
421
+ "original": "FormData",
422
+ "resolved": "FormData",
423
+ "references": {
424
+ "FormData": {
425
+ "location": "global"
426
+ }
427
+ }
428
+ }
429
+ }, {
430
+ "method": "dataChanged",
431
+ "name": "dataChanged",
432
+ "bubbles": true,
433
+ "cancelable": true,
434
+ "composed": true,
435
+ "docs": {
436
+ "tags": [],
437
+ "text": ""
438
+ },
439
+ "complexType": {
440
+ "original": "{ name: string, value: any }",
441
+ "resolved": "{ name: string; value: any; }",
442
+ "references": {}
443
+ }
444
+ }];
445
+ }
446
+ static get watchers() {
447
+ return [{
448
+ "propName": "formschema",
449
+ "methodName": "onFormSchemaChange"
450
+ }];
451
+ }
452
+ }