@3t-transform/threeteeui 0.2.107 → 0.2.108

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.
@@ -121,6 +121,30 @@ const formSchema = {
121
121
  },
122
122
  },
123
123
  },
124
+ startend: {
125
+ type: 'string',
126
+ format: 'string',
127
+ form: {
128
+ type: 'startenddate',
129
+ includeTime: true
130
+ },
131
+ },
132
+ expiration: {
133
+ type: 'radio',
134
+ form: {
135
+ type: 'radio',
136
+ label: 'Expiration',
137
+ options: [
138
+ { label: 'Never expires', value: 'neverExpires' },
139
+ { label: 'Will expire', value: 'willExpire' },
140
+ ],
141
+ validation: {
142
+ required: {
143
+ message: 'Please select validity',
144
+ },
145
+ },
146
+ },
147
+ },
124
148
  permissions: {
125
149
  form: {
126
150
  type: 'checkbox',
@@ -146,6 +170,9 @@ SingleFormItem.args = {
146
170
  type: 'text',
147
171
  label: 'Input Field',
148
172
  validation: {
173
+ invalid: {
174
+ message: "This field is invalid"
175
+ },
149
176
  required: {
150
177
  message: 'Please enter something',
151
178
  },
@@ -155,6 +182,21 @@ SingleFormItem.args = {
155
182
  },
156
183
  },
157
184
  };
185
+ export const StartAndEndDateWithoutTime = args => `<tttx-form formSchema='${JSON.stringify(args.formSchema)}'></tttx-form>`;
186
+ StartAndEndDateWithoutTime.args = {
187
+ formSchema: {
188
+ properties: {
189
+ startend: {
190
+ type: 'string',
191
+ format: 'string',
192
+ form: {
193
+ type: 'startenddate',
194
+ includeTime: false
195
+ },
196
+ },
197
+ },
198
+ },
199
+ };
158
200
  export const ExampleFormFromJSON = args => `<tttx-form id='form' formSchema='${JSON.stringify(args.formSchema)}'></tttx-form>
159
201
  <hr/>
160
202
  <button type='button' id='button'>Submit Form</button>
@@ -289,16 +331,12 @@ const formSchemaWithReadonly = {
289
331
  },
290
332
  },
291
333
  },
292
- permissions: {
334
+ startend: {
335
+ type: 'string',
336
+ format: 'string',
293
337
  form: {
294
- type: 'checkbox',
295
- label: 'Permissions',
296
- inlineLabel: 'Please grant permissions to use data',
297
- validation: {
298
- required: {
299
- message: 'Please grant permissions to use data',
300
- },
301
- },
338
+ type: 'startenddate',
339
+ includeTime: true
302
340
  },
303
341
  },
304
342
  expiration: {
@@ -317,6 +355,18 @@ const formSchemaWithReadonly = {
317
355
  },
318
356
  },
319
357
  },
358
+ permissions: {
359
+ form: {
360
+ type: 'checkbox',
361
+ label: 'Permissions',
362
+ inlineLabel: 'Please grant permissions to use data',
363
+ validation: {
364
+ required: {
365
+ message: 'Please grant permissions to use data',
366
+ },
367
+ },
368
+ },
369
+ },
320
370
  },
321
371
  };
322
372
  const data = {
@@ -328,7 +378,11 @@ const data = {
328
378
  dob: '1973-06-02',
329
379
  dropdown: 'chicken',
330
380
  permissions: 'on',
331
- expiration: 'willExpire'
381
+ expiration: 'willExpire',
382
+ 'startend-startdate': '2021-09-01',
383
+ 'startend-starttime': '09:00',
384
+ 'startend-enddate': '2024-10-15',
385
+ 'startend-endtime': '17:00'
332
386
  };
333
387
  export const PrePopulateForm = args => `<tttx-form id='form' formSchema='${JSON.stringify(args.formSchema)}' data='${JSON.stringify(args.data)}'></tttx-form>
334
388
  <hr/>
@@ -30,7 +30,7 @@ const TttxCheckbox$1 = /*@__PURE__*/ proxyCustomElement(class extends HTMLElemen
30
30
  render() {
31
31
  const checkBoxIcon = this.checked ? 'check_box' : 'check_box_outline_blank';
32
32
  const renderedIcon = this.indeterminate ? 'indeterminate_check_box' : checkBoxIcon;
33
- return (h(Host, null, h("label", { class: `tttx-checkbox ${this.inline ? '--inline' : ''}` }, h("input", { class: "tttx-checkbox__input", type: "checkbox", id: this.checkboxId, checked: this.checked, ref: (el) => this.checkbox = el }), h("tttx-icon", { color: this.checked ? 'blue' : null, icon: renderedIcon, onClick: this.onClick.bind(this) }), h("span", { class: "tttx-checkbox__label" }, this.label))));
33
+ return (h(Host, null, h("label", { class: `tttx-checkbox ${this.inline ? '--inline' : ''}` }, h("input", { class: "tttx-checkbox__input", type: "checkbox", id: this.checkboxId, checked: this.checked, ref: (el) => this.checkbox = el }), h("tttx-icon", { color: this.checked ? 'blue' : 'grey', icon: renderedIcon, onClick: this.onClick.bind(this) }), h("span", { class: "tttx-checkbox__label" }, this.label))));
34
34
  }
35
35
  static get watchers() { return {
36
36
  "indeterminate": ["watchIndeterminateChange"]
@@ -11,7 +11,9 @@ import { p as purify, d as domSanitiserOptions } from './domsanitiser.options.js
11
11
  */
12
12
  function validityCheck(event) {
13
13
  var _a, _b, _c, _d;
14
- event.preventDefault();
14
+ if (event.preventDefault) {
15
+ event.preventDefault();
16
+ }
15
17
  const target = event.target;
16
18
  let hasError = true;
17
19
  let errorMessage = '';
@@ -124,7 +126,7 @@ function setErrorState(target, hasError, errorMessage, parent = undefined) {
124
126
  }
125
127
  }
126
128
 
127
- 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}";
129
+ 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}label.flex-label{display:flex;flex-direction:column;width:300px;box-sizing:border-box}label.flex-label input[type=date]{flex-grow:1;min-width:auto}label.flex-label input[type=time]{min-width:auto;text-align:center;width:180px;margin-left:16px}";
128
130
 
129
131
  const TttxForm$1 = /*@__PURE__*/ proxyCustomElement(class extends HTMLElement {
130
132
  constructor() {
@@ -296,6 +298,59 @@ const TttxForm$1 = /*@__PURE__*/ proxyCustomElement(class extends HTMLElement {
296
298
  // Return the input element
297
299
  return input;
298
300
  }
301
+ createStartEndDateComponent(formKey, formProperties) {
302
+ const startDate = document.createElement('input');
303
+ const endDate = document.createElement('input');
304
+ startDate.type = endDate.type = 'date';
305
+ startDate.name = `${formKey}-startdate`;
306
+ endDate.name = `${formKey}-enddate`;
307
+ startDate.setAttribute('data-formkey', formKey);
308
+ endDate.setAttribute('data-formkey', formKey);
309
+ if (formProperties.readonly) {
310
+ startDate.readOnly = true;
311
+ endDate.readOnly = true;
312
+ }
313
+ this.applyValidation(startDate, { required: { message: 'Please enter a start date' }, dateCompare: { to: endDate.name } });
314
+ this.applyValidation(endDate, { required: { message: 'Please enter an end date' }, dateCompare: { with: startDate.name, message: 'End date cannot be before the start date' } });
315
+ let startTime;
316
+ let endTime;
317
+ if (formProperties.includeTime) {
318
+ startTime = document.createElement('input');
319
+ endTime = document.createElement('input');
320
+ startTime.type = endTime.type = 'time';
321
+ startTime.name = `${formKey}-starttime`;
322
+ endTime.name = `${formKey}-endtime`;
323
+ startTime.setAttribute('data-formkey', formKey);
324
+ endTime.setAttribute('data-formkey', formKey);
325
+ if (formProperties.readonly) {
326
+ startTime.readOnly = true;
327
+ endTime.readOnly = true;
328
+ }
329
+ this.applyValidation(startTime, { required: { message: 'Please enter a start time' } });
330
+ this.applyValidation(endTime, { required: { message: 'Please enter an end time' } });
331
+ }
332
+ const startLabel = document.createElement('label');
333
+ startLabel.innerText = 'Start date';
334
+ const endLabel = document.createElement('label');
335
+ endLabel.innerText = 'End date';
336
+ const startContainer = document.createElement('div');
337
+ const endContainer = document.createElement('div');
338
+ startContainer.className = endContainer.className = 'outer-container inputBlock';
339
+ startLabel.className = endLabel.className = 'flex-label';
340
+ startContainer.append(startDate);
341
+ if (startTime) {
342
+ startContainer.append(startTime);
343
+ }
344
+ startLabel.append(startContainer);
345
+ startLabel.append(this.createErrorBubble());
346
+ endContainer.append(endDate);
347
+ if (endTime) {
348
+ endContainer.append(endTime);
349
+ }
350
+ endLabel.append(endContainer);
351
+ endLabel.append(this.createErrorBubble());
352
+ return { start: startLabel, end: endLabel };
353
+ }
299
354
  /**
300
355
  * Applies validation attributes to an input element based on the specified validation object.
301
356
  * If a certain property is present in the object, it will set the corresponding attribute on
@@ -316,6 +371,7 @@ const TttxForm$1 = /*@__PURE__*/ proxyCustomElement(class extends HTMLElement {
316
371
  * @param {string} [validation.minmax.max]
317
372
  * @param {string} [validation.minmax.message]
318
373
  * @param {string} [validation.maxlength] - The maximum length of the input field.
374
+ * @param {string} [validation.datecompare] - To compare start and end date fields.
319
375
  * @return {void}
320
376
  */
321
377
  applyValidation(input, validation) {
@@ -348,6 +404,16 @@ const TttxForm$1 = /*@__PURE__*/ proxyCustomElement(class extends HTMLElement {
348
404
  if (validation.maxlength) {
349
405
  input.setAttribute('maxlength', validation.maxlength);
350
406
  }
407
+ // Custom validation parameters for start date and end date comparison
408
+ if (validation.dateCompare) {
409
+ if (validation.dateCompare.message && validation.dateCompare.with) {
410
+ input.setAttribute('data-compare', validation.dateCompare.message);
411
+ input.setAttribute('data-compare-with', validation.dateCompare.with);
412
+ }
413
+ if (validation.dateCompare.to) {
414
+ input.setAttribute('data-compare-to', validation.dateCompare.to);
415
+ }
416
+ }
351
417
  }
352
418
  // Create a new error bubble element
353
419
  createErrorBubble() {
@@ -464,6 +530,7 @@ const TttxForm$1 = /*@__PURE__*/ proxyCustomElement(class extends HTMLElement {
464
530
  * @return {void}
465
531
  */
466
532
  populateFormFromSchema() {
533
+ var _a;
467
534
  // If there is no form schema, return early
468
535
  if (!this._formSchema)
469
536
  return;
@@ -471,9 +538,17 @@ const TttxForm$1 = /*@__PURE__*/ proxyCustomElement(class extends HTMLElement {
471
538
  const properties = this._formSchema.properties;
472
539
  const propertyKeys = Object.keys(properties);
473
540
  // Loop through each property key and create an input, label, and error bubble for it
474
- propertyKeys.forEach(formKey => {
541
+ for (const formKey of propertyKeys) {
475
542
  const formItem = properties[formKey];
476
543
  const formProperties = formItem.form;
544
+ // complex form types which require
545
+ // custom HTML should be done here
546
+ if (formProperties.type === 'startenddate') {
547
+ const { start, end } = this.createStartEndDateComponent(formKey, formProperties);
548
+ this.template.content.append(start);
549
+ this.template.content.append(end);
550
+ continue;
551
+ }
477
552
  let input;
478
553
  switch (formProperties.type) {
479
554
  case 'select':
@@ -486,15 +561,22 @@ const TttxForm$1 = /*@__PURE__*/ proxyCustomElement(class extends HTMLElement {
486
561
  input = this.createInput(formKey, formProperties);
487
562
  }
488
563
  // If the form property has validation, apply it to the input
489
- if (formProperties.validation && formProperties.type !== 'radio') {
564
+ if (formProperties.validation &&
565
+ formProperties.type !== 'radio') {
490
566
  this.applyValidation(input, formProperties.validation);
491
567
  }
492
568
  // Create an error bubble and label element for the input
493
569
  const errorBubble = this.createErrorBubble();
494
570
  const label = this.createLabel(formProperties, input, errorBubble);
571
+ // If explicitly setting input as invalid, set invalid state and error message on render
572
+ if ((_a = formProperties.validation) === null || _a === void 0 ? void 0 : _a.invalid) {
573
+ const errorMessage = formProperties.validation.invalid.message;
574
+ input.setCustomValidity(errorMessage); // Prevents the invalid styling from resetting on blur
575
+ setErrorState(input, true, errorMessage);
576
+ }
495
577
  // Append the label element to the form template
496
578
  this.template.content.append(label);
497
- });
579
+ }
498
580
  }
499
581
  /**
500
582
  * Clones the form template and binds event listeners to its input elements. First, it checks if
@@ -515,47 +597,121 @@ const TttxForm$1 = /*@__PURE__*/ proxyCustomElement(class extends HTMLElement {
515
597
  // Bind event listeners to form elements
516
598
  const properties = this._formSchema.properties;
517
599
  const propertyKeys = Object.keys(properties);
518
- propertyKeys.forEach(formKey => {
519
- var _a, _b;
520
- const formInputs = formItems.querySelectorAll(`[name=${formKey}]`);
521
- for (const formInput of formInputs) {
600
+ for (const formKey of propertyKeys) {
601
+ const formItemsByKey = formItems.querySelectorAll(`[name^=${formKey}]`);
602
+ for (const formInput of formItemsByKey) {
522
603
  // Bind events to form input elements
523
604
  formInput.oninvalid = this.validityCheckWrapper.bind(this);
524
605
  formInput.onblur = this.validityCheckWrapper.bind(this);
525
606
  formInput.onkeyup = this.fieldChanged.bind(this);
526
607
  formInput.onchange = this.fieldChanged.bind(this);
527
- if ((_a = this._data) === null || _a === void 0 ? void 0 : _a[formKey]) {
528
- switch (formInput.type) {
608
+ if (properties[formKey].form.type === 'select' && formInput.classList.contains('placeholder')) {
609
+ formInput.addEventListener('change', () => {
610
+ formInput.classList.remove('placeholder');
611
+ });
612
+ }
613
+ if (properties[formKey].form.type === 'startenddate' && formInput.hasAttribute('data-compare-with')) {
614
+ formInput.oninvalid = this.endDateComparisonValidator.bind(this);
615
+ formInput.onblur = this.endDateComparisonValidator.bind(this);
616
+ }
617
+ if (properties[formKey].form.type === 'startenddate' && formInput.type === 'time') {
618
+ formInput.oninvalid = this.startEndTimeComparitor.bind(this);
619
+ formInput.onblur = this.startEndTimeComparitor.bind(this);
620
+ }
621
+ if (properties[formKey].form.type === 'startenddate' && formInput.hasAttribute('data-compare-to')) {
622
+ formInput.oninvalid = this.startDateComparisonValidator.bind(this);
623
+ formInput.onblur = this.startDateComparisonValidator.bind(this);
624
+ }
625
+ }
626
+ }
627
+ // populate with existing form data if available
628
+ if (this._data && Object.keys(this._data).length > 0) {
629
+ const dataKeys = Object.keys(this._data);
630
+ for (const key of dataKeys) {
631
+ const formItemsByKey = formItems.querySelectorAll(`[name=${key}]`);
632
+ for (const formItem of formItemsByKey) {
633
+ switch (formItem.type) {
529
634
  case 'checkbox':
530
- if (this._data[formKey] === 'on') {
531
- formInput.checked = true;
635
+ if (this._data[key] === 'on') {
636
+ formItem.checked = true;
532
637
  }
533
638
  break;
534
639
  case 'radio':
535
- if (formInput.value === this._data[formKey]) {
536
- formInput.checked = true;
640
+ if (formItem.value === this._data[key]) {
641
+ formItem.checked = true;
537
642
  }
538
643
  break;
539
644
  default:
540
- formInput.value = this._data[formKey];
645
+ formItem.value = this._data[key];
541
646
  }
542
647
  }
543
- // If explicitly setting input as invalid, set invalid state and error message on render
544
- if ((_b = properties[formKey].form.validation) === null || _b === void 0 ? void 0 : _b.invalid) {
545
- const errorMessage = properties[formKey].form.validation.invalid.message;
546
- formInput.setCustomValidity(errorMessage); // Prevents the invalid styling from resetting on blur
547
- setErrorState(formInput, true, errorMessage);
548
- }
549
- if (properties[formKey].form.type === 'select' && formInput.classList.contains('placeholder')) {
550
- formInput.addEventListener('change', () => {
551
- formInput.classList.remove('placeholder');
552
- });
553
- }
554
648
  }
555
- });
649
+ }
556
650
  // Append the cloned form elements to the fieldset
557
651
  this.fieldset.replaceChildren(formItems);
558
652
  }
653
+ startEndTimeComparitor(event) {
654
+ var _a, _b;
655
+ const target = event.target;
656
+ const formKey = target.getAttribute('data-formkey');
657
+ const timeFields = Array.from(target.parentElement.parentElement.parentElement.querySelectorAll(`[data-formkey=${formKey}]`));
658
+ if (timeFields.length === 4) {
659
+ const startTime = timeFields.find(t => t.name.endsWith('starttime'));
660
+ const endTime = timeFields.find(t => t.name.endsWith('endtime'));
661
+ const startDate = timeFields.find(t => t.name.endsWith('startdate'));
662
+ const endDate = timeFields.find(t => t.name.endsWith('enddate'));
663
+ if (startTime.valueAsNumber >= endTime.valueAsNumber && ((_a = startDate.valueAsDate) === null || _a === void 0 ? void 0 : _a.valueOf()) === ((_b = endDate.valueAsDate) === null || _b === void 0 ? void 0 : _b.valueOf())) {
664
+ setErrorState(endTime, true, 'End time cannot be the same or before the start time');
665
+ if (target.name.endsWith('starttime')) {
666
+ this.validityCheckWrapper(event);
667
+ }
668
+ }
669
+ else {
670
+ // clear any end time comparitor errors and perform the standard validity check on the event
671
+ endTime.setCustomValidity('');
672
+ setErrorState(endTime, false, null);
673
+ this.validityCheckWrapper(event);
674
+ }
675
+ }
676
+ }
677
+ clearTimeComparitor(event) {
678
+ const target = event.target;
679
+ const formKey = target.getAttribute('data-formkey');
680
+ const timeFields = Array.from(target.parentElement.parentElement.parentElement.querySelectorAll(`[type=time][data-formkey=${formKey}]`));
681
+ if (timeFields.length) {
682
+ const endTime = timeFields.find(t => t.name.endsWith('endtime'));
683
+ endTime.setCustomValidity('');
684
+ setErrorState(endTime, false, null);
685
+ }
686
+ }
687
+ startDateComparisonValidator(event) {
688
+ const startDate = event.target;
689
+ const compareToName = startDate.getAttribute('data-compare-to');
690
+ const endDate = startDate.parentElement.parentElement.parentElement.querySelector(`[name=${compareToName}]`);
691
+ this.endDateComparisonValidator({ target: endDate }, true);
692
+ this.validityCheckWrapper(event);
693
+ }
694
+ endDateComparisonValidator(event, triggeredByStartDate = false) {
695
+ var _a, _b, _c, _d;
696
+ const endDate = event.target;
697
+ if (!endDate.value && triggeredByStartDate)
698
+ return;
699
+ const compareWithName = endDate.getAttribute('data-compare-with');
700
+ const startDate = endDate.parentElement.parentElement.parentElement.querySelector(`[name=${compareWithName}]`);
701
+ if (((_a = endDate.valueAsDate) === null || _a === void 0 ? void 0 : _a.valueOf()) < ((_b = startDate.valueAsDate) === null || _b === void 0 ? void 0 : _b.valueOf())) {
702
+ endDate.setCustomValidity(endDate.getAttribute('data-compare'));
703
+ setErrorState(endDate, true, endDate.getAttribute('data-compare'));
704
+ }
705
+ else if (((_c = endDate.valueAsDate) === null || _c === void 0 ? void 0 : _c.valueOf()) === ((_d = startDate.valueAsDate) === null || _d === void 0 ? void 0 : _d.valueOf())) {
706
+ endDate.setCustomValidity('');
707
+ this.validityCheckWrapper(event);
708
+ this.startEndTimeComparitor(event);
709
+ }
710
+ else {
711
+ this.clearTimeComparitor(event);
712
+ this.validityCheckWrapper(event);
713
+ }
714
+ }
559
715
  validityCheckWrapper(event) {
560
716
  const { target, hasError, errorMessage } = validityCheck(event);
561
717
  setErrorState(target, hasError, errorMessage);
@@ -28,7 +28,7 @@ const TttxCheckbox = class {
28
28
  render() {
29
29
  const checkBoxIcon = this.checked ? 'check_box' : 'check_box_outline_blank';
30
30
  const renderedIcon = this.indeterminate ? 'indeterminate_check_box' : checkBoxIcon;
31
- return (h(Host, null, h("label", { class: `tttx-checkbox ${this.inline ? '--inline' : ''}` }, h("input", { class: "tttx-checkbox__input", type: "checkbox", id: this.checkboxId, checked: this.checked, ref: (el) => this.checkbox = el }), h("tttx-icon", { color: this.checked ? 'blue' : null, icon: renderedIcon, onClick: this.onClick.bind(this) }), h("span", { class: "tttx-checkbox__label" }, this.label))));
31
+ return (h(Host, null, h("label", { class: `tttx-checkbox ${this.inline ? '--inline' : ''}` }, h("input", { class: "tttx-checkbox__input", type: "checkbox", id: this.checkboxId, checked: this.checked, ref: (el) => this.checkbox = el }), h("tttx-icon", { color: this.checked ? 'blue' : 'grey', icon: renderedIcon, onClick: this.onClick.bind(this) }), h("span", { class: "tttx-checkbox__label" }, this.label))));
32
32
  }
33
33
  static get watchers() { return {
34
34
  "indeterminate": ["watchIndeterminateChange"]