@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
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Put text into a text field at the cursor, the way typing would.
|
|
3
|
+
*
|
|
4
|
+
* - Replaces the selection, if any.
|
|
5
|
+
* - A field that has never had the cursor in it gets the text at the end.
|
|
6
|
+
* - Adds a space before the text when it would otherwise run into a word,
|
|
7
|
+
* and after it when the next character is a letter or digit.
|
|
8
|
+
* - Leaves the cursor after the inserted text and fires `input`, so bound
|
|
9
|
+
* values (`bind:value`) and listeners see the change.
|
|
10
|
+
*/
|
|
11
|
+
export function insertTextAtCursor(field, text) {
|
|
12
|
+
const words = text.trim();
|
|
13
|
+
if (!words)
|
|
14
|
+
return;
|
|
15
|
+
const value = field.value;
|
|
16
|
+
const focused = typeof document !== 'undefined' && document.activeElement === field;
|
|
17
|
+
let start = field.selectionStart ?? value.length;
|
|
18
|
+
let end = field.selectionEnd ?? value.length;
|
|
19
|
+
// An untouched field reports 0/0; dictation then belongs at the end.
|
|
20
|
+
if (!focused && start === 0 && end === 0 && value.length > 0) {
|
|
21
|
+
start = value.length;
|
|
22
|
+
end = value.length;
|
|
23
|
+
}
|
|
24
|
+
const before = value.slice(0, start);
|
|
25
|
+
const after = value.slice(end);
|
|
26
|
+
const lead = before.length > 0 && !/\s$/.test(before) ? ' ' : '';
|
|
27
|
+
const trail = /^[\p{L}\p{N}]/u.test(after) ? ' ' : '';
|
|
28
|
+
const inserted = `${lead}${words}${trail}`;
|
|
29
|
+
field.value = `${before}${inserted}${after}`;
|
|
30
|
+
const caret = before.length + inserted.length;
|
|
31
|
+
try {
|
|
32
|
+
field.setSelectionRange(caret, caret);
|
|
33
|
+
}
|
|
34
|
+
catch {
|
|
35
|
+
// Some input types do not support selection; the value is still set.
|
|
36
|
+
}
|
|
37
|
+
field.dispatchEvent(new Event('input', { bubbles: true }));
|
|
38
|
+
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Long-press detection for pointer (mouse, pen) and touch input.
|
|
3
|
+
*
|
|
4
|
+
* A press counts as a long press when the primary pointer stays down, and
|
|
5
|
+
* within `moveTolerance` pixels of where it went down, for `delay`
|
|
6
|
+
* milliseconds (default 500ms, 10px). Moving further first is a drag or a
|
|
7
|
+
* scroll, never a long press, so a host's own drag gestures keep working.
|
|
8
|
+
* A native `dragstart` cancels a pending press for the same reason.
|
|
9
|
+
*
|
|
10
|
+
* - Only the primary button (left mouse, a finger, a pen tip) starts one; a
|
|
11
|
+
* right click is left alone, and so is the context menu it opens.
|
|
12
|
+
* - The `click` that follows a fired long press is swallowed so the release
|
|
13
|
+
* does not also "tap" the target.
|
|
14
|
+
* - `preventContextMenu` suppresses the context menu a touch long press
|
|
15
|
+
* opens (Android, desktop touch screens) — but only during a press this
|
|
16
|
+
* detector is tracking. Opt in only where the long press replaces that
|
|
17
|
+
* menu (for example on a picture), never on a text field.
|
|
18
|
+
*
|
|
19
|
+
* Framework-free: `createLongPress` returns handlers a host wires itself, and
|
|
20
|
+
* `longPress` is the Svelte action (`use:longPress={options}`) that wires
|
|
21
|
+
* them to an element.
|
|
22
|
+
*/
|
|
23
|
+
/** What a long press was made on. */
|
|
24
|
+
export interface LongPressDetail {
|
|
25
|
+
/** The element the press is about (from `filter`, else the event target). */
|
|
26
|
+
target: Element;
|
|
27
|
+
/** `mouse`, `pen` or `touch`. */
|
|
28
|
+
pointerType: string;
|
|
29
|
+
clientX: number;
|
|
30
|
+
clientY: number;
|
|
31
|
+
}
|
|
32
|
+
export interface LongPressOptions {
|
|
33
|
+
/** How long the pointer must stay down, in ms. Default 500. */
|
|
34
|
+
delay?: number;
|
|
35
|
+
/** How far (px) the pointer may move before it is a drag instead. Default 10. */
|
|
36
|
+
moveTolerance?: number;
|
|
37
|
+
/**
|
|
38
|
+
* Which presses count. Return the element the press is about, or `null` /
|
|
39
|
+
* `false` to ignore the press (for example: only pictures inside an
|
|
40
|
+
* editor). Default: every press, about its event target.
|
|
41
|
+
*/
|
|
42
|
+
filter?: (event: PointerEvent) => Element | null | boolean;
|
|
43
|
+
/** The pointer stayed down long enough. */
|
|
44
|
+
onLongPress: (detail: LongPressDetail) => void;
|
|
45
|
+
/** The pointer came up after a long press fired (a user gesture). */
|
|
46
|
+
onRelease?: (detail: LongPressDetail) => void;
|
|
47
|
+
/** The pointer went down on a counted target (before the delay). */
|
|
48
|
+
onPressStart?: (detail: LongPressDetail) => void;
|
|
49
|
+
/** A pending press ended without firing (a tap, a drag, a scroll). */
|
|
50
|
+
onCancel?: () => void;
|
|
51
|
+
/** Suppress the context menu during a tracked press. Default false. */
|
|
52
|
+
preventContextMenu?: boolean;
|
|
53
|
+
/** Ignore every press. */
|
|
54
|
+
disabled?: boolean;
|
|
55
|
+
}
|
|
56
|
+
export declare const LONG_PRESS_DEFAULT_DELAY_MS = 500;
|
|
57
|
+
export declare const LONG_PRESS_DEFAULT_MOVE_TOLERANCE_PX = 10;
|
|
58
|
+
export interface LongPressController {
|
|
59
|
+
handlePointerDown(event: PointerEvent): void;
|
|
60
|
+
handlePointerMove(event: PointerEvent): void;
|
|
61
|
+
handlePointerUp(event: PointerEvent): void;
|
|
62
|
+
handlePointerCancel(event: PointerEvent): void;
|
|
63
|
+
handleDragStart(): void;
|
|
64
|
+
handleContextMenu(event: Event): void;
|
|
65
|
+
/** Wire in the capture phase so the swallowed click never reaches the target. */
|
|
66
|
+
handleClick(event: Event): void;
|
|
67
|
+
/** Forget any press in progress (no callbacks). */
|
|
68
|
+
cancel(): void;
|
|
69
|
+
update(options: LongPressOptions): void;
|
|
70
|
+
/** A press is down and waiting for the delay. */
|
|
71
|
+
readonly pending: boolean;
|
|
72
|
+
/** The current press already fired (until the pointer comes up). */
|
|
73
|
+
readonly fired: boolean;
|
|
74
|
+
}
|
|
75
|
+
/** Long-press state and handlers (see the module comment). */
|
|
76
|
+
export declare function createLongPress(initial: LongPressOptions): LongPressController;
|
|
77
|
+
/**
|
|
78
|
+
* Svelte action: `use:longPress={{ onLongPress: () => … }}`.
|
|
79
|
+
*
|
|
80
|
+
* Listens on the element (presses bubble up from its children, so wrapping a
|
|
81
|
+
* field or an editor works). Pointer moves and releases are also tracked on
|
|
82
|
+
* the window so a release outside the element still ends the press.
|
|
83
|
+
*/
|
|
84
|
+
export declare function longPress(node: HTMLElement, options: LongPressOptions): {
|
|
85
|
+
update(next: LongPressOptions): void;
|
|
86
|
+
destroy(): void;
|
|
87
|
+
};
|
|
88
|
+
//# sourceMappingURL=long-press.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"long-press.d.ts","sourceRoot":"","sources":["../../../src/components/forms/long-press.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,qCAAqC;AACrC,MAAM,WAAW,eAAe;IAC9B,6EAA6E;IAC7E,MAAM,EAAE,OAAO,CAAC;IAChB,iCAAiC;IACjC,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,gBAAgB;IAC/B,+DAA+D;IAC/D,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,iFAAiF;IACjF,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB;;;;OAIG;IACH,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,YAAY,KAAK,OAAO,GAAG,IAAI,GAAG,OAAO,CAAC;IAC3D,2CAA2C;IAC3C,WAAW,EAAE,CAAC,MAAM,EAAE,eAAe,KAAK,IAAI,CAAC;IAC/C,qEAAqE;IACrE,SAAS,CAAC,EAAE,CAAC,MAAM,EAAE,eAAe,KAAK,IAAI,CAAC;IAC9C,oEAAoE;IACpE,YAAY,CAAC,EAAE,CAAC,MAAM,EAAE,eAAe,KAAK,IAAI,CAAC;IACjD,sEAAsE;IACtE,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAC;IACtB,uEAAuE;IACvE,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,0BAA0B;IAC1B,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,eAAO,MAAM,2BAA2B,MAAM,CAAC;AAC/C,eAAO,MAAM,oCAAoC,KAAK,CAAC;AAEvD,MAAM,WAAW,mBAAmB;IAClC,iBAAiB,CAAC,KAAK,EAAE,YAAY,GAAG,IAAI,CAAC;IAC7C,iBAAiB,CAAC,KAAK,EAAE,YAAY,GAAG,IAAI,CAAC;IAC7C,eAAe,CAAC,KAAK,EAAE,YAAY,GAAG,IAAI,CAAC;IAC3C,mBAAmB,CAAC,KAAK,EAAE,YAAY,GAAG,IAAI,CAAC;IAC/C,eAAe,IAAI,IAAI,CAAC;IACxB,iBAAiB,CAAC,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;IACtC,iFAAiF;IACjF,WAAW,CAAC,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;IAChC,mDAAmD;IACnD,MAAM,IAAI,IAAI,CAAC;IACf,MAAM,CAAC,OAAO,EAAE,gBAAgB,GAAG,IAAI,CAAC;IACxC,iDAAiD;IACjD,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,oEAAoE;IACpE,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;CACzB;AAED,8DAA8D;AAC9D,wBAAgB,eAAe,CAC7B,OAAO,EAAE,gBAAgB,GACxB,mBAAmB,CA6IrB;AAED;;;;;;GAMG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,WAAW,EAAE,OAAO,EAAE,gBAAgB;iBAmBrD,gBAAgB;;EAchC"}
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Long-press detection for pointer (mouse, pen) and touch input.
|
|
3
|
+
*
|
|
4
|
+
* A press counts as a long press when the primary pointer stays down, and
|
|
5
|
+
* within `moveTolerance` pixels of where it went down, for `delay`
|
|
6
|
+
* milliseconds (default 500ms, 10px). Moving further first is a drag or a
|
|
7
|
+
* scroll, never a long press, so a host's own drag gestures keep working.
|
|
8
|
+
* A native `dragstart` cancels a pending press for the same reason.
|
|
9
|
+
*
|
|
10
|
+
* - Only the primary button (left mouse, a finger, a pen tip) starts one; a
|
|
11
|
+
* right click is left alone, and so is the context menu it opens.
|
|
12
|
+
* - The `click` that follows a fired long press is swallowed so the release
|
|
13
|
+
* does not also "tap" the target.
|
|
14
|
+
* - `preventContextMenu` suppresses the context menu a touch long press
|
|
15
|
+
* opens (Android, desktop touch screens) — but only during a press this
|
|
16
|
+
* detector is tracking. Opt in only where the long press replaces that
|
|
17
|
+
* menu (for example on a picture), never on a text field.
|
|
18
|
+
*
|
|
19
|
+
* Framework-free: `createLongPress` returns handlers a host wires itself, and
|
|
20
|
+
* `longPress` is the Svelte action (`use:longPress={options}`) that wires
|
|
21
|
+
* them to an element.
|
|
22
|
+
*/
|
|
23
|
+
export const LONG_PRESS_DEFAULT_DELAY_MS = 500;
|
|
24
|
+
export const LONG_PRESS_DEFAULT_MOVE_TOLERANCE_PX = 10;
|
|
25
|
+
/** Long-press state and handlers (see the module comment). */
|
|
26
|
+
export function createLongPress(initial) {
|
|
27
|
+
let options = initial;
|
|
28
|
+
let timer = null;
|
|
29
|
+
let pointerId = null;
|
|
30
|
+
let startX = 0;
|
|
31
|
+
let startY = 0;
|
|
32
|
+
let detail = null;
|
|
33
|
+
let fired = false;
|
|
34
|
+
let swallowClick = false;
|
|
35
|
+
let swallowTimer = null;
|
|
36
|
+
function clearTimer() {
|
|
37
|
+
if (timer)
|
|
38
|
+
clearTimeout(timer);
|
|
39
|
+
timer = null;
|
|
40
|
+
}
|
|
41
|
+
function reset() {
|
|
42
|
+
clearTimer();
|
|
43
|
+
pointerId = null;
|
|
44
|
+
detail = null;
|
|
45
|
+
fired = false;
|
|
46
|
+
}
|
|
47
|
+
function cancelPending() {
|
|
48
|
+
if (!timer)
|
|
49
|
+
return;
|
|
50
|
+
reset();
|
|
51
|
+
options.onCancel?.();
|
|
52
|
+
}
|
|
53
|
+
function resolveTarget(event) {
|
|
54
|
+
const picked = options.filter ? options.filter(event) : true;
|
|
55
|
+
if (!picked)
|
|
56
|
+
return null;
|
|
57
|
+
if (picked === true) {
|
|
58
|
+
return event.target instanceof Element ? event.target : null;
|
|
59
|
+
}
|
|
60
|
+
return picked;
|
|
61
|
+
}
|
|
62
|
+
function armClickSwallow() {
|
|
63
|
+
swallowClick = true;
|
|
64
|
+
if (swallowTimer)
|
|
65
|
+
clearTimeout(swallowTimer);
|
|
66
|
+
// A click that never comes (the pointer moved off) must not eat a later one.
|
|
67
|
+
swallowTimer = setTimeout(() => {
|
|
68
|
+
swallowClick = false;
|
|
69
|
+
swallowTimer = null;
|
|
70
|
+
}, 400);
|
|
71
|
+
}
|
|
72
|
+
return {
|
|
73
|
+
handlePointerDown(event) {
|
|
74
|
+
if (options.disabled)
|
|
75
|
+
return;
|
|
76
|
+
if (!event.isPrimary)
|
|
77
|
+
return;
|
|
78
|
+
if (event.pointerType === 'mouse' && event.button !== 0)
|
|
79
|
+
return;
|
|
80
|
+
const target = resolveTarget(event);
|
|
81
|
+
if (!target)
|
|
82
|
+
return;
|
|
83
|
+
reset();
|
|
84
|
+
pointerId = event.pointerId;
|
|
85
|
+
startX = event.clientX;
|
|
86
|
+
startY = event.clientY;
|
|
87
|
+
detail = {
|
|
88
|
+
target,
|
|
89
|
+
pointerType: event.pointerType || 'mouse',
|
|
90
|
+
clientX: event.clientX,
|
|
91
|
+
clientY: event.clientY,
|
|
92
|
+
};
|
|
93
|
+
options.onPressStart?.(detail);
|
|
94
|
+
const pressed = detail;
|
|
95
|
+
timer = setTimeout(() => {
|
|
96
|
+
timer = null;
|
|
97
|
+
fired = true;
|
|
98
|
+
options.onLongPress(pressed);
|
|
99
|
+
}, options.delay ?? LONG_PRESS_DEFAULT_DELAY_MS);
|
|
100
|
+
},
|
|
101
|
+
handlePointerMove(event) {
|
|
102
|
+
if (!timer || event.pointerId !== pointerId)
|
|
103
|
+
return;
|
|
104
|
+
const tolerance = options.moveTolerance ?? LONG_PRESS_DEFAULT_MOVE_TOLERANCE_PX;
|
|
105
|
+
if (Math.hypot(event.clientX - startX, event.clientY - startY) > tolerance) {
|
|
106
|
+
cancelPending();
|
|
107
|
+
}
|
|
108
|
+
},
|
|
109
|
+
handlePointerUp(event) {
|
|
110
|
+
if (event.pointerId !== pointerId)
|
|
111
|
+
return;
|
|
112
|
+
if (timer) {
|
|
113
|
+
cancelPending();
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
if (fired && detail) {
|
|
117
|
+
const released = detail;
|
|
118
|
+
armClickSwallow();
|
|
119
|
+
reset();
|
|
120
|
+
options.onRelease?.(released);
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
reset();
|
|
124
|
+
},
|
|
125
|
+
handlePointerCancel(event) {
|
|
126
|
+
if (event.pointerId !== pointerId)
|
|
127
|
+
return;
|
|
128
|
+
if (timer) {
|
|
129
|
+
cancelPending();
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
if (fired && detail) {
|
|
133
|
+
const released = detail;
|
|
134
|
+
reset();
|
|
135
|
+
options.onRelease?.(released);
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
reset();
|
|
139
|
+
},
|
|
140
|
+
handleDragStart() {
|
|
141
|
+
cancelPending();
|
|
142
|
+
},
|
|
143
|
+
handleContextMenu(event) {
|
|
144
|
+
if (!options.preventContextMenu)
|
|
145
|
+
return;
|
|
146
|
+
if (timer || fired)
|
|
147
|
+
event.preventDefault();
|
|
148
|
+
},
|
|
149
|
+
handleClick(event) {
|
|
150
|
+
if (!swallowClick)
|
|
151
|
+
return;
|
|
152
|
+
swallowClick = false;
|
|
153
|
+
if (swallowTimer)
|
|
154
|
+
clearTimeout(swallowTimer);
|
|
155
|
+
swallowTimer = null;
|
|
156
|
+
event.preventDefault();
|
|
157
|
+
event.stopPropagation();
|
|
158
|
+
},
|
|
159
|
+
cancel() {
|
|
160
|
+
reset();
|
|
161
|
+
},
|
|
162
|
+
update(next) {
|
|
163
|
+
options = next;
|
|
164
|
+
if (next.disabled)
|
|
165
|
+
reset();
|
|
166
|
+
},
|
|
167
|
+
get pending() {
|
|
168
|
+
return timer !== null;
|
|
169
|
+
},
|
|
170
|
+
get fired() {
|
|
171
|
+
return fired;
|
|
172
|
+
},
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Svelte action: `use:longPress={{ onLongPress: () => … }}`.
|
|
177
|
+
*
|
|
178
|
+
* Listens on the element (presses bubble up from its children, so wrapping a
|
|
179
|
+
* field or an editor works). Pointer moves and releases are also tracked on
|
|
180
|
+
* the window so a release outside the element still ends the press.
|
|
181
|
+
*/
|
|
182
|
+
export function longPress(node, options) {
|
|
183
|
+
const controller = createLongPress(options);
|
|
184
|
+
const down = (event) => controller.handlePointerDown(event);
|
|
185
|
+
const move = (event) => controller.handlePointerMove(event);
|
|
186
|
+
const up = (event) => controller.handlePointerUp(event);
|
|
187
|
+
const cancel = (event) => controller.handlePointerCancel(event);
|
|
188
|
+
const drag = () => controller.handleDragStart();
|
|
189
|
+
const menu = (event) => controller.handleContextMenu(event);
|
|
190
|
+
const click = (event) => controller.handleClick(event);
|
|
191
|
+
node.addEventListener('pointerdown', down);
|
|
192
|
+
node.addEventListener('dragstart', drag);
|
|
193
|
+
node.addEventListener('contextmenu', menu);
|
|
194
|
+
node.addEventListener('click', click, true);
|
|
195
|
+
window.addEventListener('pointermove', move);
|
|
196
|
+
window.addEventListener('pointerup', up);
|
|
197
|
+
window.addEventListener('pointercancel', cancel);
|
|
198
|
+
return {
|
|
199
|
+
update(next) {
|
|
200
|
+
controller.update(next);
|
|
201
|
+
},
|
|
202
|
+
destroy() {
|
|
203
|
+
controller.cancel();
|
|
204
|
+
node.removeEventListener('pointerdown', down);
|
|
205
|
+
node.removeEventListener('dragstart', drag);
|
|
206
|
+
node.removeEventListener('contextmenu', menu);
|
|
207
|
+
node.removeEventListener('click', click, true);
|
|
208
|
+
window.removeEventListener('pointermove', move);
|
|
209
|
+
window.removeEventListener('pointerup', up);
|
|
210
|
+
window.removeEventListener('pointercancel', cancel);
|
|
211
|
+
},
|
|
212
|
+
};
|
|
213
|
+
}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Framework-free helpers that let a capture component (`CameraCapture`,
|
|
3
|
+
* `SignaturePad`) post a `File` it created through a plain
|
|
4
|
+
* `<form method="POST" enctype="multipart/form-data">`, without the caller
|
|
5
|
+
* wiring a hidden input or intercepting submit.
|
|
6
|
+
*
|
|
7
|
+
* Two strategies, chosen once per page by {@link detectNativeFileFieldStrategy}:
|
|
8
|
+
*
|
|
9
|
+
* - `data-transfer` (preferred): the component renders a hidden
|
|
10
|
+
* `<input type="file" name>` and fills it with `new DataTransfer()`, so the
|
|
11
|
+
* browser posts the file exactly like a user-picked one. Needs a constructible
|
|
12
|
+
* `DataTransfer` whose `files` an input accepts (Chrome 60+, Firefox 62+,
|
|
13
|
+
* Safari 14.1+).
|
|
14
|
+
* - `formdata-event` (fallback when `DataTransfer` is unavailable): the hidden
|
|
15
|
+
* input carries no `name`, and a capture-phase `formdata` listener appends the
|
|
16
|
+
* file to the form's entry list while the browser builds it. The browser fires
|
|
17
|
+
* `formdata` for native navigation submits as well as `new FormData(form)`, so
|
|
18
|
+
* the request is the same.
|
|
19
|
+
* - `none`: neither API exists. Nothing is posted for the field; the
|
|
20
|
+
* component's `onCapture` callback remains the only channel. Components expose
|
|
21
|
+
* the active strategy as `data-smrt-file-field` for diagnostics.
|
|
22
|
+
*
|
|
23
|
+
* In both posting strategies an uncommitted field posts what an empty native
|
|
24
|
+
* file input posts (an empty, unnamed `application/octet-stream` file), so the
|
|
25
|
+
* server sees one shape regardless of strategy; inside a disabled `<fieldset>`
|
|
26
|
+
* both post nothing, as a disabled native control does. A reset of the owning form
|
|
27
|
+
* empties the field like a native file input ({@link attachFormResetListener}).
|
|
28
|
+
*/
|
|
29
|
+
/** How a capture component posts its file in a native form. */
|
|
30
|
+
export type NativeFileFieldStrategy = 'data-transfer' | 'formdata-event' | 'none';
|
|
31
|
+
/** Globals the strategy probe reads; injectable for tests. */
|
|
32
|
+
export interface NativeFileFieldEnvironment {
|
|
33
|
+
DataTransfer?: unknown;
|
|
34
|
+
FormDataEvent?: unknown;
|
|
35
|
+
File?: unknown;
|
|
36
|
+
/** Creates a detached `<input type="file">` for the assignment probe. */
|
|
37
|
+
createFileInput?: () => HTMLInputElement;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Put `file` into `input` through a fresh `DataTransfer`. Returns `true` only
|
|
41
|
+
* when the input now holds exactly one file. Old engines expose a
|
|
42
|
+
* `DataTransfer` interface whose constructor throws ("Illegal constructor"), or
|
|
43
|
+
* reject the `files` assignment; both report `false`.
|
|
44
|
+
*/
|
|
45
|
+
export declare function assignFileWithDataTransfer(input: HTMLInputElement, file: File, DataTransferCtor?: unknown): boolean;
|
|
46
|
+
/**
|
|
47
|
+
* Empty a file input so it posts the native "no file chosen" entry. Scripts may
|
|
48
|
+
* always set a file input's value to the empty string (only non-empty values
|
|
49
|
+
* throw).
|
|
50
|
+
*/
|
|
51
|
+
export declare function clearFileInput(input: HTMLInputElement): void;
|
|
52
|
+
/** Pick the strategy for this page. Safe during SSR (returns `none`). */
|
|
53
|
+
export declare function detectNativeFileFieldStrategy(env?: NativeFileFieldEnvironment): NativeFileFieldStrategy;
|
|
54
|
+
/** The entry a `formdata`-event field contributes, read at submit time. */
|
|
55
|
+
export interface FormDataFieldEntry {
|
|
56
|
+
/** The form whose entry list receives the file (the hidden input's `form`). */
|
|
57
|
+
form: HTMLFormElement | null;
|
|
58
|
+
name: string;
|
|
59
|
+
/** The committed file, or `null` to post the native empty-file entry. */
|
|
60
|
+
file: File | null;
|
|
61
|
+
/**
|
|
62
|
+
* The component's hidden input. While it is `:disabled` (inside a disabled
|
|
63
|
+
* `<fieldset>`), nothing is appended — the browser leaves a disabled native
|
|
64
|
+
* control out of the entry list, and this appended entry must match.
|
|
65
|
+
*/
|
|
66
|
+
source?: Element | null;
|
|
67
|
+
}
|
|
68
|
+
/** The native "no file chosen" entry: an empty, unnamed octet-stream file. */
|
|
69
|
+
export declare function emptyFileEntry(): File;
|
|
70
|
+
/**
|
|
71
|
+
* Install the `formdata`-event fallback. The listener runs in the capture
|
|
72
|
+
* phase on `target` (normally `document`), so it sees the event whichever form
|
|
73
|
+
* currently owns the field, then appends only for that form. Returns a disposer.
|
|
74
|
+
*/
|
|
75
|
+
export declare function attachFormDataFallback(target: Pick<EventTarget, 'addEventListener' | 'removeEventListener'>, readEntry: () => FormDataFieldEntry | null): () => void;
|
|
76
|
+
/**
|
|
77
|
+
* Call `onReset` whenever the form returned by `readForm` resets
|
|
78
|
+
* (`form.reset()`, a reset button, or SvelteKit `enhance`'s `update()` after a
|
|
79
|
+
* success). A native file input is emptied by its form's reset; a capture
|
|
80
|
+
* component must empty its own committed state at the same moment, or it keeps
|
|
81
|
+
* showing a file that no longer posts.
|
|
82
|
+
*
|
|
83
|
+
* Like {@link attachFormDataFallback}, the listener runs in the capture phase
|
|
84
|
+
* on `target` (normally `document`) and reads the owning form at dispatch
|
|
85
|
+
* time, so a field moved between forms needs no re-wiring and a page listener
|
|
86
|
+
* that stops propagation cannot hide the reset. `reset` fires before the
|
|
87
|
+
* browser resets the controls, and `onReset` runs even if a later listener
|
|
88
|
+
* cancels it: the component and the posted entry are then both empty, which
|
|
89
|
+
* keeps what is shown equal to what posts. Returns a disposer.
|
|
90
|
+
*/
|
|
91
|
+
export declare function attachFormResetListener(target: Pick<EventTarget, 'addEventListener' | 'removeEventListener'>, readForm: () => HTMLFormElement | null | undefined, onReset: () => void): () => void;
|
|
92
|
+
/** `photo` + `image/png` → `photo.png`; unknown types fall back to `bin`. */
|
|
93
|
+
export declare function fileNameForType(base: string, type: string): string;
|
|
94
|
+
//# sourceMappingURL=native-file-field.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"native-file-field.d.ts","sourceRoot":"","sources":["../../../src/components/forms/native-file-field.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAEH,+DAA+D;AAC/D,MAAM,MAAM,uBAAuB,GAC/B,eAAe,GACf,gBAAgB,GAChB,MAAM,CAAC;AAEX,8DAA8D;AAC9D,MAAM,WAAW,0BAA0B;IACzC,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,yEAAyE;IACzE,eAAe,CAAC,EAAE,MAAM,gBAAgB,CAAC;CAC1C;AAqBD;;;;;GAKG;AACH,wBAAgB,0BAA0B,CACxC,KAAK,EAAE,gBAAgB,EACvB,IAAI,EAAE,IAAI,EACV,gBAAgB,GAAE,OACH,GACd,OAAO,CAUT;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,gBAAgB,GAAG,IAAI,CAE5D;AAED,yEAAyE;AACzE,wBAAgB,6BAA6B,CAC3C,GAAG,GAAE,0BAAiD,GACrD,uBAAuB,CAoBzB;AAED,2EAA2E;AAC3E,MAAM,WAAW,kBAAkB;IACjC,+EAA+E;IAC/E,IAAI,EAAE,eAAe,GAAG,IAAI,CAAC;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,yEAAyE;IACzE,IAAI,EAAE,IAAI,GAAG,IAAI,CAAC;IAClB;;;;OAIG;IACH,MAAM,CAAC,EAAE,OAAO,GAAG,IAAI,CAAC;CACzB;AAED,8EAA8E;AAC9E,wBAAgB,cAAc,IAAI,IAAI,CAErC;AAED;;;;GAIG;AACH,wBAAgB,sBAAsB,CACpC,MAAM,EAAE,IAAI,CAAC,WAAW,EAAE,kBAAkB,GAAG,qBAAqB,CAAC,EACrE,SAAS,EAAE,MAAM,kBAAkB,GAAG,IAAI,GACzC,MAAM,IAAI,CAYZ;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,uBAAuB,CACrC,MAAM,EAAE,IAAI,CAAC,WAAW,EAAE,kBAAkB,GAAG,qBAAqB,CAAC,EACrE,QAAQ,EAAE,MAAM,eAAe,GAAG,IAAI,GAAG,SAAS,EAClD,OAAO,EAAE,MAAM,IAAI,GAClB,MAAM,IAAI,CAOZ;AAQD,6EAA6E;AAC7E,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAElE"}
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Framework-free helpers that let a capture component (`CameraCapture`,
|
|
3
|
+
* `SignaturePad`) post a `File` it created through a plain
|
|
4
|
+
* `<form method="POST" enctype="multipart/form-data">`, without the caller
|
|
5
|
+
* wiring a hidden input or intercepting submit.
|
|
6
|
+
*
|
|
7
|
+
* Two strategies, chosen once per page by {@link detectNativeFileFieldStrategy}:
|
|
8
|
+
*
|
|
9
|
+
* - `data-transfer` (preferred): the component renders a hidden
|
|
10
|
+
* `<input type="file" name>` and fills it with `new DataTransfer()`, so the
|
|
11
|
+
* browser posts the file exactly like a user-picked one. Needs a constructible
|
|
12
|
+
* `DataTransfer` whose `files` an input accepts (Chrome 60+, Firefox 62+,
|
|
13
|
+
* Safari 14.1+).
|
|
14
|
+
* - `formdata-event` (fallback when `DataTransfer` is unavailable): the hidden
|
|
15
|
+
* input carries no `name`, and a capture-phase `formdata` listener appends the
|
|
16
|
+
* file to the form's entry list while the browser builds it. The browser fires
|
|
17
|
+
* `formdata` for native navigation submits as well as `new FormData(form)`, so
|
|
18
|
+
* the request is the same.
|
|
19
|
+
* - `none`: neither API exists. Nothing is posted for the field; the
|
|
20
|
+
* component's `onCapture` callback remains the only channel. Components expose
|
|
21
|
+
* the active strategy as `data-smrt-file-field` for diagnostics.
|
|
22
|
+
*
|
|
23
|
+
* In both posting strategies an uncommitted field posts what an empty native
|
|
24
|
+
* file input posts (an empty, unnamed `application/octet-stream` file), so the
|
|
25
|
+
* server sees one shape regardless of strategy; inside a disabled `<fieldset>`
|
|
26
|
+
* both post nothing, as a disabled native control does. A reset of the owning form
|
|
27
|
+
* empties the field like a native file input ({@link attachFormResetListener}).
|
|
28
|
+
*/
|
|
29
|
+
function browserEnvironment() {
|
|
30
|
+
const scope = globalThis;
|
|
31
|
+
return {
|
|
32
|
+
DataTransfer: scope.DataTransfer,
|
|
33
|
+
FormDataEvent: scope.FormDataEvent,
|
|
34
|
+
File: scope.File,
|
|
35
|
+
createFileInput: typeof document === 'undefined'
|
|
36
|
+
? undefined
|
|
37
|
+
: () => {
|
|
38
|
+
const input = document.createElement('input');
|
|
39
|
+
input.type = 'file';
|
|
40
|
+
return input;
|
|
41
|
+
},
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Put `file` into `input` through a fresh `DataTransfer`. Returns `true` only
|
|
46
|
+
* when the input now holds exactly one file. Old engines expose a
|
|
47
|
+
* `DataTransfer` interface whose constructor throws ("Illegal constructor"), or
|
|
48
|
+
* reject the `files` assignment; both report `false`.
|
|
49
|
+
*/
|
|
50
|
+
export function assignFileWithDataTransfer(input, file, DataTransferCtor = globalThis
|
|
51
|
+
.DataTransfer) {
|
|
52
|
+
if (typeof DataTransferCtor !== 'function')
|
|
53
|
+
return false;
|
|
54
|
+
try {
|
|
55
|
+
const transfer = new DataTransferCtor();
|
|
56
|
+
transfer.items.add(file);
|
|
57
|
+
input.files = transfer.files;
|
|
58
|
+
return input.files?.length === 1;
|
|
59
|
+
}
|
|
60
|
+
catch {
|
|
61
|
+
return false;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Empty a file input so it posts the native "no file chosen" entry. Scripts may
|
|
66
|
+
* always set a file input's value to the empty string (only non-empty values
|
|
67
|
+
* throw).
|
|
68
|
+
*/
|
|
69
|
+
export function clearFileInput(input) {
|
|
70
|
+
input.value = '';
|
|
71
|
+
}
|
|
72
|
+
/** Pick the strategy for this page. Safe during SSR (returns `none`). */
|
|
73
|
+
export function detectNativeFileFieldStrategy(env = browserEnvironment()) {
|
|
74
|
+
const FileCtor = env.File;
|
|
75
|
+
if (env.createFileInput && typeof FileCtor === 'function') {
|
|
76
|
+
try {
|
|
77
|
+
const probe = new FileCtor([], 'probe.bin');
|
|
78
|
+
if (assignFileWithDataTransfer(env.createFileInput(), probe, env.DataTransfer)) {
|
|
79
|
+
return 'data-transfer';
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
catch {
|
|
83
|
+
// Fall through to the next strategy.
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
if (typeof env.FormDataEvent === 'function')
|
|
87
|
+
return 'formdata-event';
|
|
88
|
+
return 'none';
|
|
89
|
+
}
|
|
90
|
+
/** The native "no file chosen" entry: an empty, unnamed octet-stream file. */
|
|
91
|
+
export function emptyFileEntry() {
|
|
92
|
+
return new File([], '', { type: 'application/octet-stream' });
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Install the `formdata`-event fallback. The listener runs in the capture
|
|
96
|
+
* phase on `target` (normally `document`), so it sees the event whichever form
|
|
97
|
+
* currently owns the field, then appends only for that form. Returns a disposer.
|
|
98
|
+
*/
|
|
99
|
+
export function attachFormDataFallback(target, readEntry) {
|
|
100
|
+
const listener = (event) => {
|
|
101
|
+
const entry = readEntry();
|
|
102
|
+
if (!entry?.form || !entry.name || event.target !== entry.form)
|
|
103
|
+
return;
|
|
104
|
+
if (entry.source?.matches(':disabled'))
|
|
105
|
+
return;
|
|
106
|
+
const formData = event.formData;
|
|
107
|
+
if (!formData)
|
|
108
|
+
return;
|
|
109
|
+
const file = entry.file ?? emptyFileEntry();
|
|
110
|
+
formData.append(entry.name, file, file.name);
|
|
111
|
+
};
|
|
112
|
+
target.addEventListener('formdata', listener, true);
|
|
113
|
+
return () => target.removeEventListener('formdata', listener, true);
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Call `onReset` whenever the form returned by `readForm` resets
|
|
117
|
+
* (`form.reset()`, a reset button, or SvelteKit `enhance`'s `update()` after a
|
|
118
|
+
* success). A native file input is emptied by its form's reset; a capture
|
|
119
|
+
* component must empty its own committed state at the same moment, or it keeps
|
|
120
|
+
* showing a file that no longer posts.
|
|
121
|
+
*
|
|
122
|
+
* Like {@link attachFormDataFallback}, the listener runs in the capture phase
|
|
123
|
+
* on `target` (normally `document`) and reads the owning form at dispatch
|
|
124
|
+
* time, so a field moved between forms needs no re-wiring and a page listener
|
|
125
|
+
* that stops propagation cannot hide the reset. `reset` fires before the
|
|
126
|
+
* browser resets the controls, and `onReset` runs even if a later listener
|
|
127
|
+
* cancels it: the component and the posted entry are then both empty, which
|
|
128
|
+
* keeps what is shown equal to what posts. Returns a disposer.
|
|
129
|
+
*/
|
|
130
|
+
export function attachFormResetListener(target, readForm, onReset) {
|
|
131
|
+
const listener = (event) => {
|
|
132
|
+
const form = readForm();
|
|
133
|
+
if (form && event.target === form)
|
|
134
|
+
onReset();
|
|
135
|
+
};
|
|
136
|
+
target.addEventListener('reset', listener, true);
|
|
137
|
+
return () => target.removeEventListener('reset', listener, true);
|
|
138
|
+
}
|
|
139
|
+
const EXTENSIONS = {
|
|
140
|
+
'image/jpeg': 'jpg',
|
|
141
|
+
'image/png': 'png',
|
|
142
|
+
'image/webp': 'webp',
|
|
143
|
+
};
|
|
144
|
+
/** `photo` + `image/png` → `photo.png`; unknown types fall back to `bin`. */
|
|
145
|
+
export function fileNameForType(base, type) {
|
|
146
|
+
return `${base}.${EXTENSIONS[type] ?? 'bin'}`;
|
|
147
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A short "ready" beep, made with the Web Audio API (no sound files).
|
|
3
|
+
*
|
|
4
|
+
* Browsers only start audio after a user gesture. A long press fires from a
|
|
5
|
+
* timer while the finger is still down, which on touch screens is not a
|
|
6
|
+
* gesture yet, so `playReadyBeep` resolves `false` when the audio could not
|
|
7
|
+
* start; the host plays it again from the release (`pointerup` is a gesture).
|
|
8
|
+
* `primeReadyBeep` creates the audio context early, from a gesture, so later
|
|
9
|
+
* beeps start immediately.
|
|
10
|
+
*/
|
|
11
|
+
/** Create (or wake) the audio context; call from a user gesture. */
|
|
12
|
+
export declare function primeReadyBeep(): void;
|
|
13
|
+
export interface ReadyBeepOptions {
|
|
14
|
+
/** Volume, 0–1. Default 0.12. */
|
|
15
|
+
volume?: number;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Play two quick rising tones (about 0.2s). Resolves `true` when it played,
|
|
19
|
+
* `false` when audio is unavailable or not allowed yet.
|
|
20
|
+
*/
|
|
21
|
+
export declare function playReadyBeep(options?: ReadyBeepOptions): Promise<boolean>;
|
|
22
|
+
/** Tests: forget the shared audio context. */
|
|
23
|
+
export declare function resetReadyBeepForTests(): void;
|
|
24
|
+
//# sourceMappingURL=ready-beep.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ready-beep.d.ts","sourceRoot":"","sources":["../../../src/components/forms/ready-beep.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AA4BH,oEAAoE;AACpE,wBAAgB,cAAc,IAAI,IAAI,CAKrC;AAED,MAAM,WAAW,gBAAgB;IAC/B,iCAAiC;IACjC,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;;GAGG;AACH,wBAAsB,aAAa,CACjC,OAAO,GAAE,gBAAqB,GAC7B,OAAO,CAAC,OAAO,CAAC,CA6BlB;AAED,8CAA8C;AAC9C,wBAAgB,sBAAsB,IAAI,IAAI,CAE7C"}
|