@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.
- package/AGENTS.md +25 -3
- package/README.md +558 -8
- package/dist/actions/__tests__/swipe-dismiss.test.js +69 -0
- package/dist/actions/swipe-dismiss.d.ts +42 -0
- package/dist/actions/swipe-dismiss.d.ts.map +1 -0
- package/dist/actions/swipe-dismiss.js +69 -0
- package/dist/components/calendar/Calendar.svelte +5 -0
- package/dist/components/calendar/Calendar.svelte.d.ts.map +1 -1
- package/dist/components/calendar/CalendarView.svelte +1250 -0
- package/dist/components/calendar/CalendarView.svelte.d.ts +66 -0
- package/dist/components/calendar/CalendarView.svelte.d.ts.map +1 -0
- package/dist/components/calendar/DayView.svelte +5 -0
- package/dist/components/calendar/DayView.svelte.d.ts.map +1 -1
- package/dist/components/calendar/__tests__/CalendarView.test.js +226 -0
- package/dist/components/calendar/__tests__/calendar-model.test.js +138 -0
- package/dist/components/calendar/calendar-model.d.ts +178 -0
- package/dist/components/calendar/calendar-model.d.ts.map +1 -0
- package/dist/components/calendar/calendar-model.js +362 -0
- package/dist/components/calendar/index.d.ts +12 -1
- package/dist/components/calendar/index.d.ts.map +1 -1
- package/dist/components/calendar/index.js +11 -1
- package/dist/components/data/CollectionList.svelte +23 -5
- package/dist/components/data/CollectionList.svelte.d.ts +8 -2
- package/dist/components/data/CollectionList.svelte.d.ts.map +1 -1
- package/dist/components/data/DataTable.svelte +178 -55
- package/dist/components/data/DataTable.svelte.d.ts.map +1 -1
- package/dist/components/data/DataTableController.d.ts +4 -0
- package/dist/components/data/DataTableController.d.ts.map +1 -1
- package/dist/components/data/DataTableController.js +11 -4
- package/dist/components/data/DataTableResponsive.d.ts +4 -0
- package/dist/components/data/DataTableResponsive.d.ts.map +1 -0
- package/dist/components/data/DataTableResponsive.js +22 -0
- package/dist/components/data/ListSortSelect.svelte +121 -0
- package/dist/components/data/ListSortSelect.svelte.d.ts +22 -0
- package/dist/components/data/ListSortSelect.svelte.d.ts.map +1 -0
- package/dist/components/data/SortableHeader.svelte +169 -0
- package/dist/components/data/SortableHeader.svelte.d.ts +30 -0
- package/dist/components/data/SortableHeader.svelte.d.ts.map +1 -0
- package/dist/components/data/__tests__/CollectionList.test.js +37 -0
- package/dist/components/data/__tests__/DataTable.test.js +76 -0
- package/dist/components/data/__tests__/DataTableController.test.js +35 -0
- package/dist/components/data/__tests__/DataTableResponsive.test.js +80 -0
- package/dist/components/data/__tests__/SortableHeader.test.js +101 -0
- package/dist/components/data/__tests__/link-step-surface.test.js +172 -0
- package/dist/components/data/__tests__/list-sort-state.svelte.test.js +71 -0
- package/dist/components/data/__tests__/list-sort.test.js +91 -0
- package/dist/components/data/__tests__/responsive-table.fixture.svelte +29 -0
- package/dist/components/data/__tests__/responsive-table.fixture.svelte.d.ts +7 -0
- package/dist/components/data/__tests__/responsive-table.fixture.svelte.d.ts.map +1 -0
- package/dist/components/data/__tests__/surface-navigation.test.js +121 -0
- package/dist/components/data/data-surface.d.ts +1 -0
- package/dist/components/data/data-surface.d.ts.map +1 -1
- package/dist/components/data/data-surface.js +1 -0
- package/dist/components/data/index.d.ts +6 -0
- package/dist/components/data/index.d.ts.map +1 -1
- package/dist/components/data/index.js +6 -0
- package/dist/components/data/link-surface.d.ts +79 -0
- package/dist/components/data/link-surface.d.ts.map +1 -0
- package/dist/components/data/link-surface.js +155 -0
- package/dist/components/data/list-sort-state.svelte.d.ts +39 -0
- package/dist/components/data/list-sort-state.svelte.d.ts.map +1 -0
- package/dist/components/data/list-sort-state.svelte.js +47 -0
- package/dist/components/data/list-sort.d.ts +94 -0
- package/dist/components/data/list-sort.d.ts.map +1 -0
- package/dist/components/data/list-sort.js +182 -0
- package/dist/components/data/step-surface.d.ts +73 -0
- package/dist/components/data/step-surface.d.ts.map +1 -0
- package/dist/components/data/step-surface.js +177 -0
- package/dist/components/data/surface-navigation.d.ts +49 -0
- package/dist/components/data/surface-navigation.d.ts.map +1 -0
- package/dist/components/data/surface-navigation.js +100 -0
- package/dist/components/data/types.d.ts +27 -0
- package/dist/components/data/types.d.ts.map +1 -1
- package/dist/components/display/DateDisplay.svelte +76 -14
- package/dist/components/display/DateDisplay.svelte.d.ts +2 -0
- package/dist/components/display/DateDisplay.svelte.d.ts.map +1 -1
- package/dist/components/display/Icon.svelte +3 -16
- package/dist/components/display/Icon.svelte.d.ts +0 -9
- package/dist/components/display/Icon.svelte.d.ts.map +1 -1
- package/dist/components/display/StatusBadge.svelte +29 -2
- package/dist/components/display/StatusBadge.svelte.d.ts +4 -2
- package/dist/components/display/StatusBadge.svelte.d.ts.map +1 -1
- package/dist/components/display/__tests__/DateDisplay.test.js +82 -1
- package/dist/components/display/__tests__/Icon.test.js +132 -2
- package/dist/components/display/__tests__/StatusBadge.test.js +78 -0
- package/dist/components/display/icons.svelte.d.ts +11 -0
- package/dist/components/display/icons.svelte.d.ts.map +1 -0
- package/dist/components/display/icons.svelte.js +60 -0
- package/dist/components/display/index.d.ts +3 -1
- package/dist/components/display/index.d.ts.map +1 -1
- package/dist/components/display/index.js +1 -0
- package/dist/components/display/types.d.ts +2 -0
- package/dist/components/display/types.d.ts.map +1 -1
- package/dist/components/feedback/ConfirmDialog.svelte +59 -48
- package/dist/components/feedback/ConfirmDialog.svelte.d.ts +14 -2
- package/dist/components/feedback/ConfirmDialog.svelte.d.ts.map +1 -1
- package/dist/components/feedback/PhoneSheet.svelte +224 -0
- package/dist/components/feedback/PhoneSheet.svelte.d.ts +35 -0
- package/dist/components/feedback/PhoneSheet.svelte.d.ts.map +1 -0
- package/dist/components/feedback/ToastViewport.svelte +156 -8
- package/dist/components/feedback/ToastViewport.svelte.d.ts +5 -1
- package/dist/components/feedback/ToastViewport.svelte.d.ts.map +1 -1
- package/dist/components/feedback/WorkingStrip.svelte +426 -0
- package/dist/components/feedback/WorkingStrip.svelte.d.ts +37 -0
- package/dist/components/feedback/WorkingStrip.svelte.d.ts.map +1 -0
- package/dist/components/feedback/__tests__/ConfirmDialog.test.js +38 -20
- package/dist/components/feedback/__tests__/PhoneSheet.test.js +101 -0
- package/dist/components/feedback/__tests__/WorkingStrip.test.js +172 -0
- package/dist/components/feedback/__tests__/confirm-message.fixture.svelte +9 -0
- package/dist/components/feedback/__tests__/confirm-message.fixture.svelte.d.ts +19 -0
- package/dist/components/feedback/__tests__/confirm-message.fixture.svelte.d.ts.map +1 -0
- package/dist/components/feedback/__tests__/toast.test.js +142 -1
- package/dist/components/feedback/index.d.ts +5 -1
- package/dist/components/feedback/index.d.ts.map +1 -1
- package/dist/components/feedback/index.js +5 -0
- package/dist/components/feedback/toast.d.ts +13 -1
- package/dist/components/feedback/toast.d.ts.map +1 -1
- package/dist/components/feedback/toast.js +51 -10
- package/dist/components/feedback/working-status.d.ts +34 -0
- package/dist/components/feedback/working-status.d.ts.map +1 -0
- package/dist/components/feedback/working-status.js +5 -0
- package/dist/components/forms/CameraCapture.svelte +619 -0
- package/dist/components/forms/CameraCapture.svelte.d.ts +42 -0
- package/dist/components/forms/CameraCapture.svelte.d.ts.map +1 -0
- package/dist/components/forms/Checkbox.svelte +34 -3
- package/dist/components/forms/Checkbox.svelte.d.ts +2 -0
- package/dist/components/forms/Checkbox.svelte.d.ts.map +1 -1
- package/dist/components/forms/Combobox.svelte +108 -12
- package/dist/components/forms/Combobox.svelte.d.ts +6 -0
- package/dist/components/forms/Combobox.svelte.d.ts.map +1 -1
- package/dist/components/forms/DictationButton.svelte +128 -0
- package/dist/components/forms/DictationButton.svelte.d.ts +19 -0
- package/dist/components/forms/DictationButton.svelte.d.ts.map +1 -0
- package/dist/components/forms/DictationStatus.svelte +164 -0
- package/dist/components/forms/DictationStatus.svelte.d.ts +11 -0
- package/dist/components/forms/DictationStatus.svelte.d.ts.map +1 -0
- package/dist/components/forms/Fieldset.svelte +5 -1
- package/dist/components/forms/Fieldset.svelte.d.ts +2 -0
- package/dist/components/forms/Fieldset.svelte.d.ts.map +1 -1
- package/dist/components/forms/Form.svelte +5 -0
- package/dist/components/forms/Form.svelte.d.ts +1 -0
- package/dist/components/forms/Form.svelte.d.ts.map +1 -1
- package/dist/components/forms/FormActionBar.svelte +84 -0
- package/dist/components/forms/FormActionBar.svelte.d.ts +23 -0
- package/dist/components/forms/FormActionBar.svelte.d.ts.map +1 -0
- package/dist/components/forms/Input.svelte +17 -1
- package/dist/components/forms/Input.svelte.d.ts +2 -0
- package/dist/components/forms/Input.svelte.d.ts.map +1 -1
- package/dist/components/forms/Listbox.svelte +39 -1
- package/dist/components/forms/Listbox.svelte.d.ts.map +1 -1
- package/dist/components/forms/MultiSelect.svelte +41 -1
- package/dist/components/forms/MultiSelect.svelte.d.ts.map +1 -1
- package/dist/components/forms/Radio.svelte +16 -4
- package/dist/components/forms/RadioGroup.svelte +7 -1
- package/dist/components/forms/RadioGroup.svelte.d.ts +2 -0
- package/dist/components/forms/RadioGroup.svelte.d.ts.map +1 -1
- package/dist/components/forms/SearchInput.svelte +207 -0
- package/dist/components/forms/SearchInput.svelte.d.ts +22 -0
- package/dist/components/forms/SearchInput.svelte.d.ts.map +1 -0
- package/dist/components/forms/SegmentedControl.svelte +152 -11
- package/dist/components/forms/SegmentedControl.svelte.d.ts +4 -0
- package/dist/components/forms/SegmentedControl.svelte.d.ts.map +1 -1
- package/dist/components/forms/Select.svelte +17 -1
- package/dist/components/forms/Select.svelte.d.ts +2 -0
- package/dist/components/forms/Select.svelte.d.ts.map +1 -1
- package/dist/components/forms/SignaturePad.svelte +436 -0
- package/dist/components/forms/SignaturePad.svelte.d.ts +32 -0
- package/dist/components/forms/SignaturePad.svelte.d.ts.map +1 -0
- package/dist/components/forms/StagedControlReview.svelte +21 -12
- package/dist/components/forms/StagedControlReview.svelte.d.ts.map +1 -1
- package/dist/components/forms/Switch.svelte +18 -5
- package/dist/components/forms/Switch.svelte.d.ts +2 -0
- package/dist/components/forms/Switch.svelte.d.ts.map +1 -1
- package/dist/components/forms/TagsInput.svelte +8 -1
- package/dist/components/forms/Textarea.svelte +16 -1
- package/dist/components/forms/Textarea.svelte.d.ts +2 -0
- package/dist/components/forms/Textarea.svelte.d.ts.map +1 -1
- package/dist/components/forms/__tests__/CameraCapture.test.js +504 -0
- package/dist/components/forms/__tests__/Combobox.test.js +354 -0
- package/dist/components/forms/__tests__/ControlHitArea.test.js +73 -0
- package/dist/components/forms/__tests__/DictationButton.test.js +176 -0
- package/dist/components/forms/__tests__/Fieldset.test.js +37 -0
- package/dist/components/forms/__tests__/Form.test.js +46 -0
- package/dist/components/forms/__tests__/FormActionBar.test.js +22 -0
- package/dist/components/forms/__tests__/Listbox.test.js +303 -0
- package/dist/components/forms/__tests__/MultiSelect.test.js +281 -0
- package/dist/components/forms/__tests__/SearchInput.test.js +92 -0
- package/dist/components/forms/__tests__/SignaturePad.test.js +338 -0
- package/dist/components/forms/__tests__/StagedControlReview.test.js +37 -3
- package/dist/components/forms/__tests__/camera-capture-session.test.js +207 -0
- package/dist/components/forms/__tests__/capture-form-retry.test.js +166 -0
- package/dist/components/forms/__tests__/capture-form.fixture.svelte +38 -0
- package/dist/components/forms/__tests__/capture-form.fixture.svelte.d.ts +10 -0
- package/dist/components/forms/__tests__/capture-form.fixture.svelte.d.ts.map +1 -0
- package/dist/components/forms/__tests__/capture-test-env.d.ts +70 -0
- package/dist/components/forms/__tests__/capture-test-env.d.ts.map +1 -0
- package/dist/components/forms/__tests__/capture-test-env.js +122 -0
- package/dist/components/forms/__tests__/capture-touch.fixture.svelte +15 -0
- package/dist/components/forms/__tests__/capture-touch.fixture.svelte.d.ts +8 -0
- package/dist/components/forms/__tests__/capture-touch.fixture.svelte.d.ts.map +1 -0
- package/dist/components/forms/__tests__/combobox-form.fixture.svelte +29 -0
- package/dist/components/forms/__tests__/combobox-form.fixture.svelte.d.ts +14 -0
- package/dist/components/forms/__tests__/combobox-form.fixture.svelte.d.ts.map +1 -0
- package/dist/components/forms/__tests__/combobox-labels.fixture.svelte +19 -0
- package/dist/components/forms/__tests__/combobox-labels.fixture.svelte.d.ts +11 -0
- package/dist/components/forms/__tests__/combobox-labels.fixture.svelte.d.ts.map +1 -0
- package/dist/components/forms/__tests__/composite-registration.fixture.svelte +29 -0
- package/dist/components/forms/__tests__/composite-registration.fixture.svelte.d.ts +8 -0
- package/dist/components/forms/__tests__/composite-registration.fixture.svelte.d.ts.map +1 -0
- package/dist/components/forms/__tests__/control-interaction.test.js +19 -1
- package/dist/components/forms/__tests__/control-proposals.test.js +63 -0
- package/dist/components/forms/__tests__/dictation-recording.test.js +582 -0
- package/dist/components/forms/__tests__/dictation.test.js +486 -0
- package/dist/components/forms/__tests__/form-attachment.fixture.svelte +28 -0
- package/dist/components/forms/__tests__/form-attachment.fixture.svelte.d.ts +11 -0
- package/dist/components/forms/__tests__/form-attachment.fixture.svelte.d.ts.map +1 -0
- package/dist/components/forms/__tests__/form-retry-form.fixture.svelte +31 -0
- package/dist/components/forms/__tests__/form-retry-form.fixture.svelte.d.ts +13 -0
- package/dist/components/forms/__tests__/form-retry-form.fixture.svelte.d.ts.map +1 -0
- package/dist/components/forms/__tests__/form-retry-form.test.js +107 -0
- package/dist/components/forms/__tests__/hidden-controls-form-retry.test.js +69 -0
- package/dist/components/forms/__tests__/hidden-controls-form.fixture.svelte +29 -0
- package/dist/components/forms/__tests__/hidden-controls-form.fixture.svelte.d.ts +9 -0
- package/dist/components/forms/__tests__/hidden-controls-form.fixture.svelte.d.ts.map +1 -0
- package/dist/components/forms/__tests__/listbox-form.fixture.svelte +27 -0
- package/dist/components/forms/__tests__/listbox-form.fixture.svelte.d.ts +13 -0
- package/dist/components/forms/__tests__/listbox-form.fixture.svelte.d.ts.map +1 -0
- package/dist/components/forms/__tests__/long-press.test.js +175 -0
- package/dist/components/forms/__tests__/multiselect-form.fixture.svelte +27 -0
- package/dist/components/forms/__tests__/multiselect-form.fixture.svelte.d.ts +13 -0
- package/dist/components/forms/__tests__/multiselect-form.fixture.svelte.d.ts.map +1 -0
- package/dist/components/forms/__tests__/native-file-field.test.js +177 -0
- package/dist/components/forms/__tests__/place-picker.fixture.svelte +33 -0
- package/dist/components/forms/__tests__/place-picker.fixture.svelte.d.ts +12 -0
- package/dist/components/forms/__tests__/place-picker.fixture.svelte.d.ts.map +1 -0
- package/dist/components/forms/__tests__/segmented-posting.fixture.svelte +28 -0
- package/dist/components/forms/__tests__/segmented-posting.fixture.svelte.d.ts +12 -0
- package/dist/components/forms/__tests__/segmented-posting.fixture.svelte.d.ts.map +1 -0
- package/dist/components/forms/__tests__/segmented-posting.test.js +92 -0
- package/dist/components/forms/__tests__/signature-pad-logic.test.js +54 -0
- package/dist/components/forms/__tests__/touch-density.fixture.svelte +34 -0
- package/dist/components/forms/__tests__/touch-density.fixture.svelte.d.ts +8 -0
- package/dist/components/forms/__tests__/touch-density.fixture.svelte.d.ts.map +1 -0
- package/dist/components/forms/__tests__/touch-density.test.js +66 -0
- package/dist/components/forms/audio-capture.d.ts +69 -0
- package/dist/components/forms/audio-capture.d.ts.map +1 -0
- package/dist/components/forms/audio-capture.js +193 -0
- package/dist/components/forms/camera-capture-session.d.ts +81 -0
- package/dist/components/forms/camera-capture-session.d.ts.map +1 -0
- package/dist/components/forms/camera-capture-session.js +130 -0
- package/dist/components/forms/control-interaction.d.ts +6 -0
- package/dist/components/forms/control-interaction.d.ts.map +1 -1
- package/dist/components/forms/control-interaction.js +12 -1
- package/dist/components/forms/control-proposals.d.ts +55 -0
- package/dist/components/forms/control-proposals.d.ts.map +1 -0
- package/dist/components/forms/control-proposals.js +188 -0
- package/dist/components/forms/dictation-transcribe.d.ts +36 -0
- package/dist/components/forms/dictation-transcribe.d.ts.map +1 -0
- package/dist/components/forms/dictation-transcribe.js +65 -0
- package/dist/components/forms/dictation.svelte.d.ts +200 -0
- package/dist/components/forms/dictation.svelte.d.ts.map +1 -0
- package/dist/components/forms/dictation.svelte.js +686 -0
- package/dist/components/forms/form-retry/__tests__/boundaries.test.js +23 -0
- package/dist/components/forms/form-retry/__tests__/content-digest.test.js +70 -0
- package/dist/components/forms/form-retry/__tests__/controller.test.js +802 -0
- package/dist/components/forms/form-retry/__tests__/fake-kit.d.ts +82 -0
- package/dist/components/forms/form-retry/__tests__/fake-kit.d.ts.map +1 -0
- package/dist/components/forms/form-retry/__tests__/fake-kit.js +180 -0
- package/dist/components/forms/form-retry/__tests__/form-draft.test.js +169 -0
- package/dist/components/forms/form-retry/__tests__/ssr.test.js +37 -0
- package/dist/components/forms/form-retry/__tests__/storage.d.ts +26 -0
- package/dist/components/forms/form-retry/__tests__/storage.d.ts.map +1 -0
- package/dist/components/forms/form-retry/__tests__/storage.js +63 -0
- package/dist/components/forms/form-retry/__tests__/submission-key.test.js +236 -0
- package/dist/components/forms/form-retry/__tests__/submitted-draft.test.js +334 -0
- package/dist/components/forms/form-retry/content-digest.d.ts +54 -0
- package/dist/components/forms/form-retry/content-digest.d.ts.map +1 -0
- package/dist/components/forms/form-retry/content-digest.js +169 -0
- package/dist/components/forms/form-retry/controller.d.ts +214 -0
- package/dist/components/forms/form-retry/controller.d.ts.map +1 -0
- package/dist/components/forms/form-retry/controller.js +404 -0
- package/dist/components/forms/form-retry/form-draft.d.ts +68 -0
- package/dist/components/forms/form-retry/form-draft.d.ts.map +1 -0
- package/dist/components/forms/form-retry/form-draft.js +161 -0
- package/dist/components/forms/form-retry/index.d.ts +19 -0
- package/dist/components/forms/form-retry/index.d.ts.map +1 -0
- package/dist/components/forms/form-retry/index.js +16 -0
- package/dist/components/forms/form-retry/submission-key.d.ts +244 -0
- package/dist/components/forms/form-retry/submission-key.d.ts.map +1 -0
- package/dist/components/forms/form-retry/submission-key.js +342 -0
- package/dist/components/forms/form-retry/submitted-draft.d.ts +112 -0
- package/dist/components/forms/form-retry/submitted-draft.d.ts.map +1 -0
- package/dist/components/forms/form-retry/submitted-draft.js +270 -0
- package/dist/components/forms/form-retry/types.d.ts +84 -0
- package/dist/components/forms/form-retry/types.d.ts.map +1 -0
- package/dist/components/forms/form-retry/types.js +13 -0
- package/dist/components/forms/index.d.ts +21 -2
- package/dist/components/forms/index.d.ts.map +1 -1
- package/dist/components/forms/index.js +22 -1
- package/dist/components/forms/insert-text.d.ts +12 -0
- package/dist/components/forms/insert-text.d.ts.map +1 -0
- package/dist/components/forms/insert-text.js +38 -0
- package/dist/components/forms/long-press.d.ts +88 -0
- package/dist/components/forms/long-press.d.ts.map +1 -0
- package/dist/components/forms/long-press.js +213 -0
- package/dist/components/forms/native-file-field.d.ts +94 -0
- package/dist/components/forms/native-file-field.d.ts.map +1 -0
- package/dist/components/forms/native-file-field.js +147 -0
- package/dist/components/forms/ready-beep.d.ts +24 -0
- package/dist/components/forms/ready-beep.d.ts.map +1 -0
- package/dist/components/forms/ready-beep.js +82 -0
- package/dist/components/forms/signature-pad-logic.d.ts +50 -0
- package/dist/components/forms/signature-pad-logic.d.ts.map +1 -0
- package/dist/components/forms/signature-pad-logic.js +48 -0
- package/dist/components/forms/types.d.ts +41 -0
- package/dist/components/forms/types.d.ts.map +1 -1
- package/dist/components/layout/PageHeader.svelte +151 -17
- package/dist/components/layout/PageHeader.svelte.d.ts +47 -7
- package/dist/components/layout/PageHeader.svelte.d.ts.map +1 -1
- package/dist/components/layout/__tests__/PageHeader.test.js +87 -1
- package/dist/components/layout/index.d.ts +1 -0
- package/dist/components/layout/index.d.ts.map +1 -1
- package/dist/components/layout/index.js +1 -0
- package/dist/components/layout/page-header-context.d.ts +25 -0
- package/dist/components/layout/page-header-context.d.ts.map +1 -0
- package/dist/components/layout/page-header-context.js +16 -0
- package/dist/components/nav/FilterChips.svelte +8 -1
- package/dist/components/nav/FilterChips.svelte.d.ts +2 -0
- package/dist/components/nav/FilterChips.svelte.d.ts.map +1 -1
- package/dist/components/nav/Tabs.svelte +241 -0
- package/dist/components/nav/Tabs.svelte.d.ts +15 -0
- package/dist/components/nav/Tabs.svelte.d.ts.map +1 -1
- package/dist/components/nav/__tests__/LinkTabs.test.js +117 -0
- package/dist/components/nav/index.d.ts +1 -0
- package/dist/components/nav/index.d.ts.map +1 -1
- package/dist/components/nav/index.js +1 -0
- package/dist/components/nav/tabs-overflow.d.ts +12 -0
- package/dist/components/nav/tabs-overflow.d.ts.map +1 -0
- package/dist/components/nav/tabs-overflow.js +17 -0
- package/dist/components/nav/types.d.ts +8 -0
- package/dist/components/nav/types.d.ts.map +1 -1
- package/dist/components/ui/Button.svelte +16 -2
- package/dist/components/ui/Button.svelte.d.ts +2 -0
- package/dist/components/ui/Button.svelte.d.ts.map +1 -1
- package/dist/components/ui/Card.svelte +14 -5
- package/dist/components/ui/Dropdown.svelte +55 -1
- package/dist/components/ui/Dropdown.svelte.d.ts +8 -0
- package/dist/components/ui/Dropdown.svelte.d.ts.map +1 -1
- package/dist/components/ui/IconToggle.svelte +139 -0
- package/dist/components/ui/IconToggle.svelte.d.ts +32 -0
- package/dist/components/ui/IconToggle.svelte.d.ts.map +1 -0
- package/dist/components/ui/IconToggleGroup.svelte +56 -0
- package/dist/components/ui/IconToggleGroup.svelte.d.ts +21 -0
- package/dist/components/ui/IconToggleGroup.svelte.d.ts.map +1 -0
- package/dist/components/ui/Pagination.svelte +1 -1
- package/dist/components/ui/Popover.svelte +17 -1
- package/dist/components/ui/Popover.svelte.d.ts +8 -0
- package/dist/components/ui/Popover.svelte.d.ts.map +1 -1
- package/dist/components/ui/__tests__/IconToggle.test.js +59 -0
- package/dist/components/ui/__tests__/IconToggleGroup.test.js +30 -0
- package/dist/components/ui/__tests__/Popover.test.js +44 -0
- package/dist/components/ui/__tests__/dropdown-popover-icon.test.js +125 -0
- package/dist/components/ui/__tests__/phone-touch-targets.test.js +39 -0
- package/dist/components/ui/index.d.ts +2 -0
- package/dist/components/ui/index.d.ts.map +1 -1
- package/dist/components/ui/index.js +2 -0
- package/dist/i18n/strings.ui.d.ts +65 -0
- package/dist/i18n/strings.ui.d.ts.map +1 -1
- package/dist/i18n/strings.ui.js +72 -0
- package/dist/styles/tokens.css +2 -0
- package/dist/svelte/__tests__/playground.test.js +3 -1
- package/dist/svelte/playground/CalendarPreview.svelte +140 -0
- package/dist/svelte/playground/CalendarPreview.svelte.d.ts +4 -0
- package/dist/svelte/playground/CalendarPreview.svelte.d.ts.map +1 -0
- package/dist/svelte/playground/CapturePreview.svelte +56 -0
- package/dist/svelte/playground/CapturePreview.svelte.d.ts +4 -0
- package/dist/svelte/playground/CapturePreview.svelte.d.ts.map +1 -0
- package/dist/svelte/playground.d.ts.map +1 -1
- package/dist/svelte/playground.js +20 -0
- package/dist/themes/ThemeProvider.svelte +9 -0
- package/dist/themes/ThemeProvider.svelte.d.ts +2 -0
- package/dist/themes/ThemeProvider.svelte.d.ts.map +1 -1
- package/dist/themes/css-generator.d.ts.map +1 -1
- package/dist/themes/css-generator.js +3 -0
- package/dist/themes/styles/glass.css +4 -0
- package/dist/themes/styles/happyvertical.css +4 -0
- package/dist/themes/styles/material.css +4 -0
- package/dist/themes/styles/smrt.css +4 -0
- package/dist/themes/styles/studio.css +4 -0
- package/dist/types-generic.d.ts +2 -0
- package/dist/types-generic.d.ts.map +1 -1
- package/package.json +9 -3
package/AGENTS.md
CHANGED
|
@@ -46,13 +46,14 @@ components are exempt — they *are* the primitives.
|
|
|
46
46
|
| `.` | barrel re-exporting the primitives, registry, and theme system |
|
|
47
47
|
| `./playground` | shared-host previews for the complete foundation catalog; rendered inside the active preset and color scheme |
|
|
48
48
|
| `./ui` | `Button`, `Card`, `Badge`, `Avatar`, `Chip`, `Skeleton`, `Tooltip`, `Dropdown`/`Menu`, `Popover`, `Disclosure`, `Accordion`, `Tree`, `Pagination`, … |
|
|
49
|
-
| `./feedback` | `Alert`, `ToastViewport`, `Modal`, `Drawer`/`Sheet`, `ConfirmDialog`, `LoadingOverlay`, `Progress`, `Meter`, `Spinner` |
|
|
49
|
+
| `./feedback` | `Alert`, `ToastViewport`, `Modal`, `Drawer`/`Sheet`, `ConfirmDialog`, `LoadingOverlay`, `Progress`, `Meter`, `Spinner`, phone surfaces `PhoneSheet` / `WorkingStrip` and the `swipeDismiss` action |
|
|
50
50
|
| `./data` | `CollectionToolbar`, `CollectionList`/`ContentList`, `DataTable` and their types — a **component barrel**: it re-exports `.svelte` files, so a plain Node `import()` of it throws `ERR_UNKNOWN_FILE_EXTENSION` |
|
|
51
51
|
| `./data-surface` | **Svelte-free**: registry contracts and shared protocol limits (`DATA_SURFACE_MAX_REQUEST_BYTES`, `DATA_SURFACE_IDENTIFIER_MAX_LENGTH`, …). Server adapters must import these from here, not from `./data` — a server entry that reaches the barrel breaks the `smrt` CLI's plain-Node `.smrt/register.js` load (issue #2924) |
|
|
52
52
|
| `./layout` | `Container`, `Grid`, `Header`, `Footer`, `PageHeader`, `EmptyState`, … |
|
|
53
|
-
| `./calendar` | `Calendar`, `DayView` |
|
|
53
|
+
| `./calendar` | `CalendarView` (time-zone-aware month grid + phone agenda) and its Svelte-free date model (`toEntries`, `layoutMonth`, `dateKeyInZone`, …); deprecated `Calendar`, `DayView` |
|
|
54
54
|
| `./chat` | `MessageBubble`, `ReactionPicker`, `TypingIndicator` |
|
|
55
|
-
| `./forms` | Provider-free fields, choice controls, sliders/ranges, combobox/listbox/multiselect/tags, date/time/file controls, plus the transport-neutral control interaction registry |
|
|
55
|
+
| `./forms` | Provider-free fields, choice controls, sliders/ranges, combobox/listbox/multiselect/tags, date/time/file controls, `CameraCapture`/`SignaturePad` capture, plus the transport-neutral control interaction registry |
|
|
56
|
+
| `./form-retry` | **Svelte-free** form retry (#3291): submission key, in-flight refusal, conditional reset, opt-in restore-after-reload, browser `runOnce()` digest. Also re-exported from `./forms`. Framework-free TS typed structurally against SvelteKit's `SubmitFunction` — never import `@sveltejs/kit` here. Guide: `docs/content/form-retry.md` |
|
|
56
57
|
| `./i18n` | i18n **client**: `useI18n`, `<Trans>`, `defineMessages`, `renderTemplate` (no `smrt-languages` import — the server resolver stays in `smrt-svelte/i18n/server`) |
|
|
57
58
|
| `./registry` | `ModuleUIRegistry` for cross-package component discovery |
|
|
58
59
|
| `./theme` | deprecated compatibility path forwarding to the canonical theme system |
|
|
@@ -149,7 +150,28 @@ other text pairing clears WCAG AA in both schemes.
|
|
|
149
150
|
|
|
150
151
|
## Gotchas
|
|
151
152
|
|
|
153
|
+
- **Calendar dates are `YYYY-MM-DD` keys in the view's `timeZone`.** Project
|
|
154
|
+
instants into the zone once (`dateKeyInZone`) and do all grid/span math on
|
|
155
|
+
keys; never `new Date(y, m, d)` / `getDay()` in the browser zone. Months are
|
|
156
|
+
1-12 in the public API (`CalendarMonth`), unlike `Date#getMonth`.
|
|
157
|
+
- **Capture controls post natively.** `CameraCapture` and `SignaturePad` fill
|
|
158
|
+
a hidden named file input via `DataTransfer`, falling back to a `formdata`
|
|
159
|
+
listener (`native-file-field.ts`). Keep camera lifecycle in the
|
|
160
|
+
framework-free `camera-capture-session.ts` and the pointer gate in
|
|
161
|
+
`signature-pad-logic.ts`; their effects must not read rendered state. The
|
|
162
|
+
`<input capture>` path stays opt-in (`fileInputFallback`), off by default.
|
|
163
|
+
A form `reset` empties a native file input, so both clear their capture on
|
|
164
|
+
their form's reset (`attachFormResetListener`), even while `disabled`;
|
|
165
|
+
otherwise the UI shows a file the next submit no longer posts.
|
|
166
|
+
|
|
152
167
|
- **i18n split**: the client (here) is dependency-free; the Node-only server
|
|
153
168
|
resolver (`buildI18nSnapshot`, → `@happyvertical/smrt-languages`) stays in
|
|
154
169
|
`@happyvertical/smrt-svelte/i18n/server`. Package string catalogs register via
|
|
155
170
|
`defineMessages` imported from `@happyvertical/smrt-ui/i18n`.
|
|
171
|
+
|
|
172
|
+
- **Consumers type-checking a local checkout** hit "two different types with
|
|
173
|
+
this name" on `Snippet` when two Svelte copies meet (svelte2tsx's
|
|
174
|
+
`/// <reference types="svelte" />` in compiled sources ignores `paths`). Keep
|
|
175
|
+
every Svelte export's `types` condition first and pointing at built
|
|
176
|
+
`*.svelte.d.ts`; the consumer recipe is
|
|
177
|
+
[docs/development/svelte-consumer-linking.md](../../docs/development/svelte-consumer-linking.md).
|
package/README.md
CHANGED
|
@@ -29,22 +29,214 @@ pnpm add @happyvertical/smrt-ui
|
|
|
29
29
|
|
|
30
30
|
| Area | Components |
|
|
31
31
|
| --- | --- |
|
|
32
|
-
| Fields | `Form`, `Field`/`FormGroup`, `Fieldset`, `InputGroup`, `ErrorSummary` |
|
|
33
|
-
| Text and structured input | `Input`, `Textarea`, `Select`, `Combobox`, `Listbox`, `MultiSelect`, `TagsInput` |
|
|
32
|
+
| Fields | `Form`, `Field`/`FormGroup`, `Fieldset`, `InputGroup`, `ErrorSummary`, `FormActionBar` |
|
|
33
|
+
| Text and structured input | `Input`, `Textarea`, `Select`, `Combobox`, `Listbox`, `MultiSelect`, `TagsInput`, `SearchInput` |
|
|
34
34
|
| Choices | `Checkbox`, `RadioGroup`/`Radio`, `Switch`, `Toggle`, `ToggleButton`, `SegmentedControl` |
|
|
35
35
|
| Values and files | `Slider`, `RangeSlider`, `DatePicker`, `TimePicker`, `FilePicker` |
|
|
36
|
+
| Capture | `CameraCapture`, `SignaturePad` |
|
|
36
37
|
| Actions and display | `Button`, `Dropdown`/`Menu`, `Badge`, `Chip`, `Avatar`, `Card`, `Skeleton`, `Tooltip`, `Tree` |
|
|
37
|
-
| Disclosure and overlays | `Popover`, `Disclosure`, `Accordion`/`AccordionItem`, `Modal`, `Drawer`/`Sheet`, `ConfirmDialog` |
|
|
38
|
-
| Feedback | `Alert`, `ToastViewport`, `Progress`, `Meter`, `Spinner`, `LoadingOverlay` |
|
|
38
|
+
| Disclosure and overlays | `Popover`, `Disclosure`, `Accordion`/`AccordionItem`, `Modal`, `Drawer`/`Sheet`, `PhoneSheet`, `ConfirmDialog` |
|
|
39
|
+
| Feedback | `Alert`, `ToastViewport`, `Progress`, `Meter`, `Spinner`, `LoadingOverlay`, `WorkingStrip` |
|
|
39
40
|
| Collections | `CollectionToolbar`, `CollectionList`/`ContentList`, `DataTable`, `Pagination` |
|
|
40
41
|
| Layout and navigation | `Container`, `Grid`, `Header`, `Footer`, `PageHeader`, `EmptyState`, `Tabs`, `FilterChips` |
|
|
41
|
-
|
|
42
|
-
|
|
42
|
+
| Calendar | `CalendarView` (deprecated: `Calendar`, `DayView`) |
|
|
43
|
+
|
|
44
|
+
### Phone surfaces
|
|
45
|
+
|
|
46
|
+
- `PhoneSheet` (`/feedback`) is the phone replacement for drawers and centered
|
|
47
|
+
modals: `variant="page"` covers the area, `variant="sheet"` rises from the
|
|
48
|
+
bottom and closes on a swipe down its header. It is non-modal, closes on its
|
|
49
|
+
button and Escape, returns focus to the opener, and can stay mounted while
|
|
50
|
+
closed (hidden and inert) so its content keeps state. It is
|
|
51
|
+
`position: absolute; inset: 0` — render it in AdminShell's `overlays`.
|
|
52
|
+
- `WorkingStrip` (`/feedback`) shows a transport-neutral `WorkingStatus`
|
|
53
|
+
(`idle` · `working` · `paused` · `waiting` · `done` · `failed` ·
|
|
54
|
+
`cancelled`, a `label` for the current step and an optional `goal`) as a
|
|
55
|
+
strip above a phone bottom bar, a floating pill (`variant="floating"`, the
|
|
56
|
+
host positions it), or a line at the top of a pane. Optional controls:
|
|
57
|
+
reopen (`onopen`), Pause/Continue (`onpause`/`onresume`; Escape pauses),
|
|
58
|
+
Review while waiting (`onreview`), Stop, and Close once finished
|
|
59
|
+
(`ondismiss`); all 44px. Done uses the success colors, failed the error
|
|
60
|
+
colors, always with an icon and text. Step changes are announced politely
|
|
61
|
+
(throttled by `announceIntervalMs`), `waiting` and `failed` assertively; it
|
|
62
|
+
never takes focus.
|
|
63
|
+
- `FormActionBar` (`/forms`) groups a form's actions (primary last). On phones
|
|
64
|
+
it is fixed to the bottom, carries `data-form-action-bar` so AdminShell hides
|
|
65
|
+
its phone bottom bar, and hides while `:root[data-keyboard-open]`.
|
|
66
|
+
- `swipeDismiss` / `swipeDismisses` (`/feedback`) are the touch action and pure
|
|
67
|
+
decision behind swipe-to-close.
|
|
68
|
+
- `Dictation` / `DictationButton` / `DictationStatus` (`/forms`) let people
|
|
69
|
+
speak into a text field (tap the microphone, or press and hold the field
|
|
70
|
+
with `longPress`). The speech `source` is normally smrt-svelte's
|
|
71
|
+
`createSttDictationSource()` (the browser's own speech recognition). Give
|
|
72
|
+
it a `transcribe` function too (`createHttpTranscriber('/your/route')`)
|
|
73
|
+
and, where the browser has no speech recognition (Firefox) or no speech
|
|
74
|
+
service behind it (Brave: `network` / `service-not-allowed`), it records
|
|
75
|
+
the message with `MediaRecorder` instead (WebM/Opus, MP4 on Safari; at most
|
|
76
|
+
`maxDurationMs` 2 minutes and `maxBytes` 10 MB), shows "Writing it down…"
|
|
77
|
+
while the route turns it into text, and puts the text at the cursor.
|
|
78
|
+
Keep the speech service's key on the server: the route takes the raw audio
|
|
79
|
+
body (`Content-Type` is the recording's type, `?language=&durationMs=`)
|
|
80
|
+
and answers `{ text }`; 413, 503 and 401/403 become "too long", "not set
|
|
81
|
+
up" and "not allowed" messages.
|
|
82
|
+
|
|
83
|
+
### Link tabs
|
|
84
|
+
|
|
85
|
+
`Tabs` with an `href` on every tab renders a page's sections as navigation —
|
|
86
|
+
a `<nav>` of links, `aria-current="page"` on the active one, each tab its own
|
|
87
|
+
URL — instead of an ARIA tablist. `maxVisible` moves extra tabs into a "More"
|
|
88
|
+
menu while the active tab always stays in the row (`splitTabs` is the pure
|
|
89
|
+
rule), `badge` marks a tab needing attention (and dots "More" when a hidden
|
|
90
|
+
tab has one), and the row scrolls sideways on phones with the active tab
|
|
91
|
+
kept in view. It carries `data-shell-tabs`, so AdminShell keeps it sticky
|
|
92
|
+
under the phone top bar. Pair it with `useLinkSurface` (smrt-svelte) so
|
|
93
|
+
agents can switch tabs too.
|
|
94
|
+
|
|
95
|
+
Use the focused subpaths (`/forms`, `/form-retry`, `/ui`, `/feedback`, `/data`,
|
|
43
96
|
`/data-surface`, `/layout`, `/themes`) to keep imports explicit. The
|
|
44
97
|
Svelte-free `/data-surface` entry exposes the registry contracts and shared
|
|
45
|
-
protocol limits for server adapters
|
|
98
|
+
protocol limits for server adapters; the Svelte-free `/form-retry` entry
|
|
99
|
+
exposes the form-retry helper. The package root remains a compatibility
|
|
46
100
|
barrel.
|
|
47
101
|
|
|
102
|
+
### Calendar dates and shop time
|
|
103
|
+
|
|
104
|
+
`DateDisplay` accepts `timeZone` (an IANA id such as `America/Edmonton`) for
|
|
105
|
+
instant inputs (`Date`, numeric timestamps, and timestamp strings). Omit it to
|
|
106
|
+
keep browser-local absolute formatting and the existing duration-based relative
|
|
107
|
+
format. With an explicit zone, relative today/yesterday/tomorrow boundaries use
|
|
108
|
+
calendar days in that zone, including daylight saving transitions.
|
|
109
|
+
|
|
110
|
+
A bare `YYYY-MM-DD` string always denotes that calendar date, independent of the
|
|
111
|
+
viewer or shop zone. It formats in UTC, retains a date-only `datetime` attribute,
|
|
112
|
+
and ignores `showTime` because it contains no time. Relative calendar-date
|
|
113
|
+
labels compare it with today's date in the supplied zone (or the browser zone).
|
|
114
|
+
Impossible calendar dates render `fallback`. Invalid explicit zones used for
|
|
115
|
+
instant or relative formatting also render `fallback` rather than throwing.
|
|
116
|
+
|
|
117
|
+
### Combobox form submission
|
|
118
|
+
|
|
119
|
+
A named `Combobox` submits its selected option value through a hidden native
|
|
120
|
+
input. Its visible search text and option label are display-only. With
|
|
121
|
+
`allowCustom`, typed text becomes the submitted value; otherwise searching keeps
|
|
122
|
+
the last committed selection. A named empty selection submits an empty string;
|
|
123
|
+
controls with no name or an empty name are omitted. Disabled controls, including
|
|
124
|
+
those inside disabled fieldsets, are omitted by native `FormData`. Native form
|
|
125
|
+
reset restores the initial selection and its label, unless reset is canceled.
|
|
126
|
+
|
|
127
|
+
### MultiSelect form submission
|
|
128
|
+
|
|
129
|
+
A named `MultiSelect` submits one hidden native input per selected option value,
|
|
130
|
+
using repeated field names in selection order. Read them with `FormData.getAll`.
|
|
131
|
+
Option labels are display-only, and numeric option values submit as strings.
|
|
132
|
+
Empty selections, missing or empty names, and disabled controls (including
|
|
133
|
+
ancestor fieldsets) contribute no entries. Native reset restores the initial
|
|
134
|
+
selection unless reset is canceled.
|
|
135
|
+
|
|
136
|
+
### Listbox form submission
|
|
137
|
+
|
|
138
|
+
A named `Listbox` submits one hidden native input containing its selected option
|
|
139
|
+
value, with numeric values encoded as strings. A named listbox without a
|
|
140
|
+
selection submits an empty string. Missing or empty names and disabled controls
|
|
141
|
+
(including ancestor fieldsets) are omitted. Native reset restores the initial
|
|
142
|
+
selection unless reset is canceled.
|
|
143
|
+
|
|
144
|
+
### Status badge tones
|
|
145
|
+
|
|
146
|
+
`StatusBadge` accepts `tone="success"`, `"warning"`, `"danger"`, `"info"`, or
|
|
147
|
+
`"neutral"` for custom status vocabulary such as `awaiting_cert`. An explicit
|
|
148
|
+
tone overrides the built-in domain scheme while label, size, and outline variant
|
|
149
|
+
continue to work independently. Tones use the active theme's paired container
|
|
150
|
+
and text tokens. Without a tone, known domain statuses retain their existing
|
|
151
|
+
colors and unknown statuses retain the neutral fallback. `StatusTone` is exported
|
|
152
|
+
as a TypeScript type from the package root.
|
|
153
|
+
|
|
154
|
+
### Touch targets for Checkbox, Radio and Switch
|
|
155
|
+
|
|
156
|
+
`Checkbox`, `Radio` and `Switch` have an invisible 44x44px hit area around the
|
|
157
|
+
visible control (about 18px for a box or mark, the 2.75rem track for a switch).
|
|
158
|
+
A transparent `::before` on the wrapping `<label>` makes it, so a click anywhere
|
|
159
|
+
in the area toggles the input, no layout shifts, and the focus ring stays on the
|
|
160
|
+
visible box. The box and the label text sit above every hit area, so adjacent
|
|
161
|
+
controls never steal each other's clicks.
|
|
162
|
+
|
|
163
|
+
The hit area paints above plain (unpositioned) content around it, so it is
|
|
164
|
+
constrained to where that cannot cost a neighbour a click:
|
|
165
|
+
|
|
166
|
+
- **Beside other content.** A `Checkbox` or `Radio` that is not the only element
|
|
167
|
+
in its parent (a row title or link next to it, a group's other options) keeps
|
|
168
|
+
its hit area in the box's own column: it grows vertically only, never over the
|
|
169
|
+
text or link beside it. Only a control standing alone gets the full 44x44.
|
|
170
|
+
- **Table cells.** Inside a `td`/`th` the area never leaves the cell. A
|
|
171
|
+
`Checkbox` that is the cell's only content uses the whole cell (clipped to its
|
|
172
|
+
edges). The cell gets `position: relative` at zero specificity (`:where()`), so
|
|
173
|
+
a sticky header or a pinned column keeps its own positioning.
|
|
174
|
+
- **Stacked controls.** Hit areas of tightly stacked controls overlap in the
|
|
175
|
+
gaps; the later control wins a gap, never a neighbour's box or text. Content
|
|
176
|
+
directly above or below a box, closer than 13px, is covered by its hit area:
|
|
177
|
+
give it room, or shrink the area with `--smrt-control-hit-size`.
|
|
178
|
+
- **Resize or turn off.** `--smrt-control-hit-size` (default `2.75rem`); `0px`
|
|
179
|
+
gives only the visible control. Pages never add their own padding hacks.
|
|
180
|
+
- `DataTable`'s built-in selection checkboxes and `CollectionList`'s select box
|
|
181
|
+
are still native inputs and do not use this yet.
|
|
182
|
+
|
|
183
|
+
## Calendar
|
|
184
|
+
|
|
185
|
+
`CalendarView` (`@happyvertical/smrt-ui/calendar`) is a generic month grid
|
|
186
|
+
with a phone agenda:
|
|
187
|
+
|
|
188
|
+
```svelte
|
|
189
|
+
<script lang="ts">
|
|
190
|
+
import { CalendarView } from '@happyvertical/smrt-ui/calendar';
|
|
191
|
+
import { goto } from '$app/navigation';
|
|
192
|
+
let { data } = $props(); // { year, month, items }
|
|
193
|
+
</script>
|
|
194
|
+
|
|
195
|
+
<CalendarView
|
|
196
|
+
items={data.items}
|
|
197
|
+
year={data.year}
|
|
198
|
+
month={data.month}
|
|
199
|
+
timeZone="America/Edmonton"
|
|
200
|
+
onNavigate={({ year, month }) => goto(`?y=${year}&m=${month}`, { keepFocus: true })}
|
|
201
|
+
/>
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
- **Items** are generic `CalendarItem`s: `{ id, title, start, end?, allDay?,
|
|
205
|
+
tone?, color?, label?, group?, href? }`. `start`/`end` take a `Date`, an ISO
|
|
206
|
+
date-time, or a `YYYY-MM-DD` date (all-day). Timed `end` is exclusive; an
|
|
207
|
+
all-day `YYYY-MM-DD` end is the inclusive last day, and an all-day end
|
|
208
|
+
instant at local midnight is exclusive. `tone` maps to the theme's color
|
|
209
|
+
roles; without it, `group` picks a stable tone. Items with `href` render as
|
|
210
|
+
links, others as buttons; both call `onItemSelect`.
|
|
211
|
+
- **Time zone and locale**: every day is computed in `timeZone` (IANA;
|
|
212
|
+
defaults to the browser's — pass it explicitly so server and client
|
|
213
|
+
agree). Month, weekday, date, and time text come from `Intl` in `locale`
|
|
214
|
+
(defaults to the i18n locale); `weekStartsOn` defaults to the locale's.
|
|
215
|
+
- **Modes**: `month` is a `role="grid"` month with roving focus (arrows,
|
|
216
|
+
Home/End, PageUp/PageDown cross months). All-day and multi-day items are
|
|
217
|
+
bands across the days they cover; a day with more than `maxPerDay` rows
|
|
218
|
+
shows "+N more", which opens the day in a panel under the grid, or follows
|
|
219
|
+
`dayHref` when given. `agenda` is a horizontally scrolling strip of the
|
|
220
|
+
month's days (44px targets, arrow keys) above a day-by-day list. `auto`
|
|
221
|
+
(default) uses the agenda below 48rem, matching AdminShell's phone
|
|
222
|
+
breakpoint. No modals.
|
|
223
|
+
- **URL state**: `year` + `month` (1-12) and `selectedDate` (`YYYY-MM-DD`) are
|
|
224
|
+
controlled when passed; changes are reported via `onNavigate` and
|
|
225
|
+
`onSelectDate`, so a page can keep them in its URL.
|
|
226
|
+
- The Svelte-free date model (`toEntries`, `layoutMonth`, `dateKeyInZone`,
|
|
227
|
+
`monthWeeks`, `shiftMonth`, …) is exported from the same subpath for
|
|
228
|
+
server-side range queries and tests.
|
|
229
|
+
|
|
230
|
+
**Migrating from `Calendar` / `DayView` (deprecated).** They compute days in
|
|
231
|
+
the browser zone, print English-only names, and hard-code game/meeting/event
|
|
232
|
+
emoji and routes. Map each `DayEventDetail` to a `CalendarItem` (`name` →
|
|
233
|
+
`title`, `type` → `group`/`label`, your own route → `href`), replace
|
|
234
|
+
`Calendar`'s `year`/`month` (0-indexed) with `CalendarView`'s `year`/`month`
|
|
235
|
+
(1-12) and `onMonthNavigate` with `onNavigate`, `baseUrl` with a `dayHref`
|
|
236
|
+
function that builds your day route from the `YYYY-MM-DD` key, and a `DayView`
|
|
237
|
+
page with `CalendarView` in agenda mode (`selectedDate` set to the day) or the
|
|
238
|
+
month grid's day panel. They will be removed in a future minor release.
|
|
239
|
+
|
|
48
240
|
### Currency display
|
|
49
241
|
|
|
50
242
|
`CurrencyDisplay` accepts ISO 4217 codes as a public `string` prop so persisted
|
|
@@ -91,6 +283,34 @@ Application-specific editors, maps, charts, media workbenches, and domain
|
|
|
91
283
|
records remain composites built from this foundation rather than generic base
|
|
92
284
|
components.
|
|
93
285
|
|
|
286
|
+
## Native and enhanced forms
|
|
287
|
+
|
|
288
|
+
`Form` forwards native form attributes and Svelte attachments through its rest
|
|
289
|
+
props to the underlying `<form>`. For a SvelteKit action, adapt `enhance` with
|
|
290
|
+
`fromAction` and set `preventDefault={false}` so the enhancement handles submission:
|
|
291
|
+
|
|
292
|
+
```svelte
|
|
293
|
+
<script lang="ts">
|
|
294
|
+
import { enhance } from '$app/forms';
|
|
295
|
+
import { fromAction } from 'svelte/attachments';
|
|
296
|
+
import { Form, Input } from '@happyvertical/smrt-ui/forms';
|
|
297
|
+
</script>
|
|
298
|
+
|
|
299
|
+
<Form method="POST" action="?/save" preventDefault={false} {@attach fromAction(enhance)}>
|
|
300
|
+
<Input name="displayName" />
|
|
301
|
+
<button type="submit">Save</button>
|
|
302
|
+
</Form>
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
For a customized enhancement, pass its callback as a getter:
|
|
306
|
+
`{@attach fromAction(enhance, () => submitFunction)}`. The attachment runs on the
|
|
307
|
+
native element and cleans up when it unmounts; the interaction registry and
|
|
308
|
+
staged-review surface remain available. A component reference obtained with
|
|
309
|
+
`bind:this` exposes `getFormElement(): HTMLFormElement | null`, which returns
|
|
310
|
+
`null` before mount and after unmount. For ordinary browser GET/POST forms, use
|
|
311
|
+
`preventDefault={false}` without an attachment. The default remains `true` for
|
|
312
|
+
existing handler-driven forms.
|
|
313
|
+
|
|
94
314
|
## Agent-addressable forms
|
|
95
315
|
|
|
96
316
|
`Form` can expose its controls to a chat, voice, tutorial, or test adapter
|
|
@@ -128,7 +348,8 @@ without coupling controls to a transport:
|
|
|
128
348
|
|
|
129
349
|
Controls publish serializable metadata, constraints, options, sensitivity, and
|
|
130
350
|
capabilities. Adapters can focus, reveal, highlight, explain, validate, and
|
|
131
|
-
stage reviewable proposals.
|
|
351
|
+
stage reviewable proposals. An agent never moves keyboard focus: its `focus`
|
|
352
|
+
reveals and highlights the control instead. Agents cannot apply, discard, clear, or undo;
|
|
132
353
|
those value-changing actions require a trusted local gesture handled by the
|
|
133
354
|
framework review surface. Secret/read-only controls reject agent mutations.
|
|
134
355
|
Staging remains separate so proposals never change user state before review.
|
|
@@ -178,6 +399,175 @@ not await a same-control mutation through a captured registry reference: that
|
|
|
178
399
|
call is indistinguishable from an independent caller in browser runtimes and,
|
|
179
400
|
like any hook that never settles, can hold the ordered queue indefinitely.
|
|
180
401
|
|
|
402
|
+
### Composite controls and form proposals
|
|
403
|
+
|
|
404
|
+
A composite field — a place picker, a slug field with its own preview, a
|
|
405
|
+
record picker — registers as ONE control with `useControlRegistration` (call
|
|
406
|
+
it during component init inside any `Form`, rich `Form`, or `FormScope`). Give
|
|
407
|
+
it a stable `controlId`, a plain `label`, and, when its value is not a plain
|
|
408
|
+
string/number/boolean, a `valueSchema` so adapters can describe it:
|
|
409
|
+
|
|
410
|
+
```svelte
|
|
411
|
+
<script lang="ts">
|
|
412
|
+
import { useControlRegistration } from '@happyvertical/smrt-ui/forms';
|
|
413
|
+
|
|
414
|
+
let { value = $bindable(null) } = $props();
|
|
415
|
+
|
|
416
|
+
useControlRegistration(() => ({
|
|
417
|
+
controlId: 'place',
|
|
418
|
+
metadata: {
|
|
419
|
+
kind: 'custom',
|
|
420
|
+
label: 'Town location',
|
|
421
|
+
valueSchema: {
|
|
422
|
+
type: 'object',
|
|
423
|
+
properties: { name: { type: 'string' }, latitude: { type: 'number' }, longitude: { type: 'number' } },
|
|
424
|
+
},
|
|
425
|
+
},
|
|
426
|
+
getValue: () => value,
|
|
427
|
+
setValue: (next) => { value = next; },
|
|
428
|
+
}));
|
|
429
|
+
</script>
|
|
430
|
+
```
|
|
431
|
+
|
|
432
|
+
Call `recordControlUserEdit(context, controlId, subject)` from the
|
|
433
|
+
composite's own user-event handlers so a staged proposal goes stale when the
|
|
434
|
+
person edits over it (native inputs bubble their events to the Form instead).
|
|
435
|
+
`focusControl`, `revealControl`, `highlightControl`, and `emitControlChange`
|
|
436
|
+
are the same DOM helpers the built-in primitives use for their `focus`,
|
|
437
|
+
`reveal`, and `highlight` handles.
|
|
438
|
+
|
|
439
|
+
`controlProposalInputSchema(registry, formId)` and
|
|
440
|
+
`stageControlProposals(registry, formId, values)` are the transport-neutral
|
|
441
|
+
halves of a "propose values for this form" tool: the schema has one optional
|
|
442
|
+
property per proposable control (never secret/sensitive, unwritable,
|
|
443
|
+
disabled, read-only, file, or password controls), and staging only ever
|
|
444
|
+
creates reviewable proposals. smrt-svelte's `<Form webmcp>` and `FormScope`
|
|
445
|
+
build their `*_stage_changes` tool from these.
|
|
446
|
+
|
|
447
|
+
## Form retry
|
|
448
|
+
|
|
449
|
+
`createFormRetry()` is the browser half of `runOnce()` (`@happyvertical/smrt-core`)
|
|
450
|
+
for a SvelteKit enhanced form: a per-tab, per-form submission key, a refused
|
|
451
|
+
second submit while one is in flight, typed values kept across a validation or
|
|
452
|
+
transport failure, a reset after success only when the fields are unchanged
|
|
453
|
+
since submit, and opt-in restore after a reload (a file that cannot be restored
|
|
454
|
+
is reported, never silently dropped). It is framework-free and imports neither
|
|
455
|
+
Svelte nor SvelteKit; the submit function is typed against SvelteKit's
|
|
456
|
+
`SubmitFunction` shape structurally.
|
|
457
|
+
|
|
458
|
+
```svelte
|
|
459
|
+
<script lang="ts">
|
|
460
|
+
import { enhance } from '$app/forms';
|
|
461
|
+
import { createFormRetry } from '@happyvertical/smrt-ui/form-retry';
|
|
462
|
+
|
|
463
|
+
const retry = createFormRetry({ form: 'report' });
|
|
464
|
+
</script>
|
|
465
|
+
|
|
466
|
+
<form method="POST" use:enhance={retry.enhance()} {@attach retry.attach}>
|
|
467
|
+
<input name="title" />
|
|
468
|
+
<button type="submit">Send</button>
|
|
469
|
+
{#if $retry.status === 'transport-error'}<p role="alert">Send it again unchanged.</p>{/if}
|
|
470
|
+
</form>
|
|
471
|
+
```
|
|
472
|
+
|
|
473
|
+
With smrt-ui's own `Form`, attach the same two pieces to it (see
|
|
474
|
+
[Native and enhanced forms](#native-and-enhanced-forms)):
|
|
475
|
+
`<Form method="POST" preventDefault={false} {@attach fromAction(enhance, () => retry.enhance())} {@attach retry.attach}>`.
|
|
476
|
+
|
|
477
|
+
`/forms` re-exports the same API as the Svelte-free `/form-retry` entry. See the
|
|
478
|
+
[form retry guide](../../docs/content/form-retry.md) for the server half, the
|
|
479
|
+
per-result table, storage and private-window behaviour, and restore.
|
|
480
|
+
|
|
481
|
+
## Camera and signature capture
|
|
482
|
+
|
|
483
|
+
`CameraCapture` takes a photo from the device camera and `SignaturePad` takes a
|
|
484
|
+
signature, each inside an ordinary form. Both live in `/forms`, need only
|
|
485
|
+
browser APIs, and post their file through a plain multipart form when given a
|
|
486
|
+
`name`; the page wires no hidden input and no submit handler.
|
|
487
|
+
|
|
488
|
+
```svelte
|
|
489
|
+
<script lang="ts">
|
|
490
|
+
import { CameraCapture, SignaturePad } from '@happyvertical/smrt-ui/forms';
|
|
491
|
+
</script>
|
|
492
|
+
|
|
493
|
+
<form method="POST" enctype="multipart/form-data">
|
|
494
|
+
<CameraCapture name="photo" facingMode="environment" />
|
|
495
|
+
<SignaturePad name="signature" stylusOnly={settings.signatureStylusOnly} />
|
|
496
|
+
<button type="submit">Send</button>
|
|
497
|
+
</form>
|
|
498
|
+
```
|
|
499
|
+
|
|
500
|
+
**CameraCapture** uses `getUserMedia` with a live preview. The user takes a
|
|
501
|
+
photo, reviews it, and can retake it before "Use photo" commits it. Committing
|
|
502
|
+
calls `onCapture({ blob, dataUrl })` and fills the named field (`photo.jpg` by
|
|
503
|
+
default; set `fileName`, `imageType`, and `quality` to change it). "Retake"
|
|
504
|
+
after a commit empties the field and calls `onClear`. The root's `data-state`
|
|
505
|
+
is one of `starting`, `streaming`, `reviewing`, `committed`, `off` (disabled),
|
|
506
|
+
`permission-denied`, `no-camera`, `unsupported`, `error`, or `fallback`. Each
|
|
507
|
+
problem state has its own copy and, where it can help, a "Try again" action.
|
|
508
|
+
`getUserMedia` exists only in a secure context, so a page served over plain
|
|
509
|
+
HTTP renders `unsupported`.
|
|
510
|
+
|
|
511
|
+
The stream's tracks are stopped on unmount, when `disabled` turns on, when
|
|
512
|
+
`facingMode` changes (the camera is then requested again), and as soon as a
|
|
513
|
+
frame is captured. A permission prompt answered after unmount or disable is
|
|
514
|
+
discarded and its stream released. The lifecycle lives in the framework-free
|
|
515
|
+
`createCameraSession()` (exported with `classifyGetUserMediaError()` and
|
|
516
|
+
`isCameraApiSupported()`), so it can be tested against a fake `MediaDevices`.
|
|
517
|
+
|
|
518
|
+
`fileInputFallback` is opt-in and off by default. With it on, browsers without
|
|
519
|
+
`getUserMedia` render an `<input type="file" accept="image/*" capture>` that
|
|
520
|
+
carries `name` itself (`disabled` blocks it with `aria-disabled` rather than
|
|
521
|
+
the native attribute, so a committed photo keeps posting). It opens the
|
|
522
|
+
operating system's picker, which has no
|
|
523
|
+
live preview and can offer the gallery, so it is not a substitute for the
|
|
524
|
+
camera flow. With it off, those browsers render the `unsupported` state.
|
|
525
|
+
|
|
526
|
+
**SignaturePad** draws on its own canvas. "Use signature" stays disabled until
|
|
527
|
+
an accepted stroke exists, then returns a PNG through `onCapture({ blob,
|
|
528
|
+
dataUrl })` and fills the named field (`signature.png` by default). A committed
|
|
529
|
+
pad is locked until "Clear", which empties the field and calls `onClear`.
|
|
530
|
+
`stylusOnly` is a prop the caller resolves; with it on, only `pointerType ===
|
|
531
|
+
'pen'` draws. `isAcceptedPointerType()` and `mapPointerToCanvasPoint()` are
|
|
532
|
+
exported as pure functions. The pad is dark ink on white paper in every theme
|
|
533
|
+
and colour scheme, and the exported PNG is opaque white, so it reads the same
|
|
534
|
+
wherever it is shown.
|
|
535
|
+
|
|
536
|
+
**Native form posting.** The named field is a hidden `<input type="file">`
|
|
537
|
+
filled through `DataTransfer` (Chrome 60+, Firefox 62+, Safari 14.1+). Where
|
|
538
|
+
`DataTransfer` cannot be constructed or assigned, the field drops its `name`
|
|
539
|
+
and a capture-phase `formdata` listener appends the file while the browser
|
|
540
|
+
builds the request, which covers native navigation submits as well as
|
|
541
|
+
`new FormData(form)`. Where neither API exists, nothing is posted and
|
|
542
|
+
`onCapture` is the only channel. The root's `data-smrt-file-field` attribute
|
|
543
|
+
reports `data-transfer`, `formdata-event`, `file-input`, or `none`. Before a
|
|
544
|
+
commit the field posts what an empty native file input posts. `disabled`
|
|
545
|
+
freezes the controls but keeps a committed file in the submission; unmount the
|
|
546
|
+
component to drop it, or put it in a disabled `<fieldset>`, which leaves the
|
|
547
|
+
field out of the submission (committed or empty) in every posting strategy.
|
|
548
|
+
|
|
549
|
+
A reset of the owning form (`form.reset()`, a reset button, or SvelteKit
|
|
550
|
+
`enhance`'s `update()` after a success, including `createFormRetry()`'s
|
|
551
|
+
conditional reset) empties the field as it empties a native file input, in
|
|
552
|
+
every posting strategy. A committed photo or signature is discarded as
|
|
553
|
+
"Retake" or "Clear" would and `onClear` is called. A photo under review or
|
|
554
|
+
ink not yet committed is discarded too, without `onClear`, so the next entry
|
|
555
|
+
cannot attach the previous one's capture. `CameraCapture` returns to the live
|
|
556
|
+
camera (`off` while `disabled`, the emptied picker with `fileInputFallback`)
|
|
557
|
+
and supersedes a frame still encoding; with nothing held, a reset changes
|
|
558
|
+
nothing and never re-prompts for the camera. `SignaturePad` returns to blank,
|
|
559
|
+
unlocked paper. A reset clears both even while `disabled`, as it clears a
|
|
560
|
+
disabled native input, and a reset a later listener cancels still leaves the
|
|
561
|
+
component and the posted field both empty.
|
|
562
|
+
|
|
563
|
+
Text comes from the `ui.camera_capture.*` and `ui.signature_pad.*` i18n keys
|
|
564
|
+
and can be overridden per instance with `labels`. Capture is a touch flow, so
|
|
565
|
+
its actions (and the fallback picker) are touch targets at every density: at
|
|
566
|
+
least `--smrt-touch-target-min` (48px by default; see
|
|
567
|
+
[Touch density](#touch-density)), which ThemeProvider `overrides` can raise.
|
|
568
|
+
Both register with an enclosing `Form`'s interaction registry as non-readable,
|
|
569
|
+
non-writable `file` controls.
|
|
570
|
+
|
|
181
571
|
## DataTable controller
|
|
182
572
|
|
|
183
573
|
`DataTable` can share one headless `DataTableController` between rendered
|
|
@@ -437,6 +827,34 @@ registry.register({
|
|
|
437
827
|
});
|
|
438
828
|
```
|
|
439
829
|
|
|
830
|
+
### Navigation and step surfaces
|
|
831
|
+
|
|
832
|
+
Two ready-made surfaces cover interactions a view intent cannot reach on its
|
|
833
|
+
own (an intent only dispatches a registry command):
|
|
834
|
+
|
|
835
|
+
- `registerLinkSurface({ registry, surfaceId, label, description, links,
|
|
836
|
+
navigate })` mounts a menu, tab row, or section list. `state.links`
|
|
837
|
+
publishes each link's id, label, description, and group — never its href —
|
|
838
|
+
and the default `open` control resolves `{ target }` (an id, a label, or a
|
|
839
|
+
unique label prefix) to one of those links and calls `navigate(href)`.
|
|
840
|
+
Nothing an agent sends can become a URL.
|
|
841
|
+
Return the navigation's promise from `navigate`: the command answers at
|
|
842
|
+
once, and `whenSurfaceNavigationSettled(registry)` resolves once it
|
|
843
|
+
finished and the new page's surfaces registered (a bespoke surface that
|
|
844
|
+
navigates calls `trackSurfaceNavigation(registry, promise)`).
|
|
845
|
+
- `registerStepSurface({ registry, surfaceId, label, description, steps,
|
|
846
|
+
current, next, back, goTo, nextWrites, showNext })` mounts a wizard.
|
|
847
|
+
`state` carries the steps with their status, the current step, and
|
|
848
|
+
`nextWrites`. `next`/`back`/`go-to` run the page's own handlers (return
|
|
849
|
+
`false` to refuse, e.g. when validation fails). When the current step's
|
|
850
|
+
forward button saves or creates something (`nextWrites`), `next` never
|
|
851
|
+
presses it: it calls `showNext` to reveal and highlight the button and
|
|
852
|
+
publishes `awaitingPerson: true` until the flow moves on.
|
|
853
|
+
|
|
854
|
+
smrt-svelte's `useLinkSurface` / `useStepSurface` / `useListSurface`
|
|
855
|
+
(`@happyvertical/smrt-svelte/web`) bind these to a component's lifetime on
|
|
856
|
+
the Provider's registry.
|
|
857
|
+
|
|
440
858
|
`inspect()` and command results are deterministic `{ version, descriptor,
|
|
441
859
|
revision, state, selection }` envelopes; neither includes a timestamp, rows,
|
|
442
860
|
functions, authority fields, tenant/principal data, SQL, or a transport handle.
|
|
@@ -529,6 +947,46 @@ hand-authored, and every text pairing clears WCAG AA.
|
|
|
529
947
|
Run the shared playground to inspect the full catalog under every preset and
|
|
530
948
|
light/dark scheme.
|
|
531
949
|
|
|
950
|
+
### Card look tokens
|
|
951
|
+
|
|
952
|
+
`Card` and `CollectionList` rows read these custom properties, so an app can
|
|
953
|
+
change the card look once in its own theme layer instead of per page. Unset,
|
|
954
|
+
they keep the stock look.
|
|
955
|
+
|
|
956
|
+
| Property | Default | Used for |
|
|
957
|
+
| --- | --- | --- |
|
|
958
|
+
| `--smrt-card-border` | `1px solid var(--smrt-color-outline-variant)` | edge of `default` and `elevated` cards, CollectionList rows |
|
|
959
|
+
| `--smrt-card-background` | `var(--smrt-color-surface)` | card and row fill |
|
|
960
|
+
| `--smrt-card-shadow` | `none` | shadow of `default` cards (`elevated` keeps its elevation) |
|
|
961
|
+
| `--smrt-card-divider` | `1px solid var(--smrt-color-outline-variant)` | rule under a card header / above its footer |
|
|
962
|
+
|
|
963
|
+
The `outlined` variant always draws its outline. For borderless cards on a
|
|
964
|
+
`surface` page, pair `--smrt-card-border: none` with a tinted background such
|
|
965
|
+
as `var(--smrt-color-surface-variant)` so cards stay distinct in both schemes:
|
|
966
|
+
|
|
967
|
+
```css
|
|
968
|
+
:root {
|
|
969
|
+
--smrt-card-border: none;
|
|
970
|
+
--smrt-card-background: var(--smrt-color-surface-variant);
|
|
971
|
+
}
|
|
972
|
+
```
|
|
973
|
+
|
|
974
|
+
### Component override hooks
|
|
975
|
+
|
|
976
|
+
Hooks an app sets once in its own theme layer instead of restyling a component's
|
|
977
|
+
classes. Unset, each keeps its stock look.
|
|
978
|
+
|
|
979
|
+
| Property | Component | Used for |
|
|
980
|
+
| --- | --- | --- |
|
|
981
|
+
| `--smrt-popover-panel-width` | `Popover` | the panel's width (never wider than the viewport minus 2rem) |
|
|
982
|
+
| `--smrt-popover-panel-padding` | `Popover` | the panel's padding |
|
|
983
|
+
| `--smrt-admin-shell-background` | `AdminShell` (smrt-svelte) | the page background behind the cards |
|
|
984
|
+
| `--smrt-admin-shell-scrollbar-track` / `-thumb` / `-thumb-hover` | `AdminShell` (smrt-svelte) | the themed thin scrollbars inside the shell |
|
|
985
|
+
| `--smrt-shell-title-font-family` | `ShellTitle` (smrt-svelte) | the workspace name's font family |
|
|
986
|
+
|
|
987
|
+
`IconToggle` takes a `tone` prop (a CSS colour such as `var(--status-draft)`) for
|
|
988
|
+
a toggle that is neutral until pressed and then wears its colour.
|
|
989
|
+
|
|
532
990
|
## Development
|
|
533
991
|
|
|
534
992
|
```bash
|
|
@@ -537,3 +995,95 @@ pnpm test
|
|
|
537
995
|
pnpm build
|
|
538
996
|
pnpm verify:pack
|
|
539
997
|
```
|
|
998
|
+
|
|
999
|
+
### Touch density
|
|
1000
|
+
|
|
1001
|
+
Set `<ThemeProvider density="touch">` for floor tablets and phones, or set
|
|
1002
|
+
`density="touch"` on Input, Select, Textarea, Checkbox, Switch, RadioGroup,
|
|
1003
|
+
Button (including links), FilterChips, SegmentedControl, or DataTable. DataTable
|
|
1004
|
+
applies density to its sort buttons. Density is independent of existing `size`
|
|
1005
|
+
props, including native Input/Select sizes. Omitted density inherits; explicitly
|
|
1006
|
+
setting `density="comfortable"` opts that control out and keeps its usual size.
|
|
1007
|
+
RadioGroup expands each option's clickable label, and Checkbox/Switch expand
|
|
1008
|
+
the label hit area while preserving the visual mark.
|
|
1009
|
+
|
|
1010
|
+
`--smrt-touch-target-min` defaults to `48px` across all presets and works without
|
|
1011
|
+
a provider. Customize it globally with ThemeProvider's `overrides`, for example
|
|
1012
|
+
`overrides={{ '--smrt-touch-target-min': '56px' }}`. Touch targets grow with larger
|
|
1013
|
+
content; normal density keeps existing component sizing. `CameraCapture` and
|
|
1014
|
+
`SignaturePad` actions use `--smrt-touch-target-min` at every density, since
|
|
1015
|
+
capture is a touch flow. TenantNav sizing is
|
|
1016
|
+
tracked separately in [#3246](https://github.com/happyvertical/smrt/issues/3246).
|
|
1017
|
+
|
|
1018
|
+
### Narrow DataTable
|
|
1019
|
+
|
|
1020
|
+
Set `responsiveMode="hide-columns"` to adapt to the table's container width.
|
|
1021
|
+
The default `responsiveBreakpoint={800}` includes both 768px tablets and 390px
|
|
1022
|
+
phones. Narrow mode budgets one column per `responsiveColumnMinWidth={160}`
|
|
1023
|
+
pixels, after reserving space for selection and expansion controls. Higher
|
|
1024
|
+
`column.responsive.priority` values survive first (missing/nonfinite values are
|
|
1025
|
+
zero); ties preserve declared display order. `responsive.keepVisible` columns
|
|
1026
|
+
always survive responsive collapse, while explicitly hidden columns stay hidden.
|
|
1027
|
+
|
|
1028
|
+
Retained cells wrap, and their desktop width and pinning settings resume when
|
|
1029
|
+
the container widens. Responsive presentation never changes controller state,
|
|
1030
|
+
sorting, selection, or persisted column visibility. Header groups and structural
|
|
1031
|
+
row colspans follow the retained columns. If keepVisible columns exceed the
|
|
1032
|
+
budget, all remain visible and share the available width. Custom cell/header
|
|
1033
|
+
snippets should fit their cells. Default `responsiveMode="scroll"` preserves
|
|
1034
|
+
existing horizontal scrolling.
|
|
1035
|
+
|
|
1036
|
+
ConfirmDialog opens a native modal dialog above existing Modal and Drawer surfaces.
|
|
1037
|
+
Its `message` accepts plain text or a Svelte snippet (including lists and emphasis);
|
|
1038
|
+
each instance owns its accessible title and description ids. Escape and backdrop
|
|
1039
|
+
clicks request `oncancel`; the parent controls `open`. The opener regains focus
|
|
1040
|
+
on close. Buttons remain disabled while `loading`, and Escape stays available.
|
|
1041
|
+
The native top layer replaces the previous fixed div; confirmation now stacks
|
|
1042
|
+
above an already open modal and makes its background inert. Escape is scoped to
|
|
1043
|
+
the active dialog rather than handled globally. Existing `open`, `loading` and
|
|
1044
|
+
action callbacks retain their controlled-state contracts.
|
|
1045
|
+
|
|
1046
|
+
Browser feedback contracts run with `pnpm --filter @happyvertical/smrt-ui test:e2e`
|
|
1047
|
+
(after installing Playwright Chromium, or setting
|
|
1048
|
+
`PLAYWRIGHT_CHROMIUM_EXECUTABLE_PATH` to a local Chromium executable).
|
|
1049
|
+
|
|
1050
|
+
ToastViewport follows the topmost native modal dialog and restores its original
|
|
1051
|
+
host when all modals close. Its live region, dismiss buttons and actions stay
|
|
1052
|
+
interactive inside Modal, Drawer and ConfirmDialog. Hover and focus independently
|
|
1053
|
+
pause auto-dismiss, then resume the remaining duration. Configure
|
|
1054
|
+
`createToaster({ successDuration: 0 })` to keep success records until dismissal;
|
|
1055
|
+
per-toast `duration: 0` remains supported. `inset` accepts a CSS length, `anchor`
|
|
1056
|
+
accepts a content-region element, and `top-center`/`bottom-center` positions center
|
|
1057
|
+
the viewport within that region (or the window). Anchor geometry follows resize
|
|
1058
|
+
and scroll. Custom injected Toaster implementations may optionally implement
|
|
1059
|
+
`pause(id, reason)` and `resume(id, reason)` to support interaction pausing.
|
|
1060
|
+
|
|
1061
|
+
### SegmentedControl forms
|
|
1062
|
+
|
|
1063
|
+
SegmentedControl renders native radio inputs. Set `name` to post the selected
|
|
1064
|
+
option value with ordinary form submission or FormData; numeric values post as
|
|
1065
|
+
strings while bound values retain their declared type. Unselected, unknown,
|
|
1066
|
+
disabled-option, disabled-control and disabled-fieldset values are omitted.
|
|
1067
|
+
`required` uses native form validation and accepts numeric zero. Nameless controls
|
|
1068
|
+
validate without posting a generated field. Arrow keys cycle enabled options;
|
|
1069
|
+
Home/End select the first/last enabled option. Touch density applies to the full
|
|
1070
|
+
clickable segment.
|
|
1071
|
+
|
|
1072
|
+
Native form reset restores the initial bound value if that option is still
|
|
1073
|
+
available and enabled; otherwise it clears the selection. Canceling the reset
|
|
1074
|
+
event preserves the current selection. Reset does not fire `onvaluechange`,
|
|
1075
|
+
matching native controls; bind:value reflects it.
|
|
1076
|
+
|
|
1077
|
+
### Application icons
|
|
1078
|
+
|
|
1079
|
+
Icon includes the original menu/search/chevron/action glyphs plus `alert`,
|
|
1080
|
+
`warning`, `info`, `home`, `user`, `settings`, `trash`, `edit`, `calendar`,
|
|
1081
|
+
`clock`, `camera`, `upload`, and `download`. Import `registerIcons` from
|
|
1082
|
+
`@happyvertical/smrt-ui` to install an application SVG path map at startup.
|
|
1083
|
+
Registrations update mounted icons; the returned cleanup function removes that
|
|
1084
|
+
registration and restores the previous active set. Last active registration wins,
|
|
1085
|
+
and an explicit Icon `path` prop wins over every named set. Unknown names retain
|
|
1086
|
+
their empty shape. Names and paths must be nonempty strings; invalid mixed sets
|
|
1087
|
+
are rejected atomically. Sets are snapshotted, so later caller mutations do not
|
|
1088
|
+
change glyphs. Register static application assets, never request or identity data,
|
|
1089
|
+
and install the same application set for SSR and client hydration.
|