@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,436 @@
1
+ <!--
2
+ Signature pad that draws on its own canvas and returns a PNG.
3
+
4
+ A committed signature leaves through `onCapture({ blob, dataUrl })` and, when
5
+ `name` is set, posts in a plain native form as a file field, the same way
6
+ `CameraCapture` posts its photo (`native-file-field.ts`).
7
+
8
+ `stylusOnly` is resolved by the caller (a tenant setting, a device profile)
9
+ and passed in; the pointer-type gate is the pure `isAcceptedPointerType()` in
10
+ `signature-pad-logic.ts`. "Use signature" stays disabled until an accepted
11
+ stroke exists, so an empty canvas can never be submitted. Once committed the
12
+ canvas is locked; "Clear" discards the signature and unlocks it. A reset of
13
+ the owning form does the same, as it empties a native file input.
14
+
15
+ The bitmap is painted white and inked dark in every theme and colour scheme:
16
+ a signature is document content, so the exported PNG reads the same wherever
17
+ it is shown, and the pad looks like paper in dark mode too.
18
+
19
+ Ported from teamworks-os `SignaturePad.svelte`, smrt#3290.
20
+ -->
21
+ <script lang="ts">
22
+ import { onMount, untrack } from 'svelte';
23
+ import { M, useI18n } from '../../i18n/index.js';
24
+ import {
25
+ focusControl,
26
+ highlightControl,
27
+ revealControl,
28
+ } from './control-dom.js';
29
+ import type { ControlInteractionOptions } from './control-interaction.js';
30
+ import { tryGetControlInteractionContext } from './control-interaction-context.js';
31
+ import {
32
+ assignFileWithDataTransfer,
33
+ attachFormDataFallback,
34
+ attachFormResetListener,
35
+ clearFileInput,
36
+ detectNativeFileFieldStrategy,
37
+ type NativeFileFieldStrategy,
38
+ } from './native-file-field.js';
39
+ import {
40
+ isAcceptedPointerType,
41
+ mapPointerToCanvasPoint,
42
+ SIGNATURE_INK_COLOR,
43
+ SIGNATURE_PAPER_COLOR,
44
+ } from './signature-pad-logic.js';
45
+ import type {
46
+ CaptureResult,
47
+ SignaturePadLabels,
48
+ SignaturePadState,
49
+ } from './types.js';
50
+ import { useControlRegistration } from './use-control-registration.svelte.js';
51
+
52
+ export interface Props {
53
+ /** Accept pen input only; the caller resolves this rule. */
54
+ stylusOnly?: boolean;
55
+ /** Ignores input and disables both actions. */
56
+ disabled?: boolean;
57
+ /** Called when the user commits a signature with "Use signature". */
58
+ onCapture?: (result: CaptureResult) => void;
59
+ /** Called when a committed signature is discarded by "Clear" or a form reset. */
60
+ onClear?: () => void;
61
+ /** Field name: the committed PNG posts as a file in a native form. */
62
+ name?: string;
63
+ /** Posted file name. */
64
+ fileName?: string;
65
+ /** Canvas backing width in pixels (the display width is responsive). */
66
+ width?: number;
67
+ /** Canvas backing height in pixels. */
68
+ height?: number;
69
+ /** Text overrides; defaults come from the `ui` i18n catalog. */
70
+ labels?: Partial<SignaturePadLabels>;
71
+ /** DOM id of the component root. */
72
+ id?: string;
73
+ /** CSS class to apply to the component root. */
74
+ class?: string;
75
+ /** Interaction options or false to disable registration. */
76
+ interaction?: ControlInteractionOptions | false;
77
+ }
78
+
79
+ let {
80
+ stylusOnly = false,
81
+ disabled = false,
82
+ onCapture,
83
+ onClear,
84
+ name,
85
+ fileName = 'signature.png',
86
+ width = 600,
87
+ height = 240,
88
+ labels,
89
+ id,
90
+ class: className = '',
91
+ interaction,
92
+ }: Props = $props();
93
+
94
+ const { t } = useI18n();
95
+ const text = $derived<SignaturePadLabels>({
96
+ region: t(M['ui.signature_pad.region']),
97
+ canvasEmpty: t(M['ui.signature_pad.canvas_empty']),
98
+ canvasSigned: t(M['ui.signature_pad.canvas_signed']),
99
+ hintAny: t(M['ui.signature_pad.hint_any']),
100
+ hintStylus: t(M['ui.signature_pad.hint_stylus']),
101
+ clear: t(M['ui.signature_pad.clear']),
102
+ useSignature: t(M['ui.signature_pad.use_signature']),
103
+ committed: t(M['ui.signature_pad.committed']),
104
+ ...labels,
105
+ });
106
+
107
+ const instanceId = $props.id();
108
+ const interactionContext = tryGetControlInteractionContext();
109
+ const resolvedId = $derived(id ?? `smrt-signature-${instanceId}`);
110
+ const hintId = $derived(`${resolvedId}-hint`);
111
+ const controlId = $derived(
112
+ interaction === false ? undefined : (interaction?.id ?? name ?? resolvedId),
113
+ );
114
+
115
+ let padState = $state<SignaturePadState>('empty');
116
+ let strategy = $state<NativeFileFieldStrategy>('data-transfer');
117
+ let rootEl = $state<HTMLDivElement | null>(null);
118
+ let canvasEl = $state<HTMLCanvasElement | null>(null);
119
+ let fieldEl = $state<HTMLInputElement | null>(null);
120
+ let activePointerId: number | null = null;
121
+ let lastPoint: { x: number; y: number } | null = null;
122
+ let committedFile: File | null = null;
123
+ // Set while "Use signature" encodes the PNG: the pad is locked so the posted
124
+ // file always equals the ink on screen, and a second click cannot commit twice.
125
+ let encoding = $state(false);
126
+ let encodeRequest = 0;
127
+
128
+ function paintPaper(): void {
129
+ const context = canvasEl?.getContext('2d');
130
+ if (!context || !canvasEl) return;
131
+ context.fillStyle = SIGNATURE_PAPER_COLOR;
132
+ context.fillRect(0, 0, canvasEl.width, canvasEl.height);
133
+ }
134
+
135
+ onMount(() => {
136
+ strategy = detectNativeFileFieldStrategy();
137
+ });
138
+
139
+ // Paint paper when the canvas mounts. A backing-size change clears the bitmap,
140
+ // so it resets the pad (discarding a committed signature) rather than leaving
141
+ // a posted file that no longer matches what is shown. Only a real change
142
+ // counts: a re-run at the same size (a parent re-spreading a fresh props
143
+ // object) leaves the bitmap intact and must not discard the signature.
144
+ let paintedFor: { canvas: HTMLCanvasElement; size: string } | null = null;
145
+ $effect(() => {
146
+ const canvas = canvasEl;
147
+ const size = `${width}x${height}`;
148
+ if (!canvas || (paintedFor?.canvas === canvas && paintedFor.size === size)) {
149
+ return;
150
+ }
151
+ paintedFor = { canvas, size };
152
+ untrack(resetPad);
153
+ });
154
+
155
+ $effect(() => {
156
+ if (strategy !== 'formdata-event' || typeof document === 'undefined') return;
157
+ return attachFormDataFallback(document, () =>
158
+ name && fieldEl
159
+ ? { form: fieldEl.form, name, file: committedFile, source: fieldEl }
160
+ : null,
161
+ );
162
+ });
163
+
164
+ // A reset of the owning form (`form.reset()`, a reset button, `enhance`'s
165
+ // `update()` after a success) empties a native file input, so it discards the
166
+ // signature exactly as "Clear" does: committed or still being drawn, and even
167
+ // while `disabled`, as a reset empties a disabled native input too.
168
+ $effect(() => {
169
+ if (typeof document === 'undefined') return;
170
+ return attachFormResetListener(
171
+ document,
172
+ () => fieldEl?.form ?? rootEl?.closest('form'),
173
+ resetPad,
174
+ );
175
+ });
176
+
177
+ function canvasPoint(event: PointerEvent): { x: number; y: number } {
178
+ if (!canvasEl) return { x: 0, y: 0 };
179
+ return mapPointerToCanvasPoint(
180
+ event.clientX,
181
+ event.clientY,
182
+ canvasEl.getBoundingClientRect(),
183
+ canvasEl.width,
184
+ canvasEl.height,
185
+ );
186
+ }
187
+
188
+ function drawSegment(
189
+ from: { x: number; y: number },
190
+ to: { x: number; y: number },
191
+ ): void {
192
+ const context = canvasEl?.getContext('2d');
193
+ if (!context) return;
194
+ context.lineCap = 'round';
195
+ context.lineJoin = 'round';
196
+ context.lineWidth = 4;
197
+ context.strokeStyle = SIGNATURE_INK_COLOR;
198
+ context.beginPath();
199
+ context.moveTo(from.x, from.y);
200
+ context.lineTo(to.x, to.y);
201
+ context.stroke();
202
+ }
203
+
204
+ function handlePointerDown(event: PointerEvent): void {
205
+ if (disabled || encoding || padState === 'committed') return;
206
+ if (!isAcceptedPointerType(event.pointerType, stylusOnly)) return;
207
+ event.preventDefault();
208
+ activePointerId = event.pointerId;
209
+ lastPoint = canvasPoint(event);
210
+ canvasEl?.setPointerCapture?.(event.pointerId);
211
+ }
212
+
213
+ function handlePointerMove(event: PointerEvent): void {
214
+ if (activePointerId !== event.pointerId) return;
215
+ if (disabled || encoding || padState === 'committed') return;
216
+ event.preventDefault();
217
+ const point = canvasPoint(event);
218
+ if (lastPoint) drawSegment(lastPoint, point);
219
+ lastPoint = point;
220
+ padState = 'signed';
221
+ }
222
+
223
+ function endStroke(event: PointerEvent): void {
224
+ if (activePointerId !== event.pointerId) return;
225
+ activePointerId = null;
226
+ lastPoint = null;
227
+ }
228
+
229
+ function writeField(file: File | null): void {
230
+ if (!fieldEl || !name || strategy !== 'data-transfer') return;
231
+ if (!file) {
232
+ clearFileInput(fieldEl);
233
+ return;
234
+ }
235
+ if (!assignFileWithDataTransfer(fieldEl, file)) {
236
+ strategy = detectNativeFileFieldStrategy({
237
+ FormDataEvent: (globalThis as Record<string, unknown>).FormDataEvent,
238
+ });
239
+ }
240
+ }
241
+
242
+ function clear(): void {
243
+ if (!disabled) resetPad();
244
+ }
245
+
246
+ function resetPad(): void {
247
+ const wasCommitted = padState === 'committed';
248
+ encodeRequest += 1;
249
+ encoding = false;
250
+ activePointerId = null;
251
+ lastPoint = null;
252
+ paintPaper();
253
+ padState = 'empty';
254
+ if (wasCommitted) {
255
+ committedFile = null;
256
+ writeField(null);
257
+ onClear?.();
258
+ }
259
+ }
260
+
261
+ function accept(): void {
262
+ const canvas = canvasEl;
263
+ if (disabled || encoding || padState !== 'signed' || !canvas) return;
264
+ encoding = true;
265
+ activePointerId = null;
266
+ lastPoint = null;
267
+ const request = ++encodeRequest;
268
+ const dataUrl = canvas.toDataURL('image/png');
269
+ canvas.toBlob((blob) => {
270
+ // A Clear (or resize) during encoding superseded this request.
271
+ if (request !== encodeRequest) return;
272
+ encoding = false;
273
+ if (!blob) return;
274
+ committedFile = new File([blob], fileName, { type: 'image/png' });
275
+ writeField(committedFile);
276
+ padState = 'committed';
277
+ onCapture?.({ blob, dataUrl });
278
+ }, 'image/png');
279
+ }
280
+
281
+ useControlRegistration(() => {
282
+ const root = rootEl;
283
+ if (!root || interaction === false) return false;
284
+ return {
285
+ controlId,
286
+ subject: interaction?.subject,
287
+ metadata: {
288
+ kind: 'file',
289
+ label: text.region,
290
+ description: interaction?.description,
291
+ sensitivity: interaction?.sensitivity ?? 'sensitive',
292
+ readable: false,
293
+ writable: false,
294
+ capabilities: ['focus', 'reveal', 'highlight', 'explain'],
295
+ },
296
+ focus: () => focusControl(root),
297
+ reveal: () => revealControl(root),
298
+ highlight: (durationMs) => highlightControl(root, durationMs),
299
+ getState: () => ({ disabled }),
300
+ };
301
+ });
302
+ </script>
303
+
304
+ <div
305
+ bind:this={rootEl}
306
+ id={resolvedId}
307
+ class="signature-pad {className}"
308
+ class:disabled
309
+ role="group"
310
+ aria-label={text.region}
311
+ data-state={padState}
312
+ data-smrt-file-field={name ? strategy : undefined}
313
+ data-smrt-control={controlId}
314
+ data-smrt-form={interactionContext?.formId}
315
+ data-smrt-subject-type={interaction === false ? undefined : interaction?.subject?.type}
316
+ data-smrt-subject-id={interaction === false ? undefined : interaction?.subject?.id}
317
+ >
318
+ <canvas
319
+ bind:this={canvasEl}
320
+ {width}
321
+ {height}
322
+ aria-label={padState === 'empty' ? text.canvasEmpty : text.canvasSigned}
323
+ aria-describedby={hintId}
324
+ data-stylus-only={stylusOnly}
325
+ style:background-color={SIGNATURE_PAPER_COLOR}
326
+ onpointerdown={handlePointerDown}
327
+ onpointermove={handlePointerMove}
328
+ onpointerup={endStroke}
329
+ onpointercancel={endStroke}
330
+ ></canvas>
331
+
332
+ <p id={hintId} class="hint">{stylusOnly ? text.hintStylus : text.hintAny}</p>
333
+ <p class="message" role="status">{padState === 'committed' ? text.committed : ''}</p>
334
+
335
+ <div class="actions">
336
+ <button type="button" class="action" onclick={clear} {disabled}>{text.clear}</button>
337
+ <button
338
+ type="button"
339
+ class="action primary"
340
+ onclick={accept}
341
+ disabled={disabled || encoding || padState !== 'signed'}
342
+ >
343
+ {text.useSignature}
344
+ </button>
345
+ </div>
346
+
347
+ {#if name}
348
+ <input
349
+ bind:this={fieldEl}
350
+ class="field"
351
+ type="file"
352
+ hidden
353
+ tabindex="-1"
354
+ name={strategy === 'data-transfer' ? name : undefined}
355
+ data-smrt-capture-field
356
+ />
357
+ {/if}
358
+ </div>
359
+
360
+ <style>
361
+ .signature-pad {
362
+ display: grid;
363
+ gap: var(--smrt-spacing-3);
364
+ width: 100%;
365
+ max-width: 40rem;
366
+ color: var(--smrt-color-on-surface);
367
+ }
368
+ canvas {
369
+ display: block;
370
+ width: 100%;
371
+ height: auto;
372
+ border: 2px solid var(--smrt-color-outline);
373
+ border-radius: var(--smrt-radius-medium);
374
+ cursor: crosshair;
375
+ touch-action: none;
376
+ }
377
+ .disabled canvas,
378
+ [data-state='committed'] canvas {
379
+ cursor: default;
380
+ }
381
+ .disabled canvas {
382
+ opacity: 0.5;
383
+ }
384
+ .hint,
385
+ .message {
386
+ margin: 0;
387
+ }
388
+ .hint {
389
+ font: var(--smrt-typography-body-medium-font);
390
+ color: var(--smrt-color-on-surface-variant);
391
+ }
392
+ .message {
393
+ font: var(--smrt-typography-body-large-font);
394
+ }
395
+ .message:empty,
396
+ .field {
397
+ display: none;
398
+ }
399
+ .actions {
400
+ display: flex;
401
+ flex-wrap: wrap;
402
+ gap: var(--smrt-spacing-3);
403
+ }
404
+ .action {
405
+ flex: 1 1 auto;
406
+ /* Capture is a touch flow: its actions are touch targets at every
407
+ density, on the shared scale (smrt#3252). */
408
+ min-width: var(--smrt-touch-target-min, 48px);
409
+ min-height: var(--smrt-touch-target-min, 48px);
410
+ padding: 0 var(--smrt-spacing-4);
411
+ border: 1px solid var(--smrt-color-outline);
412
+ border-radius: var(--smrt-radius-small);
413
+ background: var(--smrt-color-surface);
414
+ color: var(--smrt-color-on-surface);
415
+ font: var(--smrt-typography-label-large-font);
416
+ cursor: pointer;
417
+ }
418
+ .action.primary {
419
+ flex-grow: 2;
420
+ border-color: var(--smrt-color-primary);
421
+ background: var(--smrt-color-primary);
422
+ color: var(--smrt-color-on-primary);
423
+ }
424
+ .action:focus-visible {
425
+ outline: 2px solid var(--smrt-color-primary);
426
+ outline-offset: 2px;
427
+ }
428
+ .action:disabled {
429
+ opacity: 0.5;
430
+ cursor: not-allowed;
431
+ }
432
+ :global(.signature-pad[data-smrt-highlighted='true']) {
433
+ outline: 3px solid var(--smrt-color-tertiary);
434
+ outline-offset: 4px;
435
+ }
436
+ </style>
@@ -0,0 +1,32 @@
1
+ import type { ControlInteractionOptions } from './control-interaction.js';
2
+ import type { CaptureResult, SignaturePadLabels } from './types.js';
3
+ export interface Props {
4
+ /** Accept pen input only; the caller resolves this rule. */
5
+ stylusOnly?: boolean;
6
+ /** Ignores input and disables both actions. */
7
+ disabled?: boolean;
8
+ /** Called when the user commits a signature with "Use signature". */
9
+ onCapture?: (result: CaptureResult) => void;
10
+ /** Called when a committed signature is discarded by "Clear" or a form reset. */
11
+ onClear?: () => void;
12
+ /** Field name: the committed PNG posts as a file in a native form. */
13
+ name?: string;
14
+ /** Posted file name. */
15
+ fileName?: string;
16
+ /** Canvas backing width in pixels (the display width is responsive). */
17
+ width?: number;
18
+ /** Canvas backing height in pixels. */
19
+ height?: number;
20
+ /** Text overrides; defaults come from the `ui` i18n catalog. */
21
+ labels?: Partial<SignaturePadLabels>;
22
+ /** DOM id of the component root. */
23
+ id?: string;
24
+ /** CSS class to apply to the component root. */
25
+ class?: string;
26
+ /** Interaction options or false to disable registration. */
27
+ interaction?: ControlInteractionOptions | false;
28
+ }
29
+ declare const SignaturePad: import("svelte").Component<Props, {}, "">;
30
+ type SignaturePad = ReturnType<typeof SignaturePad>;
31
+ export default SignaturePad;
32
+ //# sourceMappingURL=SignaturePad.svelte.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SignaturePad.svelte.d.ts","sourceRoot":"","sources":["../../../src/components/forms/SignaturePad.svelte.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,0BAA0B,CAAC;AAgB1E,OAAO,KAAK,EACV,aAAa,EACb,kBAAkB,EAEnB,MAAM,YAAY,CAAC;AAIpB,MAAM,WAAW,KAAK;IACpB,4DAA4D;IAC5D,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,+CAA+C;IAC/C,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,qEAAqE;IACrE,SAAS,CAAC,EAAE,CAAC,MAAM,EAAE,aAAa,KAAK,IAAI,CAAC;IAC5C,iFAAiF;IACjF,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,sEAAsE;IACtE,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,wBAAwB;IACxB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,wEAAwE;IACxE,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,uCAAuC;IACvC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,gEAAgE;IAChE,MAAM,CAAC,EAAE,OAAO,CAAC,kBAAkB,CAAC,CAAC;IACrC,oCAAoC;IACpC,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,gDAAgD;IAChD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,4DAA4D;IAC5D,WAAW,CAAC,EAAE,yBAAyB,GAAG,KAAK,CAAC;CACjD;AAqQD,QAAA,MAAM,YAAY,2CAAwC,CAAC;AAC3D,KAAK,YAAY,GAAG,UAAU,CAAC,OAAO,YAAY,CAAC,CAAC;AACpD,eAAe,YAAY,CAAC"}
@@ -83,18 +83,27 @@ function formatValue(value: unknown): string {
83
83
  }
84
84
  }
85
85
 
86
- function formatIdentity(snapshot: ControlSnapshot): string {
87
- const { formId, controlId, subject } = snapshot.identity;
88
- return subject
89
- ? `${formId}/${controlId} · ${subject.type}:${subject.id}`
90
- : `${formId}/${controlId}`;
86
+ /**
87
+ * The words a person sees for a control: its label, or — for a control that
88
+ * registered none — its id made readable (`site_name` → "Site name"). The
89
+ * form id, subject type/id, and other registry addressing never show.
90
+ */
91
+ function plainLabel(snapshot: ControlSnapshot): string {
92
+ const label = snapshot.metadata.label?.trim();
93
+ if (label) return label;
94
+ const tail = snapshot.identity.controlId.split('/').at(-1) ?? '';
95
+ const words = tail
96
+ .replace(/([a-z0-9])([A-Z])/g, '$1 $2')
97
+ .replace(/[-_.:]+/g, ' ')
98
+ .trim()
99
+ .toLowerCase();
100
+ return words ? words[0].toUpperCase() + words.slice(1) : tail;
91
101
  }
92
102
 
93
103
  function actionIdentity(snapshot: ControlSnapshot): string {
94
- const label = snapshot.metadata.label ?? snapshot.identity.controlId;
104
+ const label = plainLabel(snapshot);
95
105
  const sameLabel = snapshots.filter(
96
- (candidate) =>
97
- (candidate.metadata.label ?? candidate.identity.controlId) === label,
106
+ (candidate) => plainLabel(candidate) === label,
98
107
  );
99
108
  if (sameLabel.length < 2) return label;
100
109
 
@@ -541,11 +550,11 @@ async function discardAll(event: MouseEvent): Promise<void> {
541
550
  <ul>
542
551
  {#each snapshots as snapshot (keyOf(snapshot))}
543
552
  {@const staged = snapshot.state.staged}
544
- {@const label = snapshot.metadata.label ?? snapshot.identity.controlId}
553
+ {@const label = plainLabel(snapshot)}
545
554
  {@const actionName = actionIdentity(snapshot)}
546
555
  <li data-staged-review-item class:stale={staged?.stale} class:invalid={staged?.valid === false}>
547
556
  <div class="proposal-heading">
548
- <div><strong>{label}</strong><code>{formatIdentity(snapshot)}</code></div>
557
+ <div><strong>{label}</strong>{#if snapshot.identity.subject?.label}<span class="subject">{snapshot.identity.subject.label}</span>{/if}</div>
549
558
  <span>{text.proposedBy} {staged?.provenance.actorId ?? staged?.provenance.source} · {text.stagedAt} {staged ? new Date(staged.stagedAt).toLocaleString() : ''}</span>
550
559
  </div>
551
560
  <dl>
@@ -620,8 +629,8 @@ async function discardAll(event: MouseEvent): Promise<void> {
620
629
  header, .proposal-heading, .batch-actions, .item-actions { display: flex; align-items: center; gap: var(--smrt-spacing-2, .5rem); }
621
630
  header { justify-content: space-between; align-items: flex-start; gap: var(--smrt-spacing-4, 1rem); }
622
631
  h2, p { margin: 0; } h2 { font: var(--smrt-typography-title-medium-font, 600 1rem/1.4 system-ui); }
623
- header p, .proposal-heading span, .proposal-heading code, dt { color: var(--smrt-color-on-surface-variant, #475569); font-size: .875rem; }
624
- .proposal-heading strong, .proposal-heading code { display: block; }
632
+ header p, .proposal-heading span, dt { color: var(--smrt-color-on-surface-variant, #475569); font-size: .875rem; }
633
+ .proposal-heading strong, .proposal-heading .subject { display: block; }
625
634
  ul { display: grid; gap: var(--smrt-spacing-3, .75rem); padding: 0; list-style: none; }
626
635
  li { padding: var(--smrt-spacing-3, .75rem); background: var(--smrt-color-surface, #fff); border: 1px solid var(--smrt-color-outline-variant, #cbd5e1); border-radius: var(--smrt-radius-small, .375rem); }
627
636
  li.stale, li.invalid { border-color: var(--smrt-color-error, #b3261e); }
@@ -1 +1 @@
1
- {"version":3,"file":"StagedControlReview.svelte.d.ts","sourceRoot":"","sources":["../../../src/components/forms/StagedControlReview.svelte.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAEV,0BAA0B,EAE3B,MAAM,0BAA0B,CAAC;AAKlC,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,4BAA4B,CAAC;AAG5E,MAAM,WAAW,KAAK;IACpB,gEAAgE;IAChE,QAAQ,EAAE,0BAA0B,CAAC;IACrC,8CAA8C;IAC9C,MAAM,EAAE,MAAM,CAAC;IACf,mFAAmF;IACnF,WAAW,CAAC,EAAE,eAAe,GAAG,IAAI,CAAC;IACrC,2DAA2D;IAC3D,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,uDAAuD;IACvD,MAAM,CAAC,EAAE,OAAO,CAAC,yBAAyB,CAAC,CAAC;CAC7C;AAkkBD,QAAA,MAAM,mBAAmB,2CAAwC,CAAC;AAClE,KAAK,mBAAmB,GAAG,UAAU,CAAC,OAAO,mBAAmB,CAAC,CAAC;AAClE,eAAe,mBAAmB,CAAC"}
1
+ {"version":3,"file":"StagedControlReview.svelte.d.ts","sourceRoot":"","sources":["../../../src/components/forms/StagedControlReview.svelte.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAEV,0BAA0B,EAE3B,MAAM,0BAA0B,CAAC;AAKlC,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,4BAA4B,CAAC;AAG5E,MAAM,WAAW,KAAK;IACpB,gEAAgE;IAChE,QAAQ,EAAE,0BAA0B,CAAC;IACrC,8CAA8C;IAC9C,MAAM,EAAE,MAAM,CAAC;IACf,mFAAmF;IACnF,WAAW,CAAC,EAAE,eAAe,GAAG,IAAI,CAAC;IACrC,2DAA2D;IAC3D,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,uDAAuD;IACvD,MAAM,CAAC,EAAE,OAAO,CAAC,yBAAyB,CAAC,CAAC;CAC7C;AA2kBD,QAAA,MAAM,mBAAmB,2CAAwC,CAAC;AAClE,KAAK,mBAAmB,GAAG,UAAU,CAAC,OAAO,mBAAmB,CAAC,CAAC;AAClE,eAAe,mBAAmB,CAAC"}
@@ -20,6 +20,8 @@ export interface Props
20
20
  HTMLInputAttributes,
21
21
  'type' | 'class' | 'checked' | 'role' | 'size'
22
22
  > {
23
+ /** Target density; omit to inherit the ThemeProvider setting. */
24
+ density?: 'comfortable' | 'touch';
23
25
  /** Whether the switch is checked. */
24
26
  checked?: boolean;
25
27
  /** Text displayed next to the switch. */
@@ -48,6 +50,7 @@ let {
48
50
  interaction,
49
51
  onchange,
50
52
  'aria-label': ariaLabel,
53
+ density,
51
54
  ...rest
52
55
  }: Props = $props();
53
56
  const instanceId = $props.id();
@@ -113,25 +116,31 @@ useControlRegistration(() => {
113
116
  });
114
117
  </script>
115
118
 
116
- <label class="switch switch--{size} {className}" class:switch--disabled={disabled} data-smrt-control={controlId} data-smrt-form={interactionContext?.formId}
119
+ <label data-density={density} class="switch switch--{size} {className}" class:switch--disabled={disabled} data-smrt-hit-target data-smrt-control={controlId} data-smrt-form={interactionContext?.formId}
117
120
  data-smrt-subject-type={resolvedInteraction === false ? undefined : resolvedInteraction.subject?.type}
118
121
  data-smrt-subject-id={resolvedInteraction === false ? undefined : resolvedInteraction.subject?.id}>
119
- {#if label && labelPosition === 'left'}<span>{label}</span>{/if}
122
+ {#if label && labelPosition === 'left'}<span class="switch__label">{label}</span>{/if}
120
123
  <span class="switch__control">
121
124
  <input bind:this={inputEl} id={resolvedId} type="checkbox" role="switch" {name} {value} {disabled} {required} checked={checked}
122
125
  aria-label={ariaLabel ?? label ?? formGroup?.().label} aria-describedby={formGroup?.().describedBy}
123
126
  aria-invalid={formGroup?.().invalid ? 'true' : undefined} onchange={handleChange} {...rest} />
124
127
  <span class="switch__track" aria-hidden="true"><span class="switch__thumb"></span></span>
125
128
  </span>
126
- {#if label && labelPosition === 'right'}<span>{label}</span>{/if}
129
+ {#if label && labelPosition === 'right'}<span class="switch__label">{label}</span>{/if}
127
130
  </label>
128
131
 
129
132
  <style>
130
133
  .switch { display: inline-flex; align-items: center; gap: var(--smrt-spacing-2, .5rem); cursor: pointer; color: var(--smrt-color-on-surface); }
131
134
  .switch--disabled { opacity: .5; cursor: not-allowed; }
132
- .switch__control { position: relative; display: inline-flex; }
135
+ /* Phones: the whole row (track and label) is at least a 44px touch target. */
136
+ @media (max-width: 48rem) { .switch { min-block-size: 2.75rem; } }
137
+ /* The 2.75rem-wide track gets a transparent 44px-tall ::before hit area (see Checkbox); the
138
+ track and label sit above every hit area. --smrt-control-hit-size resizes it (0px = off). */
139
+ .switch__control { --_hit: max(var(--smrt-control-hit-size, 2.75rem), 1.5rem); position: relative; display: inline-flex; }
140
+ .switch__control::before { content: ''; position: absolute; z-index: 0; inset-block-start: 50%; inset-inline: calc((100% - var(--_hit)) / 2); block-size: var(--_hit); margin-block-start: calc(var(--_hit) / -2); }
141
+ .switch__label { position: relative; z-index: 1; }
133
142
  input { position: absolute; width: 1px; height: 1px; opacity: 0; }
134
- .switch__track { width: 2.75rem; height: 1.5rem; padding: 2px; border-radius: var(--smrt-radius-full); background: var(--smrt-color-surface-container-highest); border: 1px solid var(--smrt-color-outline); transition: background var(--smrt-duration-short2); }
143
+ .switch__track { position: relative; z-index: 1; width: 2.75rem; height: 1.5rem; padding: 2px; border-radius: var(--smrt-radius-full); background: var(--smrt-color-surface-container-highest); border: 1px solid var(--smrt-color-outline); transition: background var(--smrt-duration-short2); }
135
144
  .switch__thumb { display: block; width: 1.125rem; height: 1.125rem; border-radius: 50%; background: var(--smrt-color-outline); transition: transform var(--smrt-duration-short2), background var(--smrt-duration-short2); }
136
145
  input:checked + .switch__track { background: var(--smrt-color-primary); border-color: var(--smrt-color-primary); }
137
146
  input:checked + .switch__track .switch__thumb { transform: translateX(1.25rem); background: var(--smrt-color-on-primary); }
@@ -140,4 +149,8 @@ useControlRegistration(() => {
140
149
  .switch--lg .switch__track { transform: scale(1.15); margin-inline: .2rem; }
141
150
  .switch[data-smrt-highlighted='true'] { outline: 3px solid var(--smrt-color-tertiary); outline-offset: 4px; border-radius: var(--smrt-radius-full); }
142
151
  @media (prefers-reduced-motion: reduce) { .switch__track, .switch__thumb { transition: none; } }
152
+
153
+ .switch[data-density='touch'] { --smrt-control-target-min: var(--smrt-touch-target-min, 48px); }
154
+ .switch[data-density='comfortable'] { --smrt-control-target-min: 0px; }
155
+ .switch { min-height: var(--smrt-control-target-min, 0px); min-width: var(--smrt-control-target-min, 0px); }
143
156
  </style>
@@ -1,6 +1,8 @@
1
1
  import type { HTMLInputAttributes } from 'svelte/elements';
2
2
  import type { ControlInteractionOptions } from './control-interaction.js';
3
3
  export interface Props extends Omit<HTMLInputAttributes, 'type' | 'class' | 'checked' | 'role' | 'size'> {
4
+ /** Target density; omit to inherit the ThemeProvider setting. */
5
+ density?: 'comfortable' | 'touch';
4
6
  /** Whether the switch is checked. */
5
7
  checked?: boolean;
6
8
  /** Text displayed next to the switch. */
@@ -1 +1 @@
1
- {"version":3,"file":"Switch.svelte.d.ts","sourceRoot":"","sources":["../../../src/components/forms/Switch.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AAM3D,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,0BAA0B,CAAC;AAW1E,MAAM,WAAW,KACf,SAAQ,IAAI,CACV,mBAAmB,EACnB,MAAM,GAAG,OAAO,GAAG,SAAS,GAAG,MAAM,GAAG,MAAM,CAC/C;IACD,qCAAqC;IACrC,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,yCAAyC;IACzC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,oDAAoD;IACpD,aAAa,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;IACjC,yCAAyC;IACzC,IAAI,CAAC,EAAE,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;IAC1B,kCAAkC;IAClC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,mFAAmF;IACnF,WAAW,CAAC,EAAE,yBAAyB,GAAG,KAAK,CAAC;CACjD;AAuGD,QAAA,MAAM,MAAM,kDAAwC,CAAC;AACrD,KAAK,MAAM,GAAG,UAAU,CAAC,OAAO,MAAM,CAAC,CAAC;AACxC,eAAe,MAAM,CAAC"}
1
+ {"version":3,"file":"Switch.svelte.d.ts","sourceRoot":"","sources":["../../../src/components/forms/Switch.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AAM3D,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,0BAA0B,CAAC;AAW1E,MAAM,WAAW,KACf,SAAQ,IAAI,CACV,mBAAmB,EACnB,MAAM,GAAG,OAAO,GAAG,SAAS,GAAG,MAAM,GAAG,MAAM,CAC/C;IACD,iEAAiE;IACjE,OAAO,CAAC,EAAE,aAAa,GAAG,OAAO,CAAC;IAClC,qCAAqC;IACrC,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,yCAAyC;IACzC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,oDAAoD;IACpD,aAAa,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;IACjC,yCAAyC;IACzC,IAAI,CAAC,EAAE,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;IAC1B,kCAAkC;IAClC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,mFAAmF;IACnF,WAAW,CAAC,EAAE,yBAAyB,GAAG,KAAK,CAAC;CACjD;AAwGD,QAAA,MAAM,MAAM,kDAAwC,CAAC;AACrD,KAAK,MAAM,GAAG,UAAU,CAAC,OAAO,MAAM,CAAC,CAAC;AACxC,eAAe,MAAM,CAAC"}
@@ -139,6 +139,13 @@ useControlRegistration(() => {
139
139
  .tags-field { display: grid; gap: var(--smrt-spacing-1); color: var(--smrt-color-on-surface); } label { font: var(--smrt-typography-label-large-font); }
140
140
  .tags { display: flex; flex-wrap: wrap; gap: var(--smrt-spacing-1); min-height: 2.5rem; padding: var(--smrt-spacing-1); border: 1px solid var(--smrt-color-outline); border-radius: var(--smrt-radius-small); background: var(--smrt-color-surface); }
141
141
  .tag { display: inline-flex; align-items: center; gap: var(--smrt-spacing-1); padding: var(--smrt-spacing-1) var(--smrt-spacing-2); border-radius: var(--smrt-radius-full); background: var(--smrt-color-secondary-container); color: var(--smrt-color-on-secondary-container); }
142
- .tag button { border: 0; background: transparent; color: inherit; cursor: pointer; } .tags > input { flex: 1; min-width: 8rem; border: 0; outline: 0; background: transparent; color: inherit; }
142
+ .tag button { display: inline-flex; align-items: center; justify-content: center; border: 0; background: transparent; color: inherit; cursor: pointer; }
143
+ /* Phones and touch screens: the × is a 44px target (the tag grows with it). */
144
+ @media (max-width: 48rem), (pointer: coarse) {
145
+ .tag { padding-inline-end: 0; }
146
+ .tag button { min-inline-size: 2.75rem; min-block-size: 2.75rem; font-size: 1.25rem; }
147
+ }
148
+ .tag button:focus-visible { outline: 2px solid var(--smrt-color-primary); outline-offset: -2px; border-radius: var(--smrt-radius-full); }
149
+ .tags > input { flex: 1; min-width: 8rem; border: 0; outline: 0; background: transparent; color: inherit; }
143
150
  :global(.tags[data-smrt-highlighted='true']) { outline: 3px solid var(--smrt-color-tertiary); outline-offset: 4px; }
144
151
  </style>