@happyvertical/smrt-svelte 0.55.5 → 0.55.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (134) hide show
  1. package/AGENTS.md +17 -2
  2. package/README.md +83 -4
  3. package/agents/workspace.md +202 -5
  4. package/dist/browser-ai/adapters/stt/dictation-source.d.ts.map +1 -1
  5. package/dist/browser-ai/adapters/stt/dictation-source.js +8 -2
  6. package/dist/browser-ai/adapters/stt/factory.d.ts +6 -3
  7. package/dist/browser-ai/adapters/stt/factory.d.ts.map +1 -1
  8. package/dist/browser-ai/adapters/stt/factory.js +19 -9
  9. package/dist/browser-ai/adapters/stt/index.d.ts +3 -1
  10. package/dist/browser-ai/adapters/stt/index.d.ts.map +1 -1
  11. package/dist/browser-ai/adapters/stt/index.js +3 -1
  12. package/dist/browser-ai/adapters/stt/local-speech-model.d.ts +111 -0
  13. package/dist/browser-ai/adapters/stt/local-speech-model.d.ts.map +1 -0
  14. package/dist/browser-ai/adapters/stt/local-speech-model.js +377 -0
  15. package/dist/browser-ai/adapters/stt/local-speech.d.ts +82 -0
  16. package/dist/browser-ai/adapters/stt/local-speech.d.ts.map +1 -0
  17. package/dist/browser-ai/adapters/stt/local-speech.js +317 -0
  18. package/dist/browser-ai/adapters/stt/local-speech.worker.d.ts +2 -0
  19. package/dist/browser-ai/adapters/stt/local-speech.worker.d.ts.map +1 -0
  20. package/dist/browser-ai/adapters/stt/local-speech.worker.js +32 -0
  21. package/dist/browser-ai/adapters/stt/transformers-env.d.ts +8 -0
  22. package/dist/browser-ai/adapters/stt/transformers-env.d.ts.map +1 -0
  23. package/dist/browser-ai/adapters/stt/transformers-env.js +17 -0
  24. package/dist/browser-ai/adapters/stt/types.d.ts +54 -9
  25. package/dist/browser-ai/adapters/stt/types.d.ts.map +1 -1
  26. package/dist/browser-ai/capabilities/speech-probe.d.ts +29 -0
  27. package/dist/browser-ai/capabilities/speech-probe.d.ts.map +1 -0
  28. package/dist/browser-ai/capabilities/speech-probe.js +19 -0
  29. package/dist/browser-ai/core/errors.d.ts +9 -0
  30. package/dist/browser-ai/core/errors.d.ts.map +1 -1
  31. package/dist/browser-ai/core/errors.js +12 -0
  32. package/dist/browser-ai/index.d.ts +1 -0
  33. package/dist/browser-ai/index.d.ts.map +1 -1
  34. package/dist/browser-ai/index.js +1 -0
  35. package/dist/components/app/AppShell.svelte +587 -33
  36. package/dist/components/app/AppShell.svelte.d.ts +75 -4
  37. package/dist/components/app/AppShell.svelte.d.ts.map +1 -1
  38. package/dist/components/app/DockToggles.svelte +0 -1
  39. package/dist/components/app/dock-toggle.d.ts +7 -1
  40. package/dist/components/app/dock-toggle.d.ts.map +1 -1
  41. package/dist/components/app/index.d.ts +2 -1
  42. package/dist/components/app/index.d.ts.map +1 -1
  43. package/dist/components/app/slot-item.d.ts +21 -0
  44. package/dist/components/app/slot-item.d.ts.map +1 -0
  45. package/dist/components/app/slot-item.js +1 -0
  46. package/dist/components/audit/AuditList.svelte +51 -10
  47. package/dist/components/audit/AuditList.svelte.d.ts +13 -1
  48. package/dist/components/audit/AuditList.svelte.d.ts.map +1 -1
  49. package/dist/components/audit/RecordHistory.svelte +168 -10
  50. package/dist/components/audit/RecordHistory.svelte.d.ts +11 -1
  51. package/dist/components/audit/RecordHistory.svelte.d.ts.map +1 -1
  52. package/dist/components/audit/index.d.ts +1 -1
  53. package/dist/components/audit/index.d.ts.map +1 -1
  54. package/dist/components/audit/types.d.ts +18 -0
  55. package/dist/components/audit/types.d.ts.map +1 -1
  56. package/dist/components/forms/AddressInput.svelte +2 -15
  57. package/dist/components/forms/AddressInput.svelte.d.ts.map +1 -1
  58. package/dist/components/forms/DateRangeInput.svelte +2 -15
  59. package/dist/components/forms/DateRangeInput.svelte.d.ts.map +1 -1
  60. package/dist/components/forms/DateTimeInput.svelte +8 -15
  61. package/dist/components/forms/DateTimeInput.svelte.d.ts.map +1 -1
  62. package/dist/components/forms/MeasurementInput.svelte +2 -15
  63. package/dist/components/forms/MeasurementInput.svelte.d.ts.map +1 -1
  64. package/dist/components/forms/MoneyInput.svelte +8 -25
  65. package/dist/components/forms/MoneyInput.svelte.d.ts.map +1 -1
  66. package/dist/components/forms/NumberInput.svelte +7 -15
  67. package/dist/components/forms/NumberInput.svelte.d.ts.map +1 -1
  68. package/dist/components/forms/PhoneInput.svelte +7 -15
  69. package/dist/components/forms/PhoneInput.svelte.d.ts.map +1 -1
  70. package/dist/components/forms/SelectInput.svelte +34 -40
  71. package/dist/components/forms/SelectInput.svelte.d.ts.map +1 -1
  72. package/dist/components/forms/TextInput.svelte +20 -37
  73. package/dist/components/forms/TextInput.svelte.d.ts.map +1 -1
  74. package/dist/components/forms/TextareaInput.svelte +19 -35
  75. package/dist/components/forms/TextareaInput.svelte.d.ts.map +1 -1
  76. package/dist/components/sortable/Sortable.svelte +6 -33
  77. package/dist/components/sortable/Sortable.svelte.d.ts.map +1 -1
  78. package/dist/components/sortable/announce.d.ts +5 -0
  79. package/dist/components/sortable/announce.d.ts.map +1 -0
  80. package/dist/components/sortable/announce.js +33 -0
  81. package/dist/components/workspace/admin-shell/AdminShell.svelte +424 -58
  82. package/dist/components/workspace/admin-shell/AdminShell.svelte.d.ts +27 -2
  83. package/dist/components/workspace/admin-shell/AdminShell.svelte.d.ts.map +1 -1
  84. package/dist/components/workspace/admin-shell/ShellBrand.svelte +86 -0
  85. package/dist/components/workspace/admin-shell/ShellBrand.svelte.d.ts +18 -0
  86. package/dist/components/workspace/admin-shell/ShellBrand.svelte.d.ts.map +1 -0
  87. package/dist/components/workspace/admin-shell/ShellIconButton.svelte +77 -0
  88. package/dist/components/workspace/admin-shell/ShellIconButton.svelte.d.ts +26 -0
  89. package/dist/components/workspace/admin-shell/ShellIconButton.svelte.d.ts.map +1 -0
  90. package/dist/components/workspace/admin-shell/ShellLayoutEditor.svelte +88 -16
  91. package/dist/components/workspace/admin-shell/ShellLayoutEditor.svelte.d.ts.map +1 -1
  92. package/dist/components/workspace/admin-shell/ShellNavEditor.svelte +531 -0
  93. package/dist/components/workspace/admin-shell/ShellNavEditor.svelte.d.ts +26 -0
  94. package/dist/components/workspace/admin-shell/ShellNavEditor.svelte.d.ts.map +1 -0
  95. package/dist/components/workspace/admin-shell/ShellSectionIcon.svelte +57 -0
  96. package/dist/components/workspace/admin-shell/ShellSectionIcon.svelte.d.ts +22 -0
  97. package/dist/components/workspace/admin-shell/ShellSectionIcon.svelte.d.ts.map +1 -0
  98. package/dist/components/workspace/admin-shell/ShellSectionMenu.svelte +290 -0
  99. package/dist/components/workspace/admin-shell/ShellSectionMenu.svelte.d.ts +32 -0
  100. package/dist/components/workspace/admin-shell/ShellSectionMenu.svelte.d.ts.map +1 -0
  101. package/dist/components/workspace/admin-shell/TenantNav.svelte +67 -2
  102. package/dist/components/workspace/admin-shell/TenantNav.svelte.d.ts +8 -0
  103. package/dist/components/workspace/admin-shell/TenantNav.svelte.d.ts.map +1 -1
  104. package/dist/components/workspace/admin-shell/layout-controller.svelte.d.ts +67 -0
  105. package/dist/components/workspace/admin-shell/layout-controller.svelte.d.ts.map +1 -1
  106. package/dist/components/workspace/admin-shell/layout-controller.svelte.js +129 -1
  107. package/dist/components/workspace/admin-shell/layout.d.ts +122 -0
  108. package/dist/components/workspace/admin-shell/layout.d.ts.map +1 -1
  109. package/dist/components/workspace/admin-shell/layout.js +410 -10
  110. package/dist/components/workspace/admin-shell/settings.js +4 -4
  111. package/dist/components/workspace/admin-shell/shell-icons.d.ts +45 -0
  112. package/dist/components/workspace/admin-shell/shell-icons.d.ts.map +1 -0
  113. package/dist/components/workspace/admin-shell/shell-icons.js +66 -0
  114. package/dist/components/workspace/admin-shell/slot-labels.d.ts +33 -0
  115. package/dist/components/workspace/admin-shell/slot-labels.d.ts.map +1 -0
  116. package/dist/components/workspace/admin-shell/slot-labels.js +33 -0
  117. package/dist/components/workspace/admin-shell/slots.d.ts +50 -0
  118. package/dist/components/workspace/admin-shell/slots.d.ts.map +1 -0
  119. package/dist/components/workspace/admin-shell/slots.js +77 -0
  120. package/dist/components/workspace/admin-shell/state.svelte.d.ts +35 -0
  121. package/dist/components/workspace/admin-shell/state.svelte.d.ts.map +1 -1
  122. package/dist/components/workspace/admin-shell/state.svelte.js +111 -0
  123. package/dist/components/workspace/admin-shell/types.d.ts +101 -1
  124. package/dist/components/workspace/admin-shell/types.d.ts.map +1 -1
  125. package/dist/components/workspace/index.d.ts +9 -2
  126. package/dist/components/workspace/index.d.ts.map +1 -1
  127. package/dist/components/workspace/index.js +6 -0
  128. package/dist/i18n/strings.workspace.d.ts +49 -0
  129. package/dist/i18n/strings.workspace.d.ts.map +1 -1
  130. package/dist/i18n/strings.workspace.js +49 -0
  131. package/package.json +30 -19
  132. package/dist/browser-ai/adapters/stt/whisper-wasm.d.ts +0 -51
  133. package/dist/browser-ai/adapters/stt/whisper-wasm.d.ts.map +0 -1
  134. package/dist/browser-ai/adapters/stt/whisper-wasm.js +0 -380
@@ -1,4 +1,5 @@
1
1
  <script lang="ts">
2
+ import { FieldLabel } from '@happyvertical/smrt-ui/forms';
2
3
  import { useI18n } from '@happyvertical/smrt-ui/i18n';
3
4
  import { untrack } from 'svelte';
4
5
  import { useAppState } from '../../hooks/useAppState.svelte.js';
@@ -396,10 +397,7 @@ function handleCountryChange(e: Event) {
396
397
 
397
398
  <div class="smrt-address" data-smrt-field-owner={fieldOwnerToken}>
398
399
  {#if label}
399
- <label class="smrt-label" for={primaryFieldId}>
400
- {label}
401
- {#if required}<span class="required">*</span>{/if}
402
- </label>
400
+ <FieldLabel for={primaryFieldId} {label} {required} />
403
401
  {/if}
404
402
 
405
403
  <div class="address-fields" class:smrt-mode={isSmrt}>
@@ -525,17 +523,6 @@ function handleCountryChange(e: Event) {
525
523
  gap: var(--smrt-spacing-2, 8px);
526
524
  }
527
525
 
528
- .smrt-label {
529
- font-size: var(--smrt-typography-body-medium-size, 0.875rem);
530
- font-weight: var(--smrt-typography-body-medium-weight, 500);
531
- color: var(--smrt-color-on-surface, #374151);
532
- }
533
-
534
- .smrt-label .required {
535
- color: var(--smrt-color-error, #ba1a1a);
536
- margin-left: var(--smrt-spacing-1, 4px);
537
- }
538
-
539
526
  .address-fields {
540
527
  display: flex;
541
528
  flex-direction: column;
@@ -1 +1 @@
1
- {"version":3,"file":"AddressInput.svelte.d.ts","sourceRoot":"","sources":["../../../src/components/forms/AddressInput.svelte.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAG/C,KAAK,YAAY,GAAG,MAAM,YAAY,CAAC;AAEvC,MAAM,WAAW,KAAK;IACpB,wBAAwB;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,kBAAkB;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,uCAAuC;IACvC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,+BAA+B;IAC/B,KAAK,CAAC,EAAE,OAAO,CAAC,YAAY,CAAC,CAAC;IAC9B,2BAA2B;IAC3B,MAAM,CAAC,EAAE,YAAY,EAAE,CAAC;IACxB,qBAAqB;IACrB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,qBAAqB;IACrB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,+BAA+B;IAC/B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,mCAAmC;IACnC,SAAS,CAAC,EAAE,KAAK,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACpD,0CAA0C;IAC1C,SAAS,CAAC,EAAE,KAAK,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACpD,gCAAgC;IAChC,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,CAAC,YAAY,CAAC,KAAK,IAAI,CAAC;CACnD;AAibD,QAAA,MAAM,YAAY,gDAAwC,CAAC;AAC3D,KAAK,YAAY,GAAG,UAAU,CAAC,OAAO,YAAY,CAAC,CAAC;AACpD,eAAe,YAAY,CAAC"}
1
+ {"version":3,"file":"AddressInput.svelte.d.ts","sourceRoot":"","sources":["../../../src/components/forms/AddressInput.svelte.ts"],"names":[],"mappings":"AAgBA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAG/C,KAAK,YAAY,GAAG,MAAM,YAAY,CAAC;AAEvC,MAAM,WAAW,KAAK;IACpB,wBAAwB;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,kBAAkB;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,uCAAuC;IACvC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,+BAA+B;IAC/B,KAAK,CAAC,EAAE,OAAO,CAAC,YAAY,CAAC,CAAC;IAC9B,2BAA2B;IAC3B,MAAM,CAAC,EAAE,YAAY,EAAE,CAAC;IACxB,qBAAqB;IACrB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,qBAAqB;IACrB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,+BAA+B;IAC/B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,mCAAmC;IACnC,SAAS,CAAC,EAAE,KAAK,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACpD,0CAA0C;IAC1C,SAAS,CAAC,EAAE,KAAK,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACpD,gCAAgC;IAChC,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,CAAC,YAAY,CAAC,KAAK,IAAI,CAAC;CACnD;AA+aD,QAAA,MAAM,YAAY,gDAAwC,CAAC;AAC3D,KAAK,YAAY,GAAG,UAAU,CAAC,OAAO,YAAY,CAAC,CAAC;AACpD,eAAe,YAAY,CAAC"}
@@ -1,4 +1,5 @@
1
1
  <script lang="ts">
2
+ import { FieldLabel } from '@happyvertical/smrt-ui/forms';
2
3
  import { useI18n } from '@happyvertical/smrt-ui/i18n';
3
4
  import { useAppState } from '../../hooks/useAppState.svelte.js';
4
5
  import { useSTT } from '../../hooks/useSTT.svelte.js';
@@ -452,10 +453,7 @@ const primaryControlId = $derived(isSmrt ? `${name}_voice` : `${name}_start`);
452
453
  data-smrt-field-owner={fieldOwnerToken}
453
454
  >
454
455
  {#if label}
455
- <label class="smrt-label" for={primaryControlId}>
456
- {label}
457
- {#if required}<span class="required">*</span>{/if}
458
- </label>
456
+ <FieldLabel for={primaryControlId} {label} {required} />
459
457
  {/if}
460
458
 
461
459
  <div class="range-wrapper" class:smrt-mode={isSmrt} class:invalid={showInvalid}>
@@ -575,17 +573,6 @@ const primaryControlId = $derived(isSmrt ? `${name}_voice` : `${name}_start`);
575
573
  gap: var(--smrt-spacing-2, 8px);
576
574
  }
577
575
 
578
- .smrt-label {
579
- font-size: var(--smrt-typography-body-medium-size, 0.875rem);
580
- font-weight: var(--smrt-typography-body-medium-weight, 500);
581
- color: var(--smrt-color-on-surface, #374151);
582
- }
583
-
584
- .smrt-label .required {
585
- color: var(--smrt-color-error, #ba1a1a);
586
- margin-left: var(--smrt-spacing-1, 4px);
587
- }
588
-
589
576
  .range-wrapper {
590
577
  display: flex;
591
578
  flex-direction: column;
@@ -1 +1 @@
1
- {"version":3,"file":"DateRangeInput.svelte.d.ts","sourceRoot":"","sources":["../../../src/components/forms/DateRangeInput.svelte.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAGjD,MAAM,WAAW,KAAK;IACpB,iBAAiB;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,kBAAkB;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,uCAAuC;IACvC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,6BAA6B;IAC7B,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,2BAA2B;IAC3B,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,2DAA2D;IAC3D,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,yDAAyD;IACzD,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,8BAA8B;IAC9B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,8BAA8B;IAC9B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,qBAAqB;IACrB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,qBAAqB;IACrB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,+BAA+B;IAC/B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,gCAAgC;IAChC,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,cAAc,KAAK,IAAI,CAAC;CAC5C;AAkfD,QAAA,MAAM,cAAc,gEAAwC,CAAC;AAC7D,KAAK,cAAc,GAAG,UAAU,CAAC,OAAO,cAAc,CAAC,CAAC;AACxD,eAAe,cAAc,CAAC"}
1
+ {"version":3,"file":"DateRangeInput.svelte.d.ts","sourceRoot":"","sources":["../../../src/components/forms/DateRangeInput.svelte.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAGjD,MAAM,WAAW,KAAK;IACpB,iBAAiB;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,kBAAkB;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,uCAAuC;IACvC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,6BAA6B;IAC7B,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,2BAA2B;IAC3B,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,2DAA2D;IAC3D,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,yDAAyD;IACzD,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,8BAA8B;IAC9B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,8BAA8B;IAC9B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,qBAAqB;IACrB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,qBAAqB;IACrB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,+BAA+B;IAC/B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,gCAAgC;IAChC,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,cAAc,KAAK,IAAI,CAAC;CAC5C;AAgfD,QAAA,MAAM,cAAc,gEAAwC,CAAC;AAC7D,KAAK,cAAc,GAAG,UAAU,CAAC,OAAO,cAAc,CAAC,CAAC;AACxD,eAAe,cAAc,CAAC"}
@@ -1,4 +1,5 @@
1
1
  <script lang="ts">
2
+ import { FieldLabel } from '@happyvertical/smrt-ui/forms';
2
3
  import { useI18n } from '@happyvertical/smrt-ui/i18n';
3
4
  import { importOptional } from '@happyvertical/smrt-ui/utils/import-optional.js';
4
5
  import { useAppState } from '../../hooks/useAppState.svelte.js';
@@ -48,6 +49,10 @@ let {
48
49
  onchange,
49
50
  }: Props = $props();
50
51
 
52
+ // Unique per instance: the control stays labelled even if `name` repeats on a page.
53
+ const uid = $props.id();
54
+ const labelId = `${uid}-label`;
55
+
51
56
  const app = useAppState();
52
57
  const stt = useSTT();
53
58
  const formContext = tryGetFormContext();
@@ -346,10 +351,7 @@ function handleNativeChange(e: Event) {
346
351
 
347
352
  <div class="smrt-datetime" class:listening={isHolding} class:parsing={isParsing}>
348
353
  {#if label}
349
- <label for={name} class="smrt-label">
350
- {label}
351
- {#if required}<span class="required">*</span>{/if}
352
- </label>
354
+ <FieldLabel id={labelId} for={name} {label} {required} />
353
355
  {/if}
354
356
 
355
357
  <div class="input-wrapper">
@@ -358,6 +360,7 @@ function handleNativeChange(e: Event) {
358
360
  <input
359
361
  bind:this={inputEl}
360
362
  id={name}
363
+ aria-labelledby={label ? labelId : undefined}
361
364
  name={name}
362
365
  type="text"
363
366
  placeholder={placeholder}
@@ -423,6 +426,7 @@ function handleNativeChange(e: Event) {
423
426
  <input
424
427
  bind:this={inputEl}
425
428
  id={name}
429
+ aria-labelledby={label ? labelId : undefined}
426
430
  name={name}
427
431
  type={includeTime ? 'datetime-local' : 'date'}
428
432
  value={value}
@@ -455,17 +459,6 @@ function handleNativeChange(e: Event) {
455
459
  position: relative;
456
460
  }
457
461
 
458
- .smrt-label {
459
- font-size: var(--smrt-typography-body-medium-size, 0.875rem);
460
- font-weight: var(--smrt-typography-body-medium-weight, 500);
461
- color: var(--smrt-color-on-surface, #374151);
462
- }
463
-
464
- .smrt-label .required {
465
- color: var(--smrt-color-error, #ba1a1a);
466
- margin-left: var(--smrt-spacing-1, 4px);
467
- }
468
-
469
462
  .input-wrapper {
470
463
  display: flex;
471
464
  position: relative;
@@ -1 +1 @@
1
- {"version":3,"file":"DateTimeInput.svelte.d.ts","sourceRoot":"","sources":["../../../src/components/forms/DateTimeInput.svelte.ts"],"names":[],"mappings":"AAgBA,MAAM,WAAW,KAAK;IACpB,iBAAiB;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,kBAAkB;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,2CAA2C;IAC3C,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,iCAAiC;IACjC,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,uBAAuB;IACvB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,2CAA2C;IAC3C,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,qBAAqB;IACrB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,qBAAqB;IACrB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,gCAAgC;IAChC,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;CACpC;AAiXD,QAAA,MAAM,aAAa,gDAAwC,CAAC;AAC5D,KAAK,aAAa,GAAG,UAAU,CAAC,OAAO,aAAa,CAAC,CAAC;AACtD,eAAe,aAAa,CAAC"}
1
+ {"version":3,"file":"DateTimeInput.svelte.d.ts","sourceRoot":"","sources":["../../../src/components/forms/DateTimeInput.svelte.ts"],"names":[],"mappings":"AAiBA,MAAM,WAAW,KAAK;IACpB,iBAAiB;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,kBAAkB;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,2CAA2C;IAC3C,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,iCAAiC;IACjC,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,uBAAuB;IACvB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,2CAA2C;IAC3C,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,qBAAqB;IACrB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,qBAAqB;IACrB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,gCAAgC;IAChC,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;CACpC;AAmXD,QAAA,MAAM,aAAa,gDAAwC,CAAC;AAC5D,KAAK,aAAa,GAAG,UAAU,CAAC,OAAO,aAAa,CAAC,CAAC;AACtD,eAAe,aAAa,CAAC"}
@@ -1,4 +1,5 @@
1
1
  <script lang="ts">
2
+ import { FieldLabel } from '@happyvertical/smrt-ui/forms';
2
3
  import { useI18n } from '@happyvertical/smrt-ui/i18n';
3
4
  import { useAppState } from '../../hooks/useAppState.svelte.js';
4
5
  import { M } from '../../i18n/strings.forms.js';
@@ -442,10 +443,7 @@ function handleUnitChange(e: Event) {
442
443
 
443
444
  <div class="smrt-measurement" data-smrt-field-owner={fieldOwnerToken}>
444
445
  {#if label}
445
- <label for={name} class="smrt-label">
446
- {label}
447
- {#if required}<span class="required">*</span>{/if}
448
- </label>
446
+ <FieldLabel for={name} {label} {required} />
449
447
  {/if}
450
448
 
451
449
  <div class="input-wrapper" class:smrt-mode={isSmrt}>
@@ -505,17 +503,6 @@ function handleUnitChange(e: Event) {
505
503
  gap: var(--smrt-spacing-1, 4px);
506
504
  }
507
505
 
508
- .smrt-label {
509
- font-size: var(--smrt-typography-body-medium-size, 0.875rem);
510
- font-weight: var(--smrt-typography-body-medium-weight, 500);
511
- color: var(--smrt-color-on-surface, #374151);
512
- }
513
-
514
- .smrt-label .required {
515
- color: var(--smrt-color-error, #ba1a1a);
516
- margin-left: var(--smrt-spacing-1, 4px);
517
- }
518
-
519
506
  .input-wrapper {
520
507
  display: flex;
521
508
  gap: var(--smrt-spacing-2, 8px);
@@ -1 +1 @@
1
- {"version":3,"file":"MeasurementInput.svelte.d.ts","sourceRoot":"","sources":["../../../src/components/forms/MeasurementInput.svelte.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAGpE,MAAM,WAAW,KAAK;IACpB,iBAAiB;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,kBAAkB;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,uCAAuC;IACvC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,uBAAuB;IACvB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,+BAA+B;IAC/B,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,8BAA8B;IAC9B,IAAI,CAAC,EAAE,eAAe,CAAC;IACvB,qCAAqC;IACrC,KAAK,CAAC,EAAE,eAAe,EAAE,CAAC;IAC1B,oBAAoB;IACpB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,oBAAoB;IACpB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,qBAAqB;IACrB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,qBAAqB;IACrB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,qBAAqB;IACrB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,+BAA+B;IAC/B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,gCAAgC;IAChC,QAAQ,CAAC,EAAE,CAAC,WAAW,EAAE,gBAAgB,GAAG,IAAI,KAAK,IAAI,CAAC;CAC3D;AAgcD,QAAA,MAAM,gBAAgB,yDAAwC,CAAC;AAC/D,KAAK,gBAAgB,GAAG,UAAU,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAC5D,eAAe,gBAAgB,CAAC"}
1
+ {"version":3,"file":"MeasurementInput.svelte.d.ts","sourceRoot":"","sources":["../../../src/components/forms/MeasurementInput.svelte.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAGpE,MAAM,WAAW,KAAK;IACpB,iBAAiB;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,kBAAkB;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,uCAAuC;IACvC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,uBAAuB;IACvB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,+BAA+B;IAC/B,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,8BAA8B;IAC9B,IAAI,CAAC,EAAE,eAAe,CAAC;IACvB,qCAAqC;IACrC,KAAK,CAAC,EAAE,eAAe,EAAE,CAAC;IAC1B,oBAAoB;IACpB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,oBAAoB;IACpB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,qBAAqB;IACrB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,qBAAqB;IACrB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,qBAAqB;IACrB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,+BAA+B;IAC/B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,gCAAgC;IAChC,QAAQ,CAAC,EAAE,CAAC,WAAW,EAAE,gBAAgB,GAAG,IAAI,KAAK,IAAI,CAAC;CAC3D;AA8bD,QAAA,MAAM,gBAAgB,yDAAwC,CAAC;AAC/D,KAAK,gBAAgB,GAAG,UAAU,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAC5D,eAAe,gBAAgB,CAAC"}
@@ -1,4 +1,5 @@
1
1
  <script lang="ts">
2
+ import { FieldLabel } from '@happyvertical/smrt-ui/forms';
2
3
  import { useI18n } from '@happyvertical/smrt-ui/i18n';
3
4
  import { useAppState } from '../../hooks/useAppState.svelte.js';
4
5
  import { M } from '../../i18n/strings.forms.js';
@@ -52,6 +53,10 @@ let {
52
53
  onchange,
53
54
  }: Props = $props();
54
55
 
56
+ // Unique per instance: the control stays labelled even if `name` repeats on a page.
57
+ const uid = $props.id();
58
+ const labelId = `${uid}-label`;
59
+
55
60
  const app = useAppState();
56
61
  const formContext = tryGetFormContext();
57
62
 
@@ -305,16 +310,13 @@ function handleBlur() {
305
310
 
306
311
  <div class="smrt-money">
307
312
  {#if label}
308
- <label for={name} class="smrt-label">
309
- {label}
310
- {#if required}<span class="required">*</span>{/if}
311
- </label>
313
+ <FieldLabel id={labelId} for={name} {label} {required} />
312
314
  {/if}
313
315
 
314
316
  <div class="input-wrapper">
315
- <span class="currency-symbol">{currencySymbol}</span>
316
317
  <input
317
318
  id={name}
319
+ aria-labelledby={label ? labelId : undefined}
318
320
  {name}
319
321
  type="text"
320
322
  inputmode="decimal"
@@ -357,31 +359,12 @@ function handleBlur() {
357
359
  gap: var(--smrt-spacing-1, 4px);
358
360
  }
359
361
 
360
- .smrt-label {
361
- font-size: var(--smrt-typography-body-medium-size, 0.875rem);
362
- font-weight: var(--smrt-typography-body-medium-weight, 500);
363
- color: var(--smrt-color-on-surface, #374151);
364
- }
365
-
366
- .smrt-label .required {
367
- color: var(--smrt-color-error, #ba1a1a);
368
- margin-left: var(--smrt-spacing-1, 4px);
369
- }
370
-
371
362
  .input-wrapper {
372
363
  display: flex;
373
364
  position: relative;
374
365
  align-items: center;
375
366
  }
376
367
 
377
- .currency-symbol {
378
- position: absolute;
379
- left: 12px;
380
- color: var(--smrt-color-on-surface-variant, #6b7280);
381
- font-size: var(--smrt-typography-body-large-size, 1rem);
382
- pointer-events: none;
383
- }
384
-
385
368
  .currency-code {
386
369
  position: absolute;
387
370
  right: 12px;
@@ -393,7 +376,7 @@ function handleBlur() {
393
376
 
394
377
  .smrt-input {
395
378
  flex: 1;
396
- padding: var(--smrt-spacing-2, 8px) var(--smrt-spacing-12, 48px) var(--smrt-spacing-2, 8px) var(--smrt-spacing-7, 28px);
379
+ padding: var(--smrt-spacing-2, 8px) var(--smrt-spacing-12, 48px) var(--smrt-spacing-2, 8px) var(--smrt-spacing-3, 12px);
397
380
  font-size: var(--smrt-typography-body-large-size, 1rem);
398
381
  border: 1px solid var(--smrt-color-outline-variant, #d1d5db);
399
382
  border-radius: var(--smrt-radius-small, 6px);
@@ -1 +1 @@
1
- {"version":3,"file":"MoneyInput.svelte.d.ts","sourceRoot":"","sources":["../../../src/components/forms/MoneyInput.svelte.ts"],"names":[],"mappings":"AAaA,MAAM,WAAW,KAAK;IACpB,iBAAiB;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,kBAAkB;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,uCAAuC;IACvC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,uBAAuB;IACvB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,wCAAwC;IACxC,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,gCAAgC;IAChC,QAAQ,CAAC,EAAE,KAAK,GAAG,KAAK,CAAC;IACzB,6BAA6B;IAC7B,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,6BAA6B;IAC7B,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,qBAAqB;IACrB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,qBAAqB;IACrB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,+BAA+B;IAC/B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,gCAAgC;IAChC,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,CAAC;CAC3C;AAuTD,QAAA,MAAM,UAAU,gDAAwC,CAAC;AACzD,KAAK,UAAU,GAAG,UAAU,CAAC,OAAO,UAAU,CAAC,CAAC;AAChD,eAAe,UAAU,CAAC"}
1
+ {"version":3,"file":"MoneyInput.svelte.d.ts","sourceRoot":"","sources":["../../../src/components/forms/MoneyInput.svelte.ts"],"names":[],"mappings":"AAcA,MAAM,WAAW,KAAK;IACpB,iBAAiB;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,kBAAkB;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,uCAAuC;IACvC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,uBAAuB;IACvB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,wCAAwC;IACxC,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,gCAAgC;IAChC,QAAQ,CAAC,EAAE,KAAK,GAAG,KAAK,CAAC;IACzB,6BAA6B;IAC7B,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,6BAA6B;IAC7B,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,qBAAqB;IACrB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,qBAAqB;IACrB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,+BAA+B;IAC/B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,gCAAgC;IAChC,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,CAAC;CAC3C;AAwTD,QAAA,MAAM,UAAU,gDAAwC,CAAC;AACzD,KAAK,UAAU,GAAG,UAAU,CAAC,OAAO,UAAU,CAAC,CAAC;AAChD,eAAe,UAAU,CAAC"}
@@ -1,4 +1,5 @@
1
1
  <script lang="ts">
2
+ import { FieldLabel } from '@happyvertical/smrt-ui/forms';
2
3
  import { useI18n } from '@happyvertical/smrt-ui/i18n';
3
4
  import { useAppState } from '../../hooks/useAppState.svelte.js';
4
5
  import { M } from '../../i18n/strings.forms.js';
@@ -49,6 +50,10 @@ let {
49
50
  onchange,
50
51
  }: Props = $props();
51
52
 
53
+ // Unique per instance: the control stays labelled even if `name` repeats on a page.
54
+ const uid = $props.id();
55
+ const labelId = `${uid}-label`;
56
+
52
57
  const app = useAppState();
53
58
  const formContext = tryGetFormContext();
54
59
 
@@ -230,15 +235,13 @@ function handleInput(e: Event) {
230
235
 
231
236
  <div class="smrt-number">
232
237
  {#if label}
233
- <label for={name} class="smrt-label">
234
- {label}
235
- {#if required}<span class="required">*</span>{/if}
236
- </label>
238
+ <FieldLabel id={labelId} for={name} {label} {required} />
237
239
  {/if}
238
240
 
239
241
  <div class="input-wrapper">
240
242
  <input
241
243
  id={name}
244
+ aria-labelledby={label ? labelId : undefined}
242
245
  {name}
243
246
  type="number"
244
247
  {placeholder}
@@ -277,17 +280,6 @@ function handleInput(e: Event) {
277
280
  gap: var(--smrt-spacing-1, 4px);
278
281
  }
279
282
 
280
- .smrt-label {
281
- font-size: var(--smrt-typography-body-medium-size, 0.875rem);
282
- font-weight: var(--smrt-typography-body-medium-weight, 500);
283
- color: var(--smrt-color-on-surface, #374151);
284
- }
285
-
286
- .smrt-label .required {
287
- color: var(--smrt-color-error, #ba1a1a);
288
- margin-left: var(--smrt-spacing-1, 4px);
289
- }
290
-
291
283
  .input-wrapper {
292
284
  display: flex;
293
285
  position: relative;
@@ -1 +1 @@
1
- {"version":3,"file":"NumberInput.svelte.d.ts","sourceRoot":"","sources":["../../../src/components/forms/NumberInput.svelte.ts"],"names":[],"mappings":"AAaA,MAAM,WAAW,KAAK;IACpB,iBAAiB;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,kBAAkB;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,uCAAuC;IACvC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,uBAAuB;IACvB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,+BAA+B;IAC/B,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,oBAAoB;IACpB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,oBAAoB;IACpB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,qBAAqB;IACrB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,qBAAqB;IACrB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,qBAAqB;IACrB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,gCAAgC;IAChC,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,CAAC;CAC3C;AAuOD,QAAA,MAAM,WAAW,gDAAwC,CAAC;AAC1D,KAAK,WAAW,GAAG,UAAU,CAAC,OAAO,WAAW,CAAC,CAAC;AAClD,eAAe,WAAW,CAAC"}
1
+ {"version":3,"file":"NumberInput.svelte.d.ts","sourceRoot":"","sources":["../../../src/components/forms/NumberInput.svelte.ts"],"names":[],"mappings":"AAcA,MAAM,WAAW,KAAK;IACpB,iBAAiB;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,kBAAkB;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,uCAAuC;IACvC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,uBAAuB;IACvB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,+BAA+B;IAC/B,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,oBAAoB;IACpB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,oBAAoB;IACpB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,qBAAqB;IACrB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,qBAAqB;IACrB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,qBAAqB;IACrB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,gCAAgC;IAChC,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,CAAC;CAC3C;AAyOD,QAAA,MAAM,WAAW,gDAAwC,CAAC;AAC1D,KAAK,WAAW,GAAG,UAAU,CAAC,OAAO,WAAW,CAAC,CAAC;AAClD,eAAe,WAAW,CAAC"}
@@ -1,4 +1,5 @@
1
1
  <script lang="ts">
2
+ import { FieldLabel } from '@happyvertical/smrt-ui/forms';
2
3
  import { useI18n } from '@happyvertical/smrt-ui/i18n';
3
4
  import { useAppState } from '../../hooks/useAppState.svelte.js';
4
5
  import { useSTT } from '../../hooks/useSTT.svelte.js';
@@ -45,6 +46,10 @@ let {
45
46
  onchange,
46
47
  }: Props = $props();
47
48
 
49
+ // Unique per instance: the control stays labelled even if `name` repeats on a page.
50
+ const uid = $props.id();
51
+ const labelId = `${uid}-label`;
52
+
48
53
  const app = useAppState();
49
54
  const stt = useSTT();
50
55
  const formContext = tryGetFormContext();
@@ -295,16 +300,14 @@ function handleInput(e: Event) {
295
300
 
296
301
  <div class="smrt-phone" class:listening={isHolding}>
297
302
  {#if label}
298
- <label for={name} class="smrt-label">
299
- {label}
300
- {#if required}<span class="required">*</span>{/if}
301
- </label>
303
+ <FieldLabel id={labelId} for={name} {label} {required} />
302
304
  {/if}
303
305
 
304
306
  <div class="input-wrapper">
305
307
  <input
306
308
  bind:this={inputEl}
307
309
  id={name}
310
+ aria-labelledby={label ? labelId : undefined}
308
311
  {name}
309
312
  type="tel"
310
313
  {placeholder}
@@ -395,17 +398,6 @@ function handleInput(e: Event) {
395
398
  position: relative;
396
399
  }
397
400
 
398
- .smrt-label {
399
- font-size: var(--smrt-typography-body-medium-size, 0.875rem);
400
- font-weight: var(--smrt-typography-body-medium-weight, 500);
401
- color: var(--smrt-color-on-surface, #374151);
402
- }
403
-
404
- .smrt-label .required {
405
- color: var(--smrt-color-error, #ba1a1a);
406
- margin-left: var(--smrt-spacing-1, 4px);
407
- }
408
-
409
401
  .input-wrapper {
410
402
  display: flex;
411
403
  position: relative;
@@ -1 +1 @@
1
- {"version":3,"file":"PhoneInput.svelte.d.ts","sourceRoot":"","sources":["../../../src/components/forms/PhoneInput.svelte.ts"],"names":[],"mappings":"AAkBA,MAAM,WAAW,KAAK;IACpB,iBAAiB;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,kBAAkB;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,uCAAuC;IACvC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,uBAAuB;IACvB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,+BAA+B;IAC/B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,qBAAqB;IACrB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,qBAAqB;IACrB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,gCAAgC;IAChC,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;CACpC;AAwUD,QAAA,MAAM,UAAU,gDAAwC,CAAC;AACzD,KAAK,UAAU,GAAG,UAAU,CAAC,OAAO,UAAU,CAAC,CAAC;AAChD,eAAe,UAAU,CAAC"}
1
+ {"version":3,"file":"PhoneInput.svelte.d.ts","sourceRoot":"","sources":["../../../src/components/forms/PhoneInput.svelte.ts"],"names":[],"mappings":"AAmBA,MAAM,WAAW,KAAK;IACpB,iBAAiB;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,kBAAkB;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,uCAAuC;IACvC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,uBAAuB;IACvB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,+BAA+B;IAC/B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,qBAAqB;IACrB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,qBAAqB;IACrB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,gCAAgC;IAChC,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;CACpC;AA0UD,QAAA,MAAM,UAAU,gDAAwC,CAAC;AACzD,KAAK,UAAU,GAAG,UAAU,CAAC,OAAO,UAAU,CAAC,CAAC;AAChD,eAAe,UAAU,CAAC"}
@@ -1,5 +1,6 @@
1
1
  <script lang="ts">
2
2
  import { Icon } from '@happyvertical/smrt-ui';
3
+ import { FieldLabel } from '@happyvertical/smrt-ui/forms';
3
4
  import { matchOption } from '@happyvertical/smrt-ui/utils/forms/formatters.js';
4
5
  import { useAppState } from '../../hooks/useAppState.svelte.js';
5
6
  import {
@@ -42,10 +43,24 @@ let {
42
43
  onchange,
43
44
  }: Props = $props();
44
45
 
46
+ // Unique per instance: the control stays labelled even if `name` repeats on a page.
47
+ const uid = $props.id();
48
+ const labelId = `${uid}-label`;
49
+
45
50
  const app = useAppState();
46
51
  const formContext = tryGetFormContext();
47
52
 
48
53
  let isFocused = $state(false);
54
+ /** A caller-supplied empty choice replaces the placeholder: one empty option only. */
55
+ const hasEmptyOption = $derived(options.some((o) => o.value === ''));
56
+ // Options are keyed by value: a repeated value (e.g. two empty choices) would
57
+ // throw each_key_duplicate and take the whole form down, so keep the first.
58
+ const uniqueOptions = $derived(
59
+ options.filter(
60
+ (option, index) =>
61
+ options.findIndex((o) => o.value === option.value) === index,
62
+ ),
63
+ );
49
64
  const isSmrt = $derived(app.state.mode === 'smrt');
50
65
 
51
66
  // Helper to update value
@@ -123,15 +138,14 @@ function handleChange(e: Event) {
123
138
  class:disabled
124
139
  class:has-value={!!value}
125
140
  >
141
+ {#if label}
142
+ <FieldLabel id={labelId} for={name} {label} {required} />
143
+ {/if}
126
144
  <div class="container">
127
145
  <div class="content">
128
- {#if label}
129
- <label for={name} class="label">
130
- {label}{#if required}*{/if}
131
- </label>
132
- {/if}
133
146
  <select
134
147
  id={name}
148
+ aria-labelledby={label ? labelId : undefined}
135
149
  {name}
136
150
  {value}
137
151
  {disabled}
@@ -142,8 +156,10 @@ function handleChange(e: Event) {
142
156
  onfocus={() => isFocused = true}
143
157
  onblur={() => isFocused = false}
144
158
  >
145
- <option value="" disabled selected={!value}>{placeholder}</option>
146
- {#each options as option (option.value)}
159
+ {#if !hasEmptyOption}
160
+ <option value="" disabled selected={!value}>{placeholder}</option>
161
+ {/if}
162
+ {#each uniqueOptions as option (option.value)}
147
163
  <option value={option.value}>{option.label}</option>
148
164
  {/each}
149
165
  </select>
@@ -179,10 +195,11 @@ function handleChange(e: Event) {
179
195
  position: relative;
180
196
  display: flex;
181
197
  align-items: center;
182
- background-color: var(--field-bg);
183
- border-radius: var(--smrt-radius-sm, 4px) var(--smrt-radius-sm, 4px) 0 0;
184
- min-height: 56px;
185
- padding: 0 var(--smrt-spacing-4, 16px);
198
+ background-color: var(--smrt-color-surface, #fff);
199
+ border: 1px solid var(--smrt-color-outline-variant, #d1d5db);
200
+ border-radius: var(--smrt-radius-small, 6px);
201
+ min-height: 40px;
202
+ padding: 0 var(--smrt-spacing-3, 12px);
186
203
  transition: background-color var(--smrt-duration-short3, 200ms) var(--smrt-easing-standard, cubic-bezier(0.2, 0, 0, 1));
187
204
  }
188
205
 
@@ -196,23 +213,8 @@ function handleChange(e: Event) {
196
213
  flex-direction: column;
197
214
  justify-content: center;
198
215
  height: 100%;
199
- padding-top: var(--smrt-spacing-2, 8px);
200
- }
216
+ }
201
217
 
202
- .label {
203
- font-size: var(--smrt-typography-body-large-size, 1rem);
204
- line-height: var(--smrt-typography-body-large-line-height, 1.5);
205
- letter-spacing: var(--smrt-typography-body-large-tracking, 0.5px);
206
- color: var(--field-color);
207
- pointer-events: none;
208
- transition: all var(--smrt-duration-short3, 200ms) var(--smrt-easing-standard, cubic-bezier(0.2, 0, 0, 1));
209
- transform-origin: top left;
210
- }
211
-
212
- .focused .label, .has-value .label {
213
- transform: translateY(-8px) scale(0.75);
214
- color: var(--field-active);
215
- }
216
218
 
217
219
  .input {
218
220
  border: none;
@@ -242,23 +244,15 @@ function handleChange(e: Event) {
242
244
  margin-right: -4px;
243
245
  }
244
246
 
245
- .active-indicator {
246
- position: absolute;
247
- bottom: 0;
248
- left: 0;
249
- right: 0;
250
- height: 1px;
251
- background-color: var(--field-color);
252
- transition: all 200ms cubic-bezier(0.2, 0, 0, 1);
253
- }
247
+ .active-indicator { display: none; }
254
248
 
255
- .focused .active-indicator {
256
- height: 2px;
257
- background-color: var(--field-active);
249
+ .focused .container {
250
+ border-color: var(--field-active);
251
+ box-shadow: 0 0 0 3px color-mix(in srgb, var(--field-active) 10%, transparent);
258
252
  }
259
253
 
260
254
  .supporting-text {
261
- padding: var(--smrt-spacing-1, 4px) var(--smrt-spacing-4, 16px) 0;
255
+ padding: var(--smrt-spacing-1, 4px) 0 0;
262
256
  font-size: var(--smrt-typography-body-small-size, 0.75rem);
263
257
  }
264
258
 
@@ -1 +1 @@
1
- {"version":3,"file":"SelectInput.svelte.d.ts","sourceRoot":"","sources":["../../../src/components/forms/SelectInput.svelte.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAG/C,MAAM,WAAW,KAAK;IACpB,iBAAiB;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,kBAAkB;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,uCAAuC;IACvC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,wBAAwB;IACxB,OAAO,EAAE,YAAY,EAAE,CAAC;IACxB,uBAAuB;IACvB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,+BAA+B;IAC/B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,qBAAqB;IACrB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,qBAAqB;IACrB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,gCAAgC;IAChC,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;CACpC;AAkID,QAAA,MAAM,WAAW,gDAAwC,CAAC;AAC1D,KAAK,WAAW,GAAG,UAAU,CAAC,OAAO,WAAW,CAAC,CAAC;AAClD,eAAe,WAAW,CAAC"}
1
+ {"version":3,"file":"SelectInput.svelte.d.ts","sourceRoot":"","sources":["../../../src/components/forms/SelectInput.svelte.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAG/C,MAAM,WAAW,KAAK;IACpB,iBAAiB;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,kBAAkB;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,uCAAuC;IACvC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,wBAAwB;IACxB,OAAO,EAAE,YAAY,EAAE,CAAC;IACxB,uBAAuB;IACvB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,+BAA+B;IAC/B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,qBAAqB;IACrB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,qBAAqB;IACrB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,gCAAgC;IAChC,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;CACpC;AAiJD,QAAA,MAAM,WAAW,gDAAwC,CAAC;AAC1D,KAAK,WAAW,GAAG,UAAU,CAAC,OAAO,WAAW,CAAC,CAAC;AAClD,eAAe,WAAW,CAAC"}
@@ -1,5 +1,6 @@
1
1
  <script lang="ts">
2
2
  import { Icon, ripple } from '@happyvertical/smrt-ui';
3
+ import { FieldLabel } from '@happyvertical/smrt-ui/forms';
3
4
  import { useI18n } from '@happyvertical/smrt-ui/i18n';
4
5
  import {
5
6
  formatEmail,
@@ -53,6 +54,10 @@ let {
53
54
  onchange,
54
55
  }: Props = $props();
55
56
 
57
+ // Unique per instance: the control stays labelled even if `name` repeats on a page.
58
+ const uid = $props.id();
59
+ const labelId = `${uid}-label`;
60
+
56
61
  const app = useAppState();
57
62
  const stt = useSTT();
58
63
  const formContext = tryGetFormContext();
@@ -262,14 +267,15 @@ function handleInput(e: Event) {
262
267
  class:has-value={!!value}
263
268
  class:listening={isHolding}
264
269
  >
270
+ {#if label}
271
+ <FieldLabel id={labelId} for={name} {label} {required} />
272
+ {/if}
265
273
  <div class="container">
266
274
  <div class="content">
267
- {#if label}
268
- <label for={name} class="label">{label}{#if required}*{/if}</label>
269
- {/if}
270
275
  <input
271
276
  bind:this={inputEl}
272
277
  id={name}
278
+ aria-labelledby={label ? labelId : undefined}
273
279
  {name}
274
280
  type={type}
275
281
  placeholder={isFocused ? placeholder : ''}
@@ -346,10 +352,11 @@ function handleInput(e: Event) {
346
352
  position: relative;
347
353
  display: flex;
348
354
  align-items: center;
349
- background-color: var(--field-bg);
350
- border-radius: var(--smrt-radius-sm, 4px) var(--smrt-radius-sm, 4px) 0 0;
351
- min-height: 56px;
352
- padding: 0 var(--smrt-spacing-4, 16px);
355
+ background-color: var(--smrt-color-surface, #fff);
356
+ border: 1px solid var(--smrt-color-outline-variant, #d1d5db);
357
+ border-radius: var(--smrt-radius-small, 6px);
358
+ min-height: 40px;
359
+ padding: 0 var(--smrt-spacing-3, 12px);
353
360
  transition: background-color var(--smrt-duration-short3, 200ms) var(--smrt-easing-standard, cubic-bezier(0.2, 0, 0, 1));
354
361
  }
355
362
 
@@ -363,24 +370,8 @@ function handleInput(e: Event) {
363
370
  flex-direction: column;
364
371
  justify-content: center;
365
372
  height: 100%;
366
- padding-top: var(--smrt-spacing-2, 8px);
367
- }
373
+ }
368
374
 
369
- .label {
370
- font-size: var(--smrt-typography-body-large-size, 1rem);
371
- line-height: var(--smrt-typography-body-large-line-height, 1.5);
372
- letter-spacing: var(--smrt-typography-body-large-tracking, 0.5px);
373
- color: var(--field-color);
374
- pointer-events: none;
375
- transition: all var(--smrt-duration-short3, 200ms) var(--smrt-easing-standard, cubic-bezier(0.2, 0, 0, 1));
376
- transform-origin: top left;
377
- }
378
-
379
- /* Label float logic */
380
- .focused .label, .has-value .label, .listening .label {
381
- transform: translateY(-8px) scale(0.75);
382
- color: var(--field-active);
383
- }
384
375
 
385
376
  .input {
386
377
  border: none;
@@ -399,19 +390,11 @@ function handleInput(e: Event) {
399
390
  outline: none;
400
391
  }
401
392
 
402
- .active-indicator {
403
- position: absolute;
404
- bottom: 0;
405
- left: 0;
406
- right: 0;
407
- height: 1px;
408
- background-color: var(--field-color);
409
- transition: all var(--smrt-duration-short3, 200ms) var(--smrt-easing-standard, cubic-bezier(0.2, 0, 0, 1));
410
- }
393
+ .active-indicator { display: none; }
411
394
 
412
- .focused .active-indicator {
413
- height: 2px;
414
- background-color: var(--field-active);
395
+ .focused .container {
396
+ border-color: var(--field-active);
397
+ box-shadow: 0 0 0 3px color-mix(in srgb, var(--field-active) 10%, transparent);
415
398
  }
416
399
 
417
400
  .mic-btn {
@@ -435,7 +418,7 @@ function handleInput(e: Event) {
435
418
  }
436
419
 
437
420
  .supporting-text {
438
- padding: var(--smrt-spacing-1, 4px) var(--smrt-spacing-4, 16px) 0;
421
+ padding: var(--smrt-spacing-1, 4px) 0 0;
439
422
  font-size: var(--smrt-typography-body-small-size, 0.75rem);
440
423
  min-height: 16px;
441
424
  }
@@ -1 +1 @@
1
- {"version":3,"file":"TextInput.svelte.d.ts","sourceRoot":"","sources":["../../../src/components/forms/TextInput.svelte.ts"],"names":[],"mappings":"AAoBA,MAAM,WAAW,KAAK;IACpB,iBAAiB;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,kBAAkB;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,2CAA2C;IAC3C,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,iBAAiB;IACjB,IAAI,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;IACxB,uBAAuB;IACvB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,+BAA+B;IAC/B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,qBAAqB;IACrB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,qBAAqB;IACrB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,sDAAsD;IACtD,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,gCAAgC;IAChC,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;CACpC;AA2QD,QAAA,MAAM,SAAS,gDAAwC,CAAC;AACxD,KAAK,SAAS,GAAG,UAAU,CAAC,OAAO,SAAS,CAAC,CAAC;AAC9C,eAAe,SAAS,CAAC"}
1
+ {"version":3,"file":"TextInput.svelte.d.ts","sourceRoot":"","sources":["../../../src/components/forms/TextInput.svelte.ts"],"names":[],"mappings":"AAqBA,MAAM,WAAW,KAAK;IACpB,iBAAiB;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,kBAAkB;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,2CAA2C;IAC3C,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,iBAAiB;IACjB,IAAI,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;IACxB,uBAAuB;IACvB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,+BAA+B;IAC/B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,qBAAqB;IACrB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,qBAAqB;IACrB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,sDAAsD;IACtD,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,gCAAgC;IAChC,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;CACpC;AAgRD,QAAA,MAAM,SAAS,gDAAwC,CAAC;AACxD,KAAK,SAAS,GAAG,UAAU,CAAC,OAAO,SAAS,CAAC,CAAC;AAC9C,eAAe,SAAS,CAAC"}