@happyvertical/smrt-ui 0.51.39 → 0.53.0

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 (392) hide show
  1. package/AGENTS.md +25 -3
  2. package/README.md +558 -8
  3. package/dist/actions/__tests__/swipe-dismiss.test.js +69 -0
  4. package/dist/actions/swipe-dismiss.d.ts +42 -0
  5. package/dist/actions/swipe-dismiss.d.ts.map +1 -0
  6. package/dist/actions/swipe-dismiss.js +69 -0
  7. package/dist/components/calendar/Calendar.svelte +5 -0
  8. package/dist/components/calendar/Calendar.svelte.d.ts.map +1 -1
  9. package/dist/components/calendar/CalendarView.svelte +1250 -0
  10. package/dist/components/calendar/CalendarView.svelte.d.ts +66 -0
  11. package/dist/components/calendar/CalendarView.svelte.d.ts.map +1 -0
  12. package/dist/components/calendar/DayView.svelte +5 -0
  13. package/dist/components/calendar/DayView.svelte.d.ts.map +1 -1
  14. package/dist/components/calendar/__tests__/CalendarView.test.js +226 -0
  15. package/dist/components/calendar/__tests__/calendar-model.test.js +138 -0
  16. package/dist/components/calendar/calendar-model.d.ts +178 -0
  17. package/dist/components/calendar/calendar-model.d.ts.map +1 -0
  18. package/dist/components/calendar/calendar-model.js +362 -0
  19. package/dist/components/calendar/index.d.ts +12 -1
  20. package/dist/components/calendar/index.d.ts.map +1 -1
  21. package/dist/components/calendar/index.js +11 -1
  22. package/dist/components/data/CollectionList.svelte +23 -5
  23. package/dist/components/data/CollectionList.svelte.d.ts +8 -2
  24. package/dist/components/data/CollectionList.svelte.d.ts.map +1 -1
  25. package/dist/components/data/DataTable.svelte +178 -55
  26. package/dist/components/data/DataTable.svelte.d.ts.map +1 -1
  27. package/dist/components/data/DataTableController.d.ts +4 -0
  28. package/dist/components/data/DataTableController.d.ts.map +1 -1
  29. package/dist/components/data/DataTableController.js +11 -4
  30. package/dist/components/data/DataTableResponsive.d.ts +4 -0
  31. package/dist/components/data/DataTableResponsive.d.ts.map +1 -0
  32. package/dist/components/data/DataTableResponsive.js +22 -0
  33. package/dist/components/data/ListSortSelect.svelte +121 -0
  34. package/dist/components/data/ListSortSelect.svelte.d.ts +22 -0
  35. package/dist/components/data/ListSortSelect.svelte.d.ts.map +1 -0
  36. package/dist/components/data/SortableHeader.svelte +169 -0
  37. package/dist/components/data/SortableHeader.svelte.d.ts +30 -0
  38. package/dist/components/data/SortableHeader.svelte.d.ts.map +1 -0
  39. package/dist/components/data/__tests__/CollectionList.test.js +37 -0
  40. package/dist/components/data/__tests__/DataTable.test.js +76 -0
  41. package/dist/components/data/__tests__/DataTableController.test.js +35 -0
  42. package/dist/components/data/__tests__/DataTableResponsive.test.js +80 -0
  43. package/dist/components/data/__tests__/SortableHeader.test.js +101 -0
  44. package/dist/components/data/__tests__/link-step-surface.test.js +172 -0
  45. package/dist/components/data/__tests__/list-sort-state.svelte.test.js +71 -0
  46. package/dist/components/data/__tests__/list-sort.test.js +91 -0
  47. package/dist/components/data/__tests__/responsive-table.fixture.svelte +29 -0
  48. package/dist/components/data/__tests__/responsive-table.fixture.svelte.d.ts +7 -0
  49. package/dist/components/data/__tests__/responsive-table.fixture.svelte.d.ts.map +1 -0
  50. package/dist/components/data/__tests__/surface-navigation.test.js +121 -0
  51. package/dist/components/data/data-surface.d.ts +1 -0
  52. package/dist/components/data/data-surface.d.ts.map +1 -1
  53. package/dist/components/data/data-surface.js +1 -0
  54. package/dist/components/data/index.d.ts +6 -0
  55. package/dist/components/data/index.d.ts.map +1 -1
  56. package/dist/components/data/index.js +6 -0
  57. package/dist/components/data/link-surface.d.ts +79 -0
  58. package/dist/components/data/link-surface.d.ts.map +1 -0
  59. package/dist/components/data/link-surface.js +155 -0
  60. package/dist/components/data/list-sort-state.svelte.d.ts +39 -0
  61. package/dist/components/data/list-sort-state.svelte.d.ts.map +1 -0
  62. package/dist/components/data/list-sort-state.svelte.js +47 -0
  63. package/dist/components/data/list-sort.d.ts +94 -0
  64. package/dist/components/data/list-sort.d.ts.map +1 -0
  65. package/dist/components/data/list-sort.js +182 -0
  66. package/dist/components/data/step-surface.d.ts +73 -0
  67. package/dist/components/data/step-surface.d.ts.map +1 -0
  68. package/dist/components/data/step-surface.js +177 -0
  69. package/dist/components/data/surface-navigation.d.ts +49 -0
  70. package/dist/components/data/surface-navigation.d.ts.map +1 -0
  71. package/dist/components/data/surface-navigation.js +100 -0
  72. package/dist/components/data/types.d.ts +27 -0
  73. package/dist/components/data/types.d.ts.map +1 -1
  74. package/dist/components/display/DateDisplay.svelte +76 -14
  75. package/dist/components/display/DateDisplay.svelte.d.ts +2 -0
  76. package/dist/components/display/DateDisplay.svelte.d.ts.map +1 -1
  77. package/dist/components/display/Icon.svelte +3 -16
  78. package/dist/components/display/Icon.svelte.d.ts +0 -9
  79. package/dist/components/display/Icon.svelte.d.ts.map +1 -1
  80. package/dist/components/display/StatusBadge.svelte +29 -2
  81. package/dist/components/display/StatusBadge.svelte.d.ts +4 -2
  82. package/dist/components/display/StatusBadge.svelte.d.ts.map +1 -1
  83. package/dist/components/display/__tests__/DateDisplay.test.js +82 -1
  84. package/dist/components/display/__tests__/Icon.test.js +132 -2
  85. package/dist/components/display/__tests__/StatusBadge.test.js +78 -0
  86. package/dist/components/display/icons.svelte.d.ts +11 -0
  87. package/dist/components/display/icons.svelte.d.ts.map +1 -0
  88. package/dist/components/display/icons.svelte.js +60 -0
  89. package/dist/components/display/index.d.ts +3 -1
  90. package/dist/components/display/index.d.ts.map +1 -1
  91. package/dist/components/display/index.js +1 -0
  92. package/dist/components/display/types.d.ts +2 -0
  93. package/dist/components/display/types.d.ts.map +1 -1
  94. package/dist/components/feedback/ConfirmDialog.svelte +59 -48
  95. package/dist/components/feedback/ConfirmDialog.svelte.d.ts +14 -2
  96. package/dist/components/feedback/ConfirmDialog.svelte.d.ts.map +1 -1
  97. package/dist/components/feedback/PhoneSheet.svelte +224 -0
  98. package/dist/components/feedback/PhoneSheet.svelte.d.ts +35 -0
  99. package/dist/components/feedback/PhoneSheet.svelte.d.ts.map +1 -0
  100. package/dist/components/feedback/ToastViewport.svelte +156 -8
  101. package/dist/components/feedback/ToastViewport.svelte.d.ts +5 -1
  102. package/dist/components/feedback/ToastViewport.svelte.d.ts.map +1 -1
  103. package/dist/components/feedback/WorkingStrip.svelte +426 -0
  104. package/dist/components/feedback/WorkingStrip.svelte.d.ts +37 -0
  105. package/dist/components/feedback/WorkingStrip.svelte.d.ts.map +1 -0
  106. package/dist/components/feedback/__tests__/ConfirmDialog.test.js +38 -20
  107. package/dist/components/feedback/__tests__/PhoneSheet.test.js +101 -0
  108. package/dist/components/feedback/__tests__/WorkingStrip.test.js +172 -0
  109. package/dist/components/feedback/__tests__/confirm-message.fixture.svelte +9 -0
  110. package/dist/components/feedback/__tests__/confirm-message.fixture.svelte.d.ts +19 -0
  111. package/dist/components/feedback/__tests__/confirm-message.fixture.svelte.d.ts.map +1 -0
  112. package/dist/components/feedback/__tests__/toast.test.js +142 -1
  113. package/dist/components/feedback/index.d.ts +5 -1
  114. package/dist/components/feedback/index.d.ts.map +1 -1
  115. package/dist/components/feedback/index.js +5 -0
  116. package/dist/components/feedback/toast.d.ts +13 -1
  117. package/dist/components/feedback/toast.d.ts.map +1 -1
  118. package/dist/components/feedback/toast.js +51 -10
  119. package/dist/components/feedback/working-status.d.ts +34 -0
  120. package/dist/components/feedback/working-status.d.ts.map +1 -0
  121. package/dist/components/feedback/working-status.js +5 -0
  122. package/dist/components/forms/CameraCapture.svelte +619 -0
  123. package/dist/components/forms/CameraCapture.svelte.d.ts +42 -0
  124. package/dist/components/forms/CameraCapture.svelte.d.ts.map +1 -0
  125. package/dist/components/forms/Checkbox.svelte +34 -3
  126. package/dist/components/forms/Checkbox.svelte.d.ts +2 -0
  127. package/dist/components/forms/Checkbox.svelte.d.ts.map +1 -1
  128. package/dist/components/forms/Combobox.svelte +108 -12
  129. package/dist/components/forms/Combobox.svelte.d.ts +6 -0
  130. package/dist/components/forms/Combobox.svelte.d.ts.map +1 -1
  131. package/dist/components/forms/DictationButton.svelte +128 -0
  132. package/dist/components/forms/DictationButton.svelte.d.ts +19 -0
  133. package/dist/components/forms/DictationButton.svelte.d.ts.map +1 -0
  134. package/dist/components/forms/DictationStatus.svelte +164 -0
  135. package/dist/components/forms/DictationStatus.svelte.d.ts +11 -0
  136. package/dist/components/forms/DictationStatus.svelte.d.ts.map +1 -0
  137. package/dist/components/forms/Fieldset.svelte +5 -1
  138. package/dist/components/forms/Fieldset.svelte.d.ts +2 -0
  139. package/dist/components/forms/Fieldset.svelte.d.ts.map +1 -1
  140. package/dist/components/forms/Form.svelte +5 -0
  141. package/dist/components/forms/Form.svelte.d.ts +1 -0
  142. package/dist/components/forms/Form.svelte.d.ts.map +1 -1
  143. package/dist/components/forms/FormActionBar.svelte +84 -0
  144. package/dist/components/forms/FormActionBar.svelte.d.ts +23 -0
  145. package/dist/components/forms/FormActionBar.svelte.d.ts.map +1 -0
  146. package/dist/components/forms/Input.svelte +17 -1
  147. package/dist/components/forms/Input.svelte.d.ts +2 -0
  148. package/dist/components/forms/Input.svelte.d.ts.map +1 -1
  149. package/dist/components/forms/Listbox.svelte +39 -1
  150. package/dist/components/forms/Listbox.svelte.d.ts.map +1 -1
  151. package/dist/components/forms/MultiSelect.svelte +41 -1
  152. package/dist/components/forms/MultiSelect.svelte.d.ts.map +1 -1
  153. package/dist/components/forms/Radio.svelte +16 -4
  154. package/dist/components/forms/RadioGroup.svelte +7 -1
  155. package/dist/components/forms/RadioGroup.svelte.d.ts +2 -0
  156. package/dist/components/forms/RadioGroup.svelte.d.ts.map +1 -1
  157. package/dist/components/forms/SearchInput.svelte +207 -0
  158. package/dist/components/forms/SearchInput.svelte.d.ts +22 -0
  159. package/dist/components/forms/SearchInput.svelte.d.ts.map +1 -0
  160. package/dist/components/forms/SegmentedControl.svelte +152 -11
  161. package/dist/components/forms/SegmentedControl.svelte.d.ts +4 -0
  162. package/dist/components/forms/SegmentedControl.svelte.d.ts.map +1 -1
  163. package/dist/components/forms/Select.svelte +17 -1
  164. package/dist/components/forms/Select.svelte.d.ts +2 -0
  165. package/dist/components/forms/Select.svelte.d.ts.map +1 -1
  166. package/dist/components/forms/SignaturePad.svelte +436 -0
  167. package/dist/components/forms/SignaturePad.svelte.d.ts +32 -0
  168. package/dist/components/forms/SignaturePad.svelte.d.ts.map +1 -0
  169. package/dist/components/forms/StagedControlReview.svelte +21 -12
  170. package/dist/components/forms/StagedControlReview.svelte.d.ts.map +1 -1
  171. package/dist/components/forms/Switch.svelte +18 -5
  172. package/dist/components/forms/Switch.svelte.d.ts +2 -0
  173. package/dist/components/forms/Switch.svelte.d.ts.map +1 -1
  174. package/dist/components/forms/TagsInput.svelte +8 -1
  175. package/dist/components/forms/Textarea.svelte +16 -1
  176. package/dist/components/forms/Textarea.svelte.d.ts +2 -0
  177. package/dist/components/forms/Textarea.svelte.d.ts.map +1 -1
  178. package/dist/components/forms/__tests__/CameraCapture.test.js +504 -0
  179. package/dist/components/forms/__tests__/Combobox.test.js +354 -0
  180. package/dist/components/forms/__tests__/ControlHitArea.test.js +73 -0
  181. package/dist/components/forms/__tests__/DictationButton.test.js +176 -0
  182. package/dist/components/forms/__tests__/Fieldset.test.js +37 -0
  183. package/dist/components/forms/__tests__/Form.test.js +46 -0
  184. package/dist/components/forms/__tests__/FormActionBar.test.js +22 -0
  185. package/dist/components/forms/__tests__/Listbox.test.js +303 -0
  186. package/dist/components/forms/__tests__/MultiSelect.test.js +281 -0
  187. package/dist/components/forms/__tests__/SearchInput.test.js +92 -0
  188. package/dist/components/forms/__tests__/SignaturePad.test.js +338 -0
  189. package/dist/components/forms/__tests__/StagedControlReview.test.js +37 -3
  190. package/dist/components/forms/__tests__/camera-capture-session.test.js +207 -0
  191. package/dist/components/forms/__tests__/capture-form-retry.test.js +166 -0
  192. package/dist/components/forms/__tests__/capture-form.fixture.svelte +38 -0
  193. package/dist/components/forms/__tests__/capture-form.fixture.svelte.d.ts +10 -0
  194. package/dist/components/forms/__tests__/capture-form.fixture.svelte.d.ts.map +1 -0
  195. package/dist/components/forms/__tests__/capture-test-env.d.ts +70 -0
  196. package/dist/components/forms/__tests__/capture-test-env.d.ts.map +1 -0
  197. package/dist/components/forms/__tests__/capture-test-env.js +122 -0
  198. package/dist/components/forms/__tests__/capture-touch.fixture.svelte +15 -0
  199. package/dist/components/forms/__tests__/capture-touch.fixture.svelte.d.ts +8 -0
  200. package/dist/components/forms/__tests__/capture-touch.fixture.svelte.d.ts.map +1 -0
  201. package/dist/components/forms/__tests__/combobox-form.fixture.svelte +29 -0
  202. package/dist/components/forms/__tests__/combobox-form.fixture.svelte.d.ts +14 -0
  203. package/dist/components/forms/__tests__/combobox-form.fixture.svelte.d.ts.map +1 -0
  204. package/dist/components/forms/__tests__/combobox-labels.fixture.svelte +19 -0
  205. package/dist/components/forms/__tests__/combobox-labels.fixture.svelte.d.ts +11 -0
  206. package/dist/components/forms/__tests__/combobox-labels.fixture.svelte.d.ts.map +1 -0
  207. package/dist/components/forms/__tests__/composite-registration.fixture.svelte +29 -0
  208. package/dist/components/forms/__tests__/composite-registration.fixture.svelte.d.ts +8 -0
  209. package/dist/components/forms/__tests__/composite-registration.fixture.svelte.d.ts.map +1 -0
  210. package/dist/components/forms/__tests__/control-interaction.test.js +19 -1
  211. package/dist/components/forms/__tests__/control-proposals.test.js +63 -0
  212. package/dist/components/forms/__tests__/dictation-recording.test.js +582 -0
  213. package/dist/components/forms/__tests__/dictation.test.js +486 -0
  214. package/dist/components/forms/__tests__/form-attachment.fixture.svelte +28 -0
  215. package/dist/components/forms/__tests__/form-attachment.fixture.svelte.d.ts +11 -0
  216. package/dist/components/forms/__tests__/form-attachment.fixture.svelte.d.ts.map +1 -0
  217. package/dist/components/forms/__tests__/form-retry-form.fixture.svelte +31 -0
  218. package/dist/components/forms/__tests__/form-retry-form.fixture.svelte.d.ts +13 -0
  219. package/dist/components/forms/__tests__/form-retry-form.fixture.svelte.d.ts.map +1 -0
  220. package/dist/components/forms/__tests__/form-retry-form.test.js +107 -0
  221. package/dist/components/forms/__tests__/hidden-controls-form-retry.test.js +69 -0
  222. package/dist/components/forms/__tests__/hidden-controls-form.fixture.svelte +29 -0
  223. package/dist/components/forms/__tests__/hidden-controls-form.fixture.svelte.d.ts +9 -0
  224. package/dist/components/forms/__tests__/hidden-controls-form.fixture.svelte.d.ts.map +1 -0
  225. package/dist/components/forms/__tests__/listbox-form.fixture.svelte +27 -0
  226. package/dist/components/forms/__tests__/listbox-form.fixture.svelte.d.ts +13 -0
  227. package/dist/components/forms/__tests__/listbox-form.fixture.svelte.d.ts.map +1 -0
  228. package/dist/components/forms/__tests__/long-press.test.js +175 -0
  229. package/dist/components/forms/__tests__/multiselect-form.fixture.svelte +27 -0
  230. package/dist/components/forms/__tests__/multiselect-form.fixture.svelte.d.ts +13 -0
  231. package/dist/components/forms/__tests__/multiselect-form.fixture.svelte.d.ts.map +1 -0
  232. package/dist/components/forms/__tests__/native-file-field.test.js +177 -0
  233. package/dist/components/forms/__tests__/place-picker.fixture.svelte +33 -0
  234. package/dist/components/forms/__tests__/place-picker.fixture.svelte.d.ts +12 -0
  235. package/dist/components/forms/__tests__/place-picker.fixture.svelte.d.ts.map +1 -0
  236. package/dist/components/forms/__tests__/segmented-posting.fixture.svelte +28 -0
  237. package/dist/components/forms/__tests__/segmented-posting.fixture.svelte.d.ts +12 -0
  238. package/dist/components/forms/__tests__/segmented-posting.fixture.svelte.d.ts.map +1 -0
  239. package/dist/components/forms/__tests__/segmented-posting.test.js +92 -0
  240. package/dist/components/forms/__tests__/signature-pad-logic.test.js +54 -0
  241. package/dist/components/forms/__tests__/touch-density.fixture.svelte +34 -0
  242. package/dist/components/forms/__tests__/touch-density.fixture.svelte.d.ts +8 -0
  243. package/dist/components/forms/__tests__/touch-density.fixture.svelte.d.ts.map +1 -0
  244. package/dist/components/forms/__tests__/touch-density.test.js +66 -0
  245. package/dist/components/forms/audio-capture.d.ts +69 -0
  246. package/dist/components/forms/audio-capture.d.ts.map +1 -0
  247. package/dist/components/forms/audio-capture.js +193 -0
  248. package/dist/components/forms/camera-capture-session.d.ts +81 -0
  249. package/dist/components/forms/camera-capture-session.d.ts.map +1 -0
  250. package/dist/components/forms/camera-capture-session.js +130 -0
  251. package/dist/components/forms/control-interaction.d.ts +6 -0
  252. package/dist/components/forms/control-interaction.d.ts.map +1 -1
  253. package/dist/components/forms/control-interaction.js +12 -1
  254. package/dist/components/forms/control-proposals.d.ts +55 -0
  255. package/dist/components/forms/control-proposals.d.ts.map +1 -0
  256. package/dist/components/forms/control-proposals.js +188 -0
  257. package/dist/components/forms/dictation-transcribe.d.ts +36 -0
  258. package/dist/components/forms/dictation-transcribe.d.ts.map +1 -0
  259. package/dist/components/forms/dictation-transcribe.js +65 -0
  260. package/dist/components/forms/dictation.svelte.d.ts +200 -0
  261. package/dist/components/forms/dictation.svelte.d.ts.map +1 -0
  262. package/dist/components/forms/dictation.svelte.js +686 -0
  263. package/dist/components/forms/form-retry/__tests__/boundaries.test.js +23 -0
  264. package/dist/components/forms/form-retry/__tests__/content-digest.test.js +70 -0
  265. package/dist/components/forms/form-retry/__tests__/controller.test.js +802 -0
  266. package/dist/components/forms/form-retry/__tests__/fake-kit.d.ts +82 -0
  267. package/dist/components/forms/form-retry/__tests__/fake-kit.d.ts.map +1 -0
  268. package/dist/components/forms/form-retry/__tests__/fake-kit.js +180 -0
  269. package/dist/components/forms/form-retry/__tests__/form-draft.test.js +169 -0
  270. package/dist/components/forms/form-retry/__tests__/ssr.test.js +37 -0
  271. package/dist/components/forms/form-retry/__tests__/storage.d.ts +26 -0
  272. package/dist/components/forms/form-retry/__tests__/storage.d.ts.map +1 -0
  273. package/dist/components/forms/form-retry/__tests__/storage.js +63 -0
  274. package/dist/components/forms/form-retry/__tests__/submission-key.test.js +236 -0
  275. package/dist/components/forms/form-retry/__tests__/submitted-draft.test.js +334 -0
  276. package/dist/components/forms/form-retry/content-digest.d.ts +54 -0
  277. package/dist/components/forms/form-retry/content-digest.d.ts.map +1 -0
  278. package/dist/components/forms/form-retry/content-digest.js +169 -0
  279. package/dist/components/forms/form-retry/controller.d.ts +214 -0
  280. package/dist/components/forms/form-retry/controller.d.ts.map +1 -0
  281. package/dist/components/forms/form-retry/controller.js +404 -0
  282. package/dist/components/forms/form-retry/form-draft.d.ts +68 -0
  283. package/dist/components/forms/form-retry/form-draft.d.ts.map +1 -0
  284. package/dist/components/forms/form-retry/form-draft.js +161 -0
  285. package/dist/components/forms/form-retry/index.d.ts +19 -0
  286. package/dist/components/forms/form-retry/index.d.ts.map +1 -0
  287. package/dist/components/forms/form-retry/index.js +16 -0
  288. package/dist/components/forms/form-retry/submission-key.d.ts +244 -0
  289. package/dist/components/forms/form-retry/submission-key.d.ts.map +1 -0
  290. package/dist/components/forms/form-retry/submission-key.js +342 -0
  291. package/dist/components/forms/form-retry/submitted-draft.d.ts +112 -0
  292. package/dist/components/forms/form-retry/submitted-draft.d.ts.map +1 -0
  293. package/dist/components/forms/form-retry/submitted-draft.js +270 -0
  294. package/dist/components/forms/form-retry/types.d.ts +84 -0
  295. package/dist/components/forms/form-retry/types.d.ts.map +1 -0
  296. package/dist/components/forms/form-retry/types.js +13 -0
  297. package/dist/components/forms/index.d.ts +21 -2
  298. package/dist/components/forms/index.d.ts.map +1 -1
  299. package/dist/components/forms/index.js +22 -1
  300. package/dist/components/forms/insert-text.d.ts +12 -0
  301. package/dist/components/forms/insert-text.d.ts.map +1 -0
  302. package/dist/components/forms/insert-text.js +38 -0
  303. package/dist/components/forms/long-press.d.ts +88 -0
  304. package/dist/components/forms/long-press.d.ts.map +1 -0
  305. package/dist/components/forms/long-press.js +213 -0
  306. package/dist/components/forms/native-file-field.d.ts +94 -0
  307. package/dist/components/forms/native-file-field.d.ts.map +1 -0
  308. package/dist/components/forms/native-file-field.js +147 -0
  309. package/dist/components/forms/ready-beep.d.ts +24 -0
  310. package/dist/components/forms/ready-beep.d.ts.map +1 -0
  311. package/dist/components/forms/ready-beep.js +82 -0
  312. package/dist/components/forms/signature-pad-logic.d.ts +50 -0
  313. package/dist/components/forms/signature-pad-logic.d.ts.map +1 -0
  314. package/dist/components/forms/signature-pad-logic.js +48 -0
  315. package/dist/components/forms/types.d.ts +41 -0
  316. package/dist/components/forms/types.d.ts.map +1 -1
  317. package/dist/components/layout/PageHeader.svelte +151 -17
  318. package/dist/components/layout/PageHeader.svelte.d.ts +47 -7
  319. package/dist/components/layout/PageHeader.svelte.d.ts.map +1 -1
  320. package/dist/components/layout/__tests__/PageHeader.test.js +87 -1
  321. package/dist/components/layout/index.d.ts +1 -0
  322. package/dist/components/layout/index.d.ts.map +1 -1
  323. package/dist/components/layout/index.js +1 -0
  324. package/dist/components/layout/page-header-context.d.ts +25 -0
  325. package/dist/components/layout/page-header-context.d.ts.map +1 -0
  326. package/dist/components/layout/page-header-context.js +16 -0
  327. package/dist/components/nav/FilterChips.svelte +8 -1
  328. package/dist/components/nav/FilterChips.svelte.d.ts +2 -0
  329. package/dist/components/nav/FilterChips.svelte.d.ts.map +1 -1
  330. package/dist/components/nav/Tabs.svelte +241 -0
  331. package/dist/components/nav/Tabs.svelte.d.ts +15 -0
  332. package/dist/components/nav/Tabs.svelte.d.ts.map +1 -1
  333. package/dist/components/nav/__tests__/LinkTabs.test.js +117 -0
  334. package/dist/components/nav/index.d.ts +1 -0
  335. package/dist/components/nav/index.d.ts.map +1 -1
  336. package/dist/components/nav/index.js +1 -0
  337. package/dist/components/nav/tabs-overflow.d.ts +12 -0
  338. package/dist/components/nav/tabs-overflow.d.ts.map +1 -0
  339. package/dist/components/nav/tabs-overflow.js +17 -0
  340. package/dist/components/nav/types.d.ts +8 -0
  341. package/dist/components/nav/types.d.ts.map +1 -1
  342. package/dist/components/ui/Button.svelte +16 -2
  343. package/dist/components/ui/Button.svelte.d.ts +2 -0
  344. package/dist/components/ui/Button.svelte.d.ts.map +1 -1
  345. package/dist/components/ui/Card.svelte +14 -5
  346. package/dist/components/ui/Dropdown.svelte +55 -1
  347. package/dist/components/ui/Dropdown.svelte.d.ts +8 -0
  348. package/dist/components/ui/Dropdown.svelte.d.ts.map +1 -1
  349. package/dist/components/ui/IconToggle.svelte +139 -0
  350. package/dist/components/ui/IconToggle.svelte.d.ts +32 -0
  351. package/dist/components/ui/IconToggle.svelte.d.ts.map +1 -0
  352. package/dist/components/ui/IconToggleGroup.svelte +56 -0
  353. package/dist/components/ui/IconToggleGroup.svelte.d.ts +21 -0
  354. package/dist/components/ui/IconToggleGroup.svelte.d.ts.map +1 -0
  355. package/dist/components/ui/Pagination.svelte +1 -1
  356. package/dist/components/ui/Popover.svelte +17 -1
  357. package/dist/components/ui/Popover.svelte.d.ts +8 -0
  358. package/dist/components/ui/Popover.svelte.d.ts.map +1 -1
  359. package/dist/components/ui/__tests__/IconToggle.test.js +59 -0
  360. package/dist/components/ui/__tests__/IconToggleGroup.test.js +30 -0
  361. package/dist/components/ui/__tests__/Popover.test.js +44 -0
  362. package/dist/components/ui/__tests__/dropdown-popover-icon.test.js +125 -0
  363. package/dist/components/ui/__tests__/phone-touch-targets.test.js +39 -0
  364. package/dist/components/ui/index.d.ts +2 -0
  365. package/dist/components/ui/index.d.ts.map +1 -1
  366. package/dist/components/ui/index.js +2 -0
  367. package/dist/i18n/strings.ui.d.ts +65 -0
  368. package/dist/i18n/strings.ui.d.ts.map +1 -1
  369. package/dist/i18n/strings.ui.js +72 -0
  370. package/dist/styles/tokens.css +2 -0
  371. package/dist/svelte/__tests__/playground.test.js +3 -1
  372. package/dist/svelte/playground/CalendarPreview.svelte +140 -0
  373. package/dist/svelte/playground/CalendarPreview.svelte.d.ts +4 -0
  374. package/dist/svelte/playground/CalendarPreview.svelte.d.ts.map +1 -0
  375. package/dist/svelte/playground/CapturePreview.svelte +56 -0
  376. package/dist/svelte/playground/CapturePreview.svelte.d.ts +4 -0
  377. package/dist/svelte/playground/CapturePreview.svelte.d.ts.map +1 -0
  378. package/dist/svelte/playground.d.ts.map +1 -1
  379. package/dist/svelte/playground.js +20 -0
  380. package/dist/themes/ThemeProvider.svelte +9 -0
  381. package/dist/themes/ThemeProvider.svelte.d.ts +2 -0
  382. package/dist/themes/ThemeProvider.svelte.d.ts.map +1 -1
  383. package/dist/themes/css-generator.d.ts.map +1 -1
  384. package/dist/themes/css-generator.js +3 -0
  385. package/dist/themes/styles/glass.css +4 -0
  386. package/dist/themes/styles/happyvertical.css +4 -0
  387. package/dist/themes/styles/material.css +4 -0
  388. package/dist/themes/styles/smrt.css +4 -0
  389. package/dist/themes/styles/studio.css +4 -0
  390. package/dist/types-generic.d.ts +2 -0
  391. package/dist/types-generic.d.ts.map +1 -1
  392. package/package.json +9 -3
@@ -0,0 +1,207 @@
1
+ <script lang="ts">
2
+ /**
3
+ * SearchInput — the search box of a list's filter bar.
4
+ *
5
+ * It searches as you type (`debounceMs` after the last key; Enter searches at
6
+ * once) and reports the trimmed, single-line text through `onsearch`. The page
7
+ * keeps the applied search (a `?q=` URL parameter, say) and passes it back as
8
+ * `value`; the box follows it, so Back, a filter link or an agent's search
9
+ * update the text, and a search the box itself sent never undoes newer typing.
10
+ * The × clears it. The label ("Search") is read by screen readers; the
11
+ * placeholder says what the search matches. The field is a 44px target and
12
+ * 16px on phones so iOS does not zoom when it gets focus.
13
+ */
14
+ import { onDestroy } from 'svelte';
15
+ import Input from './Input.svelte';
16
+
17
+ export interface Props {
18
+ /** The search the list is showing (from the URL or the page's state). */
19
+ value: string;
20
+ /** Called with the trimmed, single-line text (`''` to clear). */
21
+ onsearch: (query: string) => void | Promise<void>;
22
+ /** Placeholder text: what the search matches. */
23
+ placeholder?: string;
24
+ /** Accessible name of the field. */
25
+ label?: string;
26
+ /** Accessible name of the clear button. */
27
+ clearLabel?: string;
28
+ /** Pause after the last key before searching, in milliseconds. */
29
+ debounceMs?: number;
30
+ /** Form field name. */
31
+ name?: string;
32
+ /** Additional CSS class names on the wrapper. */
33
+ class?: string;
34
+ }
35
+
36
+ const {
37
+ value,
38
+ onsearch,
39
+ placeholder = 'Search',
40
+ label = 'Search',
41
+ clearLabel = 'Clear search',
42
+ debounceMs = 300,
43
+ name = 'q',
44
+ class: className = '',
45
+ }: Props = $props();
46
+
47
+ const instanceId = $props.id();
48
+ const inputId = `smrt-search-${instanceId}`;
49
+ let text = $state('');
50
+ let timer: ReturnType<typeof setTimeout> | null = null;
51
+ /** The search this box last sent; its own round trip must not undo newer typing. */
52
+ let sent: string | null = null;
53
+
54
+ // Follow the applied search (back/forward, a filter link, an agent's search).
55
+ $effect(() => {
56
+ const next = value;
57
+ if (sent !== null && next === sent) {
58
+ sent = null;
59
+ return;
60
+ }
61
+ // Any other applied value supersedes what this box last sent (the host may
62
+ // have normalized it, or the search failed): never let a stale `sent`
63
+ // swallow a later value.
64
+ sent = null;
65
+ text = next;
66
+ });
67
+
68
+ function normalized(raw: string): string {
69
+ return raw.replace(/\s+/g, ' ').trim();
70
+ }
71
+
72
+ function cancel() {
73
+ if (timer) clearTimeout(timer);
74
+ timer = null;
75
+ }
76
+
77
+ function search(raw: string) {
78
+ cancel();
79
+ const query = normalized(raw);
80
+ if (query === normalized(value)) return;
81
+ sent = query;
82
+ // A failed search is not the applied one.
83
+ const forget = () => {
84
+ if (sent === query) sent = null;
85
+ };
86
+ try {
87
+ void Promise.resolve(onsearch(query)).catch(forget);
88
+ } catch {
89
+ forget();
90
+ }
91
+ }
92
+
93
+ function handleInput() {
94
+ cancel();
95
+ timer = setTimeout(() => search(text), debounceMs);
96
+ }
97
+
98
+ // Enter searches at once. The box is not a <form>: inside a host form a
99
+ // nested form is invalid and Enter would submit the outer one.
100
+ function handleKeydown(event: KeyboardEvent) {
101
+ if (event.key !== 'Enter' || event.isComposing) return;
102
+ event.preventDefault();
103
+ search(text);
104
+ }
105
+
106
+ function clear() {
107
+ text = '';
108
+ search('');
109
+ document.getElementById(inputId)?.focus();
110
+ }
111
+
112
+ onDestroy(cancel);
113
+ </script>
114
+
115
+ <div class="search-input {className}" role="search">
116
+ <label class="search-input__label" for={inputId}>{label}</label>
117
+ <span class="search-input__icon" aria-hidden="true">
118
+ <svg viewBox="0 0 24 24" width="18" height="18" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="7" /><path d="m20 20-3.5-3.5" /></svg>
119
+ </span>
120
+ <Input
121
+ id={inputId}
122
+ type="search"
123
+ {name}
124
+ {placeholder}
125
+ enterkeyhint="search"
126
+ autocomplete="off"
127
+ class="search-input__field"
128
+ bind:value={text}
129
+ oninput={handleInput}
130
+ onkeydown={handleKeydown}
131
+ />
132
+ {#if text}
133
+ <button type="button" class="search-input__clear" aria-label={clearLabel} onclick={clear}>
134
+ <svg viewBox="0 0 24 24" width="18" height="18" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M18 6 6 18" /><path d="m6 6 12 12" /></svg>
135
+ </button>
136
+ {/if}
137
+ </div>
138
+
139
+ <style>
140
+ .search-input {
141
+ position: relative;
142
+ display: flex;
143
+ align-items: center;
144
+ flex: 1 1 16rem;
145
+ max-inline-size: 28rem;
146
+ min-inline-size: 0;
147
+ }
148
+ .search-input__label {
149
+ position: absolute;
150
+ inline-size: 1px;
151
+ block-size: 1px;
152
+ margin: -1px;
153
+ padding: 0;
154
+ overflow: hidden;
155
+ clip: rect(0 0 0 0);
156
+ white-space: nowrap;
157
+ border: 0;
158
+ }
159
+ .search-input :global(.search-input__field) {
160
+ inline-size: 100%;
161
+ min-block-size: 2.75rem;
162
+ padding-inline: 2.4rem 2.75rem;
163
+ }
164
+ /* The browser's own clear button would sit on top of ours. */
165
+ .search-input :global(.search-input__field::-webkit-search-cancel-button) {
166
+ -webkit-appearance: none;
167
+ appearance: none;
168
+ }
169
+ .search-input__icon {
170
+ position: absolute;
171
+ inset-inline-start: 0.8rem;
172
+ z-index: 1;
173
+ display: inline-flex;
174
+ color: var(--smrt-color-on-surface-variant);
175
+ pointer-events: none;
176
+ }
177
+ .search-input__clear {
178
+ position: absolute;
179
+ inset-inline-end: 0;
180
+ z-index: 1;
181
+ display: inline-flex;
182
+ align-items: center;
183
+ justify-content: center;
184
+ inline-size: 2.75rem;
185
+ block-size: 2.75rem;
186
+ padding: 0;
187
+ border: none;
188
+ border-radius: var(--smrt-radius-full, 999px);
189
+ background: transparent;
190
+ color: var(--smrt-color-on-surface-variant);
191
+ cursor: pointer;
192
+ }
193
+ .search-input__clear:hover {
194
+ color: var(--smrt-color-on-surface);
195
+ }
196
+ .search-input__clear:focus-visible {
197
+ outline: 2px solid var(--smrt-color-primary);
198
+ outline-offset: -2px;
199
+ }
200
+ /* Phones: full width. */
201
+ @media (max-width: 48rem) {
202
+ .search-input {
203
+ flex-basis: 100%;
204
+ max-inline-size: none;
205
+ }
206
+ }
207
+ </style>
@@ -0,0 +1,22 @@
1
+ export interface Props {
2
+ /** The search the list is showing (from the URL or the page's state). */
3
+ value: string;
4
+ /** Called with the trimmed, single-line text (`''` to clear). */
5
+ onsearch: (query: string) => void | Promise<void>;
6
+ /** Placeholder text: what the search matches. */
7
+ placeholder?: string;
8
+ /** Accessible name of the field. */
9
+ label?: string;
10
+ /** Accessible name of the clear button. */
11
+ clearLabel?: string;
12
+ /** Pause after the last key before searching, in milliseconds. */
13
+ debounceMs?: number;
14
+ /** Form field name. */
15
+ name?: string;
16
+ /** Additional CSS class names on the wrapper. */
17
+ class?: string;
18
+ }
19
+ declare const SearchInput: import("svelte").Component<Props, {}, "">;
20
+ type SearchInput = ReturnType<typeof SearchInput>;
21
+ export default SearchInput;
22
+ //# sourceMappingURL=SearchInput.svelte.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SearchInput.svelte.d.ts","sourceRoot":"","sources":["../../../src/components/forms/SearchInput.svelte.ts"],"names":[],"mappings":"AAmBA,MAAM,WAAW,KAAK;IACpB,yEAAyE;IACzE,KAAK,EAAE,MAAM,CAAC;IACd,iEAAiE;IACjE,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAClD,iDAAiD;IACjD,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,oCAAoC;IACpC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,2CAA2C;IAC3C,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,kEAAkE;IAClE,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,uBAAuB;IACvB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,iDAAiD;IACjD,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAuGD,QAAA,MAAM,WAAW,2CAAwC,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 { tick, untrack } from 'svelte';
2
3
  import {
3
4
  emitControlChange,
4
5
  highlightControl,
@@ -16,6 +17,8 @@ import {
16
17
  import type { SegmentedControlOption } from './types.js';
17
18
  import { useControlRegistration } from './use-control-registration.svelte.js';
18
19
  export interface Props {
20
+ /** Target density; omit to inherit the ThemeProvider setting. */
21
+ density?: 'comfortable' | 'touch';
19
22
  /** The available segments and their values. */
20
23
  options: SegmentedControlOption[];
21
24
  /** The currently selected option value. */
@@ -24,6 +27,8 @@ export interface Props {
24
27
  label: string;
25
28
  /** Form field identifier. */
26
29
  name?: string;
30
+ /** Requires an enabled option to be selected before native form submission. */
31
+ required?: boolean;
27
32
  /** Blocks interaction and submission of this control. */
28
33
  disabled?: boolean;
29
34
  /** Makes the control stretch to full container width. */
@@ -40,13 +45,128 @@ let {
40
45
  value = $bindable(),
41
46
  label,
42
47
  name,
48
+ required = false,
43
49
  disabled = false,
44
50
  fullWidth = false,
45
51
  interaction,
46
52
  onvaluechange,
47
53
  class: className = '',
54
+ density,
48
55
  }: Props = $props();
49
56
  const instanceId = $props.id();
57
+ const initialValue = untrack(() => value);
58
+ const selectedOption = $derived(
59
+ options.find((option) => option.value === value && !option.disabled),
60
+ );
61
+ const firstEnabledOption = $derived(options.find((option) => !option.disabled));
62
+ const validationOption = $derived(selectedOption ?? firstEnabledOption);
63
+ // Required nameless radio groups need explicit validity in browsers, while
64
+ // remaining unnamed so native FormData never receives a generated field.
65
+ $effect(() => {
66
+ rootEl
67
+ ?.querySelectorAll<HTMLInputElement>('input[type="radio"]')
68
+ .forEach((input) => {
69
+ input.setCustomValidity(
70
+ !name &&
71
+ required &&
72
+ !selectedOption &&
73
+ options[Number(input.dataset.optionIndex)] === validationOption
74
+ ? 'Please select an option.'
75
+ : '',
76
+ );
77
+ });
78
+ });
79
+ function initializeRadio(
80
+ node: HTMLInputElement,
81
+ option: SegmentedControlOption,
82
+ ) {
83
+ node.defaultChecked = initialValue === option.value && !option.disabled;
84
+ }
85
+ function handleKeydown(event: KeyboardEvent) {
86
+ if (
87
+ !(event.target instanceof HTMLInputElement) ||
88
+ event.target.matches(':disabled')
89
+ )
90
+ return;
91
+ const radios = Array.from(
92
+ rootEl?.querySelectorAll<HTMLInputElement>(
93
+ 'input[type="radio"]:not(:disabled)',
94
+ ) ?? [],
95
+ );
96
+ const index = radios.indexOf(event.target);
97
+ let next: number;
98
+ switch (event.key) {
99
+ case 'ArrowRight':
100
+ case 'ArrowDown':
101
+ next = (index + 1) % radios.length;
102
+ break;
103
+ case 'ArrowLeft':
104
+ case 'ArrowUp':
105
+ next = (index - 1 + radios.length) % radios.length;
106
+ break;
107
+ case 'Home':
108
+ next = 0;
109
+ break;
110
+ case 'End':
111
+ next = radios.length - 1;
112
+ break;
113
+ default:
114
+ return;
115
+ }
116
+ const radio = radios[next];
117
+ if (!radio) return;
118
+ event.preventDefault();
119
+ radio.focus();
120
+ setValue(options[Number(radio.dataset.optionIndex)].value, true);
121
+ }
122
+ $effect(() => {
123
+ const root = rootEl;
124
+ if (!root) return;
125
+ let form: HTMLFormElement | null = null;
126
+ let disposed = false;
127
+ const reset = (event: Event) => {
128
+ const valueAtDispatch = value;
129
+ // Native event dispatch may checkpoint microtasks between listeners; wait
130
+ // for the next task so a later form listener can still cancel the reset.
131
+ setTimeout(async () => {
132
+ if (
133
+ event.defaultPrevented ||
134
+ !rootEl?.isConnected ||
135
+ !Object.is(value, valueAtDispatch)
136
+ )
137
+ return;
138
+ const resetOption = options.find(
139
+ (option) => option.value === initialValue && !option.disabled,
140
+ );
141
+ value = resetOption?.value;
142
+ recordControlUserEdit(
143
+ interactionContext,
144
+ controlId,
145
+ interaction === false ? undefined : interaction?.subject,
146
+ );
147
+ await tick();
148
+ // A canceled reset never reaches this reconciliation; unchanged bound values
149
+ // still need checked properties restored after the browser's native reset.
150
+ rootEl
151
+ ?.querySelectorAll<HTMLInputElement>('input[type="radio"]')
152
+ .forEach((input) => {
153
+ input.checked =
154
+ options[Number(input.dataset.optionIndex)]?.value === value &&
155
+ !input.disabled;
156
+ });
157
+ });
158
+ };
159
+ // Parent form ancestry is complete after the mount flush, including in SSR hydration.
160
+ tick().then(() => {
161
+ if (disposed) return;
162
+ form = root.closest('form');
163
+ form?.addEventListener('reset', reset);
164
+ });
165
+ return () => {
166
+ disposed = true;
167
+ form?.removeEventListener('reset', reset);
168
+ };
169
+ });
50
170
  const interactionContext = tryGetControlInteractionContext();
51
171
  let rootEl = $state<HTMLDivElement | null>(null);
52
172
  const controlId = $derived(
@@ -54,7 +174,7 @@ const controlId = $derived(
54
174
  ? undefined
55
175
  : (interaction?.id ?? name ?? `segment-${instanceId}`),
56
176
  );
57
- function setValue(next: unknown, userEdit = false) {
177
+ function setValue(next: unknown, userEdit = false, emitChange = true) {
58
178
  const option = options.find((item) => String(item.value) === String(next));
59
179
  if (!option || option.disabled) return;
60
180
  const changed = !Object.is(value, option.value);
@@ -66,7 +186,7 @@ function setValue(next: unknown, userEdit = false) {
66
186
  controlId,
67
187
  interaction === false ? undefined : interaction?.subject,
68
188
  );
69
- if (rootEl) emitControlChange(rootEl);
189
+ if (rootEl && emitChange) emitControlChange(rootEl);
70
190
  }
71
191
  }
72
192
  useControlRegistration(() => {
@@ -83,6 +203,7 @@ useControlRegistration(() => {
83
203
  readable: interaction?.readable,
84
204
  writable: interaction?.writable,
85
205
  options,
206
+ constraints: { required },
86
207
  },
87
208
  getValue: () => value,
88
209
  prepareValue: (next) => prepareEnabledOptionValue(options, next),
@@ -93,31 +214,51 @@ useControlRegistration(() => {
93
214
  },
94
215
  focus: () =>
95
216
  root
96
- .querySelector<HTMLElement>('[role="radio"]:not([disabled])')
217
+ .querySelector<HTMLElement>('input[type="radio"]:not(:disabled)')
97
218
  ?.focus(),
98
219
  reveal: () => revealControl(root),
99
220
  highlight: (durationMs) => highlightControl(root, durationMs),
221
+ validate: () =>
222
+ root
223
+ .querySelector<HTMLInputElement>('input[type="radio"]:not(:disabled)')
224
+ ?.reportValidity() ?? true,
100
225
  validateValue: (next) => validatesEnabledOption(options, next),
101
226
  getState: () => ({
102
227
  disabled: disabled || root.closest('fieldset:disabled') !== null,
228
+ valid:
229
+ !required ||
230
+ !!selectedOption ||
231
+ disabled ||
232
+ root.closest('fieldset:disabled') !== null,
103
233
  }),
104
234
  };
105
235
  });
106
236
  </script>
107
- <div bind:this={rootEl} class="segmented {className}" class:full-width={fullWidth} role="radiogroup" aria-label={label} data-smrt-control={controlId} data-smrt-form={interactionContext?.formId}
237
+ <div bind:this={rootEl} data-density={density} class="segmented {className}" class:full-width={fullWidth} role="radiogroup" tabindex="-1" aria-label={label} aria-required={required} onkeydown={handleKeydown} data-smrt-control={controlId} data-smrt-form={interactionContext?.formId}
108
238
  data-smrt-subject-type={interaction === false ? undefined : interaction?.subject?.type}
109
239
  data-smrt-subject-id={interaction === false ? undefined : interaction?.subject?.id}>
110
- {#each options as option (option.value)}
111
- <button type="button" role="radio" aria-checked={value === option.value} disabled={disabled || option.disabled}
112
- tabindex={value === option.value || (value === undefined && option === options[0]) ? 0 : -1}
113
- class:selected={value === option.value} onclick={() => setValue(option.value, true)}>{option.label}</button>
240
+ {#each options as option, index (option.value)}
241
+ <label class:selected={selectedOption === option} class:disabled={disabled || option.disabled}>
242
+ <input type="radio" {name} value={option.value} disabled={disabled || option.disabled}
243
+ required={required && validationOption === option} aria-label={option.label}
244
+ checked={selectedOption === option} aria-checked={selectedOption === option}
245
+ tabindex={selectedOption === option || (!selectedOption && firstEnabledOption === option) ? 0 : -1}
246
+ data-option-index={index} use:initializeRadio={option}
247
+ onchange={() => setValue(option.value, true, false)} />
248
+ <span>{option.label}</span>
249
+ </label>
114
250
  {/each}
115
251
  </div>
116
252
  <style>
117
253
  .segmented { display: inline-flex; border: 1px solid var(--smrt-color-outline); border-radius: var(--smrt-radius-full); overflow: hidden; }
118
254
  .segmented.full-width { display: flex; width: 100%; }
119
- button { flex: 1; min-height: 2.5rem; padding: 0 var(--smrt-spacing-4); border: 0; border-right: 1px solid var(--smrt-color-outline); background: var(--smrt-color-surface); color: var(--smrt-color-on-surface); cursor: pointer; }
120
- button:last-child { border-right: 0; } button.selected { background: var(--smrt-color-secondary-container); color: var(--smrt-color-on-secondary-container); }
121
- button:focus-visible { outline: 2px solid var(--smrt-color-primary); outline-offset: -3px; } button:disabled { opacity: .5; cursor: not-allowed; }
255
+ label { position: relative; display: inline-flex; align-items: center; justify-content: center; flex: 1; min-height: 2.5rem; padding: 0 var(--smrt-spacing-4); border: 0; border-right: 1px solid var(--smrt-color-outline); background: var(--smrt-color-surface); color: var(--smrt-color-on-surface); cursor: pointer; }
256
+ label:last-child { border-right: 0; } label.selected { background: var(--smrt-color-secondary-container); color: var(--smrt-color-on-secondary-container); }
257
+ label:has(input:focus-visible) { outline: 2px solid var(--smrt-color-primary); outline-offset: -3px; } label.disabled, label:has(input:disabled) { opacity: .5; cursor: not-allowed; }
122
258
  :global(.segmented[data-smrt-highlighted='true']) { outline: 3px solid var(--smrt-color-tertiary); outline-offset: 4px; }
259
+
260
+ .segmented[data-density='touch'] { --smrt-control-target-min: var(--smrt-touch-target-min, 48px); }
261
+ .segmented[data-density='comfortable'] { --smrt-control-target-min: 0px; }
262
+ label { min-height: max(2.5rem, var(--smrt-control-target-min, 0px)); min-width: var(--smrt-control-target-min, 0px); }
263
+ input { position: absolute; inset: 0; width: 100%; height: 100%; margin: 0; opacity: 0; cursor: inherit; }
123
264
  </style>
@@ -1,6 +1,8 @@
1
1
  import type { ControlInteractionOptions } from './control-interaction.js';
2
2
  import type { SegmentedControlOption } from './types.js';
3
3
  export interface Props {
4
+ /** Target density; omit to inherit the ThemeProvider setting. */
5
+ density?: 'comfortable' | 'touch';
4
6
  /** The available segments and their values. */
5
7
  options: SegmentedControlOption[];
6
8
  /** The currently selected option value. */
@@ -9,6 +11,8 @@ export interface Props {
9
11
  label: string;
10
12
  /** Form field identifier. */
11
13
  name?: string;
14
+ /** Requires an enabled option to be selected before native form submission. */
15
+ required?: boolean;
12
16
  /** Blocks interaction and submission of this control. */
13
17
  disabled?: boolean;
14
18
  /** Makes the control stretch to full container width. */
@@ -1 +1 @@
1
- {"version":3,"file":"SegmentedControl.svelte.d.ts","sourceRoot":"","sources":["../../../src/components/forms/SegmentedControl.svelte.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,0BAA0B,CAAC;AAS1E,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,YAAY,CAAC;AAGzD,MAAM,WAAW,KAAK;IACpB,+CAA+C;IAC/C,OAAO,EAAE,sBAAsB,EAAE,CAAC;IAClC,2CAA2C;IAC3C,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACxB,2CAA2C;IAC3C,KAAK,EAAE,MAAM,CAAC;IACd,6BAA6B;IAC7B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,yDAAyD;IACzD,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,yDAAyD;IACzD,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,mFAAmF;IACnF,WAAW,CAAC,EAAE,yBAAyB,GAAG,KAAK,CAAC;IAChD,+CAA+C;IAC/C,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,KAAK,IAAI,CAAC;IACjD,kCAAkC;IAClC,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAuFD,QAAA,MAAM,gBAAgB,gDAAwC,CAAC;AAC/D,KAAK,gBAAgB,GAAG,UAAU,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAC5D,eAAe,gBAAgB,CAAC"}
1
+ {"version":3,"file":"SegmentedControl.svelte.d.ts","sourceRoot":"","sources":["../../../src/components/forms/SegmentedControl.svelte.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,0BAA0B,CAAC;AAS1E,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,YAAY,CAAC;AAGzD,MAAM,WAAW,KAAK;IACpB,iEAAiE;IACjE,OAAO,CAAC,EAAE,aAAa,GAAG,OAAO,CAAC;IAClC,+CAA+C;IAC/C,OAAO,EAAE,sBAAsB,EAAE,CAAC;IAClC,2CAA2C;IAC3C,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACxB,2CAA2C;IAC3C,KAAK,EAAE,MAAM,CAAC;IACd,6BAA6B;IAC7B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,+EAA+E;IAC/E,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,yDAAyD;IACzD,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,yDAAyD;IACzD,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,mFAAmF;IACnF,WAAW,CAAC,EAAE,yBAAyB,GAAG,KAAK,CAAC;IAChD,+CAA+C;IAC/C,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,KAAK,IAAI,CAAC;IACjD,kCAAkC;IAClC,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAwND,QAAA,MAAM,gBAAgB,gDAAwC,CAAC;AAC/D,KAAK,gBAAgB,GAAG,UAAU,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAC5D,eAAe,gBAAgB,CAAC"}
@@ -13,6 +13,8 @@ import { tryGetFormGroupContext } from './form-group-context.js';
13
13
  import { useControlRegistration } from './use-control-registration.svelte.js';
14
14
 
15
15
  export interface Props extends Omit<HTMLSelectAttributes, 'class' | 'value'> {
16
+ /** Target density; omit to inherit the ThemeProvider setting. */
17
+ density?: 'comfortable' | 'touch';
16
18
  /** The currently selected option value. */
17
19
  value?: string;
18
20
  /** Additional CSS class names. */
@@ -35,6 +37,7 @@ let {
35
37
  'aria-label': ariaLabel,
36
38
  'aria-describedby': ariaDescribedby,
37
39
  'aria-invalid': ariaInvalid,
40
+ density,
38
41
  ...rest
39
42
  }: Props = $props();
40
43
 
@@ -140,7 +143,7 @@ export function getElement(): HTMLSelectElement | null {
140
143
  aria-label={ariaLabel}
141
144
  aria-describedby={resolvedDescribedBy}
142
145
  aria-invalid={resolvedInvalid}
143
- class="select {className}"
146
+ data-density={density} class="select {className}"
144
147
  data-smrt-control={resolvedControlId}
145
148
  data-smrt-form={controlInteraction?.formId}
146
149
  data-smrt-subject-type={resolvedInteraction === false ? undefined : resolvedInteraction.subject?.type}
@@ -190,9 +193,22 @@ export function getElement(): HTMLSelectElement | null {
190
193
  opacity: 0.7;
191
194
  }
192
195
 
196
+ /* Phones: 16px text so iOS Safari doesn't zoom the page when the field
197
+ gets focus, and a 44px touch target. */
198
+ @media (max-width: 48rem) {
199
+ .select {
200
+ font-size: max(var(--smrt-typography-body-medium-size, 0.875rem), 16px);
201
+ min-height: 2.75rem;
202
+ }
203
+ }
204
+
193
205
  @media (prefers-reduced-motion: reduce) {
194
206
  .select {
195
207
  transition: none;
196
208
  }
197
209
  }
210
+
211
+ .select[data-density='touch'] { --smrt-control-target-min: var(--smrt-touch-target-min, 48px); }
212
+ .select[data-density='comfortable'] { --smrt-control-target-min: 0px; }
213
+ .select { min-height: var(--smrt-control-target-min, 0px); }
198
214
  </style>
@@ -2,6 +2,8 @@ import type { Snippet } from 'svelte';
2
2
  import type { HTMLSelectAttributes } from 'svelte/elements';
3
3
  import type { ControlInteractionOptions } from './control-interaction.js';
4
4
  export interface Props extends Omit<HTMLSelectAttributes, 'class' | 'value'> {
5
+ /** Target density; omit to inherit the ThemeProvider setting. */
6
+ density?: 'comfortable' | 'touch';
5
7
  /** The currently selected option value. */
6
8
  value?: string;
7
9
  /** Additional CSS class names. */
@@ -1 +1 @@
1
- {"version":3,"file":"Select.svelte.d.ts","sourceRoot":"","sources":["../../../src/components/forms/Select.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;AACtC,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAC;AAM5D,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,0BAA0B,CAAC;AAO1E,MAAM,WAAW,KAAM,SAAQ,IAAI,CAAC,oBAAoB,EAAE,OAAO,GAAG,OAAO,CAAC;IAC1E,2CAA2C;IAC3C,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,kCAAkC;IAClC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,mFAAmF;IACnF,WAAW,CAAC,EAAE,yBAAyB,GAAG,KAAK,CAAC;IAChD,wCAAwC;IACxC,QAAQ,EAAE,OAAO,CAAC;CACnB;AA+HD,QAAA,MAAM,MAAM;iBAjBO,IAAI;sBAIC,iBAAiB,GAAG,IAAI;WAaI,CAAC;AACrD,KAAK,MAAM,GAAG,UAAU,CAAC,OAAO,MAAM,CAAC,CAAC;AACxC,eAAe,MAAM,CAAC"}
1
+ {"version":3,"file":"Select.svelte.d.ts","sourceRoot":"","sources":["../../../src/components/forms/Select.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;AACtC,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAC;AAM5D,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,0BAA0B,CAAC;AAO1E,MAAM,WAAW,KAAM,SAAQ,IAAI,CAAC,oBAAoB,EAAE,OAAO,GAAG,OAAO,CAAC;IAC1E,iEAAiE;IACjE,OAAO,CAAC,EAAE,aAAa,GAAG,OAAO,CAAC;IAClC,2CAA2C;IAC3C,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,kCAAkC;IAClC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,mFAAmF;IACnF,WAAW,CAAC,EAAE,yBAAyB,GAAG,KAAK,CAAC;IAChD,wCAAwC;IACxC,QAAQ,EAAE,OAAO,CAAC;CACnB;AAgID,QAAA,MAAM,MAAM;iBAjBO,IAAI;sBAIC,iBAAiB,GAAG,IAAI;WAaI,CAAC;AACrD,KAAK,MAAM,GAAG,UAAU,CAAC,OAAO,MAAM,CAAC,CAAC;AACxC,eAAe,MAAM,CAAC"}