@felleslosninger/minid-elements 0.0.110 → 0.0.111

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (33) hide show
  1. package/dist/components/checkbox.component.d.ts +0 -14
  2. package/dist/components/checkbox.component.d.ts.map +1 -1
  3. package/dist/components/checkbox.js +1 -21
  4. package/dist/components/checkbox.js.map +1 -1
  5. package/dist/components/code-input-old.component.d.ts +55 -0
  6. package/dist/components/code-input-old.component.d.ts.map +1 -0
  7. package/dist/components/code-input-old.js +313 -0
  8. package/dist/components/code-input-old.js.map +1 -0
  9. package/dist/components/code-input.component.d.ts +59 -33
  10. package/dist/components/code-input.component.d.ts.map +1 -1
  11. package/dist/components/code-input.js +244 -230
  12. package/dist/components/code-input.js.map +1 -1
  13. package/dist/components/dropdown.js +1 -1
  14. package/dist/components/dropdown.js.map +1 -1
  15. package/dist/components/menu.component.d.ts.map +1 -1
  16. package/dist/components/menu.js +1 -3
  17. package/dist/components/menu.js.map +1 -1
  18. package/dist/components/textfield.component.d.ts +0 -1
  19. package/dist/components/textfield.component.d.ts.map +1 -1
  20. package/dist/components/textfield.js +0 -3
  21. package/dist/components/textfield.js.map +1 -1
  22. package/dist/designsystemet-tailwind.css +43 -13
  23. package/dist/index.d.ts +1 -0
  24. package/dist/index.d.ts.map +1 -1
  25. package/dist/index.js +2 -0
  26. package/dist/index.js.map +1 -1
  27. package/dist/mixins/validators.js +2 -2
  28. package/dist/mixins/validators.js.map +1 -1
  29. package/package.json +2 -1
  30. package/dist/components/code-input-2.component.d.ts +0 -15
  31. package/dist/components/code-input-2.component.d.ts.map +0 -1
  32. package/dist/components/code-input-2.js +0 -82
  33. package/dist/components/code-input-2.js.map +0 -1
@@ -1,14 +1,19 @@
1
1
  import { css, LitElement, html } from "lit";
2
- import { property, state, customElement } from "lit/decorators.js";
3
- import { createRef, ref } from "lit/directives/ref.js";
2
+ import { queryAll, property, state, customElement } from "lit/decorators.js";
4
3
  import { classMap } from "lit/directives/class-map.js";
5
4
  import { live } from "lit/directives/live.js";
6
- import { webOtpApiInit, webOtpApiClose } from "../utilities/web-otp-api.js";
5
+ import { watch } from "../internal/watch.js";
6
+ import { FormControlMixin } from "../mixins/form-control.mixin.js";
7
7
  import { styled } from "../mixins/tailwind.mixin.js";
8
- import { ConstraintsValidationMixin } from "../mixins/form-controller.mixin.js";
8
+ import { HasSlotController } from "../internal/slot.js";
9
9
  import "./icon/icon.js";
10
+ import { requiredValidator, maxLengthValidator, minLengthValidator, patternValidator } from "../mixins/validators.js";
11
+ import { webOtpApiInit } from "../utilities/web-otp-api.js";
10
12
  var __defProp = Object.defineProperty;
11
13
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
14
+ var __typeError = (msg) => {
15
+ throw TypeError(msg);
16
+ };
12
17
  var __decorateClass = (decorators, target, key, kind) => {
13
18
  var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
14
19
  for (var i = decorators.length - 1, decorator; i >= 0; i--)
@@ -17,293 +22,302 @@ var __decorateClass = (decorators, target, key, kind) => {
17
22
  if (kind && result) __defProp(target, key, result);
18
23
  return result;
19
24
  };
25
+ var __accessCheck = (obj, member, msg) => member.has(obj) || __typeError("Cannot " + msg);
26
+ var __privateGet = (obj, member, getter) => (__accessCheck(obj, member, "read from private field"), member.get(obj));
27
+ var __privateAdd = (obj, member, value) => member.has(obj) ? __typeError("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
28
+ var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "write to private field"), member.set(obj, value), value);
29
+ var _skipValueVisualUpdate;
30
+ let nextUniqueId = 0;
20
31
  const styles = [
21
32
  css`
22
33
  :host {
23
- display: flex;
24
- flex-direction: column;
25
- container: otc / inline-size;
26
- text-align: left;
27
- --otc-background: white;
28
- --otc-error-background: white;
29
- --otc-length: 5;
30
- --otc-width: 11.45cqw;
31
- }
32
-
33
- input {
34
- all: unset;
35
- background: var(--otc-background);
36
- caret-color: transparent;
37
- clip-path: inset(0% 1ch 0% 0%);
38
- font-family: ui-monospace, monospace;
39
- font-size: var(--otc-width);
40
- inline-size: calc(var(--otc-length) * 3ch);
41
- letter-spacing: 2ch;
42
- padding-block: 0.25ch;
43
- padding-inline-start: calc(0.5ch * 1.5);
44
- flex-grow: 1;
45
-
46
- &:focus-visible:focus {
47
- outline-style: none;
48
- }
49
-
50
- &:-webkit-autofill {
51
- animation-name: onAutoFillStart;
52
- }
53
- }
54
-
55
- @keyframes onAutoFillStart {
56
- from {
57
- }
58
- to {
59
- }
34
+ display: block;
60
35
  }
61
36
 
62
- input.error {
63
- background: var(--otc-error-background);
37
+ /* Hide increment/decrement buttons on inputs */
38
+ input[type='number']::-webkit-outer-spin-button,
39
+ input[type='number']::-webkit-inner-spin-button,
40
+ input[type='number'] {
41
+ -webkit-appearance: none;
42
+ margin: 0;
43
+ -moz-appearance: textfield !important;
64
44
  }
65
45
  `
66
46
  ];
67
- let nextUniqueId = 0;
68
- let MinidCodeInput = class extends ConstraintsValidationMixin(
47
+ let MinidCodeInput = class extends FormControlMixin(
69
48
  styled(LitElement, styles)
70
49
  ) {
71
50
  constructor() {
72
- super();
73
- this.caretColor = "var(--color-neutral-surface-tinted)";
74
- this.caretHighlightColor = "var(--color-accent-surface-active)";
75
- this.inputId = `mid-code-input-${nextUniqueId++}`;
76
- this.inputRef = createRef();
51
+ super(...arguments);
52
+ this.hasSlotControler = new HasSlotController(this, "label");
53
+ __privateAdd(this, _skipValueVisualUpdate, false);
54
+ this.labelId = `mid-code-input-label-${nextUniqueId++}`;
77
55
  this.value = "";
78
56
  this.label = "";
79
- this.hidelabel = false;
80
- this.required = false;
57
+ this.type = "text";
58
+ this.inputmode = "text";
59
+ this.size = "sm";
60
+ this.length = 5;
81
61
  this.disabled = false;
82
- this.inputmode = "numeric";
62
+ this.autofocus = false;
83
63
  this.invalidmessage = "";
84
- this.length = 5;
85
- this.fontsize = "11.45cqw";
86
- this.renderError = false;
87
- this.localErrorNode = null;
88
- this.handleFocusIn = () => {
89
- const currentChar = this.value.length + 1;
90
- this.inputRef.value?.style.setProperty(
91
- `--char-${currentChar}-color`,
92
- this.caretHighlightColor
93
- );
94
- };
95
- this.handleChange = (e) => {
96
- this.value = e.target.value;
97
- this.dispatchEvent(
98
- new Event("input", {
99
- bubbles: true,
100
- cancelable: true,
101
- composed: true
102
- })
103
- );
104
- };
105
- this.addEventListener("invalid", (e) => {
106
- this.renderError = true;
107
- if (this.localErrorNode) {
108
- e.preventDefault();
109
- this.localErrorNode.textContent = this.validationMessage || "Error";
110
- }
111
- });
64
+ this.hidelabel = false;
65
+ this.required = false;
66
+ this.internalValues = Array(this.length).fill("");
112
67
  }
113
- connectedCallback() {
114
- super.connectedCallback();
115
- this._whenSettled();
68
+ static get formControlValidators() {
69
+ return [
70
+ requiredValidator,
71
+ maxLengthValidator,
72
+ minLengthValidator,
73
+ patternValidator
74
+ ];
116
75
  }
117
- async _whenSettled() {
118
- await this.updateComplete;
119
- this.inputRef.value?.addEventListener(
120
- "animationstart",
121
- this.autoFillEventHandler.bind(this)
122
- );
123
- this.calcInputStyle();
76
+ get validationTarget() {
77
+ return this.inputElements?.length ? this.inputElements[0] : null;
124
78
  }
125
- disconnectedCallback() {
126
- webOtpApiClose();
127
- this.inputRef.value?.removeEventListener(
128
- "animationstart",
129
- this.autoFillEventHandler.bind(this)
130
- );
79
+ firstUpdated(_changedProperties) {
80
+ webOtpApiInit(this.renderRoot);
131
81
  }
132
- calcInputStyle() {
133
- const inputStyle = this.inputRef.value?.style;
134
- inputStyle?.setProperty("--otc-length", this.length.toString());
135
- let gradientStyle = "linear-gradient(90deg, ";
136
- for (let i = 0; i < this.length; i++) {
137
- gradientStyle += `var(--char-${i + 1}-color) ${i ? i * 3 + "ch" : "0"} ${i * 3 + 2.5}ch, transparent ${i * 3 + 2.5}ch ${i * 3 + 3}ch${i + 1 < this.length ? ", " : ""}`;
138
- }
139
- gradientStyle += `) no-repeat 0 0 / ${this.length * 3}ch 100%`;
140
- inputStyle?.setProperty("--otc-background", gradientStyle);
141
- const gradientErrorStyle = `linear-gradient(90deg, var(--color-danger-surface-active) calc(${this.length}ch / 2), transparent 0) 0 0 / 3ch 100%`;
142
- inputStyle?.setProperty("--otc-error-background", gradientErrorStyle);
143
- inputStyle?.setProperty("--otc-width", this.fontsize);
82
+ handleFocus(index) {
83
+ return () => {
84
+ this.inputElements[index].setAttribute("placeholder", "");
85
+ };
144
86
  }
145
- autoFillEventHandler(e) {
146
- e.animationName === "onAutoFillStart" && (this.value = this.inputRef.value.value);
87
+ handleBlur(index) {
88
+ return () => {
89
+ this.inputElements[index].setAttribute("placeholder", "○");
90
+ };
147
91
  }
148
- get selectionEnd() {
149
- return this.inputRef.value?.selectionEnd;
92
+ handleKeydown(index) {
93
+ return (event) => {
94
+ this.internalValues[index] = event.target.value;
95
+ this.value = this.internalValues.join("");
96
+ const input = event.target;
97
+ const hasModifier = event.metaKey || event.ctrlKey || event.shiftKey || event.altKey;
98
+ if (event.key === "Backspace") {
99
+ if (input.value === "" && index > 0) {
100
+ this.inputElements[index - 1].focus();
101
+ this.internalValues[index - 1] = "";
102
+ this.updateValueAndEmit(true);
103
+ } else if (input.value !== "") {
104
+ input.value = "";
105
+ this.internalValues[index] = "";
106
+ this.updateValueAndEmit(true);
107
+ }
108
+ } else if (event.key === "ArrowLeft") {
109
+ if (index > 0) {
110
+ this.inputElements[index - 1].focus();
111
+ }
112
+ } else if (event.key === "ArrowRight") {
113
+ if (index < this.length - 1) {
114
+ this.inputElements[index + 1].focus();
115
+ }
116
+ } else if (event.key === "Delete") {
117
+ input.value = "";
118
+ this.internalValues[index] = "";
119
+ this.updateValueAndEmit(true);
120
+ } else if (event.key === "Enter" && !hasModifier) {
121
+ setTimeout(() => {
122
+ if (!event.defaultPrevented && !event.isComposing) {
123
+ this.form?.requestSubmit();
124
+ }
125
+ });
126
+ }
127
+ };
150
128
  }
151
- handleFocusOut() {
152
- for (let i = 0; i < this.length; i++) {
153
- this.inputRef.value?.style.setProperty(
154
- `--char-${i + 1}-color`,
155
- this.caretColor
129
+ handleInput(index) {
130
+ return (event) => {
131
+ const input = event.target;
132
+ let inputValue = input.value;
133
+ if (inputValue.length > 1) {
134
+ inputValue = inputValue.charAt(0);
135
+ input.value = inputValue;
136
+ }
137
+ this.internalValues[index] = inputValue;
138
+ this.updateValueAndEmit(true);
139
+ if (inputValue !== "" && index < this.length - 1) {
140
+ this.inputElements[index + 1].focus();
141
+ }
142
+ this.dispatchEvent(
143
+ new Event("mid-input", { bubbles: true, composed: true })
156
144
  );
157
- }
158
- }
159
- handleClick() {
160
- this.value = this.value.substring(0, this.selectionEnd || 0);
161
- this.renderError = false;
162
- this.setCustomValidity("");
163
- if (this.localErrorNode) {
164
- this.localErrorNode.textContent = "";
165
- }
166
- this.setFormValue(this.value);
167
- this.setValidity({});
145
+ };
168
146
  }
169
- handleKeydown(event) {
170
- const hasModifier = event.metaKey || event.ctrlKey || event.shiftKey || event.altKey;
171
- if (event.key === "Enter" && !hasModifier) {
172
- setTimeout(() => {
173
- if (!event.defaultPrevented && !event.isComposing) {
174
- this.internals.form?.requestSubmit();
147
+ handlePaste(index) {
148
+ return (event) => {
149
+ event.preventDefault();
150
+ const pasteData = event.clipboardData?.getData("text").trim();
151
+ if (pasteData) {
152
+ const pasteChars = pasteData.split("");
153
+ for (let i = 0; i < this.length; i++) {
154
+ if (index + i < this.length && pasteChars[i]) {
155
+ this.internalValues[index + i] = pasteChars[i];
156
+ }
175
157
  }
176
- });
177
- }
158
+ this.updateValueAndEmit();
159
+ const lastFilledIndex = Math.min(
160
+ index + pasteChars.length - 1,
161
+ this.length - 1
162
+ );
163
+ this.inputElements[lastFilledIndex]?.focus();
164
+ }
165
+ };
178
166
  }
179
- handleSlotchange(e) {
180
- const slot = e.target;
181
- const childNodes = slot.assignedNodes({ flatten: true });
182
- this.localErrorNode = this.findErrorMessageNode(childNodes);
183
- if (this.localErrorNode && this.invalidmessage) {
184
- this.renderError = true;
185
- this.localErrorNode.textContent = this.invalidmessage;
186
- this.setValidity({});
167
+ updateValueAndEmit(skipValueUpdate = false) {
168
+ __privateSet(this, _skipValueVisualUpdate, skipValueUpdate);
169
+ this.value = this.internalValues.join("");
170
+ this.dispatchEvent(
171
+ new Event("mid-change", {
172
+ bubbles: true,
173
+ composed: true
174
+ })
175
+ );
176
+ if (this.value.length === this.length && this.internalValues.every((digit) => digit !== "")) {
177
+ this.dispatchEvent(
178
+ new Event("mid-complete", {
179
+ bubbles: true,
180
+ composed: true
181
+ })
182
+ );
187
183
  }
188
184
  }
189
- findErrorMessageNode(nodes) {
190
- for (const node of nodes) {
191
- if (node instanceof Element && node.classList.contains("error-message")) {
192
- return node;
193
- }
194
- const childNodesArray = Array.from(node.childNodes);
195
- const foundNode = this.findErrorMessageNode(childNodesArray);
196
- if (foundNode) {
197
- return foundNode;
198
- }
199
- }
200
- return null;
185
+ valueVisualUpdate(value) {
186
+ const chars = value.split("");
187
+ this.internalValues.forEach((_, index) => {
188
+ this.internalValues[index] = chars[index];
189
+ });
201
190
  }
202
- firstUpdated(_changedProperties) {
203
- this.focus();
204
- webOtpApiInit(this.renderRoot);
191
+ handleLengthChange() {
192
+ this.internalValues = Array(this.length).fill("");
193
+ this.valueVisualUpdate(this.value);
205
194
  }
206
- updated(_changedProperties) {
207
- for (let i = 0; i < this.length; i++) {
208
- const color = i === this.value.length ? this.caretHighlightColor : this.caretColor;
209
- this.inputRef.value?.style.setProperty(`--char-${i + 1}-color`, color);
210
- }
211
- if (_changedProperties.has("_forcedErrorMessage")) {
212
- this.setCustomValidity(this.invalidmessage);
195
+ handleValueChange() {
196
+ this.setValue(this.value);
197
+ if (__privateGet(this, _skipValueVisualUpdate)) {
198
+ __privateSet(this, _skipValueVisualUpdate, false);
199
+ return;
213
200
  }
214
- this.setFormValue(this.value);
215
- this.setValidity(
216
- this.inputRef.value.validity,
217
- this.inputRef.value.validationMessage,
218
- this.inputRef.value
219
- );
220
- }
221
- focus(options) {
222
- const notMobile = !window.matchMedia("only screen and (max-width: 768px)").matches;
223
- notMobile && this.inputRef.value?.focus(options);
224
- }
225
- setCustomValidity(error) {
226
- this.inputRef.value?.setCustomValidity(error);
201
+ this.valueVisualUpdate(this.value);
227
202
  }
228
203
  render() {
204
+ const hasLabelSlot = this.hasSlotControler.test("label");
205
+ const hasLabel = !!this.label || !!hasLabelSlot;
229
206
  return html`
230
207
  <label
208
+ id="${this.labelId}"
231
209
  class="${classMap({
232
- "sr-only": this.hidelabel,
233
- "mb-2": !!this.label
234
- })} font-medium"
235
- for="${this.inputId}"
236
- >${this.label}
210
+ "sr-only": this.hidelabel || !hasLabel,
211
+ "opacity-disabled": this.disabled
212
+ })} mb-2 block items-center gap-1 font-medium"
213
+ >
214
+ <slot name="label"> ${this.label} </slot>
237
215
  </label>
238
- <input
239
- ${ref(this.inputRef)}
240
- id=${this.inputId}
241
- .value="${live(this.value)}"
216
+ <div
217
+ part="input-container"
242
218
  class="${classMap({
243
- error: this.invalidmessage
244
- })}"
245
- autocomplete="${"one-time-code"}"
246
- inputmode="${this.inputmode}"
247
- @input="${this.handleChange}"
248
- @change="${this.handleChange}"
249
- @focusout="${this.handleFocusOut}"
250
- @focusin="${this.handleFocusIn}"
251
- @keydown="${this.handleKeydown}"
252
- @click="${this.handleClick}"
253
- minlength="${this.length}"
254
- maxlength="${this.length}"
255
- ?disabled="${this.disabled}"
256
- ?required="${this.required}"
257
- />
258
- <slot name="error-message" @slotchange=${this.handleSlotchange}>
259
- <div
260
- class="text-danger-subtle mt-2 flex gap-1"
261
- aria-live="polite"
262
- ?hidden=${!this.invalidmessage}
263
- >
264
- <mid-icon
265
- name="xmark-octagon-fill"
266
- class="mt-1 min-h-5 min-w-5"
267
- ></mid-icon>
268
- ${this.invalidmessage}
269
- </div>
270
- </slot>
219
+ "text-heading-sm": this.size === "sm",
220
+ "text-heading-md": this.size === "md",
221
+ "text-heading-lg": this.size === "lg"
222
+ })} inline-flex gap-2"
223
+ >
224
+ ${this.internalValues.map((value, index) => {
225
+ return html`
226
+ <input
227
+ .value="${live(value || "")}"
228
+ id="mid-code-input-${index}"
229
+ class="${classMap({
230
+ border: !this.invalidmessage,
231
+ "border-2": this.invalidmessage,
232
+ "border-neutral": !this.invalidmessage,
233
+ "border-danger": this.invalidmessage,
234
+ "bg-neutral": !this.invalidmessage,
235
+ "bg-danger-tinted": this.invalidmessage,
236
+ "placeholder:text-neutral-surface-active": !this.invalidmessage,
237
+ "placeholder:text-danger-surface-active": this.invalidmessage
238
+ })} focus:focus-ring-sm disabled:opacity-disabled size-9 rounded-md text-center font-mono disabled:cursor-not-allowed"
239
+ type="${this.type}"
240
+ placeholder="○"
241
+ aria-labelledby="${this.labelId}"
242
+ size="1"
243
+ ?autofocus="${!index && this.autofocus}"
244
+ ?disabled="${this.disabled}"
245
+ inputmode="${this.inputmode}"
246
+ @keydown="${this.handleKeydown(index)}"
247
+ @input="${this.handleInput(index)}"
248
+ @paste="${this.handlePaste(index)}"
249
+ @focus="${this.handleFocus(index)}"
250
+ @blur="${this.handleBlur(index)}"
251
+ />
252
+ `;
253
+ })}
254
+ </div>
255
+ <div
256
+ class="text-danger-subtle mt-2 flex gap-1"
257
+ aria-live="polite"
258
+ ?hidden=${!this.invalidmessage}
259
+ >
260
+ <mid-icon
261
+ name="xmark-octagon-fill"
262
+ class="mt-1 min-h-5 min-w-5"
263
+ ></mid-icon>
264
+ ${this.invalidmessage}
265
+ </div>
271
266
  `;
272
267
  }
273
268
  };
269
+ _skipValueVisualUpdate = /* @__PURE__ */ new WeakMap();
270
+ __decorateClass([
271
+ queryAll("input")
272
+ ], MinidCodeInput.prototype, "inputElements", 2);
274
273
  __decorateClass([
275
- property({ type: String, reflect: true })
274
+ property({ reflect: true })
276
275
  ], MinidCodeInput.prototype, "value", 2);
277
276
  __decorateClass([
278
277
  property()
279
278
  ], MinidCodeInput.prototype, "label", 2);
280
279
  __decorateClass([
281
- property({ type: Boolean })
282
- ], MinidCodeInput.prototype, "hidelabel", 2);
280
+ property()
281
+ ], MinidCodeInput.prototype, "type", 2);
283
282
  __decorateClass([
284
- property({ type: Boolean })
285
- ], MinidCodeInput.prototype, "required", 2);
283
+ property()
284
+ ], MinidCodeInput.prototype, "inputmode", 2);
285
+ __decorateClass([
286
+ property()
287
+ ], MinidCodeInput.prototype, "size", 2);
288
+ __decorateClass([
289
+ property({ type: Number })
290
+ ], MinidCodeInput.prototype, "length", 2);
286
291
  __decorateClass([
287
292
  property({ type: Boolean })
288
293
  ], MinidCodeInput.prototype, "disabled", 2);
289
294
  __decorateClass([
290
- property({ type: String })
291
- ], MinidCodeInput.prototype, "inputmode", 2);
295
+ property({ type: Boolean })
296
+ ], MinidCodeInput.prototype, "autofocus", 2);
297
+ __decorateClass([
298
+ property({ type: Number })
299
+ ], MinidCodeInput.prototype, "minlength", 2);
292
300
  __decorateClass([
293
301
  property()
294
302
  ], MinidCodeInput.prototype, "invalidmessage", 2);
295
303
  __decorateClass([
296
- property({ type: Number })
297
- ], MinidCodeInput.prototype, "length", 2);
304
+ property({ type: Boolean })
305
+ ], MinidCodeInput.prototype, "hidelabel", 2);
306
+ __decorateClass([
307
+ property({ type: Boolean })
308
+ ], MinidCodeInput.prototype, "required", 2);
298
309
  __decorateClass([
299
310
  property()
300
- ], MinidCodeInput.prototype, "fontsize", 2);
311
+ ], MinidCodeInput.prototype, "pattern", 2);
301
312
  __decorateClass([
302
313
  state()
303
- ], MinidCodeInput.prototype, "renderError", 2);
314
+ ], MinidCodeInput.prototype, "internalValues", 2);
304
315
  __decorateClass([
305
- state()
306
- ], MinidCodeInput.prototype, "localErrorNode", 2);
316
+ watch("length")
317
+ ], MinidCodeInput.prototype, "handleLengthChange", 1);
318
+ __decorateClass([
319
+ watch("value")
320
+ ], MinidCodeInput.prototype, "handleValueChange", 1);
307
321
  MinidCodeInput = __decorateClass([
308
322
  customElement("mid-code-input")
309
323
  ], MinidCodeInput);
@@ -1 +1 @@
1
- {"version":3,"file":"code-input.js","sources":["../../src/components/code-input.component.ts"],"sourcesContent":["import { css, html, LitElement, type PropertyValues } from 'lit';\nimport { customElement, property, state } from 'lit/decorators.js';\nimport { createRef, ref, type Ref } from 'lit/directives/ref.js';\nimport { classMap } from 'lit/directives/class-map.js';\nimport { live } from 'lit/directives/live.js';\nimport { webOtpApiClose, webOtpApiInit } from '../utilities/web-otp-api';\nimport { styled } from '../mixins/tailwind.mixin';\nimport { ConstraintsValidationMixin } from '../mixins/form-controller.mixin';\nimport './icon/icon.component';\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'mid-code-input': MinidCodeInput;\n }\n}\n\nconst styles = [\n css`\n :host {\n display: flex;\n flex-direction: column;\n container: otc / inline-size;\n text-align: left;\n --otc-background: white;\n --otc-error-background: white;\n --otc-length: 5;\n --otc-width: 11.45cqw;\n }\n\n input {\n all: unset;\n background: var(--otc-background);\n caret-color: transparent;\n clip-path: inset(0% 1ch 0% 0%);\n font-family: ui-monospace, monospace;\n font-size: var(--otc-width);\n inline-size: calc(var(--otc-length) * 3ch);\n letter-spacing: 2ch;\n padding-block: 0.25ch;\n padding-inline-start: calc(0.5ch * 1.5);\n flex-grow: 1;\n\n &:focus-visible:focus {\n outline-style: none;\n }\n\n &:-webkit-autofill {\n animation-name: onAutoFillStart;\n }\n }\n\n @keyframes onAutoFillStart {\n from {\n }\n to {\n }\n }\n\n input.error {\n background: var(--otc-error-background);\n }\n `,\n];\n\nlet nextUniqueId = 0;\n\n@customElement('mid-code-input')\nexport class MinidCodeInput extends ConstraintsValidationMixin(\n styled(LitElement, styles)\n) {\n private readonly caretColor = 'var(--color-neutral-surface-tinted)';\n private readonly caretHighlightColor = 'var(--color-accent-surface-active)';\n private readonly inputId = `mid-code-input-${nextUniqueId++}`;\n\n public inputRef: Ref<HTMLInputElement> = createRef();\n\n @property({ type: String, reflect: true })\n value = '';\n\n @property()\n label = '';\n\n /**\n * Visually hides `label` (still available for screen readers)\n */\n @property({ type: Boolean })\n hidelabel = false;\n\n @property({ type: Boolean })\n required = false;\n\n @property({ type: Boolean })\n disabled = false;\n\n @property({ type: String })\n inputmode = 'numeric';\n\n /**\n * Display custom error message, and force invalid state\n */\n @property()\n invalidmessage = '';\n\n /**\n * The length of the code input\n */\n @property({ type: Number })\n length = 5;\n\n @property()\n fontsize = '11.45cqw';\n\n @state()\n renderError = false;\n\n @state()\n localErrorNode: Node | null = null;\n\n constructor() {\n super();\n this.addEventListener('invalid', (e) => {\n this.renderError = true;\n if (this.localErrorNode) {\n e.preventDefault();\n this.localErrorNode.textContent = this.validationMessage || 'Error';\n }\n });\n }\n\n override connectedCallback() {\n super.connectedCallback();\n this._whenSettled();\n }\n\n async _whenSettled() {\n await this.updateComplete;\n this.inputRef.value?.addEventListener(\n 'animationstart',\n this.autoFillEventHandler.bind(this)\n );\n this.calcInputStyle();\n }\n\n override disconnectedCallback() {\n webOtpApiClose();\n this.inputRef.value?.removeEventListener(\n 'animationstart',\n this.autoFillEventHandler.bind(this)\n );\n }\n\n private calcInputStyle() {\n const inputStyle = this.inputRef.value?.style;\n\n inputStyle?.setProperty('--otc-length', this.length.toString());\n\n let gradientStyle = 'linear-gradient(90deg, ';\n for (let i = 0; i < this.length; i++) {\n gradientStyle += `var(--char-${i + 1}-color) ${i ? i * 3 + 'ch' : '0'} ${i * 3 + 2.5}ch, transparent ${i * 3 + 2.5}ch ${i * 3 + 3}ch${i + 1 < this.length ? ', ' : ''}`;\n }\n gradientStyle += `) no-repeat 0 0 / ${this.length * 3}ch 100%`;\n inputStyle?.setProperty('--otc-background', gradientStyle);\n\n const gradientErrorStyle = `linear-gradient(90deg, var(--color-danger-surface-active) calc(${this.length}ch / 2), transparent 0) 0 0 / 3ch 100%`;\n inputStyle?.setProperty('--otc-error-background', gradientErrorStyle);\n\n inputStyle?.setProperty('--otc-width', this.fontsize);\n }\n\n private autoFillEventHandler(e: AnimationEvent) {\n e.animationName === 'onAutoFillStart' &&\n (this.value = (this.inputRef.value as HTMLInputElement).value);\n }\n\n get selectionEnd() {\n return this.inputRef.value?.selectionEnd;\n }\n\n private handleFocusOut() {\n for (let i = 0; i < this.length; i++) {\n this.inputRef.value?.style.setProperty(\n `--char-${i + 1}-color`,\n this.caretColor\n );\n }\n }\n\n private handleFocusIn = () => {\n const currentChar = this.value.length + 1;\n this.inputRef.value?.style.setProperty(\n `--char-${currentChar}-color`,\n this.caretHighlightColor\n );\n };\n\n private handleClick() {\n this.value = this.value.substring(0, this.selectionEnd || 0);\n\n this.renderError = false;\n this.setCustomValidity('');\n if (this.localErrorNode) {\n this.localErrorNode.textContent = '';\n }\n\n this.setFormValue(this.value);\n this.setValidity({} as ValidityState);\n }\n\n private handleKeydown(event: KeyboardEvent) {\n const hasModifier =\n event.metaKey || event.ctrlKey || event.shiftKey || event.altKey;\n\n // Pressing enter when focused on an input should submit the form like a native input, but we wait a tick before\n // submitting to allow users to cancel the keydown event if they need to\n if (event.key === 'Enter' && !hasModifier) {\n setTimeout(() => {\n //\n // When using an Input Method Editor (IME), pressing enter will cause the form to submit unexpectedly. One way\n // to check for this is to look at event.isComposing, which will be true when the IME is open.\n if (!event.defaultPrevented && !event.isComposing) {\n this.internals.form?.requestSubmit();\n }\n });\n }\n }\n\n private handleChange = (e: Event) => {\n this.value = (e.target as HTMLInputElement).value;\n this.dispatchEvent(\n new Event('input', {\n bubbles: true,\n cancelable: true,\n composed: true,\n })\n );\n };\n\n private handleSlotchange(e: Event) {\n const slot = e.target as HTMLSlotElement;\n const childNodes = slot.assignedNodes({ flatten: true });\n\n this.localErrorNode = this.findErrorMessageNode(childNodes);\n if (this.localErrorNode && this.invalidmessage) {\n // display if slot set, and error override in effect\n this.renderError = true;\n this.localErrorNode.textContent = this.invalidmessage;\n this.setValidity({} as ValidityState); // cancel built-in visual feedback\n }\n }\n\n private findErrorMessageNode(nodes: Node[]): Node | null {\n for (const node of nodes) {\n if (node instanceof Element && node.classList.contains('error-message')) {\n return node;\n }\n const childNodesArray = Array.from(node.childNodes);\n const foundNode = this.findErrorMessageNode(childNodesArray);\n if (foundNode) {\n return foundNode;\n }\n }\n return null;\n }\n\n firstUpdated(_changedProperties: PropertyValues) {\n this.focus();\n webOtpApiInit(this.renderRoot);\n }\n\n updated(_changedProperties: PropertyValues): void {\n for (let i = 0; i < this.length; i++) {\n const color =\n i === this.value.length ? this.caretHighlightColor : this.caretColor;\n this.inputRef.value?.style.setProperty(`--char-${i + 1}-color`, color);\n }\n\n if (_changedProperties.has('_forcedErrorMessage')) {\n this.setCustomValidity(this.invalidmessage);\n }\n\n this.setFormValue(this.value);\n this.setValidity(\n this.inputRef.value!.validity,\n this.inputRef.value!.validationMessage,\n this.inputRef!.value\n );\n }\n\n focus(options?: FocusOptions) {\n const notMobile = !window.matchMedia('only screen and (max-width: 768px)')\n .matches;\n notMobile && this.inputRef.value?.focus(options);\n }\n\n setCustomValidity(error: string) {\n this.inputRef!.value?.setCustomValidity(error);\n }\n\n override render() {\n return html`\n <label\n class=\"${classMap({\n 'sr-only': this.hidelabel,\n 'mb-2': !!this.label,\n })} font-medium\"\n for=\"${this.inputId}\"\n >${this.label}\n </label>\n <input\n ${ref(this.inputRef)}\n id=${this.inputId}\n .value=\"${live(this.value)}\"\n class=\"${classMap({\n error: this.invalidmessage,\n })}\"\n autocomplete=\"${'one-time-code' as any}\"\n inputmode=\"${this.inputmode}\"\n @input=\"${this.handleChange}\"\n @change=\"${this.handleChange}\"\n @focusout=\"${this.handleFocusOut}\"\n @focusin=\"${this.handleFocusIn}\"\n @keydown=\"${this.handleKeydown}\"\n @click=\"${this.handleClick}\"\n minlength=\"${this.length}\"\n maxlength=\"${this.length}\"\n ?disabled=\"${this.disabled}\"\n ?required=\"${this.required}\"\n />\n <slot name=\"error-message\" @slotchange=${this.handleSlotchange}>\n <div\n class=\"text-danger-subtle mt-2 flex gap-1\"\n aria-live=\"polite\"\n ?hidden=${!this.invalidmessage}\n >\n <mid-icon\n name=\"xmark-octagon-fill\"\n class=\"mt-1 min-h-5 min-w-5\"\n ></mid-icon>\n ${this.invalidmessage}\n </div>\n </slot>\n `;\n }\n}\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;AAgBA,MAAM,SAAS;AAAA,EACb;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA6CF;AAEA,IAAI,eAAe;AAGZ,IAAM,iBAAN,cAA6B;AAAA,EAClC,OAAO,YAAY,MAAM;AAC3B,EAAE;AAAA,EAiDA,cAAc;AACN,UAAA;AAjDR,SAAiB,aAAa;AAC9B,SAAiB,sBAAsB;AACtB,SAAA,UAAU,kBAAkB,cAAc;AAE3D,SAAO,WAAkC,UAAU;AAG3C,SAAA,QAAA;AAGA,SAAA,QAAA;AAMI,SAAA,YAAA;AAGD,SAAA,WAAA;AAGA,SAAA,WAAA;AAGC,SAAA,YAAA;AAMK,SAAA,iBAAA;AAMR,SAAA,SAAA;AAGE,SAAA,WAAA;AAGG,SAAA,cAAA;AAGgB,SAAA,iBAAA;AAuE9B,SAAQ,gBAAgB,MAAM;AACtB,YAAA,cAAc,KAAK,MAAM,SAAS;AACnC,WAAA,SAAS,OAAO,MAAM;AAAA,QACzB,UAAU,WAAW;AAAA,QACrB,KAAK;AAAA,MACP;AAAA,IACF;AAiCQ,SAAA,eAAe,CAAC,MAAa;AAC9B,WAAA,QAAS,EAAE,OAA4B;AACvC,WAAA;AAAA,QACH,IAAI,MAAM,SAAS;AAAA,UACjB,SAAS;AAAA,UACT,YAAY;AAAA,UACZ,UAAU;AAAA,QACX,CAAA;AAAA,MACH;AAAA,IACF;AAnHO,SAAA,iBAAiB,WAAW,CAAC,MAAM;AACtC,WAAK,cAAc;AACnB,UAAI,KAAK,gBAAgB;AACvB,UAAE,eAAe;AACZ,aAAA,eAAe,cAAc,KAAK,qBAAqB;AAAA,MAAA;AAAA,IAC9D,CACD;AAAA,EAAA;AAAA,EAGM,oBAAoB;AAC3B,UAAM,kBAAkB;AACxB,SAAK,aAAa;AAAA,EAAA;AAAA,EAGpB,MAAM,eAAe;AACnB,UAAM,KAAK;AACX,SAAK,SAAS,OAAO;AAAA,MACnB;AAAA,MACA,KAAK,qBAAqB,KAAK,IAAI;AAAA,IACrC;AACA,SAAK,eAAe;AAAA,EAAA;AAAA,EAGb,uBAAuB;AACf,mBAAA;AACf,SAAK,SAAS,OAAO;AAAA,MACnB;AAAA,MACA,KAAK,qBAAqB,KAAK,IAAI;AAAA,IACrC;AAAA,EAAA;AAAA,EAGM,iBAAiB;AACjB,UAAA,aAAa,KAAK,SAAS,OAAO;AAExC,gBAAY,YAAY,gBAAgB,KAAK,OAAO,UAAU;AAE9D,QAAI,gBAAgB;AACpB,aAAS,IAAI,GAAG,IAAI,KAAK,QAAQ,KAAK;AACnB,uBAAA,cAAc,IAAI,CAAC,WAAW,IAAI,IAAI,IAAI,OAAO,GAAG,IAAI,IAAI,IAAI,GAAG,mBAAmB,IAAI,IAAI,GAAG,MAAM,IAAI,IAAI,CAAC,KAAK,IAAI,IAAI,KAAK,SAAS,OAAO,EAAE;AAAA,IAAA;AAEtJ,qBAAA,qBAAqB,KAAK,SAAS,CAAC;AACzC,gBAAA,YAAY,oBAAoB,aAAa;AAEnD,UAAA,qBAAqB,kEAAkE,KAAK,MAAM;AAC5F,gBAAA,YAAY,0BAA0B,kBAAkB;AAExD,gBAAA,YAAY,eAAe,KAAK,QAAQ;AAAA,EAAA;AAAA,EAG9C,qBAAqB,GAAmB;AAC9C,MAAE,kBAAkB,sBACjB,KAAK,QAAS,KAAK,SAAS,MAA2B;AAAA,EAAA;AAAA,EAG5D,IAAI,eAAe;AACV,WAAA,KAAK,SAAS,OAAO;AAAA,EAAA;AAAA,EAGtB,iBAAiB;AACvB,aAAS,IAAI,GAAG,IAAI,KAAK,QAAQ,KAAK;AAC/B,WAAA,SAAS,OAAO,MAAM;AAAA,QACzB,UAAU,IAAI,CAAC;AAAA,QACf,KAAK;AAAA,MACP;AAAA,IAAA;AAAA,EACF;AAAA,EAWM,cAAc;AACpB,SAAK,QAAQ,KAAK,MAAM,UAAU,GAAG,KAAK,gBAAgB,CAAC;AAE3D,SAAK,cAAc;AACnB,SAAK,kBAAkB,EAAE;AACzB,QAAI,KAAK,gBAAgB;AACvB,WAAK,eAAe,cAAc;AAAA,IAAA;AAG/B,SAAA,aAAa,KAAK,KAAK;AACvB,SAAA,YAAY,EAAmB;AAAA,EAAA;AAAA,EAG9B,cAAc,OAAsB;AAC1C,UAAM,cACJ,MAAM,WAAW,MAAM,WAAW,MAAM,YAAY,MAAM;AAI5D,QAAI,MAAM,QAAQ,WAAW,CAAC,aAAa;AACzC,iBAAW,MAAM;AAIf,YAAI,CAAC,MAAM,oBAAoB,CAAC,MAAM,aAAa;AAC5C,eAAA,UAAU,MAAM,cAAc;AAAA,QAAA;AAAA,MACrC,CACD;AAAA,IAAA;AAAA,EACH;AAAA,EAcM,iBAAiB,GAAU;AACjC,UAAM,OAAO,EAAE;AACf,UAAM,aAAa,KAAK,cAAc,EAAE,SAAS,MAAM;AAElD,SAAA,iBAAiB,KAAK,qBAAqB,UAAU;AACtD,QAAA,KAAK,kBAAkB,KAAK,gBAAgB;AAE9C,WAAK,cAAc;AACd,WAAA,eAAe,cAAc,KAAK;AAClC,WAAA,YAAY,EAAmB;AAAA,IAAA;AAAA,EACtC;AAAA,EAGM,qBAAqB,OAA4B;AACvD,eAAW,QAAQ,OAAO;AACxB,UAAI,gBAAgB,WAAW,KAAK,UAAU,SAAS,eAAe,GAAG;AAChE,eAAA;AAAA,MAAA;AAET,YAAM,kBAAkB,MAAM,KAAK,KAAK,UAAU;AAC5C,YAAA,YAAY,KAAK,qBAAqB,eAAe;AAC3D,UAAI,WAAW;AACN,eAAA;AAAA,MAAA;AAAA,IACT;AAEK,WAAA;AAAA,EAAA;AAAA,EAGT,aAAa,oBAAoC;AAC/C,SAAK,MAAM;AACX,kBAAc,KAAK,UAAU;AAAA,EAAA;AAAA,EAG/B,QAAQ,oBAA0C;AAChD,aAAS,IAAI,GAAG,IAAI,KAAK,QAAQ,KAAK;AACpC,YAAM,QACJ,MAAM,KAAK,MAAM,SAAS,KAAK,sBAAsB,KAAK;AACvD,WAAA,SAAS,OAAO,MAAM,YAAY,UAAU,IAAI,CAAC,UAAU,KAAK;AAAA,IAAA;AAGnE,QAAA,mBAAmB,IAAI,qBAAqB,GAAG;AAC5C,WAAA,kBAAkB,KAAK,cAAc;AAAA,IAAA;AAGvC,SAAA,aAAa,KAAK,KAAK;AACvB,SAAA;AAAA,MACH,KAAK,SAAS,MAAO;AAAA,MACrB,KAAK,SAAS,MAAO;AAAA,MACrB,KAAK,SAAU;AAAA,IACjB;AAAA,EAAA;AAAA,EAGF,MAAM,SAAwB;AAC5B,UAAM,YAAY,CAAC,OAAO,WAAW,oCAAoC,EACtE;AACH,iBAAa,KAAK,SAAS,OAAO,MAAM,OAAO;AAAA,EAAA;AAAA,EAGjD,kBAAkB,OAAe;AAC1B,SAAA,SAAU,OAAO,kBAAkB,KAAK;AAAA,EAAA;AAAA,EAGtC,SAAS;AACT,WAAA;AAAA;AAAA,iBAEM,SAAS;AAAA,MAChB,WAAW,KAAK;AAAA,MAChB,QAAQ,CAAC,CAAC,KAAK;AAAA,IAAA,CAChB,CAAC;AAAA,eACK,KAAK,OAAO;AAAA,WAChB,KAAK,KAAK;AAAA;AAAA;AAAA,UAGX,IAAI,KAAK,QAAQ,CAAC;AAAA,aACf,KAAK,OAAO;AAAA,kBACP,KAAK,KAAK,KAAK,CAAC;AAAA,iBACjB,SAAS;AAAA,MAChB,OAAO,KAAK;AAAA,IAAA,CACb,CAAC;AAAA,wBACc,eAAsB;AAAA,qBACzB,KAAK,SAAS;AAAA,kBACjB,KAAK,YAAY;AAAA,mBAChB,KAAK,YAAY;AAAA,qBACf,KAAK,cAAc;AAAA,oBACpB,KAAK,aAAa;AAAA,oBAClB,KAAK,aAAa;AAAA,kBACpB,KAAK,WAAW;AAAA,qBACb,KAAK,MAAM;AAAA,qBACX,KAAK,MAAM;AAAA,qBACX,KAAK,QAAQ;AAAA,qBACb,KAAK,QAAQ;AAAA;AAAA,+CAEa,KAAK,gBAAgB;AAAA;AAAA;AAAA;AAAA,oBAIhD,CAAC,KAAK,cAAc;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,YAM5B,KAAK,cAAc;AAAA;AAAA;AAAA;AAAA,EAAA;AAK/B;AA1QE,gBAAA;AAAA,EADC,SAAS,EAAE,MAAM,QAAQ,SAAS,KAAM,CAAA;AAAA,GAT9B,eAUX,WAAA,SAAA,CAAA;AAGA,gBAAA;AAAA,EADC,SAAS;AAAA,GAZC,eAaX,WAAA,SAAA,CAAA;AAMA,gBAAA;AAAA,EADC,SAAS,EAAE,MAAM,QAAS,CAAA;AAAA,GAlBhB,eAmBX,WAAA,aAAA,CAAA;AAGA,gBAAA;AAAA,EADC,SAAS,EAAE,MAAM,QAAS,CAAA;AAAA,GArBhB,eAsBX,WAAA,YAAA,CAAA;AAGA,gBAAA;AAAA,EADC,SAAS,EAAE,MAAM,QAAS,CAAA;AAAA,GAxBhB,eAyBX,WAAA,YAAA,CAAA;AAGA,gBAAA;AAAA,EADC,SAAS,EAAE,MAAM,OAAQ,CAAA;AAAA,GA3Bf,eA4BX,WAAA,aAAA,CAAA;AAMA,gBAAA;AAAA,EADC,SAAS;AAAA,GAjCC,eAkCX,WAAA,kBAAA,CAAA;AAMA,gBAAA;AAAA,EADC,SAAS,EAAE,MAAM,OAAQ,CAAA;AAAA,GAvCf,eAwCX,WAAA,UAAA,CAAA;AAGA,gBAAA;AAAA,EADC,SAAS;AAAA,GA1CC,eA2CX,WAAA,YAAA,CAAA;AAGA,gBAAA;AAAA,EADC,MAAM;AAAA,GA7CI,eA8CX,WAAA,eAAA,CAAA;AAGA,gBAAA;AAAA,EADC,MAAM;AAAA,GAhDI,eAiDX,WAAA,kBAAA,CAAA;AAjDW,iBAAN,gBAAA;AAAA,EADN,cAAc,gBAAgB;AAAA,GAClB,cAAA;"}
1
+ {"version":3,"file":"code-input.js","sources":["../../src/components/code-input.component.ts"],"sourcesContent":["import { css, html, LitElement, PropertyValues } from 'lit';\nimport { customElement, property, queryAll, state } from 'lit/decorators.js';\nimport { classMap } from 'lit/directives/class-map.js';\nimport { live } from 'lit/directives/live.js';\nimport { watch } from '../internal/watch.ts';\nimport { FormControlMixin } from '../mixins/form-control.mixin.ts';\nimport { styled } from '../mixins/tailwind.mixin.ts';\nimport { HasSlotController } from '../internal/slot.ts';\nimport './icon/icon.component.ts';\nimport {\n maxLengthValidator,\n minLengthValidator,\n patternValidator,\n requiredValidator,\n} from '../mixins/validators.ts';\nimport { webOtpApiInit } from '../utilities/web-otp-api.ts';\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'mid-code-input': MinidCodeInput;\n }\n}\n\nlet nextUniqueId = 0;\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];\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 {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 label - The input's label if you need to use HTML. Alternatively, you can use the `label` attribute.\n *\n * @csspart input-container - The wrapper around the input elements. This can be used to adjust font-size.\n *\n */\n@customElement('mid-code-input')\nexport class MinidCodeInput extends FormControlMixin(\n styled(LitElement, styles)\n) {\n private readonly hasSlotControler = new HasSlotController(this, 'label');\n #skipValueVisualUpdate = false;\n private readonly labelId = `mid-code-input-label-${nextUniqueId++}`;\n\n @queryAll('input')\n private inputElements!: NodeListOf<HTMLInputElement>;\n\n @property({ reflect: true })\n value = '';\n\n @property()\n label = '';\n\n @property()\n type: 'number' | 'text' = 'text';\n\n /**\n * For displaying appropriate virtual keyboard\n */\n @property()\n inputmode: 'numeric' | 'text' = 'text';\n\n /**\n * Adjusts the font-size of the code inputs\n */\n @property()\n size: 'sm' | 'md' | 'lg' = 'sm';\n\n /**\n * Number of input characters or digits\n */\n @property({ type: Number })\n length = 5;\n\n @property({ type: Boolean })\n disabled = false;\n\n /**\n * Focuses the first input element on page load\n */\n @property({ type: Boolean })\n autofocus = false;\n\n /**\n * The minimum length of input that will be considered valid.\n */\n @property({ type: Number })\n minlength?: number;\n\n /**\n * Error message to display when the input is invalid, also activates invalid styling\n */\n @property()\n invalidmessage = '';\n\n /**\n * Visually hides `label` (still available for screen readers)\n */\n @property({ type: Boolean })\n hidelabel = false;\n\n /**\n * Makes the input required\n */\n @property({ type: Boolean })\n required = false;\n\n /**\n * A regular expression pattern to validate input against.\n */\n @property()\n pattern?: string;\n\n @state()\n private internalValues = Array<string>(this.length).fill('');\n\n static get formControlValidators() {\n return [\n requiredValidator,\n maxLengthValidator,\n minLengthValidator,\n patternValidator,\n ];\n }\n\n get validationTarget() {\n return this.inputElements?.length ? this.inputElements[0] : null;\n }\n\n protected firstUpdated(_changedProperties: PropertyValues): void {\n webOtpApiInit(this.renderRoot);\n }\n\n private handleFocus(index: number) {\n return () => {\n this.inputElements[index].setAttribute('placeholder', '');\n };\n }\n\n private handleBlur(index: number) {\n return () => {\n this.inputElements[index].setAttribute('placeholder', '○');\n };\n }\n\n private handleKeydown(index: number) {\n return (event: KeyboardEvent) => {\n this.internalValues[index] = (event.target as HTMLInputElement).value;\n this.value = this.internalValues.join('');\n const input = event.target as HTMLInputElement;\n const hasModifier =\n event.metaKey || event.ctrlKey || event.shiftKey || event.altKey;\n\n if (event.key === 'Backspace') {\n if (input.value === '' && index > 0) {\n // If current input is empty, move to previous and clear it\n this.inputElements[index - 1].focus();\n this.internalValues[index - 1] = '';\n this.updateValueAndEmit(true);\n } else if (input.value !== '') {\n // If current input has a value, clear it\n input.value = '';\n this.internalValues[index] = '';\n this.updateValueAndEmit(true);\n }\n } else if (event.key === 'ArrowLeft') {\n if (index > 0) {\n this.inputElements[index - 1].focus();\n }\n } else if (event.key === 'ArrowRight') {\n if (index < this.length - 1) {\n this.inputElements[index + 1].focus();\n }\n } else if (event.key === 'Delete') {\n // Clear current field without moving\n input.value = '';\n this.internalValues[index] = '';\n this.updateValueAndEmit(true);\n } else if (event.key === 'Enter' && !hasModifier) {\n // Pressing enter when focused on an input should submit the form like a native input, but we wait a tick before\n // submitting to allow users to cancel the keydown event if they need to\n setTimeout(() => {\n //\n // When using an Input Method Editor (IME), pressing enter will cause the form to submit unexpectedly. One way\n // to check for this is to look at event.isComposing, which will be true when the IME is open.\n if (!event.defaultPrevented && !event.isComposing) {\n this.form?.requestSubmit();\n }\n });\n }\n };\n }\n\n private handleInput(index: number) {\n return (event: InputEvent) => {\n const input = event.target as HTMLInputElement;\n let inputValue = input.value;\n\n // Only allow single digit input\n if (inputValue.length > 1) {\n inputValue = inputValue.charAt(0);\n input.value = inputValue; // Correct the input value\n }\n\n this.internalValues[index] = inputValue;\n this.updateValueAndEmit(true);\n\n // Auto-focus next input if a digit was entered and it's not the last one\n if (inputValue !== '' && index < this.length - 1) {\n this.inputElements[index + 1].focus();\n }\n\n this.dispatchEvent(\n new Event('mid-input', { bubbles: true, composed: true })\n );\n };\n }\n\n private handlePaste(index: number) {\n return (event: ClipboardEvent) => {\n event.preventDefault(); // Prevent default paste behavior\n const pasteData = event.clipboardData?.getData('text').trim();\n\n if (pasteData) {\n const pasteChars = pasteData.split('');\n for (let i = 0; i < this.length; i++) {\n if (index + i < this.length && pasteChars[i]) {\n this.internalValues[index + i] = pasteChars[i];\n // Update the actual input element's value directly as well\n // if (this.inputElements[index + i]) {\n // this.inputElements[index + i].value = pasteChars[i];\n // }\n }\n }\n this.updateValueAndEmit();\n\n // Focus the last filled input or the last input if all are filled\n const lastFilledIndex = Math.min(\n index + pasteChars.length - 1,\n this.length - 1\n );\n this.inputElements[lastFilledIndex]?.focus();\n }\n };\n }\n\n private updateValueAndEmit(skipValueUpdate = false) {\n this.#skipValueVisualUpdate = skipValueUpdate;\n this.value = this.internalValues.join('');\n this.dispatchEvent(\n new Event('mid-change', {\n bubbles: true,\n composed: true,\n })\n );\n\n if (\n this.value.length === this.length &&\n this.internalValues.every((digit) => digit !== '')\n ) {\n this.dispatchEvent(\n new Event('mid-complete', {\n bubbles: true,\n composed: true,\n })\n );\n }\n }\n\n private valueVisualUpdate(value: string) {\n const chars = value.split('');\n this.internalValues.forEach((_, index) => {\n this.internalValues[index] = chars[index];\n });\n }\n\n @watch('length')\n handleLengthChange() {\n this.internalValues = Array(this.length).fill('');\n this.valueVisualUpdate(this.value);\n }\n\n @watch('value')\n handleValueChange() {\n this.setValue(this.value);\n\n if (this.#skipValueVisualUpdate) {\n this.#skipValueVisualUpdate = false;\n return;\n }\n\n this.valueVisualUpdate(this.value);\n }\n\n override render() {\n const hasLabelSlot = this.hasSlotControler.test('label');\n const hasLabel = !!this.label || !!hasLabelSlot;\n return html`\n <label\n id=\"${this.labelId}\"\n class=\"${classMap({\n 'sr-only': this.hidelabel || !hasLabel,\n 'opacity-disabled': this.disabled,\n })} mb-2 block items-center gap-1 font-medium\"\n >\n <slot name=\"label\"> ${this.label} </slot>\n </label>\n <div\n part=\"input-container\"\n class=\"${classMap({\n 'text-heading-sm': this.size === 'sm',\n 'text-heading-md': this.size === 'md',\n 'text-heading-lg': this.size === 'lg',\n })} inline-flex gap-2\"\n >\n ${this.internalValues.map((value, index) => {\n return html`\n <input\n .value=\"${live(value || '')}\"\n id=\"mid-code-input-${index}\"\n class=\"${classMap({\n border: !this.invalidmessage,\n 'border-2': this.invalidmessage,\n 'border-neutral': !this.invalidmessage,\n 'border-danger': this.invalidmessage,\n 'bg-neutral': !this.invalidmessage,\n 'bg-danger-tinted': this.invalidmessage,\n 'placeholder:text-neutral-surface-active': !this.invalidmessage,\n 'placeholder:text-danger-surface-active': this.invalidmessage,\n })} focus:focus-ring-sm disabled:opacity-disabled size-9 rounded-md text-center font-mono disabled:cursor-not-allowed\"\n type=\"${this.type}\"\n placeholder=\"○\"\n aria-labelledby=\"${this.labelId}\"\n size=\"1\"\n ?autofocus=\"${!index && this.autofocus}\"\n ?disabled=\"${this.disabled}\"\n inputmode=\"${this.inputmode}\"\n @keydown=\"${this.handleKeydown(index)}\"\n @input=\"${this.handleInput(index)}\"\n @paste=\"${this.handlePaste(index)}\"\n @focus=\"${this.handleFocus(index)}\"\n @blur=\"${this.handleBlur(index)}\"\n />\n `;\n })}\n </div>\n <div\n class=\"text-danger-subtle mt-2 flex gap-1\"\n aria-live=\"polite\"\n ?hidden=${!this.invalidmessage}\n >\n <mid-icon\n name=\"xmark-octagon-fill\"\n class=\"mt-1 min-h-5 min-w-5\"\n ></mid-icon>\n ${this.invalidmessage}\n </div>\n `;\n }\n}\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,IAAA;AAuBA,IAAI,eAAe;AAEnB,MAAM,SAAS;AAAA,EACb;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAcF;AAeO,IAAM,iBAAN,cAA6B;AAAA,EAClC,OAAO,YAAY,MAAM;AAC3B,EAAE;AAAA,EAFK,cAAA;AAAA,UAAA,GAAA,SAAA;AAGL,SAAiB,mBAAmB,IAAI,kBAAkB,MAAM,OAAO;AAC9C,iBAAA,MAAA,wBAAA,KAAA;AACR,SAAA,UAAU,wBAAwB,cAAc;AAMzD,SAAA,QAAA;AAGA,SAAA,QAAA;AAGkB,SAAA,OAAA;AAMM,SAAA,YAAA;AAML,SAAA,OAAA;AAMlB,SAAA,SAAA;AAGE,SAAA,WAAA;AAMC,SAAA,YAAA;AAYK,SAAA,iBAAA;AAML,SAAA,YAAA;AAMD,SAAA,WAAA;AASX,SAAQ,iBAAiB,MAAc,KAAK,MAAM,EAAE,KAAK,EAAE;AAAA,EAAA;AAAA,EAE3D,WAAW,wBAAwB;AAC1B,WAAA;AAAA,MACL;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,EAAA;AAAA,EAGF,IAAI,mBAAmB;AACrB,WAAO,KAAK,eAAe,SAAS,KAAK,cAAc,CAAC,IAAI;AAAA,EAAA;AAAA,EAGpD,aAAa,oBAA0C;AAC/D,kBAAc,KAAK,UAAU;AAAA,EAAA;AAAA,EAGvB,YAAY,OAAe;AACjC,WAAO,MAAM;AACX,WAAK,cAAc,KAAK,EAAE,aAAa,eAAe,EAAE;AAAA,IAC1D;AAAA,EAAA;AAAA,EAGM,WAAW,OAAe;AAChC,WAAO,MAAM;AACX,WAAK,cAAc,KAAK,EAAE,aAAa,eAAe,GAAG;AAAA,IAC3D;AAAA,EAAA;AAAA,EAGM,cAAc,OAAe;AACnC,WAAO,CAAC,UAAyB;AAC/B,WAAK,eAAe,KAAK,IAAK,MAAM,OAA4B;AAChE,WAAK,QAAQ,KAAK,eAAe,KAAK,EAAE;AACxC,YAAM,QAAQ,MAAM;AACpB,YAAM,cACJ,MAAM,WAAW,MAAM,WAAW,MAAM,YAAY,MAAM;AAExD,UAAA,MAAM,QAAQ,aAAa;AAC7B,YAAI,MAAM,UAAU,MAAM,QAAQ,GAAG;AAEnC,eAAK,cAAc,QAAQ,CAAC,EAAE,MAAM;AAC/B,eAAA,eAAe,QAAQ,CAAC,IAAI;AACjC,eAAK,mBAAmB,IAAI;AAAA,QAAA,WACnB,MAAM,UAAU,IAAI;AAE7B,gBAAM,QAAQ;AACT,eAAA,eAAe,KAAK,IAAI;AAC7B,eAAK,mBAAmB,IAAI;AAAA,QAAA;AAAA,MAC9B,WACS,MAAM,QAAQ,aAAa;AACpC,YAAI,QAAQ,GAAG;AACb,eAAK,cAAc,QAAQ,CAAC,EAAE,MAAM;AAAA,QAAA;AAAA,MACtC,WACS,MAAM,QAAQ,cAAc;AACjC,YAAA,QAAQ,KAAK,SAAS,GAAG;AAC3B,eAAK,cAAc,QAAQ,CAAC,EAAE,MAAM;AAAA,QAAA;AAAA,MACtC,WACS,MAAM,QAAQ,UAAU;AAEjC,cAAM,QAAQ;AACT,aAAA,eAAe,KAAK,IAAI;AAC7B,aAAK,mBAAmB,IAAI;AAAA,MACnB,WAAA,MAAM,QAAQ,WAAW,CAAC,aAAa;AAGhD,mBAAW,MAAM;AAIf,cAAI,CAAC,MAAM,oBAAoB,CAAC,MAAM,aAAa;AACjD,iBAAK,MAAM,cAAc;AAAA,UAAA;AAAA,QAC3B,CACD;AAAA,MAAA;AAAA,IAEL;AAAA,EAAA;AAAA,EAGM,YAAY,OAAe;AACjC,WAAO,CAAC,UAAsB;AAC5B,YAAM,QAAQ,MAAM;AACpB,UAAI,aAAa,MAAM;AAGnB,UAAA,WAAW,SAAS,GAAG;AACZ,qBAAA,WAAW,OAAO,CAAC;AAChC,cAAM,QAAQ;AAAA,MAAA;AAGX,WAAA,eAAe,KAAK,IAAI;AAC7B,WAAK,mBAAmB,IAAI;AAG5B,UAAI,eAAe,MAAM,QAAQ,KAAK,SAAS,GAAG;AAChD,aAAK,cAAc,QAAQ,CAAC,EAAE,MAAM;AAAA,MAAA;AAGjC,WAAA;AAAA,QACH,IAAI,MAAM,aAAa,EAAE,SAAS,MAAM,UAAU,KAAM,CAAA;AAAA,MAC1D;AAAA,IACF;AAAA,EAAA;AAAA,EAGM,YAAY,OAAe;AACjC,WAAO,CAAC,UAA0B;AAChC,YAAM,eAAe;AACrB,YAAM,YAAY,MAAM,eAAe,QAAQ,MAAM,EAAE,KAAK;AAE5D,UAAI,WAAW;AACP,cAAA,aAAa,UAAU,MAAM,EAAE;AACrC,iBAAS,IAAI,GAAG,IAAI,KAAK,QAAQ,KAAK;AACpC,cAAI,QAAQ,IAAI,KAAK,UAAU,WAAW,CAAC,GAAG;AAC5C,iBAAK,eAAe,QAAQ,CAAC,IAAI,WAAW,CAAC;AAAA,UAAA;AAAA,QAK/C;AAEF,aAAK,mBAAmB;AAGxB,cAAM,kBAAkB,KAAK;AAAA,UAC3B,QAAQ,WAAW,SAAS;AAAA,UAC5B,KAAK,SAAS;AAAA,QAChB;AACK,aAAA,cAAc,eAAe,GAAG,MAAM;AAAA,MAAA;AAAA,IAE/C;AAAA,EAAA;AAAA,EAGM,mBAAmB,kBAAkB,OAAO;AAClD,iBAAA,MAAK,wBAAyB,eAAA;AAC9B,SAAK,QAAQ,KAAK,eAAe,KAAK,EAAE;AACnC,SAAA;AAAA,MACH,IAAI,MAAM,cAAc;AAAA,QACtB,SAAS;AAAA,QACT,UAAU;AAAA,MACX,CAAA;AAAA,IACH;AAEA,QACE,KAAK,MAAM,WAAW,KAAK,UAC3B,KAAK,eAAe,MAAM,CAAC,UAAU,UAAU,EAAE,GACjD;AACK,WAAA;AAAA,QACH,IAAI,MAAM,gBAAgB;AAAA,UACxB,SAAS;AAAA,UACT,UAAU;AAAA,QACX,CAAA;AAAA,MACH;AAAA,IAAA;AAAA,EACF;AAAA,EAGM,kBAAkB,OAAe;AACjC,UAAA,QAAQ,MAAM,MAAM,EAAE;AAC5B,SAAK,eAAe,QAAQ,CAAC,GAAG,UAAU;AACxC,WAAK,eAAe,KAAK,IAAI,MAAM,KAAK;AAAA,IAAA,CACzC;AAAA,EAAA;AAAA,EAIH,qBAAqB;AACnB,SAAK,iBAAiB,MAAM,KAAK,MAAM,EAAE,KAAK,EAAE;AAC3C,SAAA,kBAAkB,KAAK,KAAK;AAAA,EAAA;AAAA,EAInC,oBAAoB;AACb,SAAA,SAAS,KAAK,KAAK;AAExB,QAAI,mBAAK,sBAAwB,GAAA;AAC/B,mBAAA,MAAK,wBAAyB,KAAA;AAC9B;AAAA,IAAA;AAGG,SAAA,kBAAkB,KAAK,KAAK;AAAA,EAAA;AAAA,EAG1B,SAAS;AAChB,UAAM,eAAe,KAAK,iBAAiB,KAAK,OAAO;AACvD,UAAM,WAAW,CAAC,CAAC,KAAK,SAAS,CAAC,CAAC;AAC5B,WAAA;AAAA;AAAA,cAEG,KAAK,OAAO;AAAA,iBACT,SAAS;AAAA,MAChB,WAAW,KAAK,aAAa,CAAC;AAAA,MAC9B,oBAAoB,KAAK;AAAA,IAAA,CAC1B,CAAC;AAAA;AAAA,8BAEoB,KAAK,KAAK;AAAA;AAAA;AAAA;AAAA,iBAIvB,SAAS;AAAA,MAChB,mBAAmB,KAAK,SAAS;AAAA,MACjC,mBAAmB,KAAK,SAAS;AAAA,MACjC,mBAAmB,KAAK,SAAS;AAAA,IAAA,CAClC,CAAC;AAAA;AAAA,UAEA,KAAK,eAAe,IAAI,CAAC,OAAO,UAAU;AACnC,aAAA;AAAA;AAAA,wBAEO,KAAK,SAAS,EAAE,CAAC;AAAA,mCACN,KAAK;AAAA,uBACjB,SAAS;AAAA,QAChB,QAAQ,CAAC,KAAK;AAAA,QACd,YAAY,KAAK;AAAA,QACjB,kBAAkB,CAAC,KAAK;AAAA,QACxB,iBAAiB,KAAK;AAAA,QACtB,cAAc,CAAC,KAAK;AAAA,QACpB,oBAAoB,KAAK;AAAA,QACzB,2CAA2C,CAAC,KAAK;AAAA,QACjD,0CAA0C,KAAK;AAAA,MAAA,CAChD,CAAC;AAAA,sBACM,KAAK,IAAI;AAAA;AAAA,iCAEE,KAAK,OAAO;AAAA;AAAA,4BAEjB,CAAC,SAAS,KAAK,SAAS;AAAA,2BACzB,KAAK,QAAQ;AAAA,2BACb,KAAK,SAAS;AAAA,0BACf,KAAK,cAAc,KAAK,CAAC;AAAA,wBAC3B,KAAK,YAAY,KAAK,CAAC;AAAA,wBACvB,KAAK,YAAY,KAAK,CAAC;AAAA,wBACvB,KAAK,YAAY,KAAK,CAAC;AAAA,uBACxB,KAAK,WAAW,KAAK,CAAC;AAAA;AAAA;AAAA,IAAA,CAGpC,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA,kBAKQ,CAAC,KAAK,cAAc;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,UAM5B,KAAK,cAAc;AAAA;AAAA;AAAA,EAAA;AAI7B;AA9TE,yBAAA,oBAAA,QAAA;AAIQ,gBAAA;AAAA,EADP,SAAS,OAAO;AAAA,GAPN,eAQH,WAAA,iBAAA,CAAA;AAGR,gBAAA;AAAA,EADC,SAAS,EAAE,SAAS,KAAM,CAAA;AAAA,GAVhB,eAWX,WAAA,SAAA,CAAA;AAGA,gBAAA;AAAA,EADC,SAAS;AAAA,GAbC,eAcX,WAAA,SAAA,CAAA;AAGA,gBAAA;AAAA,EADC,SAAS;AAAA,GAhBC,eAiBX,WAAA,QAAA,CAAA;AAMA,gBAAA;AAAA,EADC,SAAS;AAAA,GAtBC,eAuBX,WAAA,aAAA,CAAA;AAMA,gBAAA;AAAA,EADC,SAAS;AAAA,GA5BC,eA6BX,WAAA,QAAA,CAAA;AAMA,gBAAA;AAAA,EADC,SAAS,EAAE,MAAM,OAAQ,CAAA;AAAA,GAlCf,eAmCX,WAAA,UAAA,CAAA;AAGA,gBAAA;AAAA,EADC,SAAS,EAAE,MAAM,QAAS,CAAA;AAAA,GArChB,eAsCX,WAAA,YAAA,CAAA;AAMA,gBAAA;AAAA,EADC,SAAS,EAAE,MAAM,QAAS,CAAA;AAAA,GA3ChB,eA4CX,WAAA,aAAA,CAAA;AAMA,gBAAA;AAAA,EADC,SAAS,EAAE,MAAM,OAAQ,CAAA;AAAA,GAjDf,eAkDX,WAAA,aAAA,CAAA;AAMA,gBAAA;AAAA,EADC,SAAS;AAAA,GAvDC,eAwDX,WAAA,kBAAA,CAAA;AAMA,gBAAA;AAAA,EADC,SAAS,EAAE,MAAM,QAAS,CAAA;AAAA,GA7DhB,eA8DX,WAAA,aAAA,CAAA;AAMA,gBAAA;AAAA,EADC,SAAS,EAAE,MAAM,QAAS,CAAA;AAAA,GAnEhB,eAoEX,WAAA,YAAA,CAAA;AAMA,gBAAA;AAAA,EADC,SAAS;AAAA,GAzEC,eA0EX,WAAA,WAAA,CAAA;AAGQ,gBAAA;AAAA,EADP,MAAM;AAAA,GA5EI,eA6EH,WAAA,kBAAA,CAAA;AAmKR,gBAAA;AAAA,EADC,MAAM,QAAQ;AAAA,GA/OJ,eAgPX,WAAA,sBAAA,CAAA;AAMA,gBAAA;AAAA,EADC,MAAM,OAAO;AAAA,GArPH,eAsPX,WAAA,qBAAA,CAAA;AAtPW,iBAAN,gBAAA;AAAA,EADN,cAAc,gBAAgB;AAAA,GAClB,cAAA;"}
@@ -295,7 +295,7 @@ let MinidDropdown = class extends styled(LitElement, styles) {
295
295
  >
296
296
  </slot>
297
297
  <div
298
- class="bg-accent min-w-68"
298
+ class="bg-accent"
299
299
  aria-hidden=${this.open ? "false" : "true"}
300
300
  aria-labelledby="${this.popupId}"
301
301
  >