@felleslosninger/minid-elements 0.1.5 → 0.1.7

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.
@@ -1,4 +1,4 @@
1
- import { css, LitElement, nothing, html } from "lit";
1
+ import { css, LitElement, html, nothing } from "lit";
2
2
  import { query, property, state, customElement } from "lit/decorators.js";
3
3
  import { live } from "lit/directives/live.js";
4
4
  import { stringConverter } from "../internal/string-converter.js";
@@ -42,6 +42,109 @@ const styles = [
42
42
  .ds-field-affixes {
43
43
  background: var(--ds-color-neutral-background-tinted);
44
44
  }
45
+
46
+ .has-field-buttons {
47
+ --mid-field-button-size: var(--ds-size-7);
48
+ --mid-field-button-gap: var(--ds-size-3);
49
+ --mid-field-button-border: var(--ds-border-width-default);
50
+ position: relative;
51
+ display: block;
52
+ }
53
+
54
+ .has-field-buttons .ds-input {
55
+ padding-inline-end: calc(
56
+ var(--mid-field-button-size) + 2 * var(--mid-field-button-gap)
57
+ );
58
+ }
59
+
60
+ .has-field-buttons--two .ds-input {
61
+ padding-inline-end: calc(
62
+ 2 * (var(--mid-field-button-size) + 2 * var(--mid-field-button-gap))
63
+ );
64
+ }
65
+
66
+ .password-toggle-input::-ms-reveal,
67
+ .password-toggle-input::-ms-clear {
68
+ display: none;
69
+ }
70
+
71
+ .has-field-buttons .ds-input::-webkit-credentials-auto-fill-button {
72
+ margin-inline-end: calc(
73
+ var(--mid-field-button-size) + 2 * var(--mid-field-button-gap)
74
+ );
75
+ }
76
+
77
+ .has-field-buttons--two .ds-input::-webkit-credentials-auto-fill-button {
78
+ margin-inline-end: calc(
79
+ 2 * (var(--mid-field-button-size) + 2 * var(--mid-field-button-gap))
80
+ );
81
+ }
82
+
83
+ .has-field-buttons:hover
84
+ .ds-input:not(:focus-visible, :disabled, [readonly], [aria-readonly='true']) {
85
+ outline: var(--dsc-input-outline-width--hover)
86
+ var(--dsc-input-outline-style--hover)
87
+ var(--dsc-input-outline-color--hover);
88
+ }
89
+
90
+ .has-field-buttons:hover
91
+ .ds-input[aria-invalid='true']:not(:focus-visible, :disabled, [readonly], [aria-readonly='true']) {
92
+ outline-color: var(--dsc-input-accent-color--invalid);
93
+ }
94
+
95
+ .field-buttons {
96
+ position: absolute;
97
+ inset-block: var(--mid-field-button-border);
98
+ inset-inline-end: var(--mid-field-button-border);
99
+ display: flex;
100
+ pointer-events: none;
101
+ }
102
+
103
+ .field-button {
104
+ display: flex;
105
+ align-items: center;
106
+ justify-content: center;
107
+ color: var(--ds-color-neutral-text-subtle);
108
+ pointer-events: auto;
109
+ }
110
+
111
+ .field-buttons .field-button {
112
+ padding-inline: var(--mid-field-button-gap);
113
+ }
114
+
115
+ .field-button > span {
116
+ display: flex;
117
+ align-items: center;
118
+ justify-content: center;
119
+ border-radius: var(--ds-border-radius-sm);
120
+ block-size: var(--mid-field-button-size, var(--ds-size-7));
121
+ inline-size: var(--mid-field-button-size, var(--ds-size-7));
122
+ }
123
+
124
+ .field-button mid-icon {
125
+ font-size: var(--mid-field-button-size, var(--ds-size-7));
126
+ }
127
+
128
+ .field-button:focus-visible {
129
+ outline: none;
130
+ }
131
+
132
+ .field-button:focus-visible > span {
133
+ box-shadow: 0 0 0 var(--ds-border-width-focus) var(--ds-color-focus-inner);
134
+ outline: var(--ds-color-focus-outer) solid var(--ds-border-width-focus);
135
+ outline-offset: var(--ds-border-width-focus);
136
+ }
137
+
138
+ /* The validation message stays in the DOM so its text lands in a live region
139
+ the screen reader is already watching - WebKit does not reliably announce
140
+ one revealed at the same moment the text arrives. When empty it must paint
141
+ nothing and take no room: the element drops the ds-validation-message class
142
+ (whose ::before paints an error icon regardless of the text) and this rule
143
+ cancels the sibling spacing from .ds-field > * + *, which the .ds-field >
144
+ prefix is enough to outrank without !important. */
145
+ .ds-field > .validation-message--empty {
146
+ margin-top: 0;
147
+ }
45
148
  `
46
149
  ];
47
150
  let nextUniqueId = 0;
@@ -138,6 +241,9 @@ let MinidTextfield = class extends FormControlMixin(styled(LitElement, styles))
138
241
  handlePasswordToggle() {
139
242
  this.passwordvisible = !this.passwordvisible;
140
243
  }
244
+ handleInFieldButtonMousedown(event) {
245
+ event.preventDefault();
246
+ }
141
247
  handleClearClick(event) {
142
248
  event.preventDefault();
143
249
  if (this.value !== "") {
@@ -185,12 +291,46 @@ let MinidTextfield = class extends FormControlMixin(styled(LitElement, styles))
185
291
  const hasPrefix = this.hasSlotControler.test("prefix");
186
292
  const hasSuffix = this.hasSlotControler.test("suffix");
187
293
  const hasClearIcon = this.clearable && !this.disabled && !this.readonly;
188
- const hasAffixes = hasPrefix || hasSuffix || hasClearIcon || this.passwordtoggle && !this.disabled;
294
+ const hasPasswordToggle = this.passwordtoggle && !this.disabled;
295
+ const overlayFieldButtons = (hasClearIcon || hasPasswordToggle) && !hasPrefix && !hasSuffix;
296
+ const hasAffixes = hasPrefix || hasSuffix;
189
297
  const isClearIconVisible = hasClearIcon && (typeof this.value === "number" || this.value.length > 0);
190
298
  const describedBy = [
191
299
  this.description ? this.descriptionId : void 0,
192
300
  this.invalidmessage ? this.validationId : void 0
193
301
  ].filter(Boolean).join(" ");
302
+ const passwordToggleButton = html`
303
+ <button
304
+ type="button"
305
+ part="password-toggle-button"
306
+ class="field-button"
307
+ aria-label=${this.passwordvisible ? t.hidePassword : t.showPassword}
308
+ aria-controls=${this.inputId}
309
+ @mousedown=${this.handleInFieldButtonMousedown}
310
+ @click=${this.handlePasswordToggle}
311
+ >
312
+ <span>
313
+ <mid-icon
314
+ library="system"
315
+ name=${this.passwordvisible ? "eye-slash" : "eye"}
316
+ ></mid-icon>
317
+ </span>
318
+ </button>
319
+ `;
320
+ const clearButton = html`
321
+ <button
322
+ type="button"
323
+ part="clear-button"
324
+ class="field-button"
325
+ aria-label=${t.clear}
326
+ @mousedown=${this.handleInFieldButtonMousedown}
327
+ @click=${this.handleClearClick}
328
+ >
329
+ <span>
330
+ <mid-icon library="nav-aksel" name="trash"></mid-icon>
331
+ </span>
332
+ </button>
333
+ `;
194
334
  return html`
195
335
  <ds-field
196
336
  part="field"
@@ -219,17 +359,25 @@ let MinidTextfield = class extends FormControlMixin(styled(LitElement, styles))
219
359
  ` : nothing}
220
360
  <div
221
361
  part="base"
222
- class="${hasAffixes ? "ds-field-affixes" : ""}"
362
+ class="${classMap({
363
+ "ds-field-affixes": hasAffixes,
364
+ "has-field-buttons": overlayFieldButtons,
365
+ "has-field-buttons--two": overlayFieldButtons && hasClearIcon && hasPasswordToggle
366
+ })}"
223
367
  >
224
368
  ${hasPrefix ? html`<span class="ds-field-affix"><slot name="prefix"></slot></span>` : html`<slot name="prefix"></slot>`}
225
369
  <input
226
370
  id="${this.inputId}"
227
- class="ds-input"
371
+ class="${classMap({
372
+ "ds-input": true,
373
+ "password-toggle-input": hasPasswordToggle
374
+ })}"
228
375
  part="input"
229
376
  lang=${lang}
230
377
  .value=${live(this.value)}
231
378
  ?disabled=${this.disabled}
232
379
  ?readonly=${this.readonly}
380
+ ?required=${this.required}
233
381
  ?autofocus=${this.autofocus}
234
382
  autocomplete=${ifDefined(this.autocomplete)}
235
383
  type=${this.type === "password" && this.passwordvisible ? "text" : this.type}
@@ -251,51 +399,24 @@ let MinidTextfield = class extends FormControlMixin(styled(LitElement, styles))
251
399
  @blur=${this.handleBlur}
252
400
  @keydown=${this.handleKeydown}
253
401
  />
254
- ${isClearIconVisible ? html`
255
- <span class="ds-field-affix" part="clear-button">
256
- <button
257
- type="button"
258
- class="focus-visible:focus-ring flex items-center justify-center rounded-sm"
259
- aria-label=${t.clear}
260
- @click=${this.handleClearClick}
261
- >
262
- <mid-icon
263
- class="size-7"
264
- library="nav-aksel"
265
- name="trash"
266
- ></mid-icon>
267
- </button>
268
- </span>
269
- ` : ""}
270
- ${this.passwordtoggle && !this.disabled ? html`
271
- <span class="ds-field-affix" part="password-toggle-button">
272
- <button
273
- type="button"
274
- class="focus-visible:focus-ring flex items-center justify-center rounded-sm"
275
- aria-label=${this.passwordvisible ? t.hidePassword : t.showPassword}
276
- @click=${this.handlePasswordToggle}
277
- >
278
- ${this.passwordvisible ? html` <mid-icon
279
- class="size-7"
280
- library="system"
281
- name="eye-slash"
282
- ></mid-icon>` : html`
283
- <mid-icon
284
- class="size-7"
285
- library="system"
286
- name="eye"
287
- ></mid-icon>
288
- `}
289
- </button>
290
- </span>
291
- ` : ""}
402
+ ${overlayFieldButtons ? html`
403
+ <div class="field-buttons">
404
+ ${isClearIconVisible ? clearButton : nothing}
405
+ ${hasPasswordToggle ? passwordToggleButton : nothing}
406
+ </div>
407
+ ` : nothing}
408
+ ${!overlayFieldButtons && isClearIconVisible ? html`<span class="ds-field-affix">${clearButton}</span>` : nothing}
409
+ ${!overlayFieldButtons && hasPasswordToggle ? html`<span class="ds-field-affix">${passwordToggleButton}</span>` : nothing}
292
410
  ${hasSuffix ? html`<span part="suffix" class="ds-field-affix"><slot name="suffix"></slot></span>` : html`<slot name="suffix"></slot>`}
293
411
  </div>
294
412
  <p
295
- class="ds-validation-message"
413
+ class="${classMap({
414
+ "ds-validation-message": !!this.invalidmessage,
415
+ "validation-message--empty": !this.invalidmessage
416
+ })}"
417
+ part="validation-message"
296
418
  id="${this.validationId}"
297
419
  aria-live="polite"
298
- ?hidden=${!this.invalidmessage}
299
420
  >
300
421
  ${this.invalidmessage}
301
422
  </p>
@@ -1 +1 @@
1
- {"version":3,"file":"textfield.js","sources":["../../src/components/textfield.component.ts"],"sourcesContent":["import { css, html, LitElement, nothing, type PropertyValues } from 'lit';\nimport { customElement, property, query, state } from 'lit/decorators.js';\nimport { live } from 'lit/directives/live.js';\nimport { stringConverter } from '../internal/string-converter';\nimport { classMap } from 'lit/directives/class-map.js';\nimport { styled } from '../mixins/tailwind.mixin.ts';\nimport { ifDefined } from 'lit/directives/if-defined.js';\nimport { HasSlotController } from '../internal/slot';\nimport { FormControlMixin } from '../mixins/form-control.mixin';\nimport {\n maxLengthValidator,\n minLengthValidator,\n patternValidator,\n requiredValidator,\n typeMismatchValidator,\n} from '../mixins/validators';\nimport { watch } from '../internal/watch';\nimport './icon/icon.component.ts';\nimport { MaskInput, type MaskType } from 'maska';\nimport { getLang } from '../utilities/lang';\nimport { getTranslations } from '../utilities/translations';\nimport { LangController } from '../controllers/lang.controller.js';\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'mid-textfield': MinidTextfield;\n }\n}\n\nconst styles = [\n css`\n :host {\n display: block;\n }\n\n /* Hide increment/decrement buttons on inputs */\n input[type='number']::-webkit-outer-spin-button,\n input[type='number']::-webkit-inner-spin-button,\n input[type='number'] {\n -webkit-appearance: none;\n margin: 0;\n -moz-appearance: textfield !important;\n }\n\n .ds-field-affixes {\n background: var(--ds-color-neutral-background-tinted);\n }\n `,\n];\n\nlet nextUniqueId = 0;\n\n/**\n *\n * @event mid-change - Emitted when a change to the input value is comitted by the user\n * @event mid-input - Emitted when the input element recieves input\n * @event mid-clear - Emitted when the input value is cleared\n * @event mid-focus - Emitted when input element is focused\n * @event mid-blur - Emitted when focus moves away from input element\n * @event {detail: { validity: ValidityState }} mid-invalid-hide - Emitted when the error message should be hidden\n * @event {detail: { validity: ValidityState }} mid-invalid-show - Emitted when the error message should be shown\n *\n * @slot prefix - Used for decoration to the left of the input\n * @slot suffix - Used for decoration to the right of the input\n * @slot label - The input's label. Alternatively, you can use the `label` attribute.\n *\n * @csspart base - The input's wrapper that has the input field styling.\n * @csspart input - The internal `<input>` element.\n * @csspart field - The element that wraps the label, input, and help text.\n * @csspart clear-button - The clear button\n * @csspart password-toggle-button - The button for toggling password visibility\n */\n@customElement('mid-textfield')\nexport class MinidTextfield extends FormControlMixin(styled(LitElement, styles)) {\n private readonly inputId!: string;\n private readonly descriptionId!: string;\n private readonly validationId!: string;\n private readonly hasSlotControler = new HasSlotController(this, 'label', 'prefix', 'suffix');\n private inputMask?: MaskInput;\n private initialValue = '';\n\n @query('input')\n input!: HTMLInputElement;\n\n @property()\n label = '';\n\n @property()\n description = '';\n\n @property()\n value = '';\n\n @property()\n size: 'sm' | 'md' | 'lg' = 'md';\n\n @property({ converter: stringConverter })\n placeholder?: string;\n\n /**\n * Autofocus the input field on page load\n */\n @property({ type: Boolean })\n autofocus = false;\n\n /**\n * User agent autocomplete hint\n */\n @property()\n autocomplete?: AutoFill;\n\n /**\n * The minimum length of input that will be considered valid.\n */\n @property({ type: Number })\n minlength?: number;\n\n /**\n * The maximum length of input that will be considered valid.\n */\n @property({ type: Number })\n maxlength?: number;\n\n /**\n * The input's minimum value. Only applies to date and number input types.\n */\n @property()\n min?: number | string;\n\n /**\n * The input's maximum value. Only applies to date and number input types.\n */\n @property()\n max?: number | string;\n\n /**\n * Error message to display when the input is invalid, also activates invalid styling\n */\n @property()\n invalidmessage = '';\n\n @property()\n type:\n | 'date'\n | 'datetime-local'\n | 'email'\n | 'file'\n | 'month'\n | 'number'\n | 'password'\n | 'search'\n | 'tel'\n | 'text'\n | 'time'\n | 'url'\n | 'week' = 'text';\n\n /**\n * For displaying appropriate virtual keyboard\n */\n @property()\n inputmode:\n | 'none'\n | 'text'\n | 'tel'\n | 'url'\n | 'email'\n | 'numeric'\n | 'decimal' = 'text';\n\n /**\n * Adds a clear button when the input is not empty.\n */\n @property({ type: Boolean })\n clearable = false;\n\n /**\n * Adds a button to toggle the password's visibility.\n * Only applies if type is password\n */\n @property({ type: Boolean })\n passwordtoggle = false;\n\n /**\n * Determines wether the password is currently visible.\n * Only applies if type is password\n */\n @property({ type: Boolean })\n passwordvisible = false;\n\n @property({ type: Boolean, reflect: true })\n disabled = false;\n\n @property({ type: Boolean, reflect: true })\n readonly = false;\n\n /**\n * A regular expression pattern to validate input against.\n */\n @property()\n pattern?: string;\n\n /**\n * Makes the input required\n */\n @property({ type: Boolean })\n required = false;\n\n /**\n * Visually hides `label` and `description` (still available for screen readers)\n */\n @property({ type: Boolean })\n hidelabel = false;\n\n /**\n * Modify input value based on mask e.g: `\"##:##\"` = 12:34.\n * `#`: /[0-9]/\n * `@`: /[a-zA-Z]/\n * `*`: /[a-zA-Z0-9]/\n * For more information go to https://beholdr.github.io/maska\n */\n @property()\n mask?: MaskType;\n\n @state()\n hasFocus = false;\n\n static get formControlValidators() {\n return [\n requiredValidator,\n maxLengthValidator,\n minLengthValidator,\n patternValidator,\n typeMismatchValidator,\n ];\n }\n\n constructor() {\n super();\n new LangController(this);\n nextUniqueId++;\n this.inputId = `mid-textfield-input-${nextUniqueId}`;\n this.descriptionId = `mid-textfield-description-${nextUniqueId}`;\n this.validationId = `mid-textfield-validation-${nextUniqueId}`;\n this.setValue(this.value);\n }\n\n override connectedCallback(): void {\n super.connectedCallback();\n this.initialValue = this.value;\n }\n\n get validationTarget() {\n return this.input;\n }\n\n protected firstUpdated(_changedProperties: PropertyValues): void {\n super.firstUpdated(_changedProperties);\n this.setValue(this.value);\n }\n\n disconnectedCallback(): void {\n this.inputMask?.destroy();\n }\n\n private handleKeydown(event: KeyboardEvent) {\n const hasModifier =\n event.metaKey || event.ctrlKey || event.shiftKey || event.altKey;\n\n if (event.key === 'Enter' && !hasModifier) {\n setTimeout(() => {\n if (!event.defaultPrevented && !event.isComposing) {\n this.form.requestSubmit();\n }\n });\n }\n }\n\n private handleBlur() {\n this.hasFocus = false;\n this.dispatchEvent(\n new Event('mid-blur', { bubbles: true, composed: true })\n );\n }\n\n private handleChange() {\n this.value = this.input.value;\n this.dispatchEvent(\n new Event('mid-change', { bubbles: true, composed: true })\n );\n }\n\n private handleInput() {\n let tmp_value = this.input.value;\n\n if (this.type === 'number') {\n tmp_value = tmp_value.replace(/[^0-9]/g, '');\n }\n\n this.value = tmp_value;\n this.setValue(this.value);\n this.dispatchEvent(\n new Event('mid-input', { bubbles: true, composed: true })\n );\n }\n\n private handleFocus() {\n this.hasFocus = true;\n this.dispatchEvent(\n new Event('mid-focus', { composed: true, bubbles: true })\n );\n }\n\n private handlePasswordToggle() {\n this.passwordvisible = !this.passwordvisible;\n }\n\n private handleClearClick(event: MouseEvent) {\n event.preventDefault();\n\n if (this.value !== '') {\n this.value = '';\n this.dispatchEvent(\n new Event('mid-clear', { composed: true, bubbles: true })\n );\n this.dispatchEvent(\n new Event('mid-input', { composed: true, bubbles: true })\n );\n this.dispatchEvent(\n new Event('mid-change', { composed: true, bubbles: true })\n );\n }\n\n this.input.focus();\n }\n\n focus() {\n this.hasFocus = true;\n this.input.focus();\n }\n\n resetFormControl() {\n this.invalidmessage = '';\n this.value = this.initialValue;\n }\n\n forceError(message?: string): void {\n super.forceError(message);\n }\n\n @watch('value')\n handleValueUpdate() {\n this.setValue(this.value);\n }\n\n @watch('mask')\n async handleMaskUpdate() {\n if (!this.mask) return;\n await this.updateComplete;\n this.inputMask?.destroy();\n this.inputMask = new MaskInput(this.input, {\n mask: this.mask,\n eager: true,\n });\n }\n\n override render() {\n const lang = getLang(this);\n const t = getTranslations(lang);\n const hasLabelSlot = this.hasSlotControler.test('label');\n const hasLabel = !!this.label || !!hasLabelSlot;\n const hasPrefix = this.hasSlotControler.test('prefix');\n const hasSuffix = this.hasSlotControler.test('suffix');\n const hasClearIcon = this.clearable && !this.disabled && !this.readonly;\n const hasAffixes = hasPrefix || hasSuffix || hasClearIcon || (this.passwordtoggle && !this.disabled);\n const isClearIconVisible =\n hasClearIcon && (typeof this.value === 'number' || this.value.length > 0);\n const describedBy = [\n this.description ? this.descriptionId : undefined,\n this.invalidmessage ? this.validationId : undefined,\n ]\n .filter(Boolean)\n .join(' ');\n\n return html`\n <ds-field\n part=\"field\"\n class=\"ds-field\"\n data-size=${this.size}\n >\n <label\n for=\"${this.inputId}\"\n class=\"${classMap({\n 'sr-only': this.hidelabel || !hasLabel,\n })} ds-label\"\n >\n <slot name=\"label\"> ${this.label} </slot>\n </label>\n ${this.description\n ? html`\n <div\n id=\"${this.descriptionId}\"\n part=\"description\"\n aria-hidden=\"true\"\n class=\"${classMap({\n 'sr-only': this.hidelabel,\n })}\"\n >\n ${this.description}\n </div>\n `\n : nothing}\n <div\n part=\"base\"\n class=\"${hasAffixes ? 'ds-field-affixes' : ''}\"\n >\n ${hasPrefix ? html`<span class=\"ds-field-affix\"><slot name=\"prefix\"></slot></span>` : html`<slot name=\"prefix\"></slot>`}\n <input\n id=\"${this.inputId}\"\n class=\"ds-input\"\n part=\"input\"\n lang=${lang}\n .value=${live(this.value)}\n ?disabled=${this.disabled}\n ?readonly=${this.readonly}\n ?autofocus=${this.autofocus}\n autocomplete=${ifDefined(this.autocomplete as any)}\n type=${this.type === 'password' && this.passwordvisible\n ? 'text'\n : this.type}\n aria-describedby=${ifDefined(describedBy || undefined)}\n aria-invalid=${this.invalidmessage ? 'true' : 'false'}\n aria-errormessage=${ifDefined(\n this.invalidmessage ? this.validationId : undefined\n )}\n placeholder=${ifDefined(this.placeholder)}\n minlength=${ifDefined(this.minlength)}\n maxlength=${ifDefined(this.maxlength)}\n min=${ifDefined(this.min)}\n max=${ifDefined(this.max)}\n pattern=${ifDefined(this.pattern)}\n inputmode=${this.inputmode}\n @input=${this.handleInput}\n @change=${this.handleChange}\n @focus=${this.handleFocus}\n @blur=${this.handleBlur}\n @keydown=${this.handleKeydown}\n />\n ${isClearIconVisible\n ? html`\n <span class=\"ds-field-affix\" part=\"clear-button\">\n <button\n type=\"button\"\n class=\"focus-visible:focus-ring flex items-center justify-center rounded-sm\"\n aria-label=${t.clear}\n @click=${this.handleClearClick}\n >\n <mid-icon\n class=\"size-7\"\n library=\"nav-aksel\"\n name=\"trash\"\n ></mid-icon>\n </button>\n </span>\n `\n : ''}\n ${this.passwordtoggle && !this.disabled\n ? html`\n <span class=\"ds-field-affix\" part=\"password-toggle-button\">\n <button\n type=\"button\"\n class=\"focus-visible:focus-ring flex items-center justify-center rounded-sm\"\n aria-label=${this.passwordvisible\n ? t.hidePassword\n : t.showPassword}\n @click=${this.handlePasswordToggle}\n >\n ${this.passwordvisible\n ? html` <mid-icon\n class=\"size-7\"\n library=\"system\"\n name=\"eye-slash\"\n ></mid-icon>`\n : html`\n <mid-icon\n class=\"size-7\"\n library=\"system\"\n name=\"eye\"\n ></mid-icon>\n `}\n </button>\n </span>\n `\n : ''}\n ${hasSuffix ? html`<span part=\"suffix\" class=\"ds-field-affix\"><slot name=\"suffix\"></slot></span>` : html`<slot name=\"suffix\"></slot>`}\n </div>\n <p\n class=\"ds-validation-message\"\n id=\"${this.validationId}\"\n aria-live=\"polite\"\n ?hidden=${!this.invalidmessage}\n >\n ${this.invalidmessage}\n </p>\n </ds-field>\n `;\n }\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"textfield.js","sources":["../../src/components/textfield.component.ts"],"sourcesContent":["import { css, html, LitElement, nothing, type PropertyValues } from 'lit';\nimport { customElement, property, query, state } from 'lit/decorators.js';\nimport { live } from 'lit/directives/live.js';\nimport { stringConverter } from '../internal/string-converter';\nimport { classMap } from 'lit/directives/class-map.js';\nimport { styled } from '../mixins/tailwind.mixin.ts';\nimport { ifDefined } from 'lit/directives/if-defined.js';\nimport { HasSlotController } from '../internal/slot';\nimport { FormControlMixin } from '../mixins/form-control.mixin';\nimport {\n maxLengthValidator,\n minLengthValidator,\n patternValidator,\n requiredValidator,\n typeMismatchValidator,\n} from '../mixins/validators';\nimport { watch } from '../internal/watch';\nimport './icon/icon.component.ts';\nimport { MaskInput, type MaskType } from 'maska';\nimport { getLang } from '../utilities/lang';\nimport { getTranslations } from '../utilities/translations';\nimport { LangController } from '../controllers/lang.controller.js';\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'mid-textfield': MinidTextfield;\n }\n}\n\nconst styles = [\n css`\n :host {\n display: block;\n }\n\n /* Hide increment/decrement buttons on inputs */\n input[type='number']::-webkit-outer-spin-button,\n input[type='number']::-webkit-inner-spin-button,\n input[type='number'] {\n -webkit-appearance: none;\n margin: 0;\n -moz-appearance: textfield !important;\n }\n\n .ds-field-affixes {\n background: var(--ds-color-neutral-background-tinted);\n }\n\n .has-field-buttons {\n --mid-field-button-size: var(--ds-size-7);\n --mid-field-button-gap: var(--ds-size-3);\n --mid-field-button-border: var(--ds-border-width-default);\n position: relative;\n display: block;\n }\n\n .has-field-buttons .ds-input {\n padding-inline-end: calc(\n var(--mid-field-button-size) + 2 * var(--mid-field-button-gap)\n );\n }\n\n .has-field-buttons--two .ds-input {\n padding-inline-end: calc(\n 2 * (var(--mid-field-button-size) + 2 * var(--mid-field-button-gap))\n );\n }\n\n .password-toggle-input::-ms-reveal,\n .password-toggle-input::-ms-clear {\n display: none;\n }\n\n .has-field-buttons .ds-input::-webkit-credentials-auto-fill-button {\n margin-inline-end: calc(\n var(--mid-field-button-size) + 2 * var(--mid-field-button-gap)\n );\n }\n\n .has-field-buttons--two .ds-input::-webkit-credentials-auto-fill-button {\n margin-inline-end: calc(\n 2 * (var(--mid-field-button-size) + 2 * var(--mid-field-button-gap))\n );\n }\n\n .has-field-buttons:hover\n .ds-input:not(:focus-visible, :disabled, [readonly], [aria-readonly='true']) {\n outline: var(--dsc-input-outline-width--hover)\n var(--dsc-input-outline-style--hover)\n var(--dsc-input-outline-color--hover);\n }\n\n .has-field-buttons:hover\n .ds-input[aria-invalid='true']:not(:focus-visible, :disabled, [readonly], [aria-readonly='true']) {\n outline-color: var(--dsc-input-accent-color--invalid);\n }\n\n .field-buttons {\n position: absolute;\n inset-block: var(--mid-field-button-border);\n inset-inline-end: var(--mid-field-button-border);\n display: flex;\n pointer-events: none;\n }\n\n .field-button {\n display: flex;\n align-items: center;\n justify-content: center;\n color: var(--ds-color-neutral-text-subtle);\n pointer-events: auto;\n }\n\n .field-buttons .field-button {\n padding-inline: var(--mid-field-button-gap);\n }\n\n .field-button > span {\n display: flex;\n align-items: center;\n justify-content: center;\n border-radius: var(--ds-border-radius-sm);\n block-size: var(--mid-field-button-size, var(--ds-size-7));\n inline-size: var(--mid-field-button-size, var(--ds-size-7));\n }\n\n .field-button mid-icon {\n font-size: var(--mid-field-button-size, var(--ds-size-7));\n }\n\n .field-button:focus-visible {\n outline: none;\n }\n\n .field-button:focus-visible > span {\n box-shadow: 0 0 0 var(--ds-border-width-focus) var(--ds-color-focus-inner);\n outline: var(--ds-color-focus-outer) solid var(--ds-border-width-focus);\n outline-offset: var(--ds-border-width-focus);\n }\n\n /* The validation message stays in the DOM so its text lands in a live region\n the screen reader is already watching - WebKit does not reliably announce\n one revealed at the same moment the text arrives. When empty it must paint\n nothing and take no room: the element drops the ds-validation-message class\n (whose ::before paints an error icon regardless of the text) and this rule\n cancels the sibling spacing from .ds-field > * + *, which the .ds-field >\n prefix is enough to outrank without !important. */\n .ds-field > .validation-message--empty {\n margin-top: 0;\n }\n `,\n];\n\nlet nextUniqueId = 0;\n\n/**\n *\n * @event mid-change - Emitted when a change to the input value is comitted by the user\n * @event mid-input - Emitted when the input element recieves input\n * @event mid-clear - Emitted when the input value is cleared\n * @event mid-focus - Emitted when input element is focused\n * @event mid-blur - Emitted when focus moves away from input element\n * @event {detail: { validity: ValidityState }} mid-invalid-hide - Emitted when the error message should be hidden\n * @event {detail: { validity: ValidityState }} mid-invalid-show - Emitted when the error message should be shown\n *\n * @slot prefix - Used for decoration to the left of the input\n * @slot suffix - Used for decoration to the right of the input\n * @slot label - The input's label. Alternatively, you can use the `label` attribute.\n *\n * @csspart base - The input's wrapper that has the input field styling.\n * @csspart input - The internal `<input>` element.\n * @csspart field - The element that wraps the label, input, and help text.\n * @csspart clear-button - The clear button\n * @csspart password-toggle-button - The button for toggling password visibility\n * @csspart validation-message - The error message shown by `invalidmessage`.\n */\n@customElement('mid-textfield')\nexport class MinidTextfield extends FormControlMixin(styled(LitElement, styles)) {\n private readonly inputId!: string;\n private readonly descriptionId!: string;\n private readonly validationId!: string;\n private readonly hasSlotControler = new HasSlotController(this, 'label', 'prefix', 'suffix');\n private inputMask?: MaskInput;\n private initialValue = '';\n\n @query('input')\n input!: HTMLInputElement;\n\n @property()\n label = '';\n\n @property()\n description = '';\n\n @property()\n value = '';\n\n @property()\n size: 'sm' | 'md' | 'lg' = 'md';\n\n @property({ converter: stringConverter })\n placeholder?: string;\n\n /**\n * Autofocus the input field on page load\n */\n @property({ type: Boolean })\n autofocus = false;\n\n /**\n * User agent autocomplete hint\n */\n @property()\n autocomplete?: AutoFill;\n\n /**\n * The minimum length of input that will be considered valid.\n */\n @property({ type: Number })\n minlength?: number;\n\n /**\n * The maximum length of input that will be considered valid.\n */\n @property({ type: Number })\n maxlength?: number;\n\n /**\n * The input's minimum value. Only applies to date and number input types.\n */\n @property()\n min?: number | string;\n\n /**\n * The input's maximum value. Only applies to date and number input types.\n */\n @property()\n max?: number | string;\n\n /**\n * Error message to display when the input is invalid, also activates invalid styling\n */\n @property()\n invalidmessage = '';\n\n @property()\n type:\n | 'date'\n | 'datetime-local'\n | 'email'\n | 'file'\n | 'month'\n | 'number'\n | 'password'\n | 'search'\n | 'tel'\n | 'text'\n | 'time'\n | 'url'\n | 'week' = 'text';\n\n /**\n * For displaying appropriate virtual keyboard\n */\n @property()\n inputmode:\n | 'none'\n | 'text'\n | 'tel'\n | 'url'\n | 'email'\n | 'numeric'\n | 'decimal' = 'text';\n\n /**\n * Adds a clear button when the input is not empty.\n */\n @property({ type: Boolean })\n clearable = false;\n\n /**\n * Adds a button to toggle the password's visibility.\n * Only applies if type is password\n */\n @property({ type: Boolean })\n passwordtoggle = false;\n\n /**\n * Determines wether the password is currently visible.\n * Only applies if type is password\n */\n @property({ type: Boolean })\n passwordvisible = false;\n\n @property({ type: Boolean, reflect: true })\n disabled = false;\n\n @property({ type: Boolean, reflect: true })\n readonly = false;\n\n /**\n * A regular expression pattern to validate input against.\n */\n @property()\n pattern?: string;\n\n /**\n * Makes the input required\n */\n @property({ type: Boolean })\n required = false;\n\n /**\n * Visually hides `label` and `description` (still available for screen readers)\n */\n @property({ type: Boolean })\n hidelabel = false;\n\n /**\n * Modify input value based on mask e.g: `\"##:##\"` = 12:34.\n * `#`: /[0-9]/\n * `@`: /[a-zA-Z]/\n * `*`: /[a-zA-Z0-9]/\n * For more information go to https://beholdr.github.io/maska\n */\n @property()\n mask?: MaskType;\n\n @state()\n hasFocus = false;\n\n static get formControlValidators() {\n return [\n requiredValidator,\n maxLengthValidator,\n minLengthValidator,\n patternValidator,\n typeMismatchValidator,\n ];\n }\n\n constructor() {\n super();\n new LangController(this);\n nextUniqueId++;\n this.inputId = `mid-textfield-input-${nextUniqueId}`;\n this.descriptionId = `mid-textfield-description-${nextUniqueId}`;\n this.validationId = `mid-textfield-validation-${nextUniqueId}`;\n this.setValue(this.value);\n }\n\n override connectedCallback(): void {\n super.connectedCallback();\n this.initialValue = this.value;\n }\n\n get validationTarget() {\n return this.input;\n }\n\n protected firstUpdated(_changedProperties: PropertyValues): void {\n super.firstUpdated(_changedProperties);\n this.setValue(this.value);\n }\n\n disconnectedCallback(): void {\n this.inputMask?.destroy();\n }\n\n private handleKeydown(event: KeyboardEvent) {\n const hasModifier =\n event.metaKey || event.ctrlKey || event.shiftKey || event.altKey;\n\n if (event.key === 'Enter' && !hasModifier) {\n setTimeout(() => {\n if (!event.defaultPrevented && !event.isComposing) {\n this.form.requestSubmit();\n }\n });\n }\n }\n\n private handleBlur() {\n this.hasFocus = false;\n this.dispatchEvent(\n new Event('mid-blur', { bubbles: true, composed: true })\n );\n }\n\n private handleChange() {\n this.value = this.input.value;\n this.dispatchEvent(\n new Event('mid-change', { bubbles: true, composed: true })\n );\n }\n\n private handleInput() {\n let tmp_value = this.input.value;\n\n if (this.type === 'number') {\n tmp_value = tmp_value.replace(/[^0-9]/g, '');\n }\n\n this.value = tmp_value;\n this.setValue(this.value);\n this.dispatchEvent(\n new Event('mid-input', { bubbles: true, composed: true })\n );\n }\n\n private handleFocus() {\n this.hasFocus = true;\n this.dispatchEvent(\n new Event('mid-focus', { composed: true, bubbles: true })\n );\n }\n\n private handlePasswordToggle() {\n this.passwordvisible = !this.passwordvisible;\n }\n\n private handleInFieldButtonMousedown(event: MouseEvent) {\n event.preventDefault();\n }\n\n private handleClearClick(event: MouseEvent) {\n event.preventDefault();\n\n if (this.value !== '') {\n this.value = '';\n this.dispatchEvent(\n new Event('mid-clear', { composed: true, bubbles: true })\n );\n this.dispatchEvent(\n new Event('mid-input', { composed: true, bubbles: true })\n );\n this.dispatchEvent(\n new Event('mid-change', { composed: true, bubbles: true })\n );\n }\n\n this.input.focus();\n }\n\n focus() {\n this.hasFocus = true;\n this.input.focus();\n }\n\n resetFormControl() {\n this.invalidmessage = '';\n this.value = this.initialValue;\n }\n\n forceError(message?: string): void {\n super.forceError(message);\n }\n\n @watch('value')\n handleValueUpdate() {\n this.setValue(this.value);\n }\n\n @watch('mask')\n async handleMaskUpdate() {\n if (!this.mask) return;\n await this.updateComplete;\n this.inputMask?.destroy();\n this.inputMask = new MaskInput(this.input, {\n mask: this.mask,\n eager: true,\n });\n }\n\n override render() {\n const lang = getLang(this);\n const t = getTranslations(lang);\n const hasLabelSlot = this.hasSlotControler.test('label');\n const hasLabel = !!this.label || !!hasLabelSlot;\n const hasPrefix = this.hasSlotControler.test('prefix');\n const hasSuffix = this.hasSlotControler.test('suffix');\n const hasClearIcon = this.clearable && !this.disabled && !this.readonly;\n const hasPasswordToggle = this.passwordtoggle && !this.disabled;\n const overlayFieldButtons =\n (hasClearIcon || hasPasswordToggle) && !hasPrefix && !hasSuffix;\n const hasAffixes = hasPrefix || hasSuffix;\n const isClearIconVisible =\n hasClearIcon && (typeof this.value === 'number' || this.value.length > 0);\n const describedBy = [\n this.description ? this.descriptionId : undefined,\n this.invalidmessage ? this.validationId : undefined,\n ]\n .filter(Boolean)\n .join(' ');\n\n const passwordToggleButton = html`\n <button\n type=\"button\"\n part=\"password-toggle-button\"\n class=\"field-button\"\n aria-label=${this.passwordvisible ? t.hidePassword : t.showPassword}\n aria-controls=${this.inputId}\n @mousedown=${this.handleInFieldButtonMousedown}\n @click=${this.handlePasswordToggle}\n >\n <span>\n <mid-icon\n library=\"system\"\n name=${this.passwordvisible ? 'eye-slash' : 'eye'}\n ></mid-icon>\n </span>\n </button>\n `;\n\n const clearButton = html`\n <button\n type=\"button\"\n part=\"clear-button\"\n class=\"field-button\"\n aria-label=${t.clear}\n @mousedown=${this.handleInFieldButtonMousedown}\n @click=${this.handleClearClick}\n >\n <span>\n <mid-icon library=\"nav-aksel\" name=\"trash\"></mid-icon>\n </span>\n </button>\n `;\n\n return html`\n <ds-field\n part=\"field\"\n class=\"ds-field\"\n data-size=${this.size}\n >\n <label\n for=\"${this.inputId}\"\n class=\"${classMap({\n 'sr-only': this.hidelabel || !hasLabel,\n })} ds-label\"\n >\n <slot name=\"label\"> ${this.label} </slot>\n </label>\n ${this.description\n ? html`\n <div\n id=\"${this.descriptionId}\"\n part=\"description\"\n aria-hidden=\"true\"\n class=\"${classMap({\n 'sr-only': this.hidelabel,\n })}\"\n >\n ${this.description}\n </div>\n `\n : nothing}\n <div\n part=\"base\"\n class=\"${classMap({\n 'ds-field-affixes': hasAffixes,\n 'has-field-buttons': overlayFieldButtons,\n 'has-field-buttons--two':\n overlayFieldButtons && hasClearIcon && hasPasswordToggle,\n })}\"\n >\n ${hasPrefix ? html`<span class=\"ds-field-affix\"><slot name=\"prefix\"></slot></span>` : html`<slot name=\"prefix\"></slot>`}\n <input\n id=\"${this.inputId}\"\n class=\"${classMap({\n 'ds-input': true,\n 'password-toggle-input': hasPasswordToggle,\n })}\"\n part=\"input\"\n lang=${lang}\n .value=${live(this.value)}\n ?disabled=${this.disabled}\n ?readonly=${this.readonly}\n ?required=${this.required}\n ?autofocus=${this.autofocus}\n autocomplete=${ifDefined(this.autocomplete as any)}\n type=${this.type === 'password' && this.passwordvisible\n ? 'text'\n : this.type}\n aria-describedby=${ifDefined(describedBy || undefined)}\n aria-invalid=${this.invalidmessage ? 'true' : 'false'}\n aria-errormessage=${ifDefined(\n this.invalidmessage ? this.validationId : undefined\n )}\n placeholder=${ifDefined(this.placeholder)}\n minlength=${ifDefined(this.minlength)}\n maxlength=${ifDefined(this.maxlength)}\n min=${ifDefined(this.min)}\n max=${ifDefined(this.max)}\n pattern=${ifDefined(this.pattern)}\n inputmode=${this.inputmode}\n @input=${this.handleInput}\n @change=${this.handleChange}\n @focus=${this.handleFocus}\n @blur=${this.handleBlur}\n @keydown=${this.handleKeydown}\n />\n ${overlayFieldButtons\n ? html`\n <div class=\"field-buttons\">\n ${isClearIconVisible ? clearButton : nothing}\n ${hasPasswordToggle ? passwordToggleButton : nothing}\n </div>\n `\n : nothing}\n ${!overlayFieldButtons && isClearIconVisible\n ? html`<span class=\"ds-field-affix\">${clearButton}</span>`\n : nothing}\n ${!overlayFieldButtons && hasPasswordToggle\n ? html`<span class=\"ds-field-affix\">${passwordToggleButton}</span>`\n : nothing}\n ${hasSuffix ? html`<span part=\"suffix\" class=\"ds-field-affix\"><slot name=\"suffix\"></slot></span>` : html`<slot name=\"suffix\"></slot>`}\n </div>\n <p\n class=\"${classMap({\n 'ds-validation-message': !!this.invalidmessage,\n 'validation-message--empty': !this.invalidmessage,\n })}\"\n part=\"validation-message\"\n id=\"${this.validationId}\"\n aria-live=\"polite\"\n >\n ${this.invalidmessage}\n </p>\n </ds-field>\n `;\n }\n}\n"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"form-control.mixin.d.ts","sourceRoot":"","sources":["../../src/mixins/form-control.mixin.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,KAAK,CAAC;AACjC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAC1D,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,uBAAuB,CAAC;AAElE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAC;AAO9D;;;GAGG;AACH,wBAAgB,gBAAgB,CAC9B,KAAK,SAAS,WAAW,CAAC,UAAU,GAAG,YAAY,CAAC,GAAG;IACrD,kBAAkB,CAAC,EAAE,MAAM,EAAE,CAAC;CAC/B,EACD,UAAU,EAAE,KAAK,GAwmBgB,WAAW,CAAC,oBAAoB,CAAC,GAAG,KAAK,CAC3E"}
1
+ {"version":3,"file":"form-control.mixin.d.ts","sourceRoot":"","sources":["../../src/mixins/form-control.mixin.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,KAAK,CAAC;AACjC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAC1D,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,uBAAuB,CAAC;AAElE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAC;AAO9D;;;GAGG;AACH,wBAAgB,gBAAgB,CAC9B,KAAK,SAAS,WAAW,CAAC,UAAU,GAAG,YAAY,CAAC,GAAG;IACrD,kBAAkB,CAAC,EAAE,MAAM,EAAE,CAAC;CAC/B,EACD,UAAU,EAAE,KAAK,GA4nBgB,WAAW,CAAC,oBAAoB,CAAC,GAAG,KAAK,CAC3E"}
@@ -204,6 +204,25 @@ function FormControlMixin(SuperClass) {
204
204
  const showError = __privateMethod(this, _FormControl_instances, shouldShowError_fn).call(this);
205
205
  __privateMethod(this, _FormControl_instances, dispatchInvalidEvent_fn).call(this, showError);
206
206
  }
207
+ /**
208
+ * Updates the form value and re-runs validators without touching the
209
+ * error-display state: `#forceError` is left alone, no
210
+ * `validationMessageCallback('')`, no `mid-invalid-show`/`-hide` is
211
+ * dispatched, and the control is not marked touched (a programmatic set
212
+ * is not user interaction). For programmatic value changes where the host
213
+ * page owns the error presentation and a `setValue` would wipe it
214
+ * mid-flow.
215
+ * @param value {FormValue} - The value to pass to the form
216
+ */
217
+ syncFormValue(value) {
218
+ __privateSet(this, _value, value);
219
+ const valueToUpdate = this.shouldFormValueUpdate() ? value : null;
220
+ this.internals.setFormValue(valueToUpdate);
221
+ __privateMethod(this, _FormControl_instances, runValidators_fn).call(this, valueToUpdate);
222
+ if (this.valueChangedCallback) {
223
+ this.valueChangedCallback(valueToUpdate);
224
+ }
225
+ }
207
226
  /**
208
227
  * Forces the control to show an error state. If a message is passed in,
209
228
  * it will be set as the control's internal validity state message.
@@ -1 +1 @@
1
- {"version":3,"file":"form-control.mixin.js","sources":["../../src/mixins/form-control.mixin.ts"],"sourcesContent":["import { LitElement } from 'lit';\nimport type { IControlHost } from '../types/control-host';\nimport type { FormControlInterface } from '../types/form-control';\nimport type { FormValue } from '../types/form-value';\nimport type { Constructor } from '../types/mixin-constructor';\nimport type {\n CustomValidityState,\n validationMessageCallback,\n Validator,\n} from '../types/validator.type';\n\n/**\n *\n * https://github.com/open-wc/form-participation/tree/main/packages/form-control\n */\nexport function FormControlMixin<\n TBase extends Constructor<LitElement & IControlHost> & {\n observedAttributes?: string[];\n },\n>(SuperClass: TBase) {\n class FormControl extends SuperClass {\n /**\n * Wires up control instances to be form associated\n * @ignore\n */\n static get formAssociated(): boolean {\n return true;\n }\n\n /**\n * A list of Validator objects that will be evaluated when a control's form\n * value is modified or optionally when a given attribute changes.\n *\n * When a Validator's callback returns false, the entire form control will\n * be set to an invalid state.\n * @ignore\n */\n declare static formControlValidators: Validator[];\n\n /**\n * If set to true the control described should be evaluated and validated\n * as part of a group. Like a radio, if any member of the group's validity\n * changes the the other members should update as well.\n * @ignore\n */\n declare static formControlValidationGroup: boolean;\n\n /**\n * @ignore\n */\n private static get validators(): Validator[] {\n return this.formControlValidators || [];\n }\n\n /**\n * Allows the FormControl instance to respond to Validator attributes.\n * For instance, if a given Validator has a `required` attribute, that\n * validator will be evaluated whenever the host's required attribute\n * is updated.\n * @ignore\n */\n static get observedAttributes(): string[] {\n const validatorAttributes = this.validators\n .map((validator) => validator.attribute)\n .flat();\n\n const observedAttributes = super.observedAttributes || [];\n\n /** Make sure there are no duplicates inside the attributes list */\n const attributeSet = new Set([\n ...observedAttributes,\n ...validatorAttributes,\n ]);\n return [...attributeSet] as string[];\n }\n\n /**\n * Return the validator associated with a given attribute. If no\n * Validator is associated with the attribute, it will return null.\n * @ignore\n */\n static getValidator(attribute: string): Validator | null {\n return (\n this.validators.find(\n (validator) => validator.attribute === attribute\n ) || null\n );\n }\n\n /**\n * Get all validators that are set to react to a given attribute\n * @param {string} attribute - The attribute that has changed\n * @returns {Validator[]}\n * @ignore\n */\n static getValidators(attribute: string): Validator[] | null {\n return this.validators.filter((validator) => {\n if (\n validator.attribute === attribute ||\n validator.attribute?.includes(attribute)\n ) {\n return true;\n }\n });\n }\n\n /**\n * The ElementInternals instance for the control.\n * @ignore\n */\n internals = this.attachInternals();\n\n /**\n * Keep track of if the control has focus\n * @private\n * @ignore\n */\n #focused = false;\n\n /**\n * Exists to control when an error should be displayed\n * @private\n * @ignore\n */\n #forceError = false;\n\n /**\n * Toggles to true whenever the element has been altered. This property\n * will reset whenever the control's formResetCallback is called.\n * @private\n * @ignore\n */\n #touched = false;\n\n /**\n * An internal abort controller for cancelling pending async validation\n * @ignore\n */\n #abortController?: AbortController;\n\n /**\n * Used for tracking if a validation target has been set to manage focus\n * when the control's validity is reported\n * @ignore\n */\n #awaitingValidationTarget = true;\n\n /**\n * Acts as a cache for the current value so the value can be re-evaluated\n * whenever an attribute changes or on some other event.\n * @ignore\n */\n #value: FormValue = '';\n\n /**\n * Set this[focused]\n * to true when the element is focused\n * @private\n * @ignore\n */\n #onFocus = (): void => {\n this.#focused = true;\n this.#shouldShowError();\n };\n\n /**\n * Reset this[focused] on blur\n * @private\n * @ignore\n */\n #onBlur = (): void => {\n this.#focused = false;\n\n this.#runValidators(this.shouldFormValueUpdate() ? this.#value : '');\n\n /**\n * Set forceError to ensure error messages persist until\n * the value is changed.\n */\n if (!this.validity.valid && this.#touched) {\n this.#forceError = true;\n }\n const showError = this.#shouldShowError();\n\n if (this.validationMessageCallback) {\n this.validationMessageCallback(showError ? this.validationMessage : '');\n }\n this.#dispatchInvalidEvent(showError);\n };\n\n /**\n * For the show error state on invalid\n * @private\n * @ignore\n */\n #onInvalid = (event?: Event): void => {\n event?.preventDefault();\n event?.stopImmediatePropagation();\n\n if (this.#awaitingValidationTarget && this.validationTarget) {\n this.internals.setValidity(\n this.validity,\n this.validationMessage,\n this.validationTarget\n );\n this.#awaitingValidationTarget = false;\n }\n this.#touched = true;\n this.#forceError = true;\n const showError = this.#shouldShowError();\n this?.validationMessageCallback?.(\n showError ? this.validationMessage : ''\n );\n this.#dispatchInvalidEvent(showError);\n };\n\n /**\n * Return a reference to the control's form\n * @ignore\n */\n get form(): HTMLFormElement {\n return this.internals.form as HTMLFormElement;\n }\n\n /**\n * Will return true if it is recommended that the control shows an internal\n * error. If using this property, it is wise to listen for 'invalid' events\n * on the element host and call preventDefault on the event. Doing this will\n * prevent browsers from showing a validation popup.\n * @ignore\n */\n get showError(): boolean {\n return this.#shouldShowError();\n }\n\n /**\n * Forward the internals checkValidity method\n * will return the valid state of the control.\n * @ignore\n */\n checkValidity(): boolean {\n return this.internals.checkValidity();\n }\n\n /**\n * The element's validity state\n * @ignore\n */\n get validity(): ValidityState {\n return this.internals.validity;\n }\n\n /**\n * The validation message shown by a given Validator object. If the control\n * is in a valid state this should be falsy.\n * @ignore\n */\n get validationMessage(): string {\n return this.internals.validationMessage;\n }\n\n constructor(...args: any[]) {\n super(...args);\n this.addEventListener?.('focus', this.#onFocus);\n this.addEventListener?.('blur', this.#onBlur);\n this.addEventListener?.('invalid', this.#onInvalid);\n this.setValue(null);\n }\n\n attributeChangedCallback(\n name: string,\n oldValue: string,\n newValue: string\n ): void {\n super.attributeChangedCallback?.(name, oldValue, newValue);\n\n /**\n * Check to see if a Validator is associated with the changed attribute.\n * If one exists, call control's validate function which will perform\n * control validation.\n * @ignore\n */\n const proto = this.constructor as typeof FormControl;\n const validators = proto.getValidators(name);\n\n if (validators?.length && this.validationTarget) {\n this.setValue(this.#value);\n }\n }\n\n /** PUBLIC LIFECYCLE METHODS */\n\n /**\n * Sets the control's form value if the call to `shouldFormValueUpdate`\n * returns `true`. Set this[touch] if connected and value changed.\n * @param value {FormValue} - The value to pass to the form\n * @ignore\n */\n setValue(value: FormValue): void {\n if (this.isConnected && this.#value !== value) {\n this.#touched = true;\n }\n this.#forceError = false;\n this.validationMessageCallback?.('');\n this.#value = value;\n const valueShouldUpdate = this.shouldFormValueUpdate();\n const valueToUpdate = valueShouldUpdate ? value : null;\n this.internals.setFormValue(valueToUpdate as string);\n this.#runValidators(valueToUpdate);\n if (this.valueChangedCallback) {\n this.valueChangedCallback(valueToUpdate);\n }\n const showError = this.#shouldShowError();\n this.#dispatchInvalidEvent(showError);\n }\n\n /**\n * Forces the control to show an error state. If a message is passed in,\n * it will be set as the control's internal validity state message.\n * @param message { string | undefined } - The message for internals validity state\n */\n forceError(message?: string) {\n if (message) {\n this.#setValidityWithOptionalTarget({ customError: true }, message);\n }\n this.#forceError = true;\n this.#shouldShowError();\n }\n\n /**\n * This method can be overridden to determine if the control's form value\n * should be set on a call to `setValue`. An example of when a user might want\n * to skip this step is when implementing checkbox-like behavior, first checking\n * to see if `this.checked` is set to a truthy value. By default this returns\n * `true`.\n * @ignore\n */\n shouldFormValueUpdate(): boolean {\n return true;\n }\n\n /**\n * Save a reference to the validation complete resolver\n * @ignore\n */\n #validationCompleteResolver?: (value: void | PromiseLike<void>) => void;\n\n /**\n * When true validation will be pending\n * @ignore\n */\n #isValidationPending = false;\n\n /**\n * @ignore\n */\n #validationComplete = Promise.resolve();\n\n /**\n * A promise that will resolve when all pending validations are complete\n * @ignore\n */\n get validationComplete(): Promise<void> {\n return new Promise((resolve) => resolve(this.#validationComplete));\n }\n\n /** DECLARED INSTANCE METHODS AND PROPERTIES*/\n\n /**\n * Resets a form control to its initial state\n * @ignore\n */\n declare resetFormControl: () => void;\n\n /**\n * This method is used to override the controls' validity message\n * for a given Validator key. This has the highest level of priority when\n * setting a validationMessage, so use this method wisely.\n *\n * The returned value will be used as the validationMessage for the given key.\n * @param validationKey {string} - The key that has returned invalid\n * @ignore\n */\n declare validityCallback: (validationKey: string) => string | void;\n\n /**\n * Called when the control's validationMessage should be changed\n * @param message { string } - The new validation message\n * @ignore\n */\n declare validationMessageCallback: (message: string) => void;\n\n /**\n * A callback for when the controls' form value changes. The value\n * passed to this function should not be confused with the control's\n * value property, this is the value that will appear on the form.\n *\n * In cases where `checked` did not exist on the control's prototype\n * upon initialization, this value and the value property will be identical;\n * in cases where `checked` is present upon initialization, this will be\n * effectively `this.checked && this.value`.\n * @ignore\n */\n declare valueChangedCallback: (value: FormValue) => void;\n\n /**\n * The element that will receive focus when the control's validity\n * state is reported either by a form submission or via API\n *\n * We use declare since this is optional and we don't particularly\n * care how the consuming component implements this (as a field, member\n * or getter/setter)\n * @ignore\n */\n declare validationTarget: HTMLElement | null;\n\n /** PRIVATE LIFECYCLE METHODS */\n\n /**\n * Check to see if an error should be shown. This method will also\n * update the internals state object with the --show-error state\n * if necessary.\n * @private\n * @ignore\n */\n #shouldShowError(): boolean {\n if (this.hasAttribute('disabled')) {\n return false;\n }\n\n const showError =\n this.#forceError ||\n (this.#touched && !this.validity.valid && !this.#focused);\n\n if (showError && this.internals.states) {\n this.internals.states.add('--show-error');\n this.internals.states.add('--invalid');\n } else if (this.internals.states) {\n this.internals.states.delete('--show-error');\n this.internals.states.delete('--invalid');\n }\n\n return showError;\n }\n\n #runValidators(value: FormValue): void {\n const proto = this.constructor as typeof FormControl;\n const validity: CustomValidityState = {};\n const validators = proto.validators;\n const asyncValidators: Promise<boolean | void>[] = [];\n const hasAsyncValidators = validators.some(\n (validator) => validator.isValid instanceof Promise\n );\n\n if (!this.#isValidationPending) {\n this.#validationComplete = new Promise((resolve) => {\n this.#validationCompleteResolver = resolve;\n });\n this.#isValidationPending = true;\n }\n\n /**\n * If an abort controller exists from a previous validation step\n * notify still-running async validators that we are requesting they\n * discontinue any work.\n */\n if (this.#abortController) {\n this.#abortController.abort();\n }\n\n /**\n * Create a new abort controller and replace the instance reference\n * so we can clean it up for next time\n */\n const abortController = new AbortController();\n this.#abortController = abortController;\n let validationMessage: string | undefined = undefined;\n\n /** Track to see if any validity key has changed */\n let hasChange = false;\n\n if (!validators.length) {\n return;\n }\n\n validators.forEach((validator) => {\n const key = validator.key || 'customError';\n const isValid = validator.isValid(this, value, abortController.signal);\n const isAsyncValidator = isValid instanceof Promise;\n\n if (isAsyncValidator) {\n asyncValidators.push(isValid);\n\n isValid.then((isValidatorValid) => {\n if (isValidatorValid === undefined || isValidatorValid === null) {\n return;\n }\n /** Invert the validity state to correspond to the ValidityState API */\n validity[key] = !isValidatorValid;\n\n validationMessage = this.#getValidatorMessageForValue(\n validator,\n value\n );\n this.#setValidityWithOptionalTarget(validity, validationMessage);\n });\n } else {\n /** Invert the validity state to correspond to the ValidityState API */\n validity[key] = !isValid;\n\n if (this.validity[key] !== !isValid) {\n hasChange = true;\n }\n\n // only update the validationMessage for the first invalid scenario\n // so that earlier invalid validators dont get their messages overwritten by later ones\n // in the validators array\n if (!isValid && !validationMessage) {\n validationMessage = this.#getValidatorMessageForValue(\n validator,\n value\n );\n }\n }\n });\n\n /** Once all the async validators have settled, resolve validationComplete */\n Promise.allSettled(asyncValidators).then(() => {\n /** Don't resolve validations if the signal is aborted */\n if (!abortController?.signal.aborted) {\n this.#isValidationPending = false;\n this.#validationCompleteResolver?.();\n }\n });\n\n /**\n * If async validators are present:\n * Only run updates when a sync validator has a change. This is to prevent\n * situations where running sync validators can override async validators\n * that are still in progress\n *\n * If async validators are not present, always update validity\n */\n if (hasChange || !hasAsyncValidators) {\n this.#setValidityWithOptionalTarget(validity, validationMessage);\n }\n }\n\n /**\n * If the validationTarget is not set, the user can decide how they would\n * prefer to handle focus when the field is validated.\n * @ignore\n */\n #setValidityWithOptionalTarget(\n validity: Partial<ValidityState>,\n validationMessage: string | undefined\n ): void {\n if (this.validationTarget) {\n this.internals.setValidity(\n validity,\n validationMessage,\n this.validationTarget\n );\n this.#awaitingValidationTarget = false;\n } else {\n this.internals.setValidity(validity, validationMessage);\n\n if (this.internals.validity.valid) {\n return;\n }\n\n /**\n * Sets mark the component as awaiting a validation target\n * if the element dispatches an invalid event, the #onInvalid listener\n * will check to see if the validation target has been set since this call\n * has run. This useful in cases like Lit's use of the query\n * decorator for setting the validationTarget or any scenario\n * where the validationTarget isn't available upon construction\n */\n this.#awaitingValidationTarget = true;\n }\n }\n\n /**\n * Process the validator message attribute\n * @ignore\n */\n #getValidatorMessageForValue(\n validator: Validator,\n value: FormValue\n ): string {\n /** If the validity callback exists and returns, use that as the result */\n\n if (this.validityCallback) {\n const message = this.validityCallback(validator.key || 'customError');\n\n if (message) {\n return message;\n }\n }\n\n if (validator.message instanceof Function) {\n return (validator.message as validationMessageCallback)(this, value);\n } else {\n return validator.message as string;\n }\n }\n\n /**\n * Reset control state when the form is reset\n */\n formResetCallback() {\n this.#touched = false;\n this.#forceError = false;\n this.#shouldShowError();\n this.resetFormControl?.();\n const showError = this.#shouldShowError();\n this.validationMessageCallback?.(showError ? this.validationMessage : '');\n this.#dispatchInvalidEvent(showError);\n }\n\n #dispatchInvalidEvent(showError: boolean) {\n const event = showError ? 'mid-invalid-show' : 'mid-invalid-hide';\n this.dispatchEvent(\n new CustomEvent(event, {\n bubbles: true,\n composed: true,\n detail: {\n validity: this.validity,\n },\n })\n );\n }\n }\n\n return FormControl as unknown as Constructor<FormControlInterface> & TBase;\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"form-control.mixin.js","sources":["../../src/mixins/form-control.mixin.ts"],"sourcesContent":["import { LitElement } from 'lit';\nimport type { IControlHost } from '../types/control-host';\nimport type { FormControlInterface } from '../types/form-control';\nimport type { FormValue } from '../types/form-value';\nimport type { Constructor } from '../types/mixin-constructor';\nimport type {\n CustomValidityState,\n validationMessageCallback,\n Validator,\n} from '../types/validator.type';\n\n/**\n *\n * https://github.com/open-wc/form-participation/tree/main/packages/form-control\n */\nexport function FormControlMixin<\n TBase extends Constructor<LitElement & IControlHost> & {\n observedAttributes?: string[];\n },\n>(SuperClass: TBase) {\n class FormControl extends SuperClass {\n /**\n * Wires up control instances to be form associated\n * @ignore\n */\n static get formAssociated(): boolean {\n return true;\n }\n\n /**\n * A list of Validator objects that will be evaluated when a control's form\n * value is modified or optionally when a given attribute changes.\n *\n * When a Validator's callback returns false, the entire form control will\n * be set to an invalid state.\n * @ignore\n */\n declare static formControlValidators: Validator[];\n\n /**\n * If set to true the control described should be evaluated and validated\n * as part of a group. Like a radio, if any member of the group's validity\n * changes the the other members should update as well.\n * @ignore\n */\n declare static formControlValidationGroup: boolean;\n\n /**\n * @ignore\n */\n private static get validators(): Validator[] {\n return this.formControlValidators || [];\n }\n\n /**\n * Allows the FormControl instance to respond to Validator attributes.\n * For instance, if a given Validator has a `required` attribute, that\n * validator will be evaluated whenever the host's required attribute\n * is updated.\n * @ignore\n */\n static get observedAttributes(): string[] {\n const validatorAttributes = this.validators\n .map((validator) => validator.attribute)\n .flat();\n\n const observedAttributes = super.observedAttributes || [];\n\n /** Make sure there are no duplicates inside the attributes list */\n const attributeSet = new Set([\n ...observedAttributes,\n ...validatorAttributes,\n ]);\n return [...attributeSet] as string[];\n }\n\n /**\n * Return the validator associated with a given attribute. If no\n * Validator is associated with the attribute, it will return null.\n * @ignore\n */\n static getValidator(attribute: string): Validator | null {\n return (\n this.validators.find(\n (validator) => validator.attribute === attribute\n ) || null\n );\n }\n\n /**\n * Get all validators that are set to react to a given attribute\n * @param {string} attribute - The attribute that has changed\n * @returns {Validator[]}\n * @ignore\n */\n static getValidators(attribute: string): Validator[] | null {\n return this.validators.filter((validator) => {\n if (\n validator.attribute === attribute ||\n validator.attribute?.includes(attribute)\n ) {\n return true;\n }\n });\n }\n\n /**\n * The ElementInternals instance for the control.\n * @ignore\n */\n internals = this.attachInternals();\n\n /**\n * Keep track of if the control has focus\n * @private\n * @ignore\n */\n #focused = false;\n\n /**\n * Exists to control when an error should be displayed\n * @private\n * @ignore\n */\n #forceError = false;\n\n /**\n * Toggles to true whenever the element has been altered. This property\n * will reset whenever the control's formResetCallback is called.\n * @private\n * @ignore\n */\n #touched = false;\n\n /**\n * An internal abort controller for cancelling pending async validation\n * @ignore\n */\n #abortController?: AbortController;\n\n /**\n * Used for tracking if a validation target has been set to manage focus\n * when the control's validity is reported\n * @ignore\n */\n #awaitingValidationTarget = true;\n\n /**\n * Acts as a cache for the current value so the value can be re-evaluated\n * whenever an attribute changes or on some other event.\n * @ignore\n */\n #value: FormValue = '';\n\n /**\n * Set this[focused]\n * to true when the element is focused\n * @private\n * @ignore\n */\n #onFocus = (): void => {\n this.#focused = true;\n this.#shouldShowError();\n };\n\n /**\n * Reset this[focused] on blur\n * @private\n * @ignore\n */\n #onBlur = (): void => {\n this.#focused = false;\n\n this.#runValidators(this.shouldFormValueUpdate() ? this.#value : '');\n\n /**\n * Set forceError to ensure error messages persist until\n * the value is changed.\n */\n if (!this.validity.valid && this.#touched) {\n this.#forceError = true;\n }\n const showError = this.#shouldShowError();\n\n if (this.validationMessageCallback) {\n this.validationMessageCallback(showError ? this.validationMessage : '');\n }\n this.#dispatchInvalidEvent(showError);\n };\n\n /**\n * For the show error state on invalid\n * @private\n * @ignore\n */\n #onInvalid = (event?: Event): void => {\n event?.preventDefault();\n event?.stopImmediatePropagation();\n\n if (this.#awaitingValidationTarget && this.validationTarget) {\n this.internals.setValidity(\n this.validity,\n this.validationMessage,\n this.validationTarget\n );\n this.#awaitingValidationTarget = false;\n }\n this.#touched = true;\n this.#forceError = true;\n const showError = this.#shouldShowError();\n this?.validationMessageCallback?.(\n showError ? this.validationMessage : ''\n );\n this.#dispatchInvalidEvent(showError);\n };\n\n /**\n * Return a reference to the control's form\n * @ignore\n */\n get form(): HTMLFormElement {\n return this.internals.form as HTMLFormElement;\n }\n\n /**\n * Will return true if it is recommended that the control shows an internal\n * error. If using this property, it is wise to listen for 'invalid' events\n * on the element host and call preventDefault on the event. Doing this will\n * prevent browsers from showing a validation popup.\n * @ignore\n */\n get showError(): boolean {\n return this.#shouldShowError();\n }\n\n /**\n * Forward the internals checkValidity method\n * will return the valid state of the control.\n * @ignore\n */\n checkValidity(): boolean {\n return this.internals.checkValidity();\n }\n\n /**\n * The element's validity state\n * @ignore\n */\n get validity(): ValidityState {\n return this.internals.validity;\n }\n\n /**\n * The validation message shown by a given Validator object. If the control\n * is in a valid state this should be falsy.\n * @ignore\n */\n get validationMessage(): string {\n return this.internals.validationMessage;\n }\n\n constructor(...args: any[]) {\n super(...args);\n this.addEventListener?.('focus', this.#onFocus);\n this.addEventListener?.('blur', this.#onBlur);\n this.addEventListener?.('invalid', this.#onInvalid);\n this.setValue(null);\n }\n\n attributeChangedCallback(\n name: string,\n oldValue: string,\n newValue: string\n ): void {\n super.attributeChangedCallback?.(name, oldValue, newValue);\n\n /**\n * Check to see if a Validator is associated with the changed attribute.\n * If one exists, call control's validate function which will perform\n * control validation.\n * @ignore\n */\n const proto = this.constructor as typeof FormControl;\n const validators = proto.getValidators(name);\n\n if (validators?.length && this.validationTarget) {\n this.setValue(this.#value);\n }\n }\n\n /** PUBLIC LIFECYCLE METHODS */\n\n /**\n * Sets the control's form value if the call to `shouldFormValueUpdate`\n * returns `true`. Set this[touch] if connected and value changed.\n * @param value {FormValue} - The value to pass to the form\n * @ignore\n */\n setValue(value: FormValue): void {\n if (this.isConnected && this.#value !== value) {\n this.#touched = true;\n }\n this.#forceError = false;\n this.validationMessageCallback?.('');\n this.#value = value;\n const valueShouldUpdate = this.shouldFormValueUpdate();\n const valueToUpdate = valueShouldUpdate ? value : null;\n this.internals.setFormValue(valueToUpdate as string);\n this.#runValidators(valueToUpdate);\n if (this.valueChangedCallback) {\n this.valueChangedCallback(valueToUpdate);\n }\n const showError = this.#shouldShowError();\n this.#dispatchInvalidEvent(showError);\n }\n\n /**\n * Updates the form value and re-runs validators without touching the\n * error-display state: `#forceError` is left alone, no\n * `validationMessageCallback('')`, no `mid-invalid-show`/`-hide` is\n * dispatched, and the control is not marked touched (a programmatic set\n * is not user interaction). For programmatic value changes where the host\n * page owns the error presentation and a `setValue` would wipe it\n * mid-flow.\n * @param value {FormValue} - The value to pass to the form\n */\n syncFormValue(value: FormValue): void {\n this.#value = value;\n const valueToUpdate = this.shouldFormValueUpdate() ? value : null;\n this.internals.setFormValue(valueToUpdate as string);\n this.#runValidators(valueToUpdate);\n if (this.valueChangedCallback) {\n this.valueChangedCallback(valueToUpdate);\n }\n }\n\n /**\n * Forces the control to show an error state. If a message is passed in,\n * it will be set as the control's internal validity state message.\n * @param message { string | undefined } - The message for internals validity state\n */\n forceError(message?: string) {\n if (message) {\n this.#setValidityWithOptionalTarget({ customError: true }, message);\n }\n this.#forceError = true;\n this.#shouldShowError();\n }\n\n /**\n * This method can be overridden to determine if the control's form value\n * should be set on a call to `setValue`. An example of when a user might want\n * to skip this step is when implementing checkbox-like behavior, first checking\n * to see if `this.checked` is set to a truthy value. By default this returns\n * `true`.\n * @ignore\n */\n shouldFormValueUpdate(): boolean {\n return true;\n }\n\n /**\n * Save a reference to the validation complete resolver\n * @ignore\n */\n #validationCompleteResolver?: (value: void | PromiseLike<void>) => void;\n\n /**\n * When true validation will be pending\n * @ignore\n */\n #isValidationPending = false;\n\n /**\n * @ignore\n */\n #validationComplete = Promise.resolve();\n\n /**\n * A promise that will resolve when all pending validations are complete\n * @ignore\n */\n get validationComplete(): Promise<void> {\n return new Promise((resolve) => resolve(this.#validationComplete));\n }\n\n /** DECLARED INSTANCE METHODS AND PROPERTIES*/\n\n /**\n * Resets a form control to its initial state\n * @ignore\n */\n declare resetFormControl: () => void;\n\n /**\n * This method is used to override the controls' validity message\n * for a given Validator key. This has the highest level of priority when\n * setting a validationMessage, so use this method wisely.\n *\n * The returned value will be used as the validationMessage for the given key.\n * @param validationKey {string} - The key that has returned invalid\n * @ignore\n */\n declare validityCallback: (validationKey: string) => string | void;\n\n /**\n * Called when the control's validationMessage should be changed\n * @param message { string } - The new validation message\n * @ignore\n */\n declare validationMessageCallback: (message: string) => void;\n\n /**\n * A callback for when the controls' form value changes. The value\n * passed to this function should not be confused with the control's\n * value property, this is the value that will appear on the form.\n *\n * In cases where `checked` did not exist on the control's prototype\n * upon initialization, this value and the value property will be identical;\n * in cases where `checked` is present upon initialization, this will be\n * effectively `this.checked && this.value`.\n * @ignore\n */\n declare valueChangedCallback: (value: FormValue) => void;\n\n /**\n * The element that will receive focus when the control's validity\n * state is reported either by a form submission or via API\n *\n * We use declare since this is optional and we don't particularly\n * care how the consuming component implements this (as a field, member\n * or getter/setter)\n * @ignore\n */\n declare validationTarget: HTMLElement | null;\n\n /** PRIVATE LIFECYCLE METHODS */\n\n /**\n * Check to see if an error should be shown. This method will also\n * update the internals state object with the --show-error state\n * if necessary.\n * @private\n * @ignore\n */\n #shouldShowError(): boolean {\n if (this.hasAttribute('disabled')) {\n return false;\n }\n\n const showError =\n this.#forceError ||\n (this.#touched && !this.validity.valid && !this.#focused);\n\n if (showError && this.internals.states) {\n this.internals.states.add('--show-error');\n this.internals.states.add('--invalid');\n } else if (this.internals.states) {\n this.internals.states.delete('--show-error');\n this.internals.states.delete('--invalid');\n }\n\n return showError;\n }\n\n #runValidators(value: FormValue): void {\n const proto = this.constructor as typeof FormControl;\n const validity: CustomValidityState = {};\n const validators = proto.validators;\n const asyncValidators: Promise<boolean | void>[] = [];\n const hasAsyncValidators = validators.some(\n (validator) => validator.isValid instanceof Promise\n );\n\n if (!this.#isValidationPending) {\n this.#validationComplete = new Promise((resolve) => {\n this.#validationCompleteResolver = resolve;\n });\n this.#isValidationPending = true;\n }\n\n /**\n * If an abort controller exists from a previous validation step\n * notify still-running async validators that we are requesting they\n * discontinue any work.\n */\n if (this.#abortController) {\n this.#abortController.abort();\n }\n\n /**\n * Create a new abort controller and replace the instance reference\n * so we can clean it up for next time\n */\n const abortController = new AbortController();\n this.#abortController = abortController;\n let validationMessage: string | undefined = undefined;\n\n /** Track to see if any validity key has changed */\n let hasChange = false;\n\n if (!validators.length) {\n return;\n }\n\n validators.forEach((validator) => {\n const key = validator.key || 'customError';\n const isValid = validator.isValid(this, value, abortController.signal);\n const isAsyncValidator = isValid instanceof Promise;\n\n if (isAsyncValidator) {\n asyncValidators.push(isValid);\n\n isValid.then((isValidatorValid) => {\n if (isValidatorValid === undefined || isValidatorValid === null) {\n return;\n }\n /** Invert the validity state to correspond to the ValidityState API */\n validity[key] = !isValidatorValid;\n\n validationMessage = this.#getValidatorMessageForValue(\n validator,\n value\n );\n this.#setValidityWithOptionalTarget(validity, validationMessage);\n });\n } else {\n /** Invert the validity state to correspond to the ValidityState API */\n validity[key] = !isValid;\n\n if (this.validity[key] !== !isValid) {\n hasChange = true;\n }\n\n // only update the validationMessage for the first invalid scenario\n // so that earlier invalid validators dont get their messages overwritten by later ones\n // in the validators array\n if (!isValid && !validationMessage) {\n validationMessage = this.#getValidatorMessageForValue(\n validator,\n value\n );\n }\n }\n });\n\n /** Once all the async validators have settled, resolve validationComplete */\n Promise.allSettled(asyncValidators).then(() => {\n /** Don't resolve validations if the signal is aborted */\n if (!abortController?.signal.aborted) {\n this.#isValidationPending = false;\n this.#validationCompleteResolver?.();\n }\n });\n\n /**\n * If async validators are present:\n * Only run updates when a sync validator has a change. This is to prevent\n * situations where running sync validators can override async validators\n * that are still in progress\n *\n * If async validators are not present, always update validity\n */\n if (hasChange || !hasAsyncValidators) {\n this.#setValidityWithOptionalTarget(validity, validationMessage);\n }\n }\n\n /**\n * If the validationTarget is not set, the user can decide how they would\n * prefer to handle focus when the field is validated.\n * @ignore\n */\n #setValidityWithOptionalTarget(\n validity: Partial<ValidityState>,\n validationMessage: string | undefined\n ): void {\n if (this.validationTarget) {\n this.internals.setValidity(\n validity,\n validationMessage,\n this.validationTarget\n );\n this.#awaitingValidationTarget = false;\n } else {\n this.internals.setValidity(validity, validationMessage);\n\n if (this.internals.validity.valid) {\n return;\n }\n\n /**\n * Sets mark the component as awaiting a validation target\n * if the element dispatches an invalid event, the #onInvalid listener\n * will check to see if the validation target has been set since this call\n * has run. This useful in cases like Lit's use of the query\n * decorator for setting the validationTarget or any scenario\n * where the validationTarget isn't available upon construction\n */\n this.#awaitingValidationTarget = true;\n }\n }\n\n /**\n * Process the validator message attribute\n * @ignore\n */\n #getValidatorMessageForValue(\n validator: Validator,\n value: FormValue\n ): string {\n /** If the validity callback exists and returns, use that as the result */\n\n if (this.validityCallback) {\n const message = this.validityCallback(validator.key || 'customError');\n\n if (message) {\n return message;\n }\n }\n\n if (validator.message instanceof Function) {\n return (validator.message as validationMessageCallback)(this, value);\n } else {\n return validator.message as string;\n }\n }\n\n /**\n * Reset control state when the form is reset\n */\n formResetCallback() {\n this.#touched = false;\n this.#forceError = false;\n this.#shouldShowError();\n this.resetFormControl?.();\n const showError = this.#shouldShowError();\n this.validationMessageCallback?.(showError ? this.validationMessage : '');\n this.#dispatchInvalidEvent(showError);\n }\n\n #dispatchInvalidEvent(showError: boolean) {\n const event = showError ? 'mid-invalid-show' : 'mid-invalid-hide';\n this.dispatchEvent(\n new CustomEvent(event, {\n bubbles: true,\n composed: true,\n detail: {\n validity: this.validity,\n },\n })\n );\n }\n }\n\n return FormControl as unknown as Constructor<FormControlInterface> & TBase;\n}\n"],"names":[],"mappings":"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"}
@@ -17,6 +17,7 @@ export interface FormControlInterface {
17
17
  validityCallback(validationKey: string): string | void;
18
18
  validationMessageCallback(message: string): void;
19
19
  setValue(value: FormValue): void;
20
+ syncFormValue(value: FormValue): void;
20
21
  forceError(message?: string): void;
21
22
  shouldFormValueUpdate?(): boolean;
22
23
  }
@@ -1 +1 @@
1
- {"version":3,"file":"form-control.d.ts","sourceRoot":"","sources":["../../src/types/form-control.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,4BAA4B,CAAC;AACpE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAE9C,iDAAiD;AACjD,MAAM,WAAW,oBAAoB;IACnC,gBAAgB,CAAC,EAAE,WAAW,GAAG,IAAI,CAAC;IACtC,QAAQ,CAAC,IAAI,EAAE,eAAe,CAAC;IAC/B,QAAQ,CAAC,SAAS,EAAE,gBAAgB,GAAG,iBAAiB,CAAC;IACzD,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;IAC5B,QAAQ,CAAC,iBAAiB,EAAE,MAAM,CAAC;IACnC,QAAQ,CAAC,QAAQ,EAAE,aAAa,CAAC;IACjC,QAAQ,CAAC,kBAAkB,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3C,iBAAiB,IAAI,IAAI,CAAC;IAC1B,aAAa,IAAI,OAAO,CAAC;IACzB,iBAAiB,IAAI,IAAI,CAAC;IAC1B,gBAAgB,CAAC,IAAI,IAAI,CAAC;IAE1B,oBAAoB,CAAC,CAAC,KAAK,EAAE,SAAS,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC9D,gBAAgB,CAAC,aAAa,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC;IACvD,yBAAyB,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACjD,QAAQ,CAAC,KAAK,EAAE,SAAS,GAAG,IAAI,CAAC;IACjC,UAAU,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACnC,qBAAqB,CAAC,IAAI,OAAO,CAAC;CACnC"}
1
+ {"version":3,"file":"form-control.d.ts","sourceRoot":"","sources":["../../src/types/form-control.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,4BAA4B,CAAC;AACpE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAE9C,iDAAiD;AACjD,MAAM,WAAW,oBAAoB;IACnC,gBAAgB,CAAC,EAAE,WAAW,GAAG,IAAI,CAAC;IACtC,QAAQ,CAAC,IAAI,EAAE,eAAe,CAAC;IAC/B,QAAQ,CAAC,SAAS,EAAE,gBAAgB,GAAG,iBAAiB,CAAC;IACzD,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;IAC5B,QAAQ,CAAC,iBAAiB,EAAE,MAAM,CAAC;IACnC,QAAQ,CAAC,QAAQ,EAAE,aAAa,CAAC;IACjC,QAAQ,CAAC,kBAAkB,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3C,iBAAiB,IAAI,IAAI,CAAC;IAC1B,aAAa,IAAI,OAAO,CAAC;IACzB,iBAAiB,IAAI,IAAI,CAAC;IAC1B,gBAAgB,CAAC,IAAI,IAAI,CAAC;IAE1B,oBAAoB,CAAC,CAAC,KAAK,EAAE,SAAS,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC9D,gBAAgB,CAAC,aAAa,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC;IACvD,yBAAyB,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACjD,QAAQ,CAAC,KAAK,EAAE,SAAS,GAAG,IAAI,CAAC;IACjC,aAAa,CAAC,KAAK,EAAE,SAAS,GAAG,IAAI,CAAC;IACtC,UAAU,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACnC,qBAAqB,CAAC,IAAI,OAAO,CAAC;CACnC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@felleslosninger/minid-elements",
3
- "version": "0.1.5",
3
+ "version": "0.1.7",
4
4
  "description": "MinID Web Components",
5
5
  "author": "Digitaliseringsdirektoratet",
6
6
  "license": "MIT",