sveltacular 0.0.76 → 1.0.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/README.md +142 -15
- package/dist/forms/base-input-wrapper.svelte +99 -0
- package/dist/forms/base-input-wrapper.svelte.d.ts +15 -0
- package/dist/forms/bool-box/bool-box.svelte +30 -17
- package/dist/forms/bool-box/bool-box.svelte.d.ts +9 -21
- package/dist/forms/button/button.svelte +153 -89
- package/dist/forms/button/button.svelte.d.ts +25 -29
- package/dist/forms/check-box/check-box-group.svelte +63 -29
- package/dist/forms/check-box/check-box-group.svelte.d.ts +11 -27
- package/dist/forms/check-box/check-box.svelte +57 -33
- package/dist/forms/check-box/check-box.svelte.d.ts +15 -25
- package/dist/forms/check-box/index.d.ts +2 -0
- package/dist/forms/check-box/index.js +2 -0
- package/dist/forms/combo/new-or-existing-combo.svelte +37 -18
- package/dist/forms/combo/new-or-existing-combo.svelte.d.ts +15 -25
- package/dist/forms/combo-box/combo-box.svelte +655 -0
- package/dist/forms/combo-box/combo-box.svelte.d.ts +62 -0
- package/dist/forms/combo-box/index.d.ts +1 -0
- package/dist/forms/combo-box/index.js +1 -0
- package/dist/forms/date-box/date-box.svelte +80 -54
- package/dist/forms/date-box/date-box.svelte.d.ts +21 -32
- package/dist/forms/file-area/file-area.svelte +109 -68
- package/dist/forms/file-area/file-area.svelte.d.ts +12 -22
- package/dist/forms/file-box/file-box.svelte +40 -21
- package/dist/forms/file-box/file-box.svelte.d.ts +14 -25
- package/dist/forms/form-field.svelte +18 -15
- package/dist/forms/form-field.svelte.d.ts +8 -18
- package/dist/forms/form-footer.svelte +6 -3
- package/dist/forms/form-footer.svelte.d.ts +6 -26
- package/dist/forms/form-header.svelte +15 -4
- package/dist/forms/form-header.svelte.d.ts +8 -18
- package/dist/forms/form-label.svelte +15 -6
- package/dist/forms/form-label.svelte.d.ts +8 -19
- package/dist/forms/form-section.svelte +15 -4
- package/dist/forms/form-section.svelte.d.ts +8 -18
- package/dist/forms/form.svelte +30 -13
- package/dist/forms/form.svelte.d.ts +11 -22
- package/dist/forms/index.d.ts +26 -0
- package/dist/forms/index.js +31 -0
- package/dist/forms/info-box/info-box.svelte +19 -8
- package/dist/forms/info-box/info-box.svelte.d.ts +8 -19
- package/dist/forms/list-box/index.d.ts +2 -0
- package/dist/forms/list-box/index.js +1 -0
- package/dist/forms/list-box/list-box.svelte +190 -118
- package/dist/forms/list-box/list-box.svelte.d.ts +16 -26
- package/dist/forms/money-box/money-box.svelte +223 -190
- package/dist/forms/money-box/money-box.svelte.d.ts +16 -28
- package/dist/forms/number-box/number-box.svelte +84 -67
- package/dist/forms/number-box/number-box.svelte.d.ts +17 -28
- package/dist/forms/phone-box/index.d.ts +1 -0
- package/dist/forms/phone-box/index.js +1 -0
- package/dist/forms/phone-box/phone-box.svelte +152 -122
- package/dist/forms/phone-box/phone-box.svelte.d.ts +8 -20
- package/dist/forms/radio-group/index.d.ts +2 -0
- package/dist/forms/radio-group/index.js +2 -0
- package/dist/forms/radio-group/radio-box.svelte +23 -8
- package/dist/forms/radio-group/radio-box.svelte.d.ts +10 -19
- package/dist/forms/radio-group/radio-group.svelte +27 -13
- package/dist/forms/radio-group/radio-group.svelte.d.ts +10 -21
- package/dist/forms/slider/slider.svelte +210 -0
- package/dist/forms/slider/slider.svelte.d.ts +17 -0
- package/dist/forms/switch-box/switch-box.svelte +35 -21
- package/dist/forms/switch-box/switch-box.svelte.d.ts +9 -20
- package/dist/forms/text-area/text-area.svelte +89 -15
- package/dist/forms/text-area/text-area.svelte.d.ts +15 -23
- package/dist/forms/text-box/text-box.svelte +253 -75
- package/dist/forms/text-box/text-box.svelte.d.ts +28 -36
- package/dist/forms/time-box/time-box.svelte +67 -0
- package/dist/forms/time-box/time-box.svelte.d.ts +12 -0
- package/dist/forms/url-box/url-box.svelte +32 -18
- package/dist/forms/url-box/url-box.svelte.d.ts +9 -20
- package/dist/forms/validation.d.ts +60 -0
- package/dist/forms/validation.js +123 -0
- package/dist/generic/address/address.svelte +22 -13
- package/dist/generic/address/address.svelte.d.ts +9 -19
- package/dist/generic/avatar/avatar.svelte +86 -0
- package/dist/generic/avatar/avatar.svelte.d.ts +10 -0
- package/dist/generic/badge/badge.svelte +82 -0
- package/dist/generic/badge/badge.svelte.d.ts +11 -0
- package/dist/generic/card/card-container.svelte +41 -13
- package/dist/generic/card/card-container.svelte.d.ts +8 -18
- package/dist/generic/card/card.svelte +47 -29
- package/dist/generic/card/card.svelte.d.ts +9 -19
- package/dist/generic/card/index.d.ts +3 -0
- package/dist/generic/card/index.js +2 -0
- package/dist/generic/chip/chip.svelte +90 -0
- package/dist/generic/chip/chip.svelte.d.ts +11 -0
- package/dist/generic/date/date-time.svelte +86 -64
- package/dist/generic/date/date-time.svelte.d.ts +10 -20
- package/dist/generic/divider/divider.svelte.d.ts +22 -19
- package/dist/generic/dot/dot.svelte +13 -4
- package/dist/generic/dot/dot.svelte.d.ts +7 -17
- package/dist/generic/dropdown-item/dropdown-item.svelte +24 -12
- package/dist/generic/dropdown-item/dropdown-item.svelte.d.ts +10 -21
- package/dist/generic/email/email.svelte +6 -4
- package/dist/generic/email/email.svelte.d.ts +5 -15
- package/dist/generic/empty/empty.svelte +57 -26
- package/dist/generic/empty/empty.svelte.d.ts +11 -21
- package/dist/generic/header/header.svelte +26 -10
- package/dist/generic/header/header.svelte.d.ts +10 -20
- package/dist/generic/index.d.ts +28 -0
- package/dist/generic/index.js +31 -0
- package/dist/generic/link/link.svelte +20 -7
- package/dist/generic/link/link.svelte.d.ts +11 -21
- package/dist/generic/list/index.d.ts +4 -0
- package/dist/generic/list/index.js +3 -0
- package/dist/generic/list/list-item.svelte +17 -13
- package/dist/generic/list/list-item.svelte.d.ts +6 -15
- package/dist/generic/list/list.d.ts +2 -2
- package/dist/generic/list/list.svelte +28 -15
- package/dist/generic/list/list.svelte.d.ts +9 -19
- package/dist/generic/menu/menu.svelte +163 -57
- package/dist/generic/menu/menu.svelte.d.ts +16 -24
- package/dist/generic/notice/notice.svelte +119 -81
- package/dist/generic/notice/notice.svelte.d.ts +17 -26
- package/dist/generic/overlay.svelte +40 -14
- package/dist/generic/overlay.svelte.d.ts +9 -19
- package/dist/generic/panel/panel.svelte +16 -6
- package/dist/generic/panel/panel.svelte.d.ts +8 -18
- package/dist/generic/phone/phone.svelte +30 -24
- package/dist/generic/phone/phone.svelte.d.ts +6 -16
- package/dist/generic/pill/pill.svelte +47 -33
- package/dist/generic/pill/pill.svelte.d.ts +10 -21
- package/dist/generic/popover/popover.svelte +226 -0
- package/dist/generic/popover/popover.svelte.d.ts +15 -0
- package/dist/generic/rating/rating.svelte +85 -0
- package/dist/generic/rating/rating.svelte.d.ts +11 -0
- package/dist/generic/scorecard/scorecard.svelte +34 -21
- package/dist/generic/scorecard/scorecard.svelte.d.ts +9 -19
- package/dist/generic/section/section.svelte +28 -9
- package/dist/generic/section/section.svelte.d.ts +11 -21
- package/dist/generic/spinner/spinner.svelte +64 -0
- package/dist/generic/spinner/spinner.svelte.d.ts +8 -0
- package/dist/generic/theme-provider/index.d.ts +1 -0
- package/dist/generic/theme-provider/index.js +1 -0
- package/dist/generic/theme-provider/theme-provider-demo.svelte +182 -0
- package/dist/generic/theme-provider/theme-provider-demo.svelte.d.ts +3 -0
- package/dist/generic/theme-provider/theme-provider.svelte +83 -0
- package/dist/generic/theme-provider/theme-provider.svelte.d.ts +44 -0
- package/dist/generic/toaster/toaster.svelte +31 -6
- package/dist/generic/toaster/toaster.svelte.d.ts +7 -15
- package/dist/generic/tooltip/tooltip.svelte +389 -0
- package/dist/generic/tooltip/tooltip.svelte.d.ts +21 -0
- package/dist/helpers/ago.d.ts +7 -1
- package/dist/helpers/ago.js +6 -0
- package/dist/helpers/animation-actions.d.ts +124 -0
- package/dist/helpers/animation-actions.js +299 -0
- package/dist/helpers/animations.d.ts +198 -0
- package/dist/helpers/animations.js +280 -0
- package/dist/helpers/announcer.d.ts +118 -0
- package/dist/helpers/announcer.js +250 -0
- package/dist/helpers/copy-to-clipboard.svelte.d.ts +5 -0
- package/dist/helpers/copy-to-clipboard.svelte.js +28 -0
- package/dist/helpers/debounce.d.ts +7 -0
- package/dist/helpers/debounce.js +7 -0
- package/dist/helpers/focus.d.ts +123 -0
- package/dist/helpers/focus.js +335 -0
- package/dist/helpers/fuzzy-search.d.ts +41 -0
- package/dist/helpers/fuzzy-search.js +114 -0
- package/dist/helpers/index.d.ts +24 -0
- package/dist/helpers/index.js +24 -0
- package/dist/helpers/navigate-to.d.ts +4 -0
- package/dist/helpers/navigate-to.js +4 -0
- package/dist/helpers/positioning.d.ts +97 -0
- package/dist/helpers/positioning.js +230 -0
- package/dist/helpers/round-to-decimals.d.ts +7 -5
- package/dist/helpers/round-to-decimals.js +7 -5
- package/dist/helpers/spring.svelte.d.ts +97 -0
- package/dist/helpers/spring.svelte.js +216 -0
- package/dist/helpers/subscribable.d.ts +1 -2
- package/dist/helpers/theme.svelte.d.ts +63 -0
- package/dist/helpers/theme.svelte.js +123 -0
- package/dist/helpers/unique-id.d.ts +4 -0
- package/dist/helpers/unique-id.js +4 -0
- package/dist/helpers/use-position.svelte.d.ts +96 -0
- package/dist/helpers/use-position.svelte.js +189 -0
- package/dist/helpers/use-virtual-list.svelte.d.ts +121 -0
- package/dist/helpers/use-virtual-list.svelte.js +239 -0
- package/dist/icons/angle-right-icon.svelte +2 -1
- package/dist/icons/angle-right-icon.svelte.d.ts +16 -12
- package/dist/icons/angle-up-icon.svelte.d.ts +22 -19
- package/dist/icons/check-icon.svelte.d.ts +22 -19
- package/dist/icons/copy-icon.svelte +46 -0
- package/dist/icons/copy-icon.svelte.d.ts +6 -0
- package/dist/icons/envelope-icon.svelte.d.ts +22 -19
- package/dist/icons/folder-open-icon.svelte.d.ts +22 -19
- package/dist/icons/hamburger-icon.svelte.d.ts +22 -19
- package/dist/icons/home-icon.svelte +2 -1
- package/dist/icons/home-icon.svelte.d.ts +16 -12
- package/dist/icons/index.d.ts +13 -0
- package/dist/icons/index.js +13 -0
- package/dist/icons/link-icon.svelte.d.ts +22 -19
- package/dist/icons/mobile-phone-icon.svelte.d.ts +22 -19
- package/dist/icons/phone-icon.svelte.d.ts +22 -19
- package/dist/icons/svg-icon.svelte +46 -12
- package/dist/icons/svg-icon.svelte.d.ts +13 -23
- package/dist/icons/upload-icon.svelte.d.ts +22 -19
- package/dist/images/icon.svelte +9 -3
- package/dist/images/icon.svelte.d.ts +6 -16
- package/dist/images/image.svelte +28 -10
- package/dist/images/image.svelte.d.ts +14 -26
- package/dist/images/index.d.ts +2 -0
- package/dist/images/index.js +2 -0
- package/dist/index.d.ts +13 -122
- package/dist/index.js +27 -135
- package/dist/layout/flex-col.svelte +65 -22
- package/dist/layout/flex-col.svelte.d.ts +12 -22
- package/dist/layout/flex-item.svelte +13 -3
- package/dist/layout/flex-item.svelte.d.ts +8 -18
- package/dist/layout/flex-row.svelte +70 -21
- package/dist/layout/flex-row.svelte.d.ts +14 -24
- package/dist/layout/grid.svelte +7 -1
- package/dist/layout/grid.svelte.d.ts +6 -26
- package/dist/layout/index.d.ts +4 -0
- package/dist/layout/index.js +4 -0
- package/dist/modals/alert.svelte +42 -28
- package/dist/modals/alert.svelte.d.ts +13 -24
- package/dist/modals/confirm.svelte +54 -37
- package/dist/modals/confirm.svelte.d.ts +16 -27
- package/dist/modals/dialog-body.svelte +10 -4
- package/dist/modals/dialog-body.svelte.d.ts +6 -26
- package/dist/modals/dialog-close-button.svelte +15 -9
- package/dist/modals/dialog-close-button.svelte.d.ts +6 -17
- package/dist/modals/dialog-footer.svelte +6 -3
- package/dist/modals/dialog-footer.svelte.d.ts +6 -26
- package/dist/modals/dialog-header.svelte +13 -1
- package/dist/modals/dialog-header.svelte.d.ts +7 -26
- package/dist/modals/dialog-window.svelte +42 -14
- package/dist/modals/dialog-window.svelte.d.ts +9 -17
- package/dist/modals/index.d.ts +9 -0
- package/dist/modals/index.js +9 -0
- package/dist/modals/modal.svelte +88 -25
- package/dist/modals/modal.svelte.d.ts +14 -22
- package/dist/modals/prompt.svelte +71 -50
- package/dist/modals/prompt.svelte.d.ts +19 -30
- package/dist/navigation/accordion/accordion.svelte +104 -0
- package/dist/navigation/accordion/accordion.svelte.d.ts +9 -0
- package/dist/navigation/app-bar/app-bar.svelte +26 -20
- package/dist/navigation/app-bar/app-bar.svelte.d.ts +10 -20
- package/dist/navigation/app-bar/app-branding.svelte +10 -5
- package/dist/navigation/app-bar/app-branding.svelte.d.ts +6 -15
- package/dist/navigation/app-bar/app-logo.svelte +20 -5
- package/dist/navigation/app-bar/app-logo.svelte.d.ts +9 -19
- package/dist/navigation/app-bar/app-nav-item.svelte +26 -14
- package/dist/navigation/app-bar/app-nav-item.svelte.d.ts +9 -20
- package/dist/navigation/app-bar/app-nav.svelte +39 -12
- package/dist/navigation/app-bar/app-nav.svelte.d.ts +8 -18
- package/dist/navigation/app-bar/index.d.ts +5 -0
- package/dist/navigation/app-bar/index.js +5 -0
- package/dist/navigation/breadcrumbs/breadcrumbs.svelte +54 -28
- package/dist/navigation/breadcrumbs/breadcrumbs.svelte.d.ts +12 -21
- package/dist/navigation/command-palette/command-palette.svelte +758 -0
- package/dist/navigation/command-palette/command-palette.svelte.d.ts +65 -0
- package/dist/navigation/command-palette/index.d.ts +2 -0
- package/dist/navigation/command-palette/index.js +1 -0
- package/dist/navigation/context-menu/README.md +146 -0
- package/dist/navigation/context-menu/context-menu-divider.svelte +21 -0
- package/dist/navigation/context-menu/context-menu-divider.svelte.d.ts +18 -0
- package/dist/navigation/context-menu/context-menu-item.svelte +268 -0
- package/dist/navigation/context-menu/context-menu-item.svelte.d.ts +19 -0
- package/dist/navigation/context-menu/context-menu.svelte +226 -0
- package/dist/navigation/context-menu/context-menu.svelte.d.ts +38 -0
- package/dist/navigation/context-menu/index.d.ts +3 -0
- package/dist/navigation/context-menu/index.js +3 -0
- package/dist/navigation/drawer/drawer.svelte +137 -0
- package/dist/navigation/drawer/drawer.svelte.d.ts +11 -0
- package/dist/navigation/dropdown-button/dropdown-button.svelte +58 -20
- package/dist/navigation/dropdown-button/dropdown-button.svelte.d.ts +10 -20
- package/dist/navigation/index.d.ts +11 -0
- package/dist/navigation/index.js +14 -0
- package/dist/navigation/pagination/pagination.svelte +55 -42
- package/dist/navigation/pagination/pagination.svelte.d.ts +10 -21
- package/dist/navigation/side-bar/side-bar.svelte +18 -9
- package/dist/navigation/side-bar/side-bar.svelte.d.ts +7 -17
- package/dist/navigation/tabs/index.d.ts +4 -0
- package/dist/navigation/tabs/index.js +3 -0
- package/dist/navigation/tabs/tab-context.d.ts +12 -6
- package/dist/navigation/tabs/tab-group.svelte +268 -55
- package/dist/navigation/tabs/tab-group.svelte.d.ts +9 -20
- package/dist/navigation/tabs/tab.svelte +64 -34
- package/dist/navigation/tabs/tab.svelte.d.ts +11 -22
- package/dist/navigation/wizard/index.d.ts +3 -0
- package/dist/navigation/wizard/index.js +2 -0
- package/dist/navigation/wizard/wizard-context.d.ts +13 -8
- package/dist/navigation/wizard/wizard-step.svelte +38 -15
- package/dist/navigation/wizard/wizard-step.svelte.d.ts +8 -18
- package/dist/navigation/wizard/wizard.svelte +123 -89
- package/dist/navigation/wizard/wizard.svelte.d.ts +15 -26
- package/dist/placeholders/index.d.ts +6 -0
- package/dist/placeholders/index.js +6 -0
- package/dist/placeholders/loading.svelte +39 -23
- package/dist/placeholders/loading.svelte.d.ts +10 -17
- package/dist/placeholders/progress.svelte +7 -6
- package/dist/placeholders/progress.svelte.d.ts +5 -15
- package/dist/placeholders/skeleton-input.svelte +66 -38
- package/dist/placeholders/skeleton-input.svelte.d.ts +5 -15
- package/dist/placeholders/skeleton-paragraph.svelte +25 -0
- package/dist/placeholders/skeleton-paragraph.svelte.d.ts +8 -0
- package/dist/placeholders/skeleton-table.svelte +75 -0
- package/dist/placeholders/skeleton-table.svelte.d.ts +8 -0
- package/dist/placeholders/skeleton-text.svelte +46 -16
- package/dist/placeholders/skeleton-text.svelte.d.ts +7 -17
- package/dist/tables/cell-renderers.d.ts +24 -0
- package/dist/tables/cell-renderers.js +228 -0
- package/dist/tables/data-grid.svelte +332 -135
- package/dist/tables/data-grid.svelte.d.ts +34 -33
- package/dist/tables/index.d.ts +10 -0
- package/dist/tables/index.js +12 -0
- package/dist/tables/table-caption.svelte +13 -4
- package/dist/tables/table-caption.svelte.d.ts +8 -18
- package/dist/tables/table-cell.svelte +45 -15
- package/dist/tables/table-cell.svelte.d.ts +10 -19
- package/dist/tables/table-context.svelte.d.ts +32 -0
- package/dist/tables/table-context.svelte.js +160 -0
- package/dist/tables/table-header-cell.svelte +158 -19
- package/dist/tables/table-header-cell.svelte.d.ts +15 -19
- package/dist/tables/table-header.svelte +31 -6
- package/dist/tables/table-header.svelte.d.ts +7 -26
- package/dist/tables/table-row.svelte +87 -7
- package/dist/tables/table-row.svelte.d.ts +10 -26
- package/dist/tables/table.svelte +61 -2
- package/dist/tables/table.svelte.d.ts +13 -26
- package/dist/test-utils/accessibility-helpers.d.ts +80 -0
- package/dist/test-utils/accessibility-helpers.js +220 -0
- package/dist/test-utils/index.d.ts +8 -0
- package/dist/test-utils/index.js +8 -0
- package/dist/test-utils/mock-helpers.d.ts +68 -0
- package/dist/test-utils/mock-helpers.js +165 -0
- package/dist/test-utils/render-helpers.d.ts +55 -0
- package/dist/test-utils/render-helpers.js +114 -0
- package/dist/test-utils/setup.d.ts +5 -0
- package/dist/test-utils/setup.js +91 -0
- package/dist/test-utils/test-data.d.ts +102 -0
- package/dist/test-utils/test-data.js +99 -0
- package/dist/timeline/index.d.ts +2 -0
- package/dist/timeline/index.js +2 -0
- package/dist/timeline/timeline-item.svelte +26 -9
- package/dist/timeline/timeline-item.svelte.d.ts +13 -23
- package/dist/timeline/timeline.svelte +12 -6
- package/dist/timeline/timeline.svelte.d.ts +6 -26
- package/dist/types/data.d.ts +61 -0
- package/dist/types/date.d.ts +1 -1
- package/dist/types/form.d.ts +20 -2
- package/dist/types/index.d.ts +5 -0
- package/dist/types/index.js +5 -0
- package/dist/types/size.d.ts +22 -0
- package/dist/types/size.js +22 -0
- package/dist/typography/code-block.svelte +89 -10
- package/dist/typography/code-block.svelte.d.ts +7 -17
- package/dist/typography/code.svelte +89 -0
- package/dist/typography/code.svelte.d.ts +7 -0
- package/dist/typography/headline.svelte +29 -9
- package/dist/typography/headline.svelte.d.ts +8 -18
- package/dist/typography/index.d.ts +6 -0
- package/dist/typography/index.js +6 -0
- package/dist/typography/paragraph.svelte +18 -10
- package/dist/typography/paragraph.svelte.d.ts +6 -26
- package/dist/typography/subtitle.svelte +18 -4
- package/dist/typography/subtitle.svelte.d.ts +8 -18
- package/dist/typography/text.svelte +20 -5
- package/dist/typography/text.svelte.d.ts +9 -19
- package/package.json +46 -36
- package/dist/navigation/accordian/accordian.svelte +0 -62
- package/dist/navigation/accordian/accordian.svelte.d.ts +0 -19
- package/dist/tables/table-body.svelte +0 -3
- package/dist/tables/table-body.svelte.d.ts +0 -27
- package/dist/tables/table-footer-cell.svelte +0 -22
- package/dist/tables/table-footer-cell.svelte.d.ts +0 -18
- package/dist/tables/table-footer-row.svelte +0 -3
- package/dist/tables/table-footer-row.svelte.d.ts +0 -27
- package/dist/tables/table-footer.svelte +0 -13
- package/dist/tables/table-footer.svelte.d.ts +0 -27
- package/dist/tables/table-header-row.svelte +0 -4
- package/dist/tables/table-header-row.svelte.d.ts +0 -27
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Positioning Utilities
|
|
3
|
+
*
|
|
4
|
+
* Pure JavaScript positioning calculations for tooltips, popovers, dropdowns, etc.
|
|
5
|
+
* Handles viewport collision detection, auto-flip positioning, and arrow placement.
|
|
6
|
+
*
|
|
7
|
+
* Built from scratch with zero dependencies as an alternative to Floating UI.
|
|
8
|
+
*/
|
|
9
|
+
export type Placement = 'top' | 'top-start' | 'top-end' | 'bottom' | 'bottom-start' | 'bottom-end' | 'left' | 'left-start' | 'left-end' | 'right' | 'right-start' | 'right-end';
|
|
10
|
+
export type Side = 'top' | 'bottom' | 'left' | 'right';
|
|
11
|
+
export type Alignment = 'start' | 'center' | 'end';
|
|
12
|
+
export interface Position {
|
|
13
|
+
top: number;
|
|
14
|
+
left: number;
|
|
15
|
+
side: Side;
|
|
16
|
+
alignment: Alignment;
|
|
17
|
+
}
|
|
18
|
+
export interface ArrowPosition {
|
|
19
|
+
top?: number;
|
|
20
|
+
left?: number;
|
|
21
|
+
side: Side;
|
|
22
|
+
}
|
|
23
|
+
export interface PositionOptions {
|
|
24
|
+
/**
|
|
25
|
+
* Preferred placement for the floating element
|
|
26
|
+
*/
|
|
27
|
+
placement: Placement;
|
|
28
|
+
/**
|
|
29
|
+
* Offset from the reference element in pixels
|
|
30
|
+
* @default 8
|
|
31
|
+
*/
|
|
32
|
+
offset?: number;
|
|
33
|
+
/**
|
|
34
|
+
* Whether to flip to opposite side if doesn't fit
|
|
35
|
+
* @default true
|
|
36
|
+
*/
|
|
37
|
+
autoFlip?: boolean;
|
|
38
|
+
/**
|
|
39
|
+
* Whether to adjust alignment if doesn't fit
|
|
40
|
+
* @default true
|
|
41
|
+
*/
|
|
42
|
+
autoAlign?: boolean;
|
|
43
|
+
/**
|
|
44
|
+
* Padding from viewport edges in pixels
|
|
45
|
+
* @default 8
|
|
46
|
+
*/
|
|
47
|
+
viewportPadding?: number;
|
|
48
|
+
/**
|
|
49
|
+
* Whether to include an arrow/pointer
|
|
50
|
+
* @default false
|
|
51
|
+
*/
|
|
52
|
+
arrow?: boolean;
|
|
53
|
+
/**
|
|
54
|
+
* Size of the arrow in pixels
|
|
55
|
+
* @default 8
|
|
56
|
+
*/
|
|
57
|
+
arrowSize?: number;
|
|
58
|
+
}
|
|
59
|
+
export interface Rect {
|
|
60
|
+
top: number;
|
|
61
|
+
left: number;
|
|
62
|
+
right: number;
|
|
63
|
+
bottom: number;
|
|
64
|
+
width: number;
|
|
65
|
+
height: number;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Get the bounding rect of an element
|
|
69
|
+
*/
|
|
70
|
+
export declare function getRect(element: Element): Rect;
|
|
71
|
+
/**
|
|
72
|
+
* Get the viewport dimensions
|
|
73
|
+
*/
|
|
74
|
+
export declare function getViewport(): Rect;
|
|
75
|
+
/**
|
|
76
|
+
* Parse placement string into side and alignment
|
|
77
|
+
*/
|
|
78
|
+
export declare function parsePlacement(placement: Placement): {
|
|
79
|
+
side: Side;
|
|
80
|
+
alignment: Alignment;
|
|
81
|
+
};
|
|
82
|
+
/**
|
|
83
|
+
* Get the opposite side
|
|
84
|
+
*/
|
|
85
|
+
export declare function getOppositeSide(side: Side): Side;
|
|
86
|
+
/**
|
|
87
|
+
* Calculate the best position for a floating element
|
|
88
|
+
*/
|
|
89
|
+
export declare function calculatePosition(referenceElement: Element, floatingElement: Element, options: PositionOptions): Position;
|
|
90
|
+
/**
|
|
91
|
+
* Calculate arrow position relative to the floating element
|
|
92
|
+
*/
|
|
93
|
+
export declare function calculateArrowPosition(referenceElement: Element, floatingElement: Element, side: Side, arrowSize?: number): ArrowPosition;
|
|
94
|
+
/**
|
|
95
|
+
* Create a positioning update function that can be called on scroll/resize
|
|
96
|
+
*/
|
|
97
|
+
export declare function createPositionUpdater(referenceElement: Element, floatingElement: Element, options: PositionOptions, onUpdate: (position: Position, arrow?: ArrowPosition) => void): () => void;
|
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Positioning Utilities
|
|
3
|
+
*
|
|
4
|
+
* Pure JavaScript positioning calculations for tooltips, popovers, dropdowns, etc.
|
|
5
|
+
* Handles viewport collision detection, auto-flip positioning, and arrow placement.
|
|
6
|
+
*
|
|
7
|
+
* Built from scratch with zero dependencies as an alternative to Floating UI.
|
|
8
|
+
*/
|
|
9
|
+
/**
|
|
10
|
+
* Get the bounding rect of an element
|
|
11
|
+
*/
|
|
12
|
+
export function getRect(element) {
|
|
13
|
+
const rect = element.getBoundingClientRect();
|
|
14
|
+
return {
|
|
15
|
+
top: rect.top,
|
|
16
|
+
left: rect.left,
|
|
17
|
+
right: rect.right,
|
|
18
|
+
bottom: rect.bottom,
|
|
19
|
+
width: rect.width,
|
|
20
|
+
height: rect.height
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Get the viewport dimensions
|
|
25
|
+
*/
|
|
26
|
+
export function getViewport() {
|
|
27
|
+
return {
|
|
28
|
+
top: 0,
|
|
29
|
+
left: 0,
|
|
30
|
+
right: window.innerWidth,
|
|
31
|
+
bottom: window.innerHeight,
|
|
32
|
+
width: window.innerWidth,
|
|
33
|
+
height: window.innerHeight
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Parse placement string into side and alignment
|
|
38
|
+
*/
|
|
39
|
+
export function parsePlacement(placement) {
|
|
40
|
+
const [side, alignment = 'center'] = placement.split('-');
|
|
41
|
+
return { side, alignment: alignment || 'center' };
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Get the opposite side
|
|
45
|
+
*/
|
|
46
|
+
export function getOppositeSide(side) {
|
|
47
|
+
const opposites = {
|
|
48
|
+
top: 'bottom',
|
|
49
|
+
bottom: 'top',
|
|
50
|
+
left: 'right',
|
|
51
|
+
right: 'left'
|
|
52
|
+
};
|
|
53
|
+
return opposites[side];
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Calculate position for a given side and alignment
|
|
57
|
+
*/
|
|
58
|
+
function calculatePositionForSide(reference, floating, side, alignment, offset) {
|
|
59
|
+
let top = 0;
|
|
60
|
+
let left = 0;
|
|
61
|
+
// Calculate based on side
|
|
62
|
+
switch (side) {
|
|
63
|
+
case 'top':
|
|
64
|
+
top = reference.top - floating.height - offset;
|
|
65
|
+
left = reference.left;
|
|
66
|
+
break;
|
|
67
|
+
case 'bottom':
|
|
68
|
+
top = reference.bottom + offset;
|
|
69
|
+
left = reference.left;
|
|
70
|
+
break;
|
|
71
|
+
case 'left':
|
|
72
|
+
top = reference.top;
|
|
73
|
+
left = reference.left - floating.width - offset;
|
|
74
|
+
break;
|
|
75
|
+
case 'right':
|
|
76
|
+
top = reference.top;
|
|
77
|
+
left = reference.right + offset;
|
|
78
|
+
break;
|
|
79
|
+
}
|
|
80
|
+
// Adjust for alignment
|
|
81
|
+
if (side === 'top' || side === 'bottom') {
|
|
82
|
+
switch (alignment) {
|
|
83
|
+
case 'start':
|
|
84
|
+
// left already set
|
|
85
|
+
break;
|
|
86
|
+
case 'center':
|
|
87
|
+
left = reference.left + reference.width / 2 - floating.width / 2;
|
|
88
|
+
break;
|
|
89
|
+
case 'end':
|
|
90
|
+
left = reference.right - floating.width;
|
|
91
|
+
break;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
else {
|
|
95
|
+
// left or right
|
|
96
|
+
switch (alignment) {
|
|
97
|
+
case 'start':
|
|
98
|
+
// top already set
|
|
99
|
+
break;
|
|
100
|
+
case 'center':
|
|
101
|
+
top = reference.top + reference.height / 2 - floating.height / 2;
|
|
102
|
+
break;
|
|
103
|
+
case 'end':
|
|
104
|
+
top = reference.bottom - floating.height;
|
|
105
|
+
break;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
return { top, left };
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Check if floating element fits in viewport at given position
|
|
112
|
+
*/
|
|
113
|
+
function fitsInViewport(position, floating, viewport, padding) {
|
|
114
|
+
const right = position.left + floating.width;
|
|
115
|
+
const bottom = position.top + floating.height;
|
|
116
|
+
return (position.top >= viewport.top + padding &&
|
|
117
|
+
position.left >= viewport.left + padding &&
|
|
118
|
+
right <= viewport.right - padding &&
|
|
119
|
+
bottom <= viewport.bottom - padding);
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Calculate the best position for a floating element
|
|
123
|
+
*/
|
|
124
|
+
export function calculatePosition(referenceElement, floatingElement, options) {
|
|
125
|
+
const { placement, offset = 8, autoFlip = true, autoAlign = true, viewportPadding = 8 } = options;
|
|
126
|
+
const reference = getRect(referenceElement);
|
|
127
|
+
const floating = getRect(floatingElement);
|
|
128
|
+
const viewport = getViewport();
|
|
129
|
+
let { side, alignment } = parsePlacement(placement);
|
|
130
|
+
// Try preferred placement
|
|
131
|
+
let position = calculatePositionForSide(reference, floating, side, alignment, offset);
|
|
132
|
+
let fits = fitsInViewport(position, floating, viewport, viewportPadding);
|
|
133
|
+
// Try flipping to opposite side if doesn't fit
|
|
134
|
+
if (!fits && autoFlip) {
|
|
135
|
+
const oppositeSide = getOppositeSide(side);
|
|
136
|
+
const flippedPosition = calculatePositionForSide(reference, floating, oppositeSide, alignment, offset);
|
|
137
|
+
const flippedFits = fitsInViewport(flippedPosition, floating, viewport, viewportPadding);
|
|
138
|
+
if (flippedFits) {
|
|
139
|
+
side = oppositeSide;
|
|
140
|
+
position = flippedPosition;
|
|
141
|
+
fits = true;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
// Try adjusting alignment if still doesn't fit
|
|
145
|
+
if (!fits && autoAlign) {
|
|
146
|
+
const alignments = ['start', 'center', 'end'];
|
|
147
|
+
for (const testAlignment of alignments) {
|
|
148
|
+
if (testAlignment === alignment)
|
|
149
|
+
continue;
|
|
150
|
+
const adjustedPosition = calculatePositionForSide(reference, floating, side, testAlignment, offset);
|
|
151
|
+
const adjustedFits = fitsInViewport(adjustedPosition, floating, viewport, viewportPadding);
|
|
152
|
+
if (adjustedFits) {
|
|
153
|
+
alignment = testAlignment;
|
|
154
|
+
position = adjustedPosition;
|
|
155
|
+
fits = true;
|
|
156
|
+
break;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
// Clamp to viewport if still doesn't fit
|
|
161
|
+
if (!fits) {
|
|
162
|
+
position.left = Math.max(viewport.left + viewportPadding, Math.min(position.left, viewport.right - floating.width - viewportPadding));
|
|
163
|
+
position.top = Math.max(viewport.top + viewportPadding, Math.min(position.top, viewport.bottom - floating.height - viewportPadding));
|
|
164
|
+
}
|
|
165
|
+
return {
|
|
166
|
+
top: position.top,
|
|
167
|
+
left: position.left,
|
|
168
|
+
side,
|
|
169
|
+
alignment
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* Calculate arrow position relative to the floating element
|
|
174
|
+
*/
|
|
175
|
+
export function calculateArrowPosition(referenceElement, floatingElement, side, arrowSize = 8) {
|
|
176
|
+
const reference = getRect(referenceElement);
|
|
177
|
+
const floating = getRect(floatingElement);
|
|
178
|
+
// Arrow should be on the opposite side of where the tooltip is positioned
|
|
179
|
+
// If tooltip is on top, arrow is on bottom pointing down toward the element
|
|
180
|
+
const arrowPosition = { side: getOppositeSide(side) };
|
|
181
|
+
// Calculate arrow position to point at center of reference element
|
|
182
|
+
// Arrow CSS creates a triangle using borders, which is arrowSize*2 wide/tall
|
|
183
|
+
// The point of the arrow is at the center of that dimension
|
|
184
|
+
if (side === 'top' || side === 'bottom') {
|
|
185
|
+
// Arrow is horizontally positioned
|
|
186
|
+
const referenceCenterX = reference.left + reference.width / 2;
|
|
187
|
+
const floatingLeft = floating.left;
|
|
188
|
+
// Center the arrow (which is arrowSize*2 wide) on the reference center
|
|
189
|
+
arrowPosition.left = referenceCenterX - floatingLeft - arrowSize;
|
|
190
|
+
// Clamp arrow within floating element bounds, with padding to avoid rounded corners
|
|
191
|
+
// Typical border-radius is 4-6px, so keep arrow at least 8px from edges
|
|
192
|
+
const cornerPadding = arrowSize; // Keep arrow away from border-radius areas
|
|
193
|
+
arrowPosition.left = Math.max(cornerPadding, Math.min(arrowPosition.left, floating.width - cornerPadding - arrowSize * 2));
|
|
194
|
+
}
|
|
195
|
+
else {
|
|
196
|
+
// Arrow is vertically positioned
|
|
197
|
+
const referenceCenterY = reference.top + reference.height / 2;
|
|
198
|
+
const floatingTop = floating.top;
|
|
199
|
+
// Center the arrow (which is arrowSize*2 tall) on the reference center
|
|
200
|
+
arrowPosition.top = referenceCenterY - floatingTop - arrowSize;
|
|
201
|
+
// Clamp arrow within floating element bounds, with padding to avoid rounded corners
|
|
202
|
+
// Typical border-radius is 4-6px, so keep arrow at least 8px from edges
|
|
203
|
+
const cornerPadding = arrowSize; // Keep arrow away from border-radius areas
|
|
204
|
+
arrowPosition.top = Math.max(cornerPadding, Math.min(arrowPosition.top, floating.height - cornerPadding - arrowSize * 2));
|
|
205
|
+
}
|
|
206
|
+
return arrowPosition;
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* Create a positioning update function that can be called on scroll/resize
|
|
210
|
+
*/
|
|
211
|
+
export function createPositionUpdater(referenceElement, floatingElement, options, onUpdate) {
|
|
212
|
+
const update = () => {
|
|
213
|
+
const position = calculatePosition(referenceElement, floatingElement, options);
|
|
214
|
+
let arrow;
|
|
215
|
+
if (options.arrow) {
|
|
216
|
+
arrow = calculateArrowPosition(referenceElement, floatingElement, position.side, options.arrowSize);
|
|
217
|
+
}
|
|
218
|
+
onUpdate(position, arrow);
|
|
219
|
+
};
|
|
220
|
+
// Initial update
|
|
221
|
+
update();
|
|
222
|
+
// Listen for scroll and resize
|
|
223
|
+
window.addEventListener('scroll', update, { passive: true });
|
|
224
|
+
window.addEventListener('resize', update, { passive: true });
|
|
225
|
+
// Return cleanup function
|
|
226
|
+
return () => {
|
|
227
|
+
window.removeEventListener('scroll', update);
|
|
228
|
+
window.removeEventListener('resize', update);
|
|
229
|
+
};
|
|
230
|
+
}
|
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Round a number to a specific number of decimals
|
|
3
|
-
*
|
|
4
|
-
* @param
|
|
5
|
-
* @
|
|
6
|
-
* @returns
|
|
3
|
+
* @param value - The number to round
|
|
4
|
+
* @param decimals - Number of decimal places
|
|
5
|
+
* @returns The rounded number
|
|
7
6
|
*/
|
|
8
7
|
export declare const roundToDecimals: (value: number, decimals: number) => number;
|
|
9
8
|
/**
|
|
10
|
-
* Format a number to a string with a specific number of decimals
|
|
9
|
+
* Format a number to a string with a specific number of decimals using locale formatting
|
|
10
|
+
* @param value - The number to format
|
|
11
|
+
* @param decimals - Number of decimal places
|
|
12
|
+
* @returns The formatted number string
|
|
11
13
|
*/
|
|
12
14
|
export declare const formatNumber: (value: number, decimals: number) => string;
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Round a number to a specific number of decimals
|
|
3
|
-
*
|
|
4
|
-
* @param
|
|
5
|
-
* @
|
|
6
|
-
* @returns
|
|
3
|
+
* @param value - The number to round
|
|
4
|
+
* @param decimals - Number of decimal places
|
|
5
|
+
* @returns The rounded number
|
|
7
6
|
*/
|
|
8
7
|
export const roundToDecimals = (value, decimals) => {
|
|
9
8
|
if (!decimals)
|
|
@@ -11,7 +10,10 @@ export const roundToDecimals = (value, decimals) => {
|
|
|
11
10
|
return Number(Math.round(Number(`${value}e${decimals}`)) + `e-${decimals}`);
|
|
12
11
|
};
|
|
13
12
|
/**
|
|
14
|
-
* Format a number to a string with a specific number of decimals
|
|
13
|
+
* Format a number to a string with a specific number of decimals using locale formatting
|
|
14
|
+
* @param value - The number to format
|
|
15
|
+
* @param decimals - Number of decimal places
|
|
16
|
+
* @returns The formatted number string
|
|
15
17
|
*/
|
|
16
18
|
export const formatNumber = (value, decimals) => {
|
|
17
19
|
return roundToDecimals(value, decimals).toLocaleString();
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Spring animation composable using Svelte 5 runes
|
|
3
|
+
* Provides smooth, physics-based animations with $state reactivity
|
|
4
|
+
* @module spring
|
|
5
|
+
*/
|
|
6
|
+
import { springPresets, type SpringConfig } from './animations.js';
|
|
7
|
+
/**
|
|
8
|
+
* Options for creating a spring animation
|
|
9
|
+
*/
|
|
10
|
+
export interface UseSpringOptions extends SpringConfig {
|
|
11
|
+
/** Initial value */
|
|
12
|
+
initialValue?: number;
|
|
13
|
+
/** Preset configuration name */
|
|
14
|
+
preset?: keyof typeof springPresets;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Spring animation composable with Svelte 5 $state
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* ```typescript
|
|
21
|
+
* const scale = useSpring({ initialValue: 1, preset: 'wobbly' });
|
|
22
|
+
*
|
|
23
|
+
* // Set target value - spring will animate toward it
|
|
24
|
+
* scale.set(1.5);
|
|
25
|
+
*
|
|
26
|
+
* // Use the current value in your template
|
|
27
|
+
* console.log(scale.value); // Reactive value that updates during animation
|
|
28
|
+
* ```
|
|
29
|
+
*/
|
|
30
|
+
export declare function useSpring(options?: UseSpringOptions): {
|
|
31
|
+
/**
|
|
32
|
+
* Current animated value (reactive)
|
|
33
|
+
*/
|
|
34
|
+
readonly value: number;
|
|
35
|
+
/**
|
|
36
|
+
* Current velocity (reactive)
|
|
37
|
+
*/
|
|
38
|
+
readonly velocity: number;
|
|
39
|
+
/**
|
|
40
|
+
* Whether the spring is currently animating
|
|
41
|
+
*/
|
|
42
|
+
readonly isAnimating: boolean;
|
|
43
|
+
/**
|
|
44
|
+
* Set target value to animate toward
|
|
45
|
+
*/
|
|
46
|
+
set(newTarget: number, hard?: boolean): void;
|
|
47
|
+
/**
|
|
48
|
+
* Update spring configuration
|
|
49
|
+
*/
|
|
50
|
+
updateConfig(newConfig: Partial<SpringConfig>): void;
|
|
51
|
+
/**
|
|
52
|
+
* Stop animation and reset to value
|
|
53
|
+
*/
|
|
54
|
+
reset(value?: number): void;
|
|
55
|
+
/**
|
|
56
|
+
* Cleanup - stop animation
|
|
57
|
+
*/
|
|
58
|
+
destroy(): void;
|
|
59
|
+
};
|
|
60
|
+
/**
|
|
61
|
+
* Spring animation for object with multiple properties
|
|
62
|
+
*
|
|
63
|
+
* @example
|
|
64
|
+
* ```typescript
|
|
65
|
+
* const transform = useSpringObject({ x: 0, y: 0, scale: 1 });
|
|
66
|
+
*
|
|
67
|
+
* transform.set({ x: 100, y: 50, scale: 1.5 });
|
|
68
|
+
*
|
|
69
|
+
* console.log(transform.values); // { x: 0-100, y: 0-50, scale: 1-1.5 } (animated)
|
|
70
|
+
* ```
|
|
71
|
+
*/
|
|
72
|
+
export declare function useSpringObject<T extends Record<string, number>>(initialValues: T, options?: UseSpringOptions): {
|
|
73
|
+
/**
|
|
74
|
+
* Current animated values (reactive)
|
|
75
|
+
*/
|
|
76
|
+
readonly values: T;
|
|
77
|
+
/**
|
|
78
|
+
* Whether any spring is currently animating
|
|
79
|
+
*/
|
|
80
|
+
readonly isAnimating: boolean;
|
|
81
|
+
/**
|
|
82
|
+
* Set target values to animate toward
|
|
83
|
+
*/
|
|
84
|
+
set(newTargets: Partial<T>, hard?: boolean): void;
|
|
85
|
+
/**
|
|
86
|
+
* Update spring configuration for all springs
|
|
87
|
+
*/
|
|
88
|
+
updateConfig(newConfig: Partial<SpringConfig>): void;
|
|
89
|
+
/**
|
|
90
|
+
* Reset all springs to values
|
|
91
|
+
*/
|
|
92
|
+
reset(values?: Partial<T>): void;
|
|
93
|
+
/**
|
|
94
|
+
* Cleanup - stop all animations
|
|
95
|
+
*/
|
|
96
|
+
destroy(): void;
|
|
97
|
+
};
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Spring animation composable using Svelte 5 runes
|
|
3
|
+
* Provides smooth, physics-based animations with $state reactivity
|
|
4
|
+
* @module spring
|
|
5
|
+
*/
|
|
6
|
+
import { stepSpring, isSpringSettled, springPresets, prefersReducedMotion } from './animations.js';
|
|
7
|
+
/**
|
|
8
|
+
* Spring animation composable with Svelte 5 $state
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* ```typescript
|
|
12
|
+
* const scale = useSpring({ initialValue: 1, preset: 'wobbly' });
|
|
13
|
+
*
|
|
14
|
+
* // Set target value - spring will animate toward it
|
|
15
|
+
* scale.set(1.5);
|
|
16
|
+
*
|
|
17
|
+
* // Use the current value in your template
|
|
18
|
+
* console.log(scale.value); // Reactive value that updates during animation
|
|
19
|
+
* ```
|
|
20
|
+
*/
|
|
21
|
+
export function useSpring(options = {}) {
|
|
22
|
+
const preset = options.preset ? springPresets[options.preset] : springPresets.default;
|
|
23
|
+
const config = {
|
|
24
|
+
stiffness: options.stiffness ?? preset.stiffness,
|
|
25
|
+
damping: options.damping ?? preset.damping,
|
|
26
|
+
precision: options.precision ?? preset.precision
|
|
27
|
+
};
|
|
28
|
+
let state = $state({
|
|
29
|
+
value: options.initialValue ?? 0,
|
|
30
|
+
velocity: 0
|
|
31
|
+
});
|
|
32
|
+
let target = $state(options.initialValue ?? 0);
|
|
33
|
+
let animationFrameId = null;
|
|
34
|
+
let lastTime = null;
|
|
35
|
+
/**
|
|
36
|
+
* Animation loop using requestAnimationFrame
|
|
37
|
+
*/
|
|
38
|
+
function tick(currentTime) {
|
|
39
|
+
// Initialize lastTime on first frame
|
|
40
|
+
if (lastTime === null) {
|
|
41
|
+
lastTime = currentTime;
|
|
42
|
+
animationFrameId = requestAnimationFrame(tick);
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
// Calculate delta time in seconds
|
|
46
|
+
const deltaTime = Math.min((currentTime - lastTime) / 1000, 0.1); // Cap at 100ms
|
|
47
|
+
lastTime = currentTime;
|
|
48
|
+
// Skip animation if user prefers reduced motion - snap to target
|
|
49
|
+
if (prefersReducedMotion()) {
|
|
50
|
+
state.value = target;
|
|
51
|
+
state.velocity = 0;
|
|
52
|
+
stopAnimation();
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
// Calculate next spring state
|
|
56
|
+
const newState = stepSpring(state, target, config, deltaTime);
|
|
57
|
+
state = newState;
|
|
58
|
+
// Check if spring has settled
|
|
59
|
+
if (isSpringSettled(state, target, config.precision)) {
|
|
60
|
+
// Snap to target and stop
|
|
61
|
+
state.value = target;
|
|
62
|
+
state.velocity = 0;
|
|
63
|
+
stopAnimation();
|
|
64
|
+
}
|
|
65
|
+
else {
|
|
66
|
+
// Continue animation
|
|
67
|
+
animationFrameId = requestAnimationFrame(tick);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Start the animation loop
|
|
72
|
+
*/
|
|
73
|
+
function startAnimation() {
|
|
74
|
+
if (animationFrameId !== null)
|
|
75
|
+
return; // Already animating
|
|
76
|
+
lastTime = null;
|
|
77
|
+
animationFrameId = requestAnimationFrame(tick);
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Stop the animation loop
|
|
81
|
+
*/
|
|
82
|
+
function stopAnimation() {
|
|
83
|
+
if (animationFrameId !== null) {
|
|
84
|
+
cancelAnimationFrame(animationFrameId);
|
|
85
|
+
animationFrameId = null;
|
|
86
|
+
}
|
|
87
|
+
lastTime = null;
|
|
88
|
+
}
|
|
89
|
+
return {
|
|
90
|
+
/**
|
|
91
|
+
* Current animated value (reactive)
|
|
92
|
+
*/
|
|
93
|
+
get value() {
|
|
94
|
+
return state.value;
|
|
95
|
+
},
|
|
96
|
+
/**
|
|
97
|
+
* Current velocity (reactive)
|
|
98
|
+
*/
|
|
99
|
+
get velocity() {
|
|
100
|
+
return state.velocity;
|
|
101
|
+
},
|
|
102
|
+
/**
|
|
103
|
+
* Whether the spring is currently animating
|
|
104
|
+
*/
|
|
105
|
+
get isAnimating() {
|
|
106
|
+
return animationFrameId !== null;
|
|
107
|
+
},
|
|
108
|
+
/**
|
|
109
|
+
* Set target value to animate toward
|
|
110
|
+
*/
|
|
111
|
+
set(newTarget, hard = false) {
|
|
112
|
+
target = newTarget;
|
|
113
|
+
if (hard || prefersReducedMotion()) {
|
|
114
|
+
// Hard set - snap immediately without animation
|
|
115
|
+
state.value = newTarget;
|
|
116
|
+
state.velocity = 0;
|
|
117
|
+
stopAnimation();
|
|
118
|
+
}
|
|
119
|
+
else {
|
|
120
|
+
// Soft set - animate toward target
|
|
121
|
+
startAnimation();
|
|
122
|
+
}
|
|
123
|
+
},
|
|
124
|
+
/**
|
|
125
|
+
* Update spring configuration
|
|
126
|
+
*/
|
|
127
|
+
updateConfig(newConfig) {
|
|
128
|
+
Object.assign(config, newConfig);
|
|
129
|
+
},
|
|
130
|
+
/**
|
|
131
|
+
* Stop animation and reset to value
|
|
132
|
+
*/
|
|
133
|
+
reset(value = 0) {
|
|
134
|
+
target = value;
|
|
135
|
+
state.value = value;
|
|
136
|
+
state.velocity = 0;
|
|
137
|
+
stopAnimation();
|
|
138
|
+
},
|
|
139
|
+
/**
|
|
140
|
+
* Cleanup - stop animation
|
|
141
|
+
*/
|
|
142
|
+
destroy() {
|
|
143
|
+
stopAnimation();
|
|
144
|
+
}
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Spring animation for object with multiple properties
|
|
149
|
+
*
|
|
150
|
+
* @example
|
|
151
|
+
* ```typescript
|
|
152
|
+
* const transform = useSpringObject({ x: 0, y: 0, scale: 1 });
|
|
153
|
+
*
|
|
154
|
+
* transform.set({ x: 100, y: 50, scale: 1.5 });
|
|
155
|
+
*
|
|
156
|
+
* console.log(transform.values); // { x: 0-100, y: 0-50, scale: 1-1.5 } (animated)
|
|
157
|
+
* ```
|
|
158
|
+
*/
|
|
159
|
+
export function useSpringObject(initialValues, options = {}) {
|
|
160
|
+
// Create a spring for each property
|
|
161
|
+
const springs = Object.fromEntries(Object.entries(initialValues).map(([key, value]) => [
|
|
162
|
+
key,
|
|
163
|
+
useSpring({ ...options, initialValue: value })
|
|
164
|
+
]));
|
|
165
|
+
return {
|
|
166
|
+
/**
|
|
167
|
+
* Current animated values (reactive)
|
|
168
|
+
*/
|
|
169
|
+
get values() {
|
|
170
|
+
return Object.fromEntries(Object.entries(springs).map(([key, spring]) => [key, spring.value]));
|
|
171
|
+
},
|
|
172
|
+
/**
|
|
173
|
+
* Whether any spring is currently animating
|
|
174
|
+
*/
|
|
175
|
+
get isAnimating() {
|
|
176
|
+
return Object.values(springs).some((spring) => spring.isAnimating);
|
|
177
|
+
},
|
|
178
|
+
/**
|
|
179
|
+
* Set target values to animate toward
|
|
180
|
+
*/
|
|
181
|
+
set(newTargets, hard = false) {
|
|
182
|
+
for (const [key, value] of Object.entries(newTargets)) {
|
|
183
|
+
if (key in springs && typeof value === 'number') {
|
|
184
|
+
springs[key].set(value, hard);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
},
|
|
188
|
+
/**
|
|
189
|
+
* Update spring configuration for all springs
|
|
190
|
+
*/
|
|
191
|
+
updateConfig(newConfig) {
|
|
192
|
+
Object.values(springs).forEach((spring) => spring.updateConfig(newConfig));
|
|
193
|
+
},
|
|
194
|
+
/**
|
|
195
|
+
* Reset all springs to values
|
|
196
|
+
*/
|
|
197
|
+
reset(values) {
|
|
198
|
+
if (values) {
|
|
199
|
+
for (const [key, value] of Object.entries(values)) {
|
|
200
|
+
if (key in springs && typeof value === 'number') {
|
|
201
|
+
springs[key].reset(value);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
else {
|
|
206
|
+
Object.values(springs).forEach((spring) => spring.reset());
|
|
207
|
+
}
|
|
208
|
+
},
|
|
209
|
+
/**
|
|
210
|
+
* Cleanup - stop all animations
|
|
211
|
+
*/
|
|
212
|
+
destroy() {
|
|
213
|
+
Object.values(springs).forEach((spring) => spring.destroy());
|
|
214
|
+
}
|
|
215
|
+
};
|
|
216
|
+
}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
/// <reference types="svelte" />
|
|
2
1
|
import type { Writable } from 'svelte/store';
|
|
3
2
|
export declare const subscribable: <T>(subject: Writable<T>) => {
|
|
4
|
-
subscribe: (this: void, run: import("svelte/store").Subscriber<T>, invalidate?:
|
|
3
|
+
subscribe: (this: void, run: import("svelte/store").Subscriber<T>, invalidate?: () => void) => import("svelte/store").Unsubscriber;
|
|
5
4
|
};
|
|
6
5
|
export type Subscribable<T> = ReturnType<typeof subscribable<T>>;
|