@felleslosninger/minid-elements 0.1.4 → 0.1.6

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, html } from "lit";
1
+ import { css, LitElement, nothing, html } from "lit";
2
2
  import { query, property, state, customElement } from "lit/decorators.js";
3
3
  import { classMap } from "lit/directives/class-map.js";
4
4
  import { ifDefined } from "lit/directives/if-defined.js";
@@ -35,7 +35,32 @@ const styles = [
35
35
  font-weight: 500;
36
36
  }
37
37
 
38
- .code-input-label--hidden {
38
+ .code-input-label--with-description {
39
+ margin-block-end: 0.25rem;
40
+ }
41
+
42
+ .code-input-label--sm {
43
+ font-size: var(--ds-body-sm-font-size, 1rem);
44
+ line-height: var(--ds-body-md-line-height, 1.5);
45
+ }
46
+
47
+ .code-input-label--md {
48
+ font-size: var(--ds-body-md-font-size, 1.125rem);
49
+ line-height: var(--ds-body-md-line-height, 1.5);
50
+ }
51
+
52
+ .code-input-label--lg {
53
+ font-size: var(--ds-body-lg-font-size, 1.3125rem);
54
+ line-height: var(--ds-body-md-line-height, 1.5);
55
+ }
56
+
57
+ .code-input-description {
58
+ margin-block-end: 0.5rem;
59
+ color: var(--ds-color-neutral-text-subtle, #545e6b);
60
+ }
61
+
62
+ .code-input-label--hidden,
63
+ .code-input-description--hidden {
39
64
  position: absolute;
40
65
  width: 1px;
41
66
  height: 1px;
@@ -48,6 +73,7 @@ const styles = [
48
73
  }
49
74
 
50
75
  .code-input-label--disabled,
76
+ .code-input-description--disabled,
51
77
  .input-wrapper--disabled {
52
78
  opacity: var(--ds-opacity-disabled, 0.38);
53
79
  }
@@ -215,27 +241,25 @@ const styles = [
215
241
  animation: blink 1s step-end infinite;
216
242
  }
217
243
 
244
+ .character-box--caret-leading::after {
245
+ inset-inline-start: 0;
246
+ }
247
+
218
248
  /* Hide placeholder circle when the caret is at this position */
219
249
  .character-box--caret.character-box--empty {
220
250
  color: transparent;
221
251
  }
222
252
 
223
- /* Hide increment/decrement buttons on number inputs */
224
- input[type='number']::-webkit-outer-spin-button,
225
- input[type='number']::-webkit-inner-spin-button,
226
- input[type='number'] {
227
- -webkit-appearance: none;
228
- margin: 0;
229
- -moz-appearance: textfield !important;
230
- }
231
-
232
253
  .validation-message {
233
254
  display: flex;
234
255
  gap: 0.25rem;
235
- margin-block-start: 0.5rem;
236
256
  color: var(--ds-color-danger-text-subtle, #a22e2e);
237
257
  }
238
258
 
259
+ .validation-message--visible {
260
+ margin-block-start: 0.5rem;
261
+ }
262
+
239
263
  .validation-icon {
240
264
  margin-block-start: 0.25rem;
241
265
  min-width: 1.25rem;
@@ -248,11 +272,17 @@ let MinidCodeInput = class extends FormControlMixin(
248
272
  ) {
249
273
  constructor() {
250
274
  super(...arguments);
251
- this.hasSlotControler = new HasSlotController(this, "label");
275
+ this.hasSlotControler = new HasSlotController(
276
+ this,
277
+ "label",
278
+ "description"
279
+ );
252
280
  this.labelId = `mid-code-input-label-${nextUniqueId++}`;
281
+ this.descriptionId = `mid-code-input-description-${nextUniqueId++}`;
253
282
  this.validationId = `mid-code-input-validation-${nextUniqueId++}`;
254
283
  this.value = "";
255
284
  this.label = "";
285
+ this.description = "";
256
286
  this.type = "text";
257
287
  this.inputmode = "numeric";
258
288
  this.size = "sm";
@@ -261,8 +291,15 @@ let MinidCodeInput = class extends FormControlMixin(
261
291
  this.autofocus = false;
262
292
  this.invalidmessage = "";
263
293
  this.hidelabel = false;
294
+ this.hidedescription = false;
264
295
  this.required = false;
265
296
  this.isFocused = false;
297
+ this.caretPosition = 0;
298
+ this.handleSelectionChange = () => {
299
+ if (this.isFocused) {
300
+ this.syncCaretPosition();
301
+ }
302
+ };
266
303
  }
267
304
  static get formControlValidators() {
268
305
  return [
@@ -278,6 +315,20 @@ let MinidCodeInput = class extends FormControlMixin(
278
315
  firstUpdated(_changedProperties) {
279
316
  webOtpApiInit(this.renderRoot);
280
317
  }
318
+ connectedCallback() {
319
+ super.connectedCallback();
320
+ this.ownerDocument.addEventListener(
321
+ "selectionchange",
322
+ this.handleSelectionChange
323
+ );
324
+ }
325
+ disconnectedCallback() {
326
+ super.disconnectedCallback();
327
+ this.ownerDocument.removeEventListener(
328
+ "selectionchange",
329
+ this.handleSelectionChange
330
+ );
331
+ }
281
332
  handleBoxClick(index) {
282
333
  if (this.disabled) {
283
334
  return;
@@ -285,30 +336,67 @@ let MinidCodeInput = class extends FormControlMixin(
285
336
  this.value = this.value.substring(0, index);
286
337
  this.focus();
287
338
  }
339
+ syncCaretPosition() {
340
+ const input = this.inputElement;
341
+ if (!input) {
342
+ return;
343
+ }
344
+ this.caretPosition = (input.selectionDirection === "backward" ? input.selectionStart : input.selectionEnd) ?? this.value.length;
345
+ }
288
346
  handleFocus() {
289
347
  this.isFocused = true;
290
348
  if (this.value.length > 0) {
291
349
  const length = this.value.length;
292
350
  this.inputElement.setSelectionRange(length, length);
293
351
  }
352
+ this.syncCaretPosition();
294
353
  }
295
354
  handleBlur() {
296
355
  this.isFocused = false;
297
356
  }
357
+ filterValue(value) {
358
+ return this.type === "number" ? value.replace(/\D/g, "") : value;
359
+ }
298
360
  handleBeforeInput(e) {
299
- if (e.inputType === "insertText" && e.data) {
300
- if (this.type == "number" && e.data.replace(/\D/g, "") == "") {
361
+ if (this.type === "number" && e.inputType === "insertText" && e.data) {
362
+ if (!/\d/.test(e.data)) {
301
363
  e.preventDefault();
302
364
  }
303
365
  }
304
366
  }
367
+ handlePaste(event) {
368
+ if (this.type !== "number") {
369
+ return;
370
+ }
371
+ const pasted = event.clipboardData?.getData("text") ?? "";
372
+ const digits = this.filterValue(pasted);
373
+ if (digits === pasted) {
374
+ return;
375
+ }
376
+ event.preventDefault();
377
+ const input = this.inputElement;
378
+ const start = input.selectionStart ?? input.value.length;
379
+ const end = input.selectionEnd ?? start;
380
+ const next = (input.value.slice(0, start) + digits + input.value.slice(end)).substring(0, this.length);
381
+ if (next === input.value) {
382
+ return;
383
+ }
384
+ input.value = next;
385
+ input.dispatchEvent(
386
+ new InputEvent("input", { bubbles: true, composed: true })
387
+ );
388
+ const caret = Math.min(start + digits.length, input.value.length);
389
+ input.setSelectionRange(caret, caret);
390
+ this.syncCaretPosition();
391
+ }
305
392
  handleInput(event) {
306
393
  const input = event.target;
307
- let value = input.value;
394
+ let value = this.filterValue(input.value);
308
395
  value = value.substring(0, this.length);
309
396
  if (input.value !== value) {
310
397
  input.value = value;
311
398
  }
399
+ this.syncCaretPosition();
312
400
  if (this.value !== value) {
313
401
  this.value = value;
314
402
  this.setValue(value);
@@ -344,6 +432,7 @@ let MinidCodeInput = class extends FormControlMixin(
344
432
  this.value = "";
345
433
  this.setValue("");
346
434
  this.invalidmessage = "";
435
+ this.caretPosition = 0;
347
436
  }
348
437
  resetFormControl() {
349
438
  this.clear();
@@ -354,34 +443,65 @@ let MinidCodeInput = class extends FormControlMixin(
354
443
  const length = this.value.length;
355
444
  this.inputElement.setSelectionRange(length, length);
356
445
  }
446
+ this.syncCaretPosition();
357
447
  }
358
448
  handleLengthChange() {
359
449
  if (this.value.length > this.length) {
360
450
  this.value = this.value.substring(0, this.length);
361
451
  }
362
452
  }
453
+ handleValueChange() {
454
+ const filtered = this.filterValue(this.value);
455
+ if (filtered !== this.value) {
456
+ this.value = filtered;
457
+ }
458
+ this.caretPosition = Math.min(this.caretPosition, this.value.length);
459
+ this.setValue(filtered);
460
+ }
363
461
  render() {
364
462
  const hasLabelSlot = this.hasSlotControler.test("label");
365
463
  const hasLabel = !!this.label || !!hasLabelSlot;
366
- const describedBy = this.invalidmessage ? this.validationId : void 0;
464
+ const hasDescriptionSlot = this.hasSlotControler.test("description");
465
+ const hasDescription = !!this.description || !!hasDescriptionSlot;
466
+ const describedBy = [
467
+ hasDescription ? this.descriptionId : void 0,
468
+ this.invalidmessage ? this.validationId : void 0
469
+ ].filter(Boolean).join(" ") || void 0;
367
470
  const chars = this.value.split("");
368
471
  const displayValues = Array.from(
369
472
  { length: this.length },
370
473
  (_, i) => chars[i] || ""
371
474
  );
372
- const isComplete = this.value.length >= this.length;
373
- const caretIndex = this.disabled || this.length === 0 ? -1 : Math.min(this.value.length, this.length - 1);
475
+ const caretIndex = this.disabled || this.length === 0 ? -1 : Math.min(this.caretPosition, this.length - 1);
476
+ const caretIsInABox = this.caretPosition < this.length;
374
477
  return html`
375
478
  <label
376
479
  id="${this.labelId}"
377
480
  for="mid-code-input-hidden"
378
481
  class="code-input-label ${classMap({
482
+ "code-input-label--sm": this.labelsize === "sm",
483
+ "code-input-label--md": this.labelsize === "md",
484
+ "code-input-label--lg": this.labelsize === "lg",
379
485
  "code-input-label--hidden": this.hidelabel || !hasLabel,
486
+ "code-input-label--with-description": hasDescription,
380
487
  "code-input-label--disabled": this.disabled
381
488
  })}"
382
489
  >
383
490
  <slot name="label"> ${this.label} </slot>
384
491
  </label>
492
+ ${hasDescription ? html`
493
+ <div
494
+ id="${this.descriptionId}"
495
+ part="description"
496
+ aria-hidden="true"
497
+ class="code-input-description ${classMap({
498
+ "code-input-description--hidden": this.hidedescription,
499
+ "code-input-description--disabled": this.disabled
500
+ })}"
501
+ >
502
+ <slot name="description"> ${this.description} </slot>
503
+ </div>
504
+ ` : nothing}
385
505
  <div
386
506
  part="input-container"
387
507
  class="input-wrapper ${classMap({
@@ -395,7 +515,7 @@ let MinidCodeInput = class extends FormControlMixin(
395
515
  <div part="character-boxes" class="character-boxes" aria-hidden="true">
396
516
  ${displayValues.map((char, index) => {
397
517
  const isFocusTarget = this.isFocused && index === caretIndex;
398
- const hasCaret = isFocusTarget && !isComplete;
518
+ const hasCaret = isFocusTarget && caretIsInABox;
399
519
  return html`
400
520
  <div
401
521
  part="character-box"
@@ -403,6 +523,7 @@ let MinidCodeInput = class extends FormControlMixin(
403
523
  class="character-box ${classMap({
404
524
  "character-box--invalid": !!this.invalidmessage,
405
525
  "character-box--caret": hasCaret,
526
+ "character-box--caret-leading": hasCaret && !!char,
406
527
  "character-box--empty": !char,
407
528
  "focus-ring-sm": isFocusTarget
408
529
  })}"
@@ -420,7 +541,7 @@ let MinidCodeInput = class extends FormControlMixin(
420
541
  class="hidden-input"
421
542
  .value="${live(this.value)}"
422
543
  id="mid-code-input-hidden"
423
- type="${this.type}"
544
+ type="text"
424
545
  aria-labelledby="${this.labelId}"
425
546
  aria-describedby=${ifDefined(describedBy)}
426
547
  aria-invalid=${this.invalidmessage ? "true" : "false"}
@@ -435,6 +556,7 @@ let MinidCodeInput = class extends FormControlMixin(
435
556
  inputmode="${this.inputmode}"
436
557
  autocomplete="one-time-code"
437
558
  @beforeinput="${this.handleBeforeInput}"
559
+ @paste="${this.handlePaste}"
438
560
  @input="${this.handleInput}"
439
561
  @change="${this.handleChange}"
440
562
  @keydown="${this.handleKeydown}"
@@ -443,13 +565,18 @@ let MinidCodeInput = class extends FormControlMixin(
443
565
  />
444
566
  </div>
445
567
  <div
446
- class="validation-message"
568
+ class="validation-message ${classMap({
569
+ "validation-message--visible": !!this.invalidmessage
570
+ })}"
571
+ part="validation-message"
447
572
  id="${this.validationId}"
448
573
  aria-live="polite"
449
- ?hidden=${!this.invalidmessage}
450
574
  >
451
- <mid-icon name="xmark-octagon-fill" class="validation-icon"></mid-icon>
452
- ${this.invalidmessage}
575
+ ${this.invalidmessage ? html`<mid-icon
576
+ name="xmark-octagon-fill"
577
+ class="validation-icon"
578
+ ></mid-icon>
579
+ ${this.invalidmessage}` : nothing}
453
580
  </div>
454
581
  `;
455
582
  }
@@ -463,6 +590,9 @@ __decorateClass([
463
590
  __decorateClass([
464
591
  property()
465
592
  ], MinidCodeInput.prototype, "label", 2);
593
+ __decorateClass([
594
+ property()
595
+ ], MinidCodeInput.prototype, "description", 2);
466
596
  __decorateClass([
467
597
  property()
468
598
  ], MinidCodeInput.prototype, "type", 2);
@@ -472,6 +602,9 @@ __decorateClass([
472
602
  __decorateClass([
473
603
  property()
474
604
  ], MinidCodeInput.prototype, "size", 2);
605
+ __decorateClass([
606
+ property()
607
+ ], MinidCodeInput.prototype, "labelsize", 2);
475
608
  __decorateClass([
476
609
  property({ type: Number })
477
610
  ], MinidCodeInput.prototype, "length", 2);
@@ -490,6 +623,9 @@ __decorateClass([
490
623
  __decorateClass([
491
624
  property({ type: Boolean })
492
625
  ], MinidCodeInput.prototype, "hidelabel", 2);
626
+ __decorateClass([
627
+ property({ type: Boolean })
628
+ ], MinidCodeInput.prototype, "hidedescription", 2);
493
629
  __decorateClass([
494
630
  property({ type: Boolean })
495
631
  ], MinidCodeInput.prototype, "required", 2);
@@ -499,9 +635,15 @@ __decorateClass([
499
635
  __decorateClass([
500
636
  state()
501
637
  ], MinidCodeInput.prototype, "isFocused", 2);
638
+ __decorateClass([
639
+ state()
640
+ ], MinidCodeInput.prototype, "caretPosition", 2);
502
641
  __decorateClass([
503
642
  watch("length")
504
643
  ], MinidCodeInput.prototype, "handleLengthChange", 1);
644
+ __decorateClass([
645
+ watch(["value", "type"])
646
+ ], MinidCodeInput.prototype, "handleValueChange", 1);
505
647
  MinidCodeInput = __decorateClass([
506
648
  customElement("mid-code-input")
507
649
  ], MinidCodeInput);
@@ -1 +1 @@
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, query, state } from 'lit/decorators.js';\nimport { classMap } from 'lit/directives/class-map.js';\nimport { ifDefined } from 'lit/directives/if-defined.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 .code-input-label {\n display: inline-flex;\n align-items: center;\n gap: 0.25rem;\n margin-block-end: 0.5rem;\n font-weight: 500;\n }\n\n .code-input-label--hidden {\n position: absolute;\n width: 1px;\n height: 1px;\n padding: 0;\n margin: -1px;\n overflow: hidden;\n clip: rect(0, 0, 0, 0);\n white-space: nowrap;\n border: 0;\n }\n\n .code-input-label--disabled,\n .input-wrapper--disabled {\n opacity: var(--ds-opacity-disabled, 0.38);\n }\n\n .input-wrapper {\n position: relative;\n cursor: text;\n width: fit-content;\n font-size: 1rem;\n line-height: 1.3;\n font-weight: 500;\n }\n\n .input-wrapper--sm {\n font-size: var(--ds-heading-sm-font-size, 1.125rem);\n line-height: var(--ds-heading-sm-line-height, 1.3);\n font-weight: var(--ds-heading-sm-font-weight, 500);\n }\n\n .input-wrapper--md {\n font-size: var(--ds-heading-md-font-size, 1.25rem);\n line-height: var(--ds-heading-md-line-height, 1.3);\n font-weight: var(--ds-heading-md-font-weight, 500);\n }\n\n .input-wrapper--lg {\n font-size: var(--ds-heading-lg-font-size, 1.5rem);\n line-height: var(--ds-heading-lg-line-height, 1.3);\n font-weight: var(--ds-heading-lg-font-weight, 500);\n }\n\n .input-wrapper--disabled {\n cursor: not-allowed;\n }\n\n .character-boxes {\n display: inline-flex;\n gap: var(--mid-code-input-gap, 0.5rem);\n }\n\n .character-box {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n position: relative;\n box-sizing: border-box;\n width: var(--mid-code-input-box-size, var(--ds-size-13, 3.25rem));\n height: var(--mid-code-input-box-size, var(--ds-size-13, 3.25rem));\n padding-inline: 0.25rem;\n user-select: none;\n text-align: center;\n font-family:\n ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas,\n 'Liberation Mono', 'Courier New', monospace;\n line-height: 1;\n border: var(--ds-border-width-default, 1px) solid\n var(\n --mid-code-input-border-color,\n var(--ds-color-neutral-border-default, #717a84)\n );\n border-radius: var(\n --mid-code-input-border-radius,\n var(--ds-border-radius-md, 0.25rem)\n );\n background-color: var(\n --mid-code-input-background,\n var(--ds-color-neutral-background-default, #ffffff)\n );\n color: var(--mid-code-input-text-color, inherit);\n }\n\n .character-box--empty {\n color: var(\n --mid-code-input-placeholder-color,\n var(--ds-color-neutral-surface-active, #c7cacf)\n );\n }\n\n .character-box--empty::before {\n content: '';\n box-sizing: border-box;\n width: 0.55em;\n height: 0.55em;\n border: 2px solid currentColor;\n border-radius: 50%;\n }\n\n .character-box--invalid {\n border-width: 2px;\n border-color: var(\n --mid-code-input-danger-border-color,\n var(--ds-color-danger-border-default, #ce4d4d)\n );\n background-color: var(\n --mid-code-input-danger-background,\n var(--ds-color-danger-surface-tinted, #f8e4e4)\n );\n }\n\n .character-box--invalid.character-box--empty {\n color: var(\n --mid-code-input-danger-placeholder-color,\n var(--ds-color-danger-surface-active, #edbfbf)\n );\n }\n\n .focus-ring-sm {\n outline: 2px solid var(--ds-color-focus-outer, #1f2c3d);\n outline-offset: 2px;\n box-shadow: 0 0 0 2px var(--ds-color-focus-inner, #ffffff);\n }\n\n /* Ensure hidden attribute always hides elements even when Tailwind's\n @layer base [hidden] rule is not available in the shadow DOM */\n [hidden] {\n display: none !important;\n }\n\n .hidden-input {\n position: absolute;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n padding: 0;\n margin: 0;\n background: transparent;\n border: none;\n outline: none;\n color: transparent;\n caret-color: transparent;\n font-size: 16px; /* Minimum 16px prevents iOS zoom on focus */\n pointer-events: none;\n -webkit-appearance: none;\n appearance: none;\n }\n\n .hidden-input:focus {\n outline: none;\n }\n\n .hidden-input:disabled {\n cursor: not-allowed;\n }\n\n @keyframes blink {\n 0%,\n 100% {\n opacity: 1;\n }\n 50% {\n opacity: 0;\n }\n }\n\n .character-box--caret::after {\n content: '';\n position: absolute;\n width: 2px;\n height: 60%;\n background-color: var(\n --color-accent-base,\n var(--ds-color-accent-base-default, blue)\n );\n animation: blink 1s step-end infinite;\n }\n\n /* Hide placeholder circle when the caret is at this position */\n .character-box--caret.character-box--empty {\n color: transparent;\n }\n\n /* Hide increment/decrement buttons on number 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 .validation-message {\n display: flex;\n gap: 0.25rem;\n margin-block-start: 0.5rem;\n color: var(--ds-color-danger-text-subtle, #a22e2e);\n }\n\n .validation-icon {\n margin-block-start: 0.25rem;\n min-width: 1.25rem;\n min-height: 1.25rem;\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 mid-complete - Emitted when all characters have been entered.\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 * @csspart character-boxes - The container for the visual character boxes.\n * @csspart character-box - An individual character box.\n *\n * @method focus - Focuses the input element\n * @method clear - Clears the input\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 private readonly labelId = `mid-code-input-label-${nextUniqueId++}`;\n private readonly validationId = `mid-code-input-validation-${nextUniqueId++}`;\n\n @query('.hidden-input')\n private inputElement!: 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' = 'numeric';\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 isFocused = false;\n\n static get formControlValidators() {\n return [\n requiredValidator,\n maxLengthValidator,\n minLengthValidator,\n patternValidator,\n ];\n }\n\n get validationTarget() {\n return this.inputElement;\n }\n\n protected firstUpdated(_changedProperties: PropertyValues): void {\n webOtpApiInit(this.renderRoot);\n }\n\n private handleBoxClick(index: number) {\n if (this.disabled) {\n return;\n }\n\n // Truncate the value to the clicked position\n this.value = this.value.substring(0, index);\n\n // Focus the input to move the caret\n this.focus();\n }\n\n private handleFocus() {\n this.isFocused = true;\n /* don't select text, move caret to the end */\n if (this.value.length > 0) {\n const length = this.value.length;\n this.inputElement.setSelectionRange(length, length);\n }\n }\n\n private handleBlur() {\n this.isFocused = false;\n }\n\n private handleBeforeInput(e: InputEvent) {\n if (e.inputType === 'insertText' && e.data) {\n if (this.type == 'number' && e.data.replace(/\\D/g, '') == '') {\n e.preventDefault();\n }\n }\n }\n\n private handleInput(event: InputEvent) {\n const input = event.target as HTMLInputElement;\n let value = input.value;\n\n value = value.substring(0, this.length);\n\n if (input.value !== value) {\n input.value = value;\n }\n\n // Update the component's public `value` property if it has changed.\n if (this.value !== value) {\n this.value = value;\n this.setValue(value);\n }\n\n this.inputElement.dispatchEvent(\n new Event('mid-input', { bubbles: true, composed: true })\n );\n\n if (this.value.length === this.length && this.value.length > 0) {\n this.inputElement.dispatchEvent(\n new Event('mid-complete', { bubbles: true, composed: true })\n );\n }\n }\n\n private handleChange() {\n this.inputElement.dispatchEvent(\n new Event('mid-change', {\n bubbles: true,\n composed: true,\n })\n );\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 // 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 // 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 clear() {\n this.value = '';\n this.setValue('');\n this.invalidmessage = '';\n }\n\n resetFormControl() {\n this.clear();\n }\n\n focus(options?: FocusOptions): void {\n this.inputElement?.focus(options);\n /* don't select text, move caret to the end */\n if (this.value.length > 0) {\n const length = this.value.length;\n this.inputElement.setSelectionRange(length, length);\n }\n }\n\n @watch('length')\n handleLengthChange() {\n if (this.value.length > this.length) {\n this.value = this.value.substring(0, this.length);\n }\n }\n\n override render() {\n const hasLabelSlot = this.hasSlotControler.test('label');\n const hasLabel = !!this.label || !!hasLabelSlot;\n const describedBy = this.invalidmessage ? this.validationId : undefined;\n\n const chars = this.value.split('');\n const displayValues = Array.from(\n { length: this.length },\n (_, i) => chars[i] || ''\n );\n const isComplete = this.value.length >= this.length;\n const caretIndex =\n this.disabled || this.length === 0\n ? -1\n : Math.min(this.value.length, this.length - 1);\n\n return html`\n <label\n id=\"${this.labelId}\"\n for=\"mid-code-input-hidden\"\n class=\"code-input-label ${classMap({\n 'code-input-label--hidden': this.hidelabel || !hasLabel,\n 'code-input-label--disabled': this.disabled,\n })}\"\n >\n <slot name=\"label\"> ${this.label} </slot>\n </label>\n <div\n part=\"input-container\"\n class=\"input-wrapper ${classMap({\n 'input-wrapper--sm': this.size === 'sm',\n 'input-wrapper--md': this.size === 'md',\n 'input-wrapper--lg': this.size === 'lg',\n 'input-wrapper--disabled': this.disabled,\n })}\"\n @click=${() => this.focus()}\n >\n <div part=\"character-boxes\" class=\"character-boxes\" aria-hidden=\"true\">\n ${displayValues.map((char, index) => {\n const isFocusTarget = this.isFocused && index === caretIndex;\n const hasCaret = isFocusTarget && !isComplete;\n return html`\n <div\n part=\"character-box\"\n aria-hidden=\"true\"\n class=\"character-box ${classMap({\n 'character-box--invalid': !!this.invalidmessage,\n 'character-box--caret': hasCaret,\n 'character-box--empty': !char,\n 'focus-ring-sm': isFocusTarget,\n })}\"\n @click=${(e: MouseEvent) => {\n e.stopPropagation();\n this.handleBoxClick(index);\n }}\n >\n ${char}\n </div>\n `;\n })}\n </div>\n <input\n class=\"hidden-input\"\n .value=\"${live(this.value)}\"\n id=\"mid-code-input-hidden\"\n type=\"${this.type}\"\n aria-labelledby=\"${this.labelId}\"\n aria-describedby=${ifDefined(describedBy)}\n aria-invalid=${this.invalidmessage ? 'true' : 'false'}\n aria-errormessage=${ifDefined(\n this.invalidmessage ? this.validationId : undefined\n )}\n maxlength=\"${this.length}\"\n ?autofocus=\"${this.autofocus}\"\n ?disabled=\"${this.disabled}\"\n ?required=\"${this.required}\"\n pattern=${ifDefined(this.pattern)}\n inputmode=\"${this.inputmode}\"\n autocomplete=\"one-time-code\"\n @beforeinput=\"${this.handleBeforeInput}\"\n @input=\"${this.handleInput}\"\n @change=\"${this.handleChange}\"\n @keydown=\"${this.handleKeydown}\"\n @focus=\"${this.handleFocus}\"\n @blur=\"${this.handleBlur}\"\n />\n </div>\n <div\n class=\"validation-message\"\n id=\"${this.validationId}\"\n aria-live=\"polite\"\n ?hidden=${!this.invalidmessage}\n >\n <mid-icon name=\"xmark-octagon-fill\" class=\"validation-icon\"></mid-icon>\n ${this.invalidmessage}\n </div>\n `;\n }\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"code-input.js","sources":["../../src/components/code-input.component.ts"],"sourcesContent":["import { css, html, LitElement, nothing, PropertyValues } from 'lit';\nimport { customElement, property, query, state } from 'lit/decorators.js';\nimport { classMap } from 'lit/directives/class-map.js';\nimport { ifDefined } from 'lit/directives/if-defined.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 .code-input-label {\n display: inline-flex;\n align-items: center;\n gap: 0.25rem;\n margin-block-end: 0.5rem;\n font-weight: 500;\n }\n\n .code-input-label--with-description {\n margin-block-end: 0.25rem;\n }\n\n .code-input-label--sm {\n font-size: var(--ds-body-sm-font-size, 1rem);\n line-height: var(--ds-body-md-line-height, 1.5);\n }\n\n .code-input-label--md {\n font-size: var(--ds-body-md-font-size, 1.125rem);\n line-height: var(--ds-body-md-line-height, 1.5);\n }\n\n .code-input-label--lg {\n font-size: var(--ds-body-lg-font-size, 1.3125rem);\n line-height: var(--ds-body-md-line-height, 1.5);\n }\n\n .code-input-description {\n margin-block-end: 0.5rem;\n color: var(--ds-color-neutral-text-subtle, #545e6b);\n }\n\n .code-input-label--hidden,\n .code-input-description--hidden {\n position: absolute;\n width: 1px;\n height: 1px;\n padding: 0;\n margin: -1px;\n overflow: hidden;\n clip: rect(0, 0, 0, 0);\n white-space: nowrap;\n border: 0;\n }\n\n .code-input-label--disabled,\n .code-input-description--disabled,\n .input-wrapper--disabled {\n opacity: var(--ds-opacity-disabled, 0.38);\n }\n\n .input-wrapper {\n position: relative;\n cursor: text;\n width: fit-content;\n font-size: 1rem;\n line-height: 1.3;\n font-weight: 500;\n }\n\n .input-wrapper--sm {\n font-size: var(--ds-heading-sm-font-size, 1.125rem);\n line-height: var(--ds-heading-sm-line-height, 1.3);\n font-weight: var(--ds-heading-sm-font-weight, 500);\n }\n\n .input-wrapper--md {\n font-size: var(--ds-heading-md-font-size, 1.25rem);\n line-height: var(--ds-heading-md-line-height, 1.3);\n font-weight: var(--ds-heading-md-font-weight, 500);\n }\n\n .input-wrapper--lg {\n font-size: var(--ds-heading-lg-font-size, 1.5rem);\n line-height: var(--ds-heading-lg-line-height, 1.3);\n font-weight: var(--ds-heading-lg-font-weight, 500);\n }\n\n .input-wrapper--disabled {\n cursor: not-allowed;\n }\n\n .character-boxes {\n display: inline-flex;\n gap: var(--mid-code-input-gap, 0.5rem);\n }\n\n .character-box {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n position: relative;\n box-sizing: border-box;\n width: var(--mid-code-input-box-size, var(--ds-size-13, 3.25rem));\n height: var(--mid-code-input-box-size, var(--ds-size-13, 3.25rem));\n padding-inline: 0.25rem;\n user-select: none;\n text-align: center;\n font-family:\n ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas,\n 'Liberation Mono', 'Courier New', monospace;\n line-height: 1;\n border: var(--ds-border-width-default, 1px) solid\n var(\n --mid-code-input-border-color,\n var(--ds-color-neutral-border-default, #717a84)\n );\n border-radius: var(\n --mid-code-input-border-radius,\n var(--ds-border-radius-md, 0.25rem)\n );\n background-color: var(\n --mid-code-input-background,\n var(--ds-color-neutral-background-default, #ffffff)\n );\n color: var(--mid-code-input-text-color, inherit);\n }\n\n .character-box--empty {\n color: var(\n --mid-code-input-placeholder-color,\n var(--ds-color-neutral-surface-active, #c7cacf)\n );\n }\n\n .character-box--empty::before {\n content: '';\n box-sizing: border-box;\n width: 0.55em;\n height: 0.55em;\n border: 2px solid currentColor;\n border-radius: 50%;\n }\n\n .character-box--invalid {\n border-width: 2px;\n border-color: var(\n --mid-code-input-danger-border-color,\n var(--ds-color-danger-border-default, #ce4d4d)\n );\n background-color: var(\n --mid-code-input-danger-background,\n var(--ds-color-danger-surface-tinted, #f8e4e4)\n );\n }\n\n .character-box--invalid.character-box--empty {\n color: var(\n --mid-code-input-danger-placeholder-color,\n var(--ds-color-danger-surface-active, #edbfbf)\n );\n }\n\n .focus-ring-sm {\n outline: 2px solid var(--ds-color-focus-outer, #1f2c3d);\n outline-offset: 2px;\n box-shadow: 0 0 0 2px var(--ds-color-focus-inner, #ffffff);\n }\n\n /* Ensure hidden attribute always hides elements even when Tailwind's\n @layer base [hidden] rule is not available in the shadow DOM */\n [hidden] {\n display: none !important;\n }\n\n .hidden-input {\n position: absolute;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n padding: 0;\n margin: 0;\n background: transparent;\n border: none;\n outline: none;\n color: transparent;\n caret-color: transparent;\n font-size: 16px; /* Minimum 16px prevents iOS zoom on focus */\n pointer-events: none;\n -webkit-appearance: none;\n appearance: none;\n }\n\n .hidden-input:focus {\n outline: none;\n }\n\n .hidden-input:disabled {\n cursor: not-allowed;\n }\n\n @keyframes blink {\n 0%,\n 100% {\n opacity: 1;\n }\n 50% {\n opacity: 0;\n }\n }\n\n .character-box--caret::after {\n content: '';\n position: absolute;\n width: 2px;\n height: 60%;\n background-color: var(\n --color-accent-base,\n var(--ds-color-accent-base-default, blue)\n );\n animation: blink 1s step-end infinite;\n }\n\n .character-box--caret-leading::after {\n inset-inline-start: 0;\n }\n\n /* Hide placeholder circle when the caret is at this position */\n .character-box--caret.character-box--empty {\n color: transparent;\n }\n\n .validation-message {\n display: flex;\n gap: 0.25rem;\n color: var(--ds-color-danger-text-subtle, #a22e2e);\n }\n\n .validation-message--visible {\n margin-block-start: 0.5rem;\n }\n\n .validation-icon {\n margin-block-start: 0.25rem;\n min-width: 1.25rem;\n min-height: 1.25rem;\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 mid-complete - Emitted when all characters have been entered.\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 * @slot description - The input's description if you need to use HTML. Alternatively, you can use the `description` attribute.\n *\n * @csspart input-container - The wrapper around the input elements. This can be used to adjust font-size.\n * @csspart description - The description shown between the label and the character boxes.\n * @csspart character-boxes - The container for the visual character boxes.\n * @csspart character-box - An individual character box.\n * @csspart validation-message - The error message shown by `invalidmessage`.\n *\n * @method focus - Focuses the input element\n * @method clear - Clears the input\n */\n@customElement('mid-code-input')\nexport class MinidCodeInput extends FormControlMixin(\n styled(LitElement, styles)\n) {\n private readonly hasSlotControler = new HasSlotController(\n this,\n 'label',\n 'description'\n );\n private readonly labelId = `mid-code-input-label-${nextUniqueId++}`;\n private readonly descriptionId = `mid-code-input-description-${nextUniqueId++}`;\n private readonly validationId = `mid-code-input-validation-${nextUniqueId++}`;\n\n @query('.hidden-input')\n private inputElement!: HTMLInputElement;\n\n @property({ reflect: true })\n value = '';\n\n @property()\n label = '';\n\n @property()\n description = '';\n\n @property()\n type: 'number' | 'text' = 'text';\n\n /**\n * For displaying appropriate virtual keyboard\n */\n @property()\n inputmode: 'numeric' | 'text' = 'numeric';\n\n /**\n * Adjusts the font-size of the code inputs\n */\n @property()\n size: 'sm' | 'md' | 'lg' = 'sm';\n\n @property()\n labelsize?: 'sm' | 'md' | 'lg';\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 @property({ type: Boolean })\n hidedescription = 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 isFocused = false;\n\n @state()\n private caretPosition = 0;\n\n static get formControlValidators() {\n return [\n requiredValidator,\n maxLengthValidator,\n minLengthValidator,\n patternValidator,\n ];\n }\n\n get validationTarget() {\n return this.inputElement;\n }\n\n protected firstUpdated(_changedProperties: PropertyValues): void {\n webOtpApiInit(this.renderRoot);\n }\n\n override connectedCallback() {\n super.connectedCallback();\n this.ownerDocument.addEventListener(\n 'selectionchange',\n this.handleSelectionChange\n );\n }\n\n override disconnectedCallback() {\n super.disconnectedCallback();\n this.ownerDocument.removeEventListener(\n 'selectionchange',\n this.handleSelectionChange\n );\n }\n\n private handleBoxClick(index: number) {\n if (this.disabled) {\n return;\n }\n\n // Truncate the value to the clicked position\n this.value = this.value.substring(0, index);\n\n // Focus the input to move the caret\n this.focus();\n }\n\n /**\n * Catches caret moves we did not make - arrow keys, Home/End, word jumps,\n * shift-selection - without enumerating keys, and keeps up while a key is held.\n *\n * Must be listened for on the document: for a caret move the user made,\n * Chromium fires it only there, not at the input and not into the shadow root,\n * so an `@selectionchange` binding on either never runs.\n */\n private handleSelectionChange = () => {\n // Fires for selection changes anywhere on the page\n if (this.isFocused) {\n this.syncCaretPosition();\n }\n };\n\n private syncCaretPosition() {\n const input = this.inputElement;\n\n if (!input) {\n return;\n }\n\n this.caretPosition =\n (input.selectionDirection === 'backward'\n ? input.selectionStart\n : input.selectionEnd) ?? this.value.length;\n }\n\n private handleFocus() {\n this.isFocused = true;\n /* don't select text, move caret to the end */\n if (this.value.length > 0) {\n const length = this.value.length;\n this.inputElement.setSelectionRange(length, length);\n }\n this.syncCaretPosition();\n }\n\n private handleBlur() {\n this.isFocused = false;\n }\n\n private filterValue(value: string) {\n return this.type === 'number' ? value.replace(/\\D/g, '') : value;\n }\n\n private handleBeforeInput(e: InputEvent) {\n if (this.type === 'number' && e.inputType === 'insertText' && e.data) {\n if (!/\\d/.test(e.data)) {\n e.preventDefault();\n }\n }\n }\n\n private handlePaste(event: ClipboardEvent) {\n if (this.type !== 'number') {\n return;\n }\n\n const pasted = event.clipboardData?.getData('text') ?? '';\n const digits = this.filterValue(pasted);\n\n if (digits === pasted) {\n return;\n }\n\n event.preventDefault();\n\n const input = this.inputElement;\n const start = input.selectionStart ?? input.value.length;\n const end = input.selectionEnd ?? start;\n const next = (\n input.value.slice(0, start) +\n digits +\n input.value.slice(end)\n ).substring(0, this.length);\n\n if (next === input.value) {\n return;\n }\n\n input.value = next;\n input.dispatchEvent(\n new InputEvent('input', { bubbles: true, composed: true })\n );\n\n const caret = Math.min(start + digits.length, input.value.length);\n input.setSelectionRange(caret, caret);\n this.syncCaretPosition();\n }\n\n private handleInput(event: InputEvent) {\n const input = event.target as HTMLInputElement;\n let value = this.filterValue(input.value);\n\n value = value.substring(0, this.length);\n\n if (input.value !== value) {\n input.value = value;\n }\n\n this.syncCaretPosition();\n\n // Update the component's public `value` property if it has changed.\n if (this.value !== value) {\n this.value = value;\n this.setValue(value);\n }\n\n this.inputElement.dispatchEvent(\n new Event('mid-input', { bubbles: true, composed: true })\n );\n\n if (this.value.length === this.length && this.value.length > 0) {\n this.inputElement.dispatchEvent(\n new Event('mid-complete', { bubbles: true, composed: true })\n );\n }\n }\n\n private handleChange() {\n this.inputElement.dispatchEvent(\n new Event('mid-change', {\n bubbles: true,\n composed: true,\n })\n );\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 // 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 // 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 clear() {\n this.value = '';\n this.setValue('');\n this.invalidmessage = '';\n this.caretPosition = 0;\n }\n\n resetFormControl() {\n this.clear();\n }\n\n focus(options?: FocusOptions): void {\n this.inputElement?.focus(options);\n /* don't select text, move caret to the end */\n if (this.value.length > 0) {\n const length = this.value.length;\n this.inputElement.setSelectionRange(length, length);\n }\n this.syncCaretPosition();\n }\n\n @watch('length')\n handleLengthChange() {\n if (this.value.length > this.length) {\n this.value = this.value.substring(0, this.length);\n }\n }\n\n @watch(['value', 'type'])\n handleValueChange() {\n const filtered = this.filterValue(this.value);\n\n if (filtered !== this.value) {\n this.value = filtered;\n }\n\n this.caretPosition = Math.min(this.caretPosition, this.value.length);\n this.setValue(filtered);\n }\n\n override render() {\n const hasLabelSlot = this.hasSlotControler.test('label');\n const hasLabel = !!this.label || !!hasLabelSlot;\n const hasDescriptionSlot = this.hasSlotControler.test('description');\n const hasDescription = !!this.description || !!hasDescriptionSlot;\n const describedBy =\n [\n hasDescription ? this.descriptionId : undefined,\n this.invalidmessage ? this.validationId : undefined,\n ]\n .filter(Boolean)\n .join(' ') || undefined;\n\n const chars = this.value.split('');\n const displayValues = Array.from(\n { length: this.length },\n (_, i) => chars[i] || ''\n );\n const caretIndex =\n this.disabled || this.length === 0\n ? -1\n : Math.min(this.caretPosition, this.length - 1);\n const caretIsInABox = this.caretPosition < this.length;\n\n return html`\n <label\n id=\"${this.labelId}\"\n for=\"mid-code-input-hidden\"\n class=\"code-input-label ${classMap({\n 'code-input-label--sm': this.labelsize === 'sm',\n 'code-input-label--md': this.labelsize === 'md',\n 'code-input-label--lg': this.labelsize === 'lg',\n 'code-input-label--hidden': this.hidelabel || !hasLabel,\n 'code-input-label--with-description': hasDescription,\n 'code-input-label--disabled': this.disabled,\n })}\"\n >\n <slot name=\"label\"> ${this.label} </slot>\n </label>\n ${hasDescription\n ? html`\n <div\n id=\"${this.descriptionId}\"\n part=\"description\"\n aria-hidden=\"true\"\n class=\"code-input-description ${classMap({\n 'code-input-description--hidden': this.hidedescription,\n 'code-input-description--disabled': this.disabled,\n })}\"\n >\n <slot name=\"description\"> ${this.description} </slot>\n </div>\n `\n : nothing}\n <div\n part=\"input-container\"\n class=\"input-wrapper ${classMap({\n 'input-wrapper--sm': this.size === 'sm',\n 'input-wrapper--md': this.size === 'md',\n 'input-wrapper--lg': this.size === 'lg',\n 'input-wrapper--disabled': this.disabled,\n })}\"\n @click=${() => this.focus()}\n >\n <div part=\"character-boxes\" class=\"character-boxes\" aria-hidden=\"true\">\n ${displayValues.map((char, index) => {\n const isFocusTarget = this.isFocused && index === caretIndex;\n const hasCaret = isFocusTarget && caretIsInABox;\n return html`\n <div\n part=\"character-box\"\n aria-hidden=\"true\"\n class=\"character-box ${classMap({\n 'character-box--invalid': !!this.invalidmessage,\n 'character-box--caret': hasCaret,\n 'character-box--caret-leading': hasCaret && !!char,\n 'character-box--empty': !char,\n 'focus-ring-sm': isFocusTarget,\n })}\"\n @click=${(e: MouseEvent) => {\n e.stopPropagation();\n this.handleBoxClick(index);\n }}\n >\n ${char}\n </div>\n `;\n })}\n </div>\n <input\n class=\"hidden-input\"\n .value=\"${live(this.value)}\"\n id=\"mid-code-input-hidden\"\n type=\"text\"\n aria-labelledby=\"${this.labelId}\"\n aria-describedby=${ifDefined(describedBy)}\n aria-invalid=${this.invalidmessage ? 'true' : 'false'}\n aria-errormessage=${ifDefined(\n this.invalidmessage ? this.validationId : undefined\n )}\n maxlength=\"${this.length}\"\n ?autofocus=\"${this.autofocus}\"\n ?disabled=\"${this.disabled}\"\n ?required=\"${this.required}\"\n pattern=${ifDefined(this.pattern)}\n inputmode=\"${this.inputmode}\"\n autocomplete=\"one-time-code\"\n @beforeinput=\"${this.handleBeforeInput}\"\n @paste=\"${this.handlePaste}\"\n @input=\"${this.handleInput}\"\n @change=\"${this.handleChange}\"\n @keydown=\"${this.handleKeydown}\"\n @focus=\"${this.handleFocus}\"\n @blur=\"${this.handleBlur}\"\n />\n </div>\n <div\n class=\"validation-message ${classMap({\n 'validation-message--visible': !!this.invalidmessage,\n })}\"\n part=\"validation-message\"\n id=\"${this.validationId}\"\n aria-live=\"polite\"\n >\n ${this.invalidmessage\n ? html`<mid-icon\n name=\"xmark-octagon-fill\"\n class=\"validation-icon\"\n ></mid-icon>\n ${this.invalidmessage}`\n : nothing}\n </div>\n `;\n }\n}\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;AAwBA,IAAI,eAAe;AAEnB,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;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;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;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;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;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAoPF;AAuBO,IAAM,iBAAN,cAA6B;AAAA,EAClC,OAAO,YAAY,MAAM;AAC3B,EAAE;AAAA,EAFK,cAAA;AAAA,UAAA,GAAA,SAAA;AAGL,SAAiB,mBAAmB,IAAI;AAAA,MACtC;AAAA,MACA;AAAA,MACA;AAAA,IAAA;AAEF,SAAiB,UAAU,wBAAwB,cAAc;AACjE,SAAiB,gBAAgB,8BAA8B,cAAc;AAC7E,SAAiB,eAAe,6BAA6B,cAAc;AAM3E,SAAA,QAAQ;AAGR,SAAA,QAAQ;AAGR,SAAA,cAAc;AAGd,SAAA,OAA0B;AAM1B,SAAA,YAAgC;AAMhC,SAAA,OAA2B;AAS3B,SAAA,SAAS;AAGT,SAAA,WAAW;AAMX,SAAA,YAAY;AAYZ,SAAA,iBAAiB;AAMjB,SAAA,YAAY;AAGZ,SAAA,kBAAkB;AAMlB,SAAA,WAAW;AASX,SAAQ,YAAY;AAGpB,SAAQ,gBAAgB;AAuDxB,SAAQ,wBAAwB,MAAM;AAEpC,UAAI,KAAK,WAAW;AAClB,aAAK,kBAAA;AAAA,MACP;AAAA,IACF;AAAA,EAAA;AAAA,EA1DA,WAAW,wBAAwB;AACjC,WAAO;AAAA,MACL;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IAAA;AAAA,EAEJ;AAAA,EAEA,IAAI,mBAAmB;AACrB,WAAO,KAAK;AAAA,EACd;AAAA,EAEU,aAAa,oBAA0C;AAC/D,kBAAc,KAAK,UAAU;AAAA,EAC/B;AAAA,EAES,oBAAoB;AAC3B,UAAM,kBAAA;AACN,SAAK,cAAc;AAAA,MACjB;AAAA,MACA,KAAK;AAAA,IAAA;AAAA,EAET;AAAA,EAES,uBAAuB;AAC9B,UAAM,qBAAA;AACN,SAAK,cAAc;AAAA,MACjB;AAAA,MACA,KAAK;AAAA,IAAA;AAAA,EAET;AAAA,EAEQ,eAAe,OAAe;AACpC,QAAI,KAAK,UAAU;AACjB;AAAA,IACF;AAGA,SAAK,QAAQ,KAAK,MAAM,UAAU,GAAG,KAAK;AAG1C,SAAK,MAAA;AAAA,EACP;AAAA,EAiBQ,oBAAoB;AAC1B,UAAM,QAAQ,KAAK;AAEnB,QAAI,CAAC,OAAO;AACV;AAAA,IACF;AAEA,SAAK,iBACF,MAAM,uBAAuB,aAC1B,MAAM,iBACN,MAAM,iBAAiB,KAAK,MAAM;AAAA,EAC1C;AAAA,EAEQ,cAAc;AACpB,SAAK,YAAY;AAEjB,QAAI,KAAK,MAAM,SAAS,GAAG;AACzB,YAAM,SAAS,KAAK,MAAM;AAC1B,WAAK,aAAa,kBAAkB,QAAQ,MAAM;AAAA,IACpD;AACA,SAAK,kBAAA;AAAA,EACP;AAAA,EAEQ,aAAa;AACnB,SAAK,YAAY;AAAA,EACnB;AAAA,EAEQ,YAAY,OAAe;AACjC,WAAO,KAAK,SAAS,WAAW,MAAM,QAAQ,OAAO,EAAE,IAAI;AAAA,EAC7D;AAAA,EAEQ,kBAAkB,GAAe;AACvC,QAAI,KAAK,SAAS,YAAY,EAAE,cAAc,gBAAgB,EAAE,MAAM;AACpE,UAAI,CAAC,KAAK,KAAK,EAAE,IAAI,GAAG;AACtB,UAAE,eAAA;AAAA,MACJ;AAAA,IACF;AAAA,EACF;AAAA,EAEQ,YAAY,OAAuB;AACzC,QAAI,KAAK,SAAS,UAAU;AAC1B;AAAA,IACF;AAEA,UAAM,SAAS,MAAM,eAAe,QAAQ,MAAM,KAAK;AACvD,UAAM,SAAS,KAAK,YAAY,MAAM;AAEtC,QAAI,WAAW,QAAQ;AACrB;AAAA,IACF;AAEA,UAAM,eAAA;AAEN,UAAM,QAAQ,KAAK;AACnB,UAAM,QAAQ,MAAM,kBAAkB,MAAM,MAAM;AAClD,UAAM,MAAM,MAAM,gBAAgB;AAClC,UAAM,QACJ,MAAM,MAAM,MAAM,GAAG,KAAK,IAC1B,SACA,MAAM,MAAM,MAAM,GAAG,GACrB,UAAU,GAAG,KAAK,MAAM;AAE1B,QAAI,SAAS,MAAM,OAAO;AACxB;AAAA,IACF;AAEA,UAAM,QAAQ;AACd,UAAM;AAAA,MACJ,IAAI,WAAW,SAAS,EAAE,SAAS,MAAM,UAAU,MAAM;AAAA,IAAA;AAG3D,UAAM,QAAQ,KAAK,IAAI,QAAQ,OAAO,QAAQ,MAAM,MAAM,MAAM;AAChE,UAAM,kBAAkB,OAAO,KAAK;AACpC,SAAK,kBAAA;AAAA,EACP;AAAA,EAEQ,YAAY,OAAmB;AACrC,UAAM,QAAQ,MAAM;AACpB,QAAI,QAAQ,KAAK,YAAY,MAAM,KAAK;AAExC,YAAQ,MAAM,UAAU,GAAG,KAAK,MAAM;AAEtC,QAAI,MAAM,UAAU,OAAO;AACzB,YAAM,QAAQ;AAAA,IAChB;AAEA,SAAK,kBAAA;AAGL,QAAI,KAAK,UAAU,OAAO;AACxB,WAAK,QAAQ;AACb,WAAK,SAAS,KAAK;AAAA,IACrB;AAEA,SAAK,aAAa;AAAA,MAChB,IAAI,MAAM,aAAa,EAAE,SAAS,MAAM,UAAU,MAAM;AAAA,IAAA;AAG1D,QAAI,KAAK,MAAM,WAAW,KAAK,UAAU,KAAK,MAAM,SAAS,GAAG;AAC9D,WAAK,aAAa;AAAA,QAChB,IAAI,MAAM,gBAAgB,EAAE,SAAS,MAAM,UAAU,MAAM;AAAA,MAAA;AAAA,IAE/D;AAAA,EACF;AAAA,EAEQ,eAAe;AACrB,SAAK,aAAa;AAAA,MAChB,IAAI,MAAM,cAAc;AAAA,QACtB,SAAS;AAAA,QACT,UAAU;AAAA,MAAA,CACX;AAAA,IAAA;AAAA,EAEL;AAAA,EAEQ,cAAc,OAAsB;AAC1C,UAAM,cACJ,MAAM,WAAW,MAAM,WAAW,MAAM,YAAY,MAAM;AAE5D,QAAI,MAAM,QAAQ,WAAW,CAAC,aAAa;AAGzC,iBAAW,MAAM;AAGf,YAAI,CAAC,MAAM,oBAAoB,CAAC,MAAM,aAAa;AACjD,eAAK,MAAM,cAAA;AAAA,QACb;AAAA,MACF,CAAC;AAAA,IACH;AAAA,EACF;AAAA,EAEA,QAAQ;AACN,SAAK,QAAQ;AACb,SAAK,SAAS,EAAE;AAChB,SAAK,iBAAiB;AACtB,SAAK,gBAAgB;AAAA,EACvB;AAAA,EAEA,mBAAmB;AACjB,SAAK,MAAA;AAAA,EACP;AAAA,EAEA,MAAM,SAA8B;AAClC,SAAK,cAAc,MAAM,OAAO;AAEhC,QAAI,KAAK,MAAM,SAAS,GAAG;AACzB,YAAM,SAAS,KAAK,MAAM;AAC1B,WAAK,aAAa,kBAAkB,QAAQ,MAAM;AAAA,IACpD;AACA,SAAK,kBAAA;AAAA,EACP;AAAA,EAGA,qBAAqB;AACnB,QAAI,KAAK,MAAM,SAAS,KAAK,QAAQ;AACnC,WAAK,QAAQ,KAAK,MAAM,UAAU,GAAG,KAAK,MAAM;AAAA,IAClD;AAAA,EACF;AAAA,EAGA,oBAAoB;AAClB,UAAM,WAAW,KAAK,YAAY,KAAK,KAAK;AAE5C,QAAI,aAAa,KAAK,OAAO;AAC3B,WAAK,QAAQ;AAAA,IACf;AAEA,SAAK,gBAAgB,KAAK,IAAI,KAAK,eAAe,KAAK,MAAM,MAAM;AACnE,SAAK,SAAS,QAAQ;AAAA,EACxB;AAAA,EAES,SAAS;AAChB,UAAM,eAAe,KAAK,iBAAiB,KAAK,OAAO;AACvD,UAAM,WAAW,CAAC,CAAC,KAAK,SAAS,CAAC,CAAC;AACnC,UAAM,qBAAqB,KAAK,iBAAiB,KAAK,aAAa;AACnE,UAAM,iBAAiB,CAAC,CAAC,KAAK,eAAe,CAAC,CAAC;AAC/C,UAAM,cACJ;AAAA,MACE,iBAAiB,KAAK,gBAAgB;AAAA,MACtC,KAAK,iBAAiB,KAAK,eAAe;AAAA,IAAA,EAEzC,OAAO,OAAO,EACd,KAAK,GAAG,KAAK;AAElB,UAAM,QAAQ,KAAK,MAAM,MAAM,EAAE;AACjC,UAAM,gBAAgB,MAAM;AAAA,MAC1B,EAAE,QAAQ,KAAK,OAAA;AAAA,MACf,CAAC,GAAG,MAAM,MAAM,CAAC,KAAK;AAAA,IAAA;AAExB,UAAM,aACJ,KAAK,YAAY,KAAK,WAAW,IAC7B,KACA,KAAK,IAAI,KAAK,eAAe,KAAK,SAAS,CAAC;AAClD,UAAM,gBAAgB,KAAK,gBAAgB,KAAK;AAEhD,WAAO;AAAA;AAAA,cAEG,KAAK,OAAO;AAAA;AAAA,kCAEQ,SAAS;AAAA,MACjC,wBAAwB,KAAK,cAAc;AAAA,MAC3C,wBAAwB,KAAK,cAAc;AAAA,MAC3C,wBAAwB,KAAK,cAAc;AAAA,MAC3C,4BAA4B,KAAK,aAAa,CAAC;AAAA,MAC/C,sCAAsC;AAAA,MACtC,8BAA8B,KAAK;AAAA,IAAA,CACpC,CAAC;AAAA;AAAA,8BAEoB,KAAK,KAAK;AAAA;AAAA,QAEhC,iBACE;AAAA;AAAA,oBAEU,KAAK,aAAa;AAAA;AAAA;AAAA,8CAGQ,SAAS;AAAA,MACvC,kCAAkC,KAAK;AAAA,MACvC,oCAAoC,KAAK;AAAA,IAAA,CAC1C,CAAC;AAAA;AAAA,0CAE0B,KAAK,WAAW;AAAA;AAAA,cAGhD,OAAO;AAAA;AAAA;AAAA,+BAGc,SAAS;AAAA,MAC9B,qBAAqB,KAAK,SAAS;AAAA,MACnC,qBAAqB,KAAK,SAAS;AAAA,MACnC,qBAAqB,KAAK,SAAS;AAAA,MACnC,2BAA2B,KAAK;AAAA,IAAA,CACjC,CAAC;AAAA,iBACO,MAAM,KAAK,MAAA,CAAO;AAAA;AAAA;AAAA,YAGvB,cAAc,IAAI,CAAC,MAAM,UAAU;AACnC,YAAM,gBAAgB,KAAK,aAAa,UAAU;AAClD,YAAM,WAAW,iBAAiB;AAClC,aAAO;AAAA;AAAA;AAAA;AAAA,uCAIoB,SAAS;AAAA,QAC9B,0BAA0B,CAAC,CAAC,KAAK;AAAA,QACjC,wBAAwB;AAAA,QACxB,gCAAgC,YAAY,CAAC,CAAC;AAAA,QAC9C,wBAAwB,CAAC;AAAA,QACzB,iBAAiB;AAAA,MAAA,CAClB,CAAC;AAAA,yBACO,CAAC,MAAkB;AAC1B,UAAE,gBAAA;AACF,aAAK,eAAe,KAAK;AAAA,MAC3B,CAAC;AAAA;AAAA,kBAEC,IAAI;AAAA;AAAA;AAAA,IAGZ,CAAC,CAAC;AAAA;AAAA;AAAA;AAAA,oBAIQ,KAAK,KAAK,KAAK,CAAC;AAAA;AAAA;AAAA,6BAGP,KAAK,OAAO;AAAA,6BACZ,UAAU,WAAW,CAAC;AAAA,yBAC1B,KAAK,iBAAiB,SAAS,OAAO;AAAA,8BACjC;AAAA,MAClB,KAAK,iBAAiB,KAAK,eAAe;AAAA,IAAA,CAC3C;AAAA,uBACY,KAAK,MAAM;AAAA,wBACV,KAAK,SAAS;AAAA,uBACf,KAAK,QAAQ;AAAA,uBACb,KAAK,QAAQ;AAAA,oBAChB,UAAU,KAAK,OAAO,CAAC;AAAA,uBACpB,KAAK,SAAS;AAAA;AAAA,0BAEX,KAAK,iBAAiB;AAAA,oBAC5B,KAAK,WAAW;AAAA,oBAChB,KAAK,WAAW;AAAA,qBACf,KAAK,YAAY;AAAA,sBAChB,KAAK,aAAa;AAAA,oBACpB,KAAK,WAAW;AAAA,mBACjB,KAAK,UAAU;AAAA;AAAA;AAAA;AAAA,oCAIE,SAAS;AAAA,MACnC,+BAA+B,CAAC,CAAC,KAAK;AAAA,IAAA,CACvC,CAAC;AAAA;AAAA,cAEI,KAAK,YAAY;AAAA;AAAA;AAAA,UAGrB,KAAK,iBACH;AAAA;AAAA;AAAA;AAAA,gBAII,KAAK,cAAc,KACvB,OAAO;AAAA;AAAA;AAAA,EAGjB;AACF;AAhcU,gBAAA;AAAA,EADP,MAAM,eAAe;AAAA,GAZX,eAaH,WAAA,gBAAA,CAAA;AAGR,gBAAA;AAAA,EADC,SAAS,EAAE,SAAS,KAAA,CAAM;AAAA,GAfhB,eAgBX,WAAA,SAAA,CAAA;AAGA,gBAAA;AAAA,EADC,SAAA;AAAS,GAlBC,eAmBX,WAAA,SAAA,CAAA;AAGA,gBAAA;AAAA,EADC,SAAA;AAAS,GArBC,eAsBX,WAAA,eAAA,CAAA;AAGA,gBAAA;AAAA,EADC,SAAA;AAAS,GAxBC,eAyBX,WAAA,QAAA,CAAA;AAMA,gBAAA;AAAA,EADC,SAAA;AAAS,GA9BC,eA+BX,WAAA,aAAA,CAAA;AAMA,gBAAA;AAAA,EADC,SAAA;AAAS,GApCC,eAqCX,WAAA,QAAA,CAAA;AAGA,gBAAA;AAAA,EADC,SAAA;AAAS,GAvCC,eAwCX,WAAA,aAAA,CAAA;AAMA,gBAAA;AAAA,EADC,SAAS,EAAE,MAAM,OAAA,CAAQ;AAAA,GA7Cf,eA8CX,WAAA,UAAA,CAAA;AAGA,gBAAA;AAAA,EADC,SAAS,EAAE,MAAM,QAAA,CAAS;AAAA,GAhDhB,eAiDX,WAAA,YAAA,CAAA;AAMA,gBAAA;AAAA,EADC,SAAS,EAAE,MAAM,QAAA,CAAS;AAAA,GAtDhB,eAuDX,WAAA,aAAA,CAAA;AAMA,gBAAA;AAAA,EADC,SAAS,EAAE,MAAM,OAAA,CAAQ;AAAA,GA5Df,eA6DX,WAAA,aAAA,CAAA;AAMA,gBAAA;AAAA,EADC,SAAA;AAAS,GAlEC,eAmEX,WAAA,kBAAA,CAAA;AAMA,gBAAA;AAAA,EADC,SAAS,EAAE,MAAM,QAAA,CAAS;AAAA,GAxEhB,eAyEX,WAAA,aAAA,CAAA;AAGA,gBAAA;AAAA,EADC,SAAS,EAAE,MAAM,QAAA,CAAS;AAAA,GA3EhB,eA4EX,WAAA,mBAAA,CAAA;AAMA,gBAAA;AAAA,EADC,SAAS,EAAE,MAAM,QAAA,CAAS;AAAA,GAjFhB,eAkFX,WAAA,YAAA,CAAA;AAMA,gBAAA;AAAA,EADC,SAAA;AAAS,GAvFC,eAwFX,WAAA,WAAA,CAAA;AAGQ,gBAAA;AAAA,EADP,MAAA;AAAM,GA1FI,eA2FH,WAAA,aAAA,CAAA;AAGA,gBAAA;AAAA,EADP,MAAA;AAAM,GA7FI,eA8FH,WAAA,iBAAA,CAAA;AAuNR,gBAAA;AAAA,EADC,MAAM,QAAQ;AAAA,GApTJ,eAqTX,WAAA,sBAAA,CAAA;AAOA,gBAAA;AAAA,EADC,MAAM,CAAC,SAAS,MAAM,CAAC;AAAA,GA3Tb,eA4TX,WAAA,qBAAA,CAAA;AA5TW,iBAAN,gBAAA;AAAA,EADN,cAAc,gBAAgB;AAAA,GAClB,cAAA;"}
@@ -79,7 +79,8 @@ let MinidDialog = class extends styled(LitElement, styles) {
79
79
  this.dialog,
80
80
  panelAnimation.keyframes,
81
81
  panelAnimation.options
82
- ), this.dispatchEvent(
82
+ );
83
+ this.dispatchEvent(
83
84
  new Event("mid-after-show", { bubbles: true, composed: true })
84
85
  );
85
86
  } else {
@@ -1 +1 @@
1
- {"version":3,"file":"dialog.js","sources":["../../src/components/dialog.component.ts"],"sourcesContent":["import { css, html, LitElement, nothing } from 'lit';\nimport { customElement, property, query } from 'lit/decorators.js';\nimport { getLang } from '../utilities/lang';\nimport { getTranslations } from '../utilities/translations';\nimport { LangController } from '../controllers/lang.controller.js';\nimport { waitForEvent } from '../internal/event';\nimport { watch } from '../internal/watch';\nimport { styled } from '../mixins/tailwind.mixin';\nimport './icon/icon.component';\nimport {\n getAnimation,\n setDefaultAnimation,\n} from '../utilities/animation-registry';\nimport { animateTo, stopAnimations } from '../internal/animate';\nimport { lockBodyScrolling, unlockBodyScrolling } from '../internal/scroll';\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'mid-dialog': MinidDialog;\n }\n}\n\nconst styles = [\n css`\n :host {\n --width: 40rem;\n display: block;\n }\n `,\n];\n\n/**\n *\n * @event mid-show - Emitted when the dialog opens.\n * @event mid-after-show - Emitted after the dialog opens and all animations are complete.\n * @event mid-hide - Emitted when the dialog closes.\n * @event mid-after-hide - Emitted after the dialog closes and all animations are complete.\n * @event {{ source: 'close-button' | 'keyboard' | 'overlay' }} mid-request-close - Emitted when the user attempts to\n * close the dialog by clicking the close button, clicking the overlay, or pressing escape. Calling\n * `event.preventDefault()` will keep the dialog open. Avoid using this unless closing the dialog will result in\n * destructive behavior such as data loss.\n *\n * @csspart base - Select the base `dialog` element\n * @csspart header - Select the `header` element\n * @csspart body - Select the `article` element\n * @csspart footer - Select the `footer` element\n *\n * @slot -- The default slot for the body content of the dialog\n * @slot heading - The content inside the `h2` element\n * @slot footer - The content inside the `footer` element\n *\n * @animation dialog.show - The animation to use when showing the dialog.\n * @animation dialog.hide - The animation to use when hiding the dialog.\n * @animation dialog.denyClose - The animation to use when closing the dialog is prevented.\n *\n */\n@customElement('mid-dialog')\nexport class MinidDialog extends styled(LitElement, styles) {\n @query('#dialog')\n dialog!: HTMLDialogElement;\n\n /**\n * Indicates whether or not the dialog is open. You can toggle this attribute to show and hide the dialog, or you can\n * use the `show()` and `hide()` methods and this attribute will reflect the dialog's open state.\n */\n @property({ type: Boolean, reflect: true })\n open = false;\n\n /**\n * The dialog's label as displayed in the header. You should always include a relevant label even when using\n * `no-header`, as it is required for proper accessibility. If you need to display HTML, use the `label` slot instead.\n */\n @property({ reflect: true })\n heading = '';\n\n /**\n * When set, the dialog uses `role=\"alertdialog\"` instead of `role=\"dialog\"`, signalling urgency to screen readers.\n * Use this for blocking dialogs that require immediate user attention.\n */\n @property({ type: Boolean, reflect: true })\n alertdialog = false;\n\n private requestClose(source: 'close-button' | 'keyboard' | 'overlay') {\n const requestCloseEvent = new CustomEvent('mid-request-close', {\n bubbles: true,\n composed: true,\n cancelable: true,\n detail: { source },\n });\n this.dispatchEvent(requestCloseEvent);\n\n if (requestCloseEvent.defaultPrevented) {\n const { keyframes, options } = getAnimation(this, 'dialog.denyClose');\n animateTo(this.dialog, keyframes, options);\n return;\n }\n\n this.hide();\n }\n\n handleDialogCancel = (event: Event) => {\n event.preventDefault();\n\n if (event.defaultPrevented) {\n this.requestClose('keyboard');\n } else {\n this.hide();\n }\n };\n\n private handleBackdropClick({ target }: Event) {\n // if the target is the dialog, the backdrop was clicked\n if (target === this.dialog) {\n this.requestClose('overlay');\n }\n }\n\n @watch('open', { waitUntilFirstUpdate: true })\n async handleOpenChange() {\n if (this.open) {\n // Show\n this.dispatchEvent(\n new Event('mid-show', { composed: true, bubbles: true })\n );\n // this.addOpenListeners();\n this.dialog.showModal();\n\n if (this.alertdialog) {\n this.dialog.focus();\n }\n\n lockBodyScrolling(this);\n\n const panelAnimation = getAnimation(this, 'dialog.show');\n await animateTo(\n this.dialog,\n panelAnimation.keyframes,\n panelAnimation.options\n ),\n this.dispatchEvent(\n new Event('mid-after-show', { bubbles: true, composed: true })\n );\n } else {\n // Hide\n this.dispatchEvent(\n new Event('mid-hide', { bubbles: true, composed: true })\n );\n\n await Promise.all([stopAnimations(this.dialog)]);\n const panelAnimation = getAnimation(this, 'dialog.hide');\n\n // The backdrop can only be animated with css because it's a pseudo element\n // so we add a animation class to trigger it at the same time as the dialog animation\n this.dialog.classList.add('backdrop:animate-fade-out');\n await animateTo(\n this.dialog,\n panelAnimation.keyframes,\n panelAnimation.options\n );\n this.dialog.classList.remove('backdrop:animate-fade-out');\n this.dialog.close();\n this.dialog.hidden = false;\n unlockBodyScrolling(this);\n this.dispatchEvent(\n new Event('mid-after-hide', { composed: true, bubbles: true })\n );\n }\n }\n\n /**\n * Shows the dialog.\n */\n async show() {\n if (this.open) {\n return undefined;\n }\n\n this.open = true;\n return waitForEvent(this, 'mid-after-show');\n }\n\n /**\n * Hides the dialog\n */\n async hide() {\n if (!this.open) {\n return undefined;\n }\n\n this.open = false;\n return waitForEvent(this, 'mid-after-hide');\n }\n\n constructor() {\n super();\n new LangController(this);\n }\n\n override render() {\n const t = getTranslations(getLang(this));\n\n return html`\n <dialog\n part=\"base\"\n id=\"dialog\"\n tabindex=\"-1\"\n role=${this.alertdialog ? 'alertdialog' : 'dialog'}\n aria-label=${this.heading || nothing}\n aria-modal=\"true\"\n class=\"text-neutral m-auto max-h-[calc(100%---spacing(8))] w-(--width) max-w-[calc(100%---spacing(8))] flex-col rounded-lg p-6 shadow-xl backdrop:bg-black/50 open:flex open:animate-none\"\n @cancel=${this.handleDialogCancel}\n @click=${this.handleBackdropClick}\n >\n <header part=\"header\" class=\"text-heading-sm flex justify-between\">\n <slot name=\"heading\"></slot>\n <div class=\"h-0\">\n <button\n autofocus\n aria-label=${t.close}\n @click=\"${() => this.requestClose('close-button')}\"\n class=\"text-body-md focus-visible:focus-ring text-neutral hover:bg-neutral-surface-hover float-right flex size-12 translate-x-3 -translate-y-3 items-center justify-center rounded\"\n >\n <mid-icon class=\"size-6\" name=\"xmark\"></mid-icon>\n </button>\n </div>\n </header>\n <div part=\"body\" class=\"overflow-auto\">\n <slot> </slot>\n </div>\n <footer part=\"footer\">\n <slot name=\"footer\"> </slot>\n </footer>\n </dialog>\n `;\n }\n}\n\nsetDefaultAnimation('dialog.show', {\n keyframes: [\n { opacity: 0, scale: 0.8 },\n { opacity: 1, scale: 1 },\n ],\n options: { duration: 250, easing: 'ease' },\n});\n\nsetDefaultAnimation('dialog.hide', {\n keyframes: [\n { opacity: 1, scale: 1 },\n { opacity: 0, scale: 0.8 },\n ],\n options: { duration: 250, easing: 'ease' },\n});\n\nsetDefaultAnimation('dialog.denyClose', {\n keyframes: [{ scale: 1 }, { scale: 1.03 }, { scale: 1 }],\n options: { duration: 100 },\n});\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"dialog.js","sources":["../../src/components/dialog.component.ts"],"sourcesContent":["import { css, html, LitElement, nothing } from 'lit';\nimport { customElement, property, query } from 'lit/decorators.js';\nimport { getLang } from '../utilities/lang';\nimport { getTranslations } from '../utilities/translations';\nimport { LangController } from '../controllers/lang.controller.js';\nimport { waitForEvent } from '../internal/event';\nimport { watch } from '../internal/watch';\nimport { styled } from '../mixins/tailwind.mixin';\nimport './icon/icon.component';\nimport {\n getAnimation,\n setDefaultAnimation,\n} from '../utilities/animation-registry';\nimport { animateTo, stopAnimations } from '../internal/animate';\nimport { lockBodyScrolling, unlockBodyScrolling } from '../internal/scroll';\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'mid-dialog': MinidDialog;\n }\n}\n\nconst styles = [\n css`\n :host {\n --width: 40rem;\n display: block;\n }\n `,\n];\n\n/**\n *\n * @event mid-show - Emitted when the dialog opens.\n * @event mid-after-show - Emitted after the dialog opens and all animations are complete.\n * @event mid-hide - Emitted when the dialog closes.\n * @event mid-after-hide - Emitted after the dialog closes and all animations are complete.\n * @event {{ source: 'close-button' | 'keyboard' | 'overlay' }} mid-request-close - Emitted when the user attempts to\n * close the dialog by clicking the close button, clicking the overlay, or pressing escape. Calling\n * `event.preventDefault()` will keep the dialog open. Avoid using this unless closing the dialog will result in\n * destructive behavior such as data loss.\n *\n * @csspart base - Select the base `dialog` element\n * @csspart header - Select the `header` element\n * @csspart body - Select the `article` element\n * @csspart footer - Select the `footer` element\n *\n * @slot -- The default slot for the body content of the dialog\n * @slot heading - The content inside the `h2` element\n * @slot footer - The content inside the `footer` element\n *\n * @animation dialog.show - The animation to use when showing the dialog.\n * @animation dialog.hide - The animation to use when hiding the dialog.\n * @animation dialog.denyClose - The animation to use when closing the dialog is prevented.\n *\n */\n@customElement('mid-dialog')\nexport class MinidDialog extends styled(LitElement, styles) {\n @query('#dialog')\n dialog!: HTMLDialogElement;\n\n /**\n * Indicates whether or not the dialog is open. You can toggle this attribute to show and hide the dialog, or you can\n * use the `show()` and `hide()` methods and this attribute will reflect the dialog's open state.\n */\n @property({ type: Boolean, reflect: true })\n open = false;\n\n /**\n * The dialog's label as displayed in the header. You should always include a relevant label even when using\n * `no-header`, as it is required for proper accessibility. If you need to display HTML, use the `label` slot instead.\n */\n @property({ reflect: true })\n heading = '';\n\n /**\n * When set, the dialog uses `role=\"alertdialog\"` instead of `role=\"dialog\"`, signalling urgency to screen readers.\n * Use this for blocking dialogs that require immediate user attention.\n */\n @property({ type: Boolean, reflect: true })\n alertdialog = false;\n\n private requestClose(source: 'close-button' | 'keyboard' | 'overlay') {\n const requestCloseEvent = new CustomEvent('mid-request-close', {\n bubbles: true,\n composed: true,\n cancelable: true,\n detail: { source },\n });\n this.dispatchEvent(requestCloseEvent);\n\n if (requestCloseEvent.defaultPrevented) {\n const { keyframes, options } = getAnimation(this, 'dialog.denyClose');\n animateTo(this.dialog, keyframes, options);\n return;\n }\n\n this.hide();\n }\n\n handleDialogCancel = (event: Event) => {\n event.preventDefault();\n\n if (event.defaultPrevented) {\n this.requestClose('keyboard');\n } else {\n this.hide();\n }\n };\n\n private handleBackdropClick({ target }: Event) {\n // if the target is the dialog, the backdrop was clicked\n if (target === this.dialog) {\n this.requestClose('overlay');\n }\n }\n\n @watch('open', { waitUntilFirstUpdate: true })\n async handleOpenChange() {\n if (this.open) {\n // Show\n this.dispatchEvent(\n new Event('mid-show', { composed: true, bubbles: true })\n );\n // this.addOpenListeners();\n this.dialog.showModal();\n\n if (this.alertdialog) {\n this.dialog.focus();\n }\n\n lockBodyScrolling(this);\n\n const panelAnimation = getAnimation(this, 'dialog.show');\n await animateTo(\n this.dialog,\n panelAnimation.keyframes,\n panelAnimation.options\n );\n this.dispatchEvent(\n new Event('mid-after-show', { bubbles: true, composed: true })\n );\n } else {\n // Hide\n this.dispatchEvent(\n new Event('mid-hide', { bubbles: true, composed: true })\n );\n\n await Promise.all([stopAnimations(this.dialog)]);\n const panelAnimation = getAnimation(this, 'dialog.hide');\n\n // The backdrop can only be animated with css because it's a pseudo element\n // so we add a animation class to trigger it at the same time as the dialog animation\n this.dialog.classList.add('backdrop:animate-fade-out');\n await animateTo(\n this.dialog,\n panelAnimation.keyframes,\n panelAnimation.options\n );\n this.dialog.classList.remove('backdrop:animate-fade-out');\n this.dialog.close();\n this.dialog.hidden = false;\n unlockBodyScrolling(this);\n this.dispatchEvent(\n new Event('mid-after-hide', { composed: true, bubbles: true })\n );\n }\n }\n\n /**\n * Shows the dialog.\n */\n async show() {\n if (this.open) {\n return undefined;\n }\n\n this.open = true;\n return waitForEvent(this, 'mid-after-show');\n }\n\n /**\n * Hides the dialog\n */\n async hide() {\n if (!this.open) {\n return undefined;\n }\n\n this.open = false;\n return waitForEvent(this, 'mid-after-hide');\n }\n\n constructor() {\n super();\n new LangController(this);\n }\n\n override render() {\n const t = getTranslations(getLang(this));\n\n return html`\n <dialog\n part=\"base\"\n id=\"dialog\"\n tabindex=\"-1\"\n role=${this.alertdialog ? 'alertdialog' : 'dialog'}\n aria-label=${this.heading || nothing}\n aria-modal=\"true\"\n class=\"text-neutral m-auto max-h-[calc(100%---spacing(8))] w-(--width) max-w-[calc(100%---spacing(8))] flex-col rounded-lg p-6 shadow-xl backdrop:bg-black/50 open:flex open:animate-none\"\n @cancel=${this.handleDialogCancel}\n @click=${this.handleBackdropClick}\n >\n <header part=\"header\" class=\"text-heading-sm flex justify-between\">\n <slot name=\"heading\"></slot>\n <div class=\"h-0\">\n <button\n autofocus\n aria-label=${t.close}\n @click=\"${() => this.requestClose('close-button')}\"\n class=\"text-body-md focus-visible:focus-ring text-neutral hover:bg-neutral-surface-hover float-right flex size-12 translate-x-3 -translate-y-3 items-center justify-center rounded\"\n >\n <mid-icon class=\"size-6\" name=\"xmark\"></mid-icon>\n </button>\n </div>\n </header>\n <div part=\"body\" class=\"overflow-auto\">\n <slot> </slot>\n </div>\n <footer part=\"footer\">\n <slot name=\"footer\"> </slot>\n </footer>\n </dialog>\n `;\n }\n}\n\nsetDefaultAnimation('dialog.show', {\n keyframes: [\n { opacity: 0, scale: 0.8 },\n { opacity: 1, scale: 1 },\n ],\n options: { duration: 250, easing: 'ease' },\n});\n\nsetDefaultAnimation('dialog.hide', {\n keyframes: [\n { opacity: 1, scale: 1 },\n { opacity: 0, scale: 0.8 },\n ],\n options: { duration: 250, easing: 'ease' },\n});\n\nsetDefaultAnimation('dialog.denyClose', {\n keyframes: [{ scale: 1 }, { scale: 1.03 }, { scale: 1 }],\n options: { duration: 100 },\n});\n"],"names":[],"mappings":"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"}
@@ -9,6 +9,7 @@ declare const MinidSpinner_base: typeof LitElement;
9
9
  * Size and color can be adjusted with `font-size` and `color` css properties
10
10
  */
11
11
  export declare class MinidSpinner extends MinidSpinner_base {
12
+ label: string;
12
13
  render(): import('lit').TemplateResult<1>;
13
14
  }
14
15
  export {};
@@ -1 +1 @@
1
- {"version":3,"file":"spinner.component.d.ts","sourceRoot":"","sources":["../../src/components/spinner.component.ts"],"names":[],"mappings":"AACA,OAAO,EAAa,UAAU,EAAE,MAAM,KAAK,CAAC;AAG5C,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,qBAAqB;QAC7B,aAAa,EAAE,YAAY,CAAC;KAC7B;CACF;;AAUD;;GAEG;AACH,qBACa,YAAa,SAAQ,iBAA0B;IACjD,MAAM;CA6BhB"}
1
+ {"version":3,"file":"spinner.component.d.ts","sourceRoot":"","sources":["../../src/components/spinner.component.ts"],"names":[],"mappings":"AACA,OAAO,EAAa,UAAU,EAAW,MAAM,KAAK,CAAC;AAGrD,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,qBAAqB;QAC7B,aAAa,EAAE,YAAY,CAAC;KAC7B;CACF;;AAUD;;GAEG;AACH,qBACa,YAAa,SAAQ,iBAA0B;IAG1D,KAAK,SAAM;IAEF,MAAM;CAiChB"}
@@ -1,12 +1,14 @@
1
- import { customElement } from "lit/decorators.js";
2
- import { css, LitElement, html } from "lit";
1
+ import { property, customElement } from "lit/decorators.js";
2
+ import { css, LitElement, nothing, html } from "lit";
3
3
  import { styled } from "../mixins/tailwind.mixin.js";
4
+ var __defProp = Object.defineProperty;
4
5
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
6
  var __decorateClass = (decorators, target, key, kind) => {
6
7
  var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
7
8
  for (var i = decorators.length - 1, decorator; i >= 0; i--)
8
9
  if (decorator = decorators[i])
9
- result = decorator(result) || result;
10
+ result = (kind ? decorator(target, key, result) : decorator(result)) || result;
11
+ if (kind && result) __defProp(target, key, result);
10
12
  return result;
11
13
  };
12
14
  const styles = [
@@ -17,13 +19,19 @@ const styles = [
17
19
  `
18
20
  ];
19
21
  let MinidSpinner = class extends styled(LitElement, styles) {
22
+ constructor() {
23
+ super(...arguments);
24
+ this.label = "";
25
+ }
20
26
  render() {
21
27
  return html`
28
+ ${!this.label ? nothing : html`<span class="sr-only">${this.label}</span>`}
22
29
  <svg
23
30
  class="animate-spin-slow"
24
31
  width="1em"
25
32
  height="1em"
26
33
  viewBox="0 0 50 50"
34
+ aria-hidden="true"
27
35
  >
28
36
  <circle
29
37
  opacity="0.2"
@@ -47,6 +55,9 @@ let MinidSpinner = class extends styled(LitElement, styles) {
47
55
  `;
48
56
  }
49
57
  };
58
+ __decorateClass([
59
+ property()
60
+ ], MinidSpinner.prototype, "label", 2);
50
61
  MinidSpinner = __decorateClass([
51
62
  customElement("mid-spinner")
52
63
  ], MinidSpinner);