@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
@@ -9,6 +9,8 @@
9
9
  * - Uses aria-hidden="true" by default (decorative)
10
10
  */
11
11
 
12
+ import { resolveIconPath } from './icons.svelte.js';
13
+
12
14
  /** Props for Icon component */
13
15
  export interface Props {
14
16
  /** Preset icon name */
@@ -36,22 +38,7 @@ const {
36
38
 
37
39
  const isInformative = $derived(!!ariaLabel);
38
40
 
39
- // Common M3 Icon Paths (Simplified)
40
- const presets: Record<string, string> = {
41
- menu: 'M3 18h18v-2H3v2zm0-5h18v-2H3v2zm0-7v2h18V6H3z',
42
- search:
43
- 'M15.5 14h-.79l-.28-.27C15.41 12.59 16 11.11 16 9.5 16 5.91 13.09 3 9.5 3S3 5.91 3 9.5 5.91 16 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z',
44
- close:
45
- 'M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12 19 6.41z',
46
- 'chevron-right': 'M10 6L8.59 7.41 13.17 12l-4.58 4.59L10 18l6-6-6-6z',
47
- 'chevron-left': 'M15.41 7.41L14 6l-6 6 6 6 1.41-1.41L10.83 12l4.58-4.59z',
48
- 'chevron-down': 'M7.41 8.59L12 13.17l4.59-4.58L18 10l-6 6-6-6 1.41-1.41z',
49
- check: 'M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41L9 16.17z',
50
- add: 'M19 13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z',
51
- mic: 'M12 14c1.66 0 3-1.34 3-3V5c0-1.66-1.34-3-3-3S9 3.34 9 5v6c0 1.66 1.34 3 3 3z M17 11c0 2.76-2.24 5-5 5s-5-2.24-5-5H5c0 3.53 2.04 6.43 5 7.07V21h4v-2.93c2.96-.64 5-3.54 5-7.07h-2z',
52
- };
53
-
54
- const finalPath = $derived(path ?? (name ? presets[name] : ''));
41
+ const finalPath = $derived(path ?? (name ? resolveIconPath(name) : ''));
55
42
  const pxSize = $derived(typeof size === 'number' ? `${size}px` : size);
56
43
  </script>
57
44
 
@@ -1,12 +1,3 @@
1
- /**
2
- * Icon - SVG icon component
3
- *
4
- * Displays SVG icons from presets or custom paths.
5
- *
6
- * Accessibility:
7
- * - Use aria-label for informative icons
8
- * - Uses aria-hidden="true" by default (decorative)
9
- */
10
1
  /** Props for Icon component */
11
2
  export interface Props {
12
3
  /** Preset icon name */
@@ -1 +1 @@
1
- {"version":3,"file":"Icon.svelte.d.ts","sourceRoot":"","sources":["../../../src/components/display/Icon.svelte.ts"],"names":[],"mappings":"AAGA;;;;;;;;GAQG;AAEH,+BAA+B;AAC/B,MAAM,WAAW,KAAK;IACpB,uBAAuB;IACvB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,sBAAsB;IACtB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,0DAA0D;IAC1D,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACvB,iBAAiB;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,kBAAkB;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,gDAAgD;IAChD,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AA0CD,QAAA,MAAM,IAAI,2CAAwC,CAAC;AACnD,KAAK,IAAI,GAAG,UAAU,CAAC,OAAO,IAAI,CAAC,CAAC;AACpC,eAAe,IAAI,CAAC"}
1
+ {"version":3,"file":"Icon.svelte.d.ts","sourceRoot":"","sources":["../../../src/components/display/Icon.svelte.ts"],"names":[],"mappings":"AAeA,+BAA+B;AAC/B,MAAM,WAAW,KAAK;IACpB,uBAAuB;IACvB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,sBAAsB;IACtB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,0DAA0D;IAC1D,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACvB,iBAAiB;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,kBAAkB;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,gDAAgD;IAChD,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AA8BD,QAAA,MAAM,IAAI,2CAAwC,CAAC;AACnD,KAAK,IAAI,GAAG,UAAU,CAAC,OAAO,IAAI,CAAC,CAAC;AACpC,eAAe,IAAI,CAAC"}
@@ -3,10 +3,10 @@
3
3
  * StatusBadge - Generic status indicator badge
4
4
  *
5
5
  * Provides pre-defined color schemes for common status domains (invoice, project, etc.)
6
- * or allows custom styling via CSS variables.
6
+ * or an explicit semantic tone for custom status vocabulary.
7
7
  */
8
8
 
9
- import type { StatusType } from './types.js';
9
+ import type { StatusTone, StatusType } from './types.js';
10
10
 
11
11
  /** Props for StatusBadge component */
12
12
  export interface Props {
@@ -14,6 +14,8 @@ export interface Props {
14
14
  status: string;
15
15
  /** Pre-defined color scheme type */
16
16
  type?: StatusType;
17
+ /** Semantic tone overriding the domain color scheme. */
18
+ tone?: StatusTone;
17
19
  /** Badge size */
18
20
  size?: 'sm' | 'md' | 'lg';
19
21
  /** Visual variant */
@@ -25,6 +27,7 @@ export interface Props {
25
27
  const {
26
28
  status,
27
29
  type = 'default',
30
+ tone,
28
31
  size = 'md',
29
32
  variant = 'filled',
30
33
  label,
@@ -201,11 +204,35 @@ const colorSchemes: Record<
201
204
  },
202
205
  };
203
206
 
207
+ const toneSchemes: Record<StatusTone, { bg: string; text: string }> = {
208
+ success: {
209
+ bg: 'var(--smrt-color-success-container)',
210
+ text: 'var(--smrt-color-on-success-container)',
211
+ },
212
+ warning: {
213
+ bg: 'var(--smrt-color-warning-container)',
214
+ text: 'var(--smrt-color-on-warning-container)',
215
+ },
216
+ danger: {
217
+ bg: 'var(--smrt-color-error-container)',
218
+ text: 'var(--smrt-color-on-error-container)',
219
+ },
220
+ info: {
221
+ bg: 'var(--smrt-color-primary-container)',
222
+ text: 'var(--smrt-color-on-primary-container)',
223
+ },
224
+ neutral: {
225
+ bg: 'var(--smrt-color-surface-container-highest)',
226
+ text: 'var(--smrt-color-on-surface-variant)',
227
+ },
228
+ };
229
+
204
230
  // Normalize status for lookup (lowercase, handle spaces/underscores)
205
231
  const normalizedStatus = $derived(status.toLowerCase().replace(/[\s-]/g, '_'));
206
232
 
207
233
  // Get colors for current status
208
234
  const colors = $derived.by(() => {
235
+ if (tone && Object.hasOwn(toneSchemes, tone)) return toneSchemes[tone];
209
236
  const scheme = colorSchemes[type] ?? colorSchemes.default;
210
237
  return (
211
238
  scheme[normalizedStatus] ?? {
@@ -2,15 +2,17 @@
2
2
  * StatusBadge - Generic status indicator badge
3
3
  *
4
4
  * Provides pre-defined color schemes for common status domains (invoice, project, etc.)
5
- * or allows custom styling via CSS variables.
5
+ * or an explicit semantic tone for custom status vocabulary.
6
6
  */
7
- import type { StatusType } from './types.js';
7
+ import type { StatusTone, StatusType } from './types.js';
8
8
  /** Props for StatusBadge component */
9
9
  export interface Props {
10
10
  /** The status value to display */
11
11
  status: string;
12
12
  /** Pre-defined color scheme type */
13
13
  type?: StatusType;
14
+ /** Semantic tone overriding the domain color scheme. */
15
+ tone?: StatusTone;
14
16
  /** Badge size */
15
17
  size?: 'sm' | 'md' | 'lg';
16
18
  /** Visual variant */
@@ -1 +1 @@
1
- {"version":3,"file":"StatusBadge.svelte.d.ts","sourceRoot":"","sources":["../../../src/components/display/StatusBadge.svelte.ts"],"names":[],"mappings":"AAGA;;;;;GAKG;AACH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAG7C,sCAAsC;AACtC,MAAM,WAAW,KAAK;IACpB,kCAAkC;IAClC,MAAM,EAAE,MAAM,CAAC;IACf,oCAAoC;IACpC,IAAI,CAAC,EAAE,UAAU,CAAC;IAClB,iBAAiB;IACjB,IAAI,CAAC,EAAE,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;IAC1B,qBAAqB;IACrB,OAAO,CAAC,EAAE,QAAQ,GAAG,SAAS,CAAC;IAC/B,uDAAuD;IACvD,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAoND,QAAA,MAAM,WAAW,2CAAwC,CAAC;AAC1D,KAAK,WAAW,GAAG,UAAU,CAAC,OAAO,WAAW,CAAC,CAAC;AAClD,eAAe,WAAW,CAAC"}
1
+ {"version":3,"file":"StatusBadge.svelte.d.ts","sourceRoot":"","sources":["../../../src/components/display/StatusBadge.svelte.ts"],"names":[],"mappings":"AAGA;;;;;GAKG;AACH,OAAO,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAGzD,sCAAsC;AACtC,MAAM,WAAW,KAAK;IACpB,kCAAkC;IAClC,MAAM,EAAE,MAAM,CAAC;IACf,oCAAoC;IACpC,IAAI,CAAC,EAAE,UAAU,CAAC;IAClB,wDAAwD;IACxD,IAAI,CAAC,EAAE,UAAU,CAAC;IAClB,iBAAiB;IACjB,IAAI,CAAC,EAAE,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;IAC1B,qBAAqB;IACrB,OAAO,CAAC,EAAE,QAAQ,GAAG,SAAS,CAAC;IAC/B,uDAAuD;IACvD,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AA6OD,QAAA,MAAM,WAAW,2CAAwC,CAAC;AAC1D,KAAK,WAAW,GAAG,UAAU,CAAC,OAAO,WAAW,CAAC,CAAC;AAClD,eAAe,WAAW,CAAC"}
@@ -8,7 +8,7 @@
8
8
  * offsets from `now`. Tests assert the real API from DateDisplay.svelte.
9
9
  */
10
10
  import { render, screen } from '@testing-library/svelte';
11
- import { describe, expect, it } from 'vitest';
11
+ import { afterEach, describe, expect, it, vi } from 'vitest';
12
12
  import { expectNoA11yViolations } from '../../../test-support/a11y';
13
13
  import DateDisplay from '../DateDisplay.svelte';
14
14
  const LOCALE = 'en-CA';
@@ -25,6 +25,83 @@ const LONG = {
25
25
  day: 'numeric',
26
26
  };
27
27
  describe('DateDisplay', () => {
28
+ afterEach(() => vi.useRealTimers());
29
+ it.each([
30
+ 'America/Edmonton',
31
+ 'UTC',
32
+ 'Asia/Tokyo',
33
+ ])('formats timestamp dates and times in %s', (timeZone) => {
34
+ const instant = '2026-10-01T04:00:00Z';
35
+ const { container } = render(DateDisplay, {
36
+ props: { date: instant, timeZone, showTime: true },
37
+ });
38
+ expect(container.querySelector('time')?.textContent?.trim()).toBe(expected(new Date(instant), {
39
+ ...MEDIUM,
40
+ timeZone,
41
+ hour: 'numeric',
42
+ minute: '2-digit',
43
+ }));
44
+ });
45
+ it.each([
46
+ 'America/Edmonton',
47
+ 'Asia/Tokyo',
48
+ ])('keeps a bare calendar date in %s', (timeZone) => {
49
+ const { container } = render(DateDisplay, {
50
+ props: { date: '2026-09-30', timeZone },
51
+ });
52
+ expect(container.querySelector('time')).toHaveAttribute('datetime', '2026-09-30');
53
+ expect(container.querySelector('time')?.textContent?.trim()).toBe(expected(new Date('2026-09-30T00:00:00Z'), {
54
+ ...MEDIUM,
55
+ timeZone: 'UTC',
56
+ }));
57
+ });
58
+ it('keeps date-only values stable in the default browser zone', () => {
59
+ render(DateDisplay, { props: { date: '2026-09-30' } });
60
+ expect(screen.getByText(expected(new Date('2026-09-30T00:00:00Z'), {
61
+ ...MEDIUM,
62
+ timeZone: 'UTC',
63
+ }))).toBeInTheDocument();
64
+ });
65
+ it.each([
66
+ '2026-02-30',
67
+ '2025-02-29',
68
+ '2026-13-01',
69
+ ])('rejects impossible calendar date %s', (date) => {
70
+ render(DateDisplay, { props: { date, fallback: 'Invalid date' } });
71
+ expect(screen.getByText('Invalid date')).toBeInTheDocument();
72
+ });
73
+ it('accepts a leap day', () => {
74
+ const { container } = render(DateDisplay, {
75
+ props: { date: '2028-02-29' },
76
+ });
77
+ expect(container.querySelector('time')).toHaveAttribute('datetime', '2028-02-29');
78
+ });
79
+ it.each([
80
+ ['2026-10-01T06:30:00Z', '2026-10-01T05:30:00Z', 'yesterday'],
81
+ ['2026-10-01T05:30:00Z', '2026-10-01T06:30:00Z', 'tomorrow'],
82
+ ['2026-10-01T06:30:00Z', '2026-10-01T07:30:00Z', 'today'],
83
+ ['2026-03-09T06:30:00Z', '2026-03-08T07:30:00Z', 'yesterday'],
84
+ ['2026-10-01T05:30:00Z', '2026-09-30', 'today'],
85
+ ['2026-10-01T05:30:00Z', '2026-10-01', 'tomorrow'],
86
+ ['2026-10-01T05:30:00Z', '2026-09-29', 'yesterday'],
87
+ ])('uses calendar boundaries for %s → %s', (now, date, text) => {
88
+ vi.useFakeTimers({ toFake: ['Date'] });
89
+ vi.setSystemTime(new Date(now));
90
+ render(DateDisplay, {
91
+ props: { date, format: 'relative', timeZone: 'America/Edmonton' },
92
+ });
93
+ expect(screen.getByText(text)).toBeInTheDocument();
94
+ });
95
+ it('uses the fallback for an invalid explicit zone', () => {
96
+ render(DateDisplay, {
97
+ props: {
98
+ date: '2026-09-30T00:00:00Z',
99
+ timeZone: 'not/a/zone',
100
+ fallback: 'Invalid date',
101
+ },
102
+ });
103
+ expect(screen.getByText('Invalid date')).toBeInTheDocument();
104
+ });
28
105
  // Use a fixed local-noon date so date-part is timezone-stable.
29
106
  const sample = new Date(2024, 0, 15, 12, 0, 0); // 15 Jan 2024, local noon
30
107
  it('renders a <time> element with the ISO datetime attribute', () => {
@@ -79,6 +156,10 @@ describe('DateDisplay', () => {
79
156
  const span = screen.getByText('No date');
80
157
  expect(span).toHaveClass('date-fallback');
81
158
  });
159
+ it('renders the fallback for an invalid Date instance', () => {
160
+ render(DateDisplay, { props: { date: new Date(Number.NaN) } });
161
+ expect(screen.getByText('N/A')).toBeInTheDocument();
162
+ });
82
163
  it('renders the default fallback for an invalid date string', () => {
83
164
  render(DateDisplay, { props: { date: 'not-a-date' } });
84
165
  expect(screen.getByText('N/A')).toBeInTheDocument();
@@ -6,10 +6,14 @@
6
6
  * from Icon.svelte — preset/path resolution, size/color, and axe-cleanliness in
7
7
  * both decorative and labelled modes.
8
8
  */
9
- import { render } from '@testing-library/svelte';
10
- import { describe, expect, it } from 'vitest';
9
+ import { svelte } from '@sveltejs/vite-plugin-svelte';
10
+ import { act, render } from '@testing-library/svelte';
11
+ import { hydrate, tick, unmount } from 'svelte';
12
+ import { createServer } from 'vite';
13
+ import { afterEach, describe, expect, it } from 'vitest';
11
14
  import { expectNoA11yViolations } from '../../../test-support/a11y';
12
15
  import Icon from '../Icon.svelte';
16
+ import { registerIcons } from '../icons.svelte.js';
13
17
  /** The 'check' preset path, used to assert preset resolution. */
14
18
  const CHECK_PATH = 'M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41L9 16.17z';
15
19
  describe('Icon', () => {
@@ -91,3 +95,129 @@ describe('Icon', () => {
91
95
  await expectNoA11yViolations(container);
92
96
  });
93
97
  });
98
+ const cleanupRegistrations = [];
99
+ afterEach(() => {
100
+ for (const cleanup of cleanupRegistrations.splice(0))
101
+ cleanup();
102
+ });
103
+ describe('Icon catalog and application registration', () => {
104
+ it.each([
105
+ 'alert',
106
+ 'warning',
107
+ 'info',
108
+ 'home',
109
+ 'user',
110
+ 'settings',
111
+ 'trash',
112
+ 'edit',
113
+ 'calendar',
114
+ 'clock',
115
+ 'camera',
116
+ 'upload',
117
+ 'download',
118
+ ])('ships a nonempty %s glyph', (name) => {
119
+ const { container } = render(Icon, { props: { name } });
120
+ expect(container.querySelector('path')?.getAttribute('d')).toMatch(/^M/);
121
+ });
122
+ it('updates already mounted icons when an application set is registered and removed', async () => {
123
+ const { container } = render(Icon, { props: { name: 'shop-machine' } });
124
+ const path = container.querySelector('path');
125
+ expect(path).not.toHaveAttribute('d');
126
+ let cleanup = () => { };
127
+ await act(() => {
128
+ cleanup = registerIcons({ 'shop-machine': 'M2 2h20v20H2z' });
129
+ });
130
+ cleanupRegistrations.push(cleanup);
131
+ expect(path).toHaveAttribute('d', 'M2 2h20v20H2z');
132
+ await act(cleanup);
133
+ expect(path).not.toHaveAttribute('d');
134
+ });
135
+ it('preserves override precedence and supports out-of-order, repeated cleanup', async () => {
136
+ const { container } = render(Icon, { props: { name: 'check' } });
137
+ const path = container.querySelector('path');
138
+ const first = registerIcons({ check: 'M1 1h2v2H1z' });
139
+ const second = registerIcons({ check: 'M3 3h4v4H3z' });
140
+ cleanupRegistrations.push(first, second);
141
+ await act(() => { });
142
+ expect(path).toHaveAttribute('d', 'M3 3h4v4H3z');
143
+ await act(first);
144
+ expect(path).toHaveAttribute('d', 'M3 3h4v4H3z');
145
+ await act(first);
146
+ expect(path).toHaveAttribute('d', 'M3 3h4v4H3z');
147
+ await act(second);
148
+ expect(path).toHaveAttribute('d', CHECK_PATH);
149
+ });
150
+ it('keeps explicit path higher priority than a registered name', () => {
151
+ cleanupRegistrations.push(registerIcons({ check: 'M1 1h2v2H1z' }));
152
+ const { container } = render(Icon, {
153
+ props: {
154
+ name: 'check',
155
+ path: 'M0 0h10v10H0z',
156
+ 'aria-label': 'Custom check',
157
+ },
158
+ });
159
+ expect(container.querySelector('path')).toHaveAttribute('d', 'M0 0h10v10H0z');
160
+ expect(container.querySelector('svg')).toHaveAccessibleName('Custom check');
161
+ });
162
+ it('snapshots caller maps and rejects malformed mixed sets atomically', () => {
163
+ const icons = { brand: 'M1 1h2v2H1z' };
164
+ cleanupRegistrations.push(registerIcons(icons));
165
+ icons.brand = 'M3 3h4v4H3z';
166
+ expect(() => registerIcons({ brand: 'M5 5h6v6H5z', bad: 3 })).toThrow(TypeError);
167
+ expect(() => registerIcons({ '': 'M1 1h2v2H1z' })).toThrow(TypeError);
168
+ expect(() => registerIcons({ empty: '' })).toThrow(TypeError);
169
+ const { container } = render(Icon, { props: { name: 'brand' } });
170
+ expect(container.querySelector('path')).toHaveAttribute('d', 'M1 1h2v2H1z');
171
+ });
172
+ it('does not resolve inherited map properties or builtin prototype names', () => {
173
+ cleanupRegistrations.push(registerIcons(Object.create({ inherited: 'M1 1h2v2H1z' })));
174
+ for (const name of ['inherited', 'toString', '__proto__', 'constructor']) {
175
+ const { container } = render(Icon, { props: { name } });
176
+ expect(container.querySelector('path')).not.toHaveAttribute('d');
177
+ }
178
+ });
179
+ });
180
+ it('renders registered icons on the server and hydrates with the same application set', async () => {
181
+ const vite = await createServer({
182
+ configFile: false,
183
+ plugins: [svelte()],
184
+ root: process.cwd(),
185
+ server: { middlewareMode: true },
186
+ });
187
+ const custom = 'M4 4h16v16H4z';
188
+ const unregisterClient = registerIcons({ brand: custom });
189
+ cleanupRegistrations.push(unregisterClient);
190
+ try {
191
+ const { default: ServerIcon } = await vite.ssrLoadModule('/src/components/display/Icon.svelte');
192
+ const { registerIcons: registerServerIcons } = await vite.ssrLoadModule('/src/components/display/icons.svelte.ts');
193
+ const unregisterServer = registerServerIcons({ brand: custom });
194
+ try {
195
+ const { render: renderSsr } = await vite.ssrLoadModule('svelte/server');
196
+ const props = { name: 'brand', 'aria-label': 'Brand' };
197
+ const result = await renderSsr(ServerIcon, { props });
198
+ expect(result.body).toContain(`d="${custom}"`);
199
+ const host = document.createElement('div');
200
+ host.innerHTML = result.body;
201
+ document.body.append(host);
202
+ const instance = hydrate(Icon, { target: host, props });
203
+ try {
204
+ expect(host.querySelector('svg')).toHaveAccessibleName('Brand');
205
+ expect(host.querySelector('path')).toHaveAttribute('d', custom);
206
+ const override = registerIcons({ brand: 'M2 2h20v20H2z' });
207
+ cleanupRegistrations.push(override);
208
+ await tick();
209
+ expect(host.querySelector('path')).toHaveAttribute('d', 'M2 2h20v20H2z');
210
+ }
211
+ finally {
212
+ await unmount(instance);
213
+ host.remove();
214
+ }
215
+ }
216
+ finally {
217
+ unregisterServer();
218
+ }
219
+ }
220
+ finally {
221
+ await vite.close();
222
+ }
223
+ });
@@ -12,6 +12,84 @@ import { describe, expect, it } from 'vitest';
12
12
  import { expectNoA11yViolations } from '../../../test-support/a11y';
13
13
  import StatusBadge from '../StatusBadge.svelte';
14
14
  describe('StatusBadge', () => {
15
+ it.each([
16
+ [
17
+ 'success',
18
+ 'var(--smrt-color-success-container)',
19
+ 'var(--smrt-color-on-success-container)',
20
+ ],
21
+ [
22
+ 'warning',
23
+ 'var(--smrt-color-warning-container)',
24
+ 'var(--smrt-color-on-warning-container)',
25
+ ],
26
+ [
27
+ 'danger',
28
+ 'var(--smrt-color-error-container)',
29
+ 'var(--smrt-color-on-error-container)',
30
+ ],
31
+ [
32
+ 'info',
33
+ 'var(--smrt-color-primary-container)',
34
+ 'var(--smrt-color-on-primary-container)',
35
+ ],
36
+ [
37
+ 'neutral',
38
+ 'var(--smrt-color-surface-container-highest)',
39
+ 'var(--smrt-color-on-surface-variant)',
40
+ ],
41
+ ])('applies %s tone to a custom status', (tone, background, foreground) => {
42
+ const { container } = render(StatusBadge, {
43
+ props: { status: 'awaiting_cert', tone },
44
+ });
45
+ const span = container.querySelector('span');
46
+ expect(span.style.getPropertyValue('--badge-bg')).toBe(background);
47
+ expect(span.style.getPropertyValue('--badge-text')).toBe(foreground);
48
+ expect(screen.getByText('awaiting cert')).toBeInTheDocument();
49
+ });
50
+ it('lets explicit tone override a domain scheme and updates reactively', async () => {
51
+ const { container, rerender } = render(StatusBadge, {
52
+ props: {
53
+ status: 'overdue',
54
+ type: 'invoice',
55
+ tone: 'success',
56
+ variant: 'outline',
57
+ size: 'sm',
58
+ label: 'Reviewed',
59
+ },
60
+ });
61
+ const span = container.querySelector('span');
62
+ expect(span.style.getPropertyValue('--badge-bg')).toBe('var(--smrt-color-success-container)');
63
+ expect(span).toHaveClass('outline', 'sm');
64
+ expect(screen.getByText('Reviewed')).toBeInTheDocument();
65
+ await rerender({ status: 'overdue', type: 'invoice', tone: 'warning' });
66
+ expect(span.style.getPropertyValue('--badge-bg')).toBe('var(--smrt-color-warning-container)');
67
+ await rerender({ status: 'overdue', type: 'invoice', tone: undefined });
68
+ expect(span.style.getPropertyValue('--badge-bg')).toBe('var(--smrt-color-error-container)');
69
+ });
70
+ it.each([
71
+ 'unknown',
72
+ '__proto__',
73
+ 'constructor',
74
+ ])('safely ignores invalid runtime tone %s', (tone) => {
75
+ const { container } = render(StatusBadge, {
76
+ props: { status: 'paid', type: 'invoice', tone: tone },
77
+ });
78
+ const span = container.querySelector('span');
79
+ expect(span.style.getPropertyValue('--badge-bg')).toBe('var(--smrt-color-primary-container)');
80
+ });
81
+ it.each([
82
+ 'success',
83
+ 'warning',
84
+ 'danger',
85
+ 'info',
86
+ 'neutral',
87
+ ])('is axe-clean for custom status tone=%s', async (tone) => {
88
+ const { container } = render(StatusBadge, {
89
+ props: { status: 'awaiting_cert', tone },
90
+ });
91
+ await expectNoA11yViolations(container);
92
+ });
15
93
  it('renders the status value as its label', () => {
16
94
  render(StatusBadge, { props: { status: 'active' } });
17
95
  expect(screen.getByText('active')).toBeInTheDocument();
@@ -0,0 +1,11 @@
1
+ /** Application icon paths, using the Icon component's configured viewBox. */
2
+ export type IconPathMap = Readonly<Record<string, string>>;
3
+ /**
4
+ * Register an application icon set, returning an idempotent cleanup function.
5
+ * Last active registration wins; caller-owned maps are snapshotted. Register
6
+ * static application sets at startup, not request-specific or identity data.
7
+ */
8
+ export declare function registerIcons(icons: IconPathMap): () => void;
9
+ /** Resolve the last active application path, falling back to the builtin set. */
10
+ export declare function resolveIconPath(name: string): string | undefined;
11
+ //# sourceMappingURL=icons.svelte.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"icons.svelte.d.ts","sourceRoot":"","sources":["../../../src/components/display/icons.svelte.ts"],"names":[],"mappings":"AAAA,6EAA6E;AAC7E,MAAM,MAAM,WAAW,GAAG,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;AA0C3D;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,WAAW,GAAG,MAAM,IAAI,CAkB5D;AAED,iFAAiF;AACjF,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAMhE"}
@@ -0,0 +1,60 @@
1
+ // Common M3 Icon Paths (Simplified)
2
+ const presets = new Map(Object.entries({
3
+ menu: 'M3 18h18v-2H3v2zm0-5h18v-2H3v2zm0-7v2h18V6H3z',
4
+ search: 'M15.5 14h-.79l-.28-.27C15.41 12.59 16 11.11 16 9.5 16 5.91 13.09 3 9.5 3S3 5.91 3 9.5 5.91 16 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z',
5
+ close: 'M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12 19 6.41z',
6
+ 'chevron-right': 'M10 6L8.59 7.41 13.17 12l-4.58 4.59L10 18l6-6-6-6z',
7
+ 'chevron-left': 'M15.41 7.41L14 6l-6 6 6 6 1.41-1.41L10.83 12l4.58-4.59z',
8
+ 'chevron-down': 'M7.41 8.59L12 13.17l4.59-4.58L18 10l-6 6-6-6 1.41-1.41z',
9
+ check: 'M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41L9 16.17z',
10
+ add: 'M19 13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z',
11
+ mic: 'M12 14c1.66 0 3-1.34 3-3V5c0-1.66-1.34-3-3-3S9 3.34 9 5v6c0 1.66 1.34 3 3 3z M17 11c0 2.76-2.24 5-5 5s-5-2.24-5-5H5c0 3.53 2.04 6.43 5 7.07V21h4v-2.93c2.96-.64 5-3.54 5-7.07h-2z',
12
+ alert: 'M12 2a10 10 0 1 0 0 20 10 10 0 0 0 0-20zm-1 5h2v6h-2zm0 8h2v2h-2z',
13
+ warning: 'M1 21h22L12 2 1 21zm12-3h-2v-2h2zm0-4h-2v-4h2z',
14
+ info: 'M12 2a10 10 0 1 0 0 20 10 10 0 0 0 0-20zm1 15h-2v-6h2zm0-8h-2V7h2z',
15
+ home: 'M3 10 12 2 21 10v11h-6v-7H9v7H3z',
16
+ user: 'M12 3a4 4 0 1 0 0 8 4 4 0 0 0 0-8zM4 21v-2c0-4 16-4 16 0v2H4z',
17
+ settings: 'M10 2h4l1 3 3-1 3 3-1 3 3 1v4l-3 1 1 3-3 3-3-1-1 3h-4l-1-3-3 1-3-3 1-3-3-1v-4l3-1-1-3 3-3 3 1 1-3zm2 6a4 4 0 1 0 0 8 4 4 0 0 0 0-8z',
18
+ trash: 'M7 3h10v2h4v2H3V5h4V3zm-2 6h14v12H5V9zm4 2v8h2v-8H9zm4 0v8h2v-8h-2z',
19
+ edit: 'M3 17.25V21h3.75L17.81 9.94l-3.75-3.75L3 17.25zM20.71 7.04a1 1 0 0 0 0-1.41l-2.34-2.34a1 1 0 0 0-1.41 0l-1.83 1.83 3.75 3.75 1.83-1.83z',
20
+ calendar: 'M7 2h2v2h6V2h2v2h4v18H3V4h4V2zm-2 8v10h14V10H5zm2 2h4v4H7v-4z',
21
+ clock: 'M12 2a10 10 0 1 0 0 20 10 10 0 0 0 0-20zm0 2a8 8 0 1 1 0 16 8 8 0 0 1 0-16zm-1 2h2v5.5l4 2.5-1 1.5-5-3V6z',
22
+ camera: 'M9 3h6l2 3h4v15H3V6h4l2-3zm3 5a5 5 0 1 0 0 10 5 5 0 0 0 0-10zm0 2a3 3 0 1 1 0 6 3 3 0 0 1 0-6z',
23
+ upload: 'M11 16V7l-4 4-1.5-1.5L12 3l6.5 6.5L17 11l-4-4v9h-2zM3 18h18v3H3v-3z',
24
+ download: 'M11 3h2v9l4-4 1.5 1.5L12 16l-6.5-6.5L7 8l4 4V3zM3 18h18v3H3v-3z',
25
+ }));
26
+ let registrations = $state.raw([]);
27
+ /**
28
+ * Register an application icon set, returning an idempotent cleanup function.
29
+ * Last active registration wins; caller-owned maps are snapshotted. Register
30
+ * static application sets at startup, not request-specific or identity data.
31
+ */
32
+ export function registerIcons(icons) {
33
+ if (!icons || typeof icons !== 'object' || Array.isArray(icons)) {
34
+ throw new TypeError('Icon set must be a map of names to SVG path strings');
35
+ }
36
+ const entries = Object.entries(icons);
37
+ for (const [name, path] of entries) {
38
+ if (!name.trim() || typeof path !== 'string' || !path.trim()) {
39
+ throw new TypeError('Icon names and paths must be nonempty strings');
40
+ }
41
+ }
42
+ const registration = { paths: new Map(entries) };
43
+ registrations = [...registrations, registration];
44
+ let active = true;
45
+ return () => {
46
+ if (!active)
47
+ return;
48
+ active = false;
49
+ registrations = registrations.filter((entry) => entry !== registration);
50
+ };
51
+ }
52
+ /** Resolve the last active application path, falling back to the builtin set. */
53
+ export function resolveIconPath(name) {
54
+ for (let index = registrations.length - 1; index >= 0; index--) {
55
+ const path = registrations[index].paths.get(name);
56
+ if (path !== undefined)
57
+ return path;
58
+ }
59
+ return presets.get(name);
60
+ }
@@ -6,5 +6,7 @@ export { default as CurrencyDisplay } from './CurrencyDisplay.svelte';
6
6
  export { default as DateDisplay } from './DateDisplay.svelte';
7
7
  export { default as Icon } from './Icon.svelte';
8
8
  export { default as StatusBadge } from './StatusBadge.svelte';
9
- export type { StatusType } from './types.js';
9
+ export type { IconPathMap } from './icons.svelte.js';
10
+ export { registerIcons } from './icons.svelte.js';
11
+ export type { StatusTone, StatusType } from './types.js';
10
12
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/display/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,EAAE,OAAO,IAAI,eAAe,EAAE,MAAM,0BAA0B,CAAC;AACtE,OAAO,EAAE,OAAO,IAAI,eAAe,EAAE,MAAM,0BAA0B,CAAC;AACtE,OAAO,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAC9D,OAAO,EAAE,OAAO,IAAI,IAAI,EAAE,MAAM,eAAe,CAAC;AAChD,OAAO,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAO9D,YAAY,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/display/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,EAAE,OAAO,IAAI,eAAe,EAAE,MAAM,0BAA0B,CAAC;AACtE,OAAO,EAAE,OAAO,IAAI,eAAe,EAAE,MAAM,0BAA0B,CAAC;AACtE,OAAO,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAC9D,OAAO,EAAE,OAAO,IAAI,IAAI,EAAE,MAAM,eAAe,CAAC;AAChD,OAAO,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAM9D,YAAY,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AACrD,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAElD,YAAY,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC"}
@@ -7,3 +7,4 @@ export { default as CurrencyDisplay } from './CurrencyDisplay.svelte';
7
7
  export { default as DateDisplay } from './DateDisplay.svelte';
8
8
  export { default as Icon } from './Icon.svelte';
9
9
  export { default as StatusBadge } from './StatusBadge.svelte';
10
+ export { registerIcons } from './icons.svelte.js';
@@ -2,4 +2,6 @@
2
2
  * Display component types
3
3
  */
4
4
  export type StatusType = 'default' | 'invoice' | 'project' | 'expense' | 'time' | 'compliance' | 'estimate';
5
+ /** Semantic tone for custom status vocabulary. */
6
+ export type StatusTone = 'success' | 'warning' | 'danger' | 'info' | 'neutral';
5
7
  //# sourceMappingURL=types.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/components/display/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,MAAM,MAAM,UAAU,GAClB,SAAS,GACT,SAAS,GACT,SAAS,GACT,SAAS,GACT,MAAM,GACN,YAAY,GACZ,UAAU,CAAC"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/components/display/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,MAAM,MAAM,UAAU,GAClB,SAAS,GACT,SAAS,GACT,SAAS,GACT,SAAS,GACT,MAAM,GACN,YAAY,GACZ,UAAU,CAAC;AAEf,kDAAkD;AAClD,MAAM,MAAM,UAAU,GAAG,SAAS,GAAG,SAAS,GAAG,QAAQ,GAAG,MAAM,GAAG,SAAS,CAAC"}