@yewdiel21/motion-kit 0.10.0 → 0.15.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 +81 -0
- package/bin/motion-kit.mjs +3 -3
- package/dist/auto/index.d.ts +56 -3
- package/dist/auto/index.js +107 -3
- package/dist/auto/index.js.map +1 -1
- package/dist/{chunk-XAKC2QY7.js → chunk-6IRJYXJU.js} +3 -28
- package/dist/chunk-6IRJYXJU.js.map +1 -0
- package/dist/chunk-A6QQCF6V.js +21333 -0
- package/dist/chunk-A6QQCF6V.js.map +1 -0
- package/dist/chunk-JXRS3OEA.js +3638 -0
- package/dist/chunk-JXRS3OEA.js.map +1 -0
- package/dist/chunk-QP4QJOEN.js +72 -0
- package/dist/chunk-QP4QJOEN.js.map +1 -0
- package/dist/css/components/activity-feed.css +240 -0
- package/dist/css/components/add-to-cart.css +72 -0
- package/dist/css/components/attachment.css +266 -0
- package/dist/css/components/bidirectional-slider.css +168 -0
- package/dist/css/components/breadcrumb.css +110 -0
- package/dist/css/components/chart-tooltip.css +168 -0
- package/dist/css/components/chart.css +296 -0
- package/dist/css/components/code-block.css +264 -0
- package/dist/css/components/color-area.css +82 -0
- package/dist/css/components/color-field.css +67 -0
- package/dist/css/components/color-picker.css +131 -0
- package/dist/css/components/color-slider.css +113 -0
- package/dist/css/components/command-search.css +252 -0
- package/dist/css/components/context-menu.css +23 -0
- package/dist/css/components/credit-card.css +288 -0
- package/dist/css/components/data-table.css +686 -0
- package/dist/css/components/date-range-picker.css +168 -0
- package/dist/css/components/date-time-picker.css +178 -0
- package/dist/css/components/drawer.css +58 -0
- package/dist/css/components/editable-table.css +201 -0
- package/dist/css/components/empty-state.css +61 -0
- package/dist/css/components/file-explorer.css +633 -0
- package/dist/css/components/filter-builder.css +227 -0
- package/dist/css/components/heatmap-chart.css +391 -0
- package/dist/css/components/hold-confirm.css +87 -0
- package/dist/css/components/hover-card.css +19 -7
- package/dist/css/components/image-preview.css +283 -0
- package/dist/css/components/json-viewer.css +304 -0
- package/dist/css/components/like-button.css +78 -0
- package/dist/css/components/link-preview.css +275 -0
- package/dist/css/components/loading-button.css +77 -0
- package/dist/css/components/multi-select.css +267 -0
- package/dist/css/components/nav.css +102 -102
- package/dist/css/components/notification-bell.css +127 -0
- package/dist/css/components/orbit-menu.css +130 -0
- package/dist/css/components/pagination.css +10 -5
- package/dist/css/components/password-strength.css +183 -0
- package/dist/css/components/pie-chart.css +390 -0
- package/dist/css/components/pill-button.css +135 -0
- package/dist/css/components/popover.css +25 -0
- package/dist/css/components/progress.css +140 -0
- package/dist/css/components/questionnaire.css +217 -0
- package/dist/css/components/radar-chart.css +370 -0
- package/dist/css/components/radial-chart.css +398 -0
- package/dist/css/components/range-calendar.css +186 -0
- package/dist/css/components/range-slider.css +192 -0
- package/dist/css/components/resizable-panel.css +243 -0
- package/dist/css/components/rich-text-editor.css +284 -0
- package/dist/css/components/sankey-chart.css +358 -0
- package/dist/css/components/scratch-card.css +75 -0
- package/dist/css/components/scroll-island.css +111 -0
- package/dist/css/components/select.css +136 -0
- package/dist/css/components/skeleton.css +108 -0
- package/dist/css/components/split-view.css +19 -0
- package/dist/css/components/star-rating.css +80 -0
- package/dist/css/components/stat-card.css +164 -0
- package/dist/css/components/swipe-actions.css +105 -0
- package/dist/css/components/tag-input.css +244 -0
- package/dist/css/components/time-field.css +88 -0
- package/dist/css/components/timeline.css +219 -0
- package/dist/css/components/toolbar.css +122 -0
- package/dist/css/components/tooltip.css +79 -0
- package/dist/css/components/tree-view.css +289 -0
- package/dist/css/components/virtual-list.css +195 -0
- package/dist/css/components/voice-dots.css +70 -0
- package/dist/css/tokens.css +185 -181
- package/dist/elements/index.d.ts +849 -7
- package/dist/elements/index.js +1642 -8
- package/dist/elements/index.js.map +1 -1
- package/dist/{engine-GDGwQnJE.d.ts → engine-5zRIssTO.d.ts} +11 -1
- package/dist/engine-QECQnGz1.d.ts +33 -0
- package/dist/index.css +10890 -311
- package/dist/index.d.ts +89 -5
- package/dist/index.js +273 -9
- package/dist/index.js.map +1 -1
- package/dist/react/index.d.ts +686 -24
- package/dist/react/index.js +2345 -9
- package/dist/react/index.js.map +1 -1
- package/dist/{reduced-BmBzKH94.d.ts → reduced-6X_bns60.d.ts} +9 -2
- package/dist/reveal-BfyKKsw4.d.ts +2474 -0
- package/dist/store-CA2Qcun_.d.ts +453 -0
- package/package.json +9 -16
- package/registry.json +1096 -543
- package/src/auto/auto-init.ts +104 -0
- package/src/components/activity-feed/README.md +56 -0
- package/src/components/activity-feed/activity-feed.css +239 -0
- package/src/components/activity-feed/element.ts +36 -0
- package/src/components/activity-feed/engine.ts +351 -0
- package/src/components/activity-feed/react.tsx +32 -0
- package/src/components/add-to-cart/README.md +34 -0
- package/src/components/add-to-cart/add-to-cart.css +71 -0
- package/src/components/add-to-cart/element.ts +20 -0
- package/src/components/add-to-cart/engine.ts +206 -0
- package/src/components/add-to-cart/react.tsx +29 -0
- package/src/components/attachment/README.md +27 -0
- package/src/components/attachment/attachment.css +265 -0
- package/src/components/attachment/element.ts +50 -0
- package/src/components/attachment/engine.ts +469 -0
- package/src/components/attachment/react.tsx +65 -0
- package/src/components/bidirectional-slider/README.md +46 -0
- package/src/components/bidirectional-slider/bidirectional-slider.css +167 -0
- package/src/components/bidirectional-slider/element.ts +42 -0
- package/src/components/bidirectional-slider/engine.ts +289 -0
- package/src/components/bidirectional-slider/react.tsx +45 -0
- package/src/components/breadcrumb/README.md +38 -0
- package/src/components/breadcrumb/breadcrumb.css +109 -0
- package/src/components/breadcrumb/element.ts +27 -0
- package/src/components/breadcrumb/engine.ts +249 -0
- package/src/components/breadcrumb/react.tsx +30 -0
- package/src/components/chart/README.md +34 -0
- package/src/components/chart/chart.css +295 -0
- package/src/components/chart/element.ts +43 -0
- package/src/components/chart/engine.ts +838 -0
- package/src/components/chart/parts.ts +237 -0
- package/src/components/chart/react.tsx +50 -0
- package/src/components/chart/use-chart.ts +55 -0
- package/src/components/chart-tooltip/README.md +53 -0
- package/src/components/chart-tooltip/chart-tooltip.css +167 -0
- package/src/components/chart-tooltip/element.ts +37 -0
- package/src/components/chart-tooltip/engine.ts +442 -0
- package/src/components/chart-tooltip/react.tsx +47 -0
- package/src/components/code-block/README.md +19 -0
- package/src/components/code-block/code-block.css +263 -0
- package/src/components/code-block/element.ts +34 -0
- package/src/components/code-block/engine.ts +254 -0
- package/src/components/code-block/highlight.ts +244 -0
- package/src/components/code-block/react.tsx +94 -0
- package/src/components/color-area/README.md +50 -0
- package/src/components/color-area/color-area.css +81 -0
- package/src/components/color-area/element.ts +26 -0
- package/src/components/color-area/engine.ts +246 -0
- package/src/components/color-area/react.tsx +42 -0
- package/src/components/color-field/README.md +43 -0
- package/src/components/color-field/color-field.css +66 -0
- package/src/components/color-field/element.ts +26 -0
- package/src/components/color-field/engine.ts +174 -0
- package/src/components/color-field/react.tsx +42 -0
- package/src/components/color-picker/README.md +45 -0
- package/src/components/color-picker/color-picker.css +130 -0
- package/src/components/color-picker/element.ts +45 -0
- package/src/components/color-picker/engine.ts +314 -0
- package/src/components/color-picker/react.tsx +47 -0
- package/src/components/color-slider/README.md +55 -0
- package/src/components/color-slider/color-slider.css +112 -0
- package/src/components/color-slider/element.ts +26 -0
- package/src/components/color-slider/engine.ts +234 -0
- package/src/components/color-slider/react.tsx +45 -0
- package/src/components/command-search/README.md +15 -0
- package/src/components/command-search/command-search.css +251 -0
- package/src/components/command-search/element.ts +36 -0
- package/src/components/command-search/engine.ts +356 -0
- package/src/components/command-search/match.ts +53 -0
- package/src/components/command-search/react.tsx +66 -0
- package/src/components/context-menu/README.md +31 -0
- package/src/components/context-menu/context-menu.css +22 -0
- package/src/components/context-menu/element.ts +40 -0
- package/src/components/context-menu/engine.ts +153 -0
- package/src/components/context-menu/react.tsx +57 -0
- package/src/components/credit-card/README.md +20 -0
- package/src/components/credit-card/card.ts +77 -0
- package/src/components/credit-card/credit-card.css +287 -0
- package/src/components/credit-card/element.ts +33 -0
- package/src/components/credit-card/engine.ts +410 -0
- package/src/components/credit-card/react.tsx +36 -0
- package/src/components/data-table/README.md +83 -0
- package/src/components/data-table/data-table.css +685 -0
- package/src/components/data-table/element.ts +54 -0
- package/src/components/data-table/engine.ts +1111 -0
- package/src/components/data-table/react.tsx +94 -0
- package/src/components/date-range-picker/README.md +51 -0
- package/src/components/date-range-picker/date-range-picker.css +167 -0
- package/src/components/date-range-picker/element.ts +52 -0
- package/src/components/date-range-picker/engine.ts +352 -0
- package/src/components/date-range-picker/react.tsx +57 -0
- package/src/components/date-time-picker/README.md +48 -0
- package/src/components/date-time-picker/date-time-picker.css +177 -0
- package/src/components/date-time-picker/element.ts +47 -0
- package/src/components/date-time-picker/engine.ts +350 -0
- package/src/components/date-time-picker/react.tsx +54 -0
- package/src/components/dialog/README.md +103 -102
- package/src/components/dialog/element.ts +123 -118
- package/src/components/dialog/engine.ts +449 -432
- package/src/components/drawer/README.md +28 -0
- package/src/components/drawer/drawer.css +57 -0
- package/src/components/drawer/element.ts +21 -0
- package/src/components/drawer/engine.ts +56 -0
- package/src/components/drawer/react.tsx +85 -0
- package/src/components/editable-table/README.md +67 -0
- package/src/components/editable-table/editable-table.css +200 -0
- package/src/components/editable-table/element.ts +45 -0
- package/src/components/editable-table/engine.ts +831 -0
- package/src/components/editable-table/react.tsx +79 -0
- package/src/components/empty-state/README.md +41 -0
- package/src/components/empty-state/element.ts +23 -0
- package/src/components/empty-state/empty-state.css +60 -0
- package/src/components/empty-state/engine.ts +232 -0
- package/src/components/empty-state/react.tsx +34 -0
- package/src/components/file-explorer/README.md +67 -0
- package/src/components/file-explorer/element.ts +39 -0
- package/src/components/file-explorer/engine.ts +1307 -0
- package/src/components/file-explorer/file-explorer.css +632 -0
- package/src/components/file-explorer/react.tsx +77 -0
- package/src/components/filter-builder/README.md +47 -0
- package/src/components/filter-builder/element.ts +34 -0
- package/src/components/filter-builder/engine.ts +396 -0
- package/src/components/filter-builder/filter-builder.css +226 -0
- package/src/components/filter-builder/react.tsx +36 -0
- package/src/components/heatmap-chart/README.md +48 -0
- package/src/components/heatmap-chart/element.ts +21 -0
- package/src/components/heatmap-chart/engine.ts +281 -0
- package/src/components/heatmap-chart/heatmap-chart.css +93 -0
- package/src/components/heatmap-chart/react.tsx +14 -0
- package/src/components/hold-confirm/README.md +13 -0
- package/src/components/hold-confirm/element.ts +24 -0
- package/src/components/hold-confirm/engine.ts +216 -0
- package/src/components/hold-confirm/hold-confirm.css +86 -0
- package/src/components/hold-confirm/react.tsx +28 -0
- package/src/components/hover-card/README.md +6 -2
- package/src/components/hover-card/engine.ts +98 -7
- package/src/components/hover-card/hover-card.css +19 -7
- package/src/components/image-preview/README.md +16 -0
- package/src/components/image-preview/element.ts +26 -0
- package/src/components/image-preview/engine.ts +405 -0
- package/src/components/image-preview/image-preview.css +282 -0
- package/src/components/image-preview/react.tsx +43 -0
- package/src/components/json-viewer/README.md +56 -0
- package/src/components/json-viewer/element.ts +37 -0
- package/src/components/json-viewer/engine.ts +449 -0
- package/src/components/json-viewer/json-viewer.css +303 -0
- package/src/components/json-viewer/react.tsx +56 -0
- package/src/components/like-button/README.md +13 -0
- package/src/components/like-button/element.ts +47 -0
- package/src/components/like-button/engine.ts +145 -0
- package/src/components/like-button/like-button.css +77 -0
- package/src/components/like-button/react.tsx +39 -0
- package/src/components/link-preview/README.md +19 -0
- package/src/components/link-preview/element.ts +29 -0
- package/src/components/link-preview/engine.ts +192 -0
- package/src/components/link-preview/link-preview.css +274 -0
- package/src/components/link-preview/react.tsx +101 -0
- package/src/components/loading-button/README.md +45 -0
- package/src/components/loading-button/element.ts +34 -0
- package/src/components/loading-button/engine.ts +201 -0
- package/src/components/loading-button/loading-button.css +76 -0
- package/src/components/loading-button/react.tsx +43 -0
- package/src/components/multi-select/README.md +48 -0
- package/src/components/multi-select/element.ts +28 -0
- package/src/components/multi-select/engine.ts +294 -0
- package/src/components/multi-select/multi-select.css +266 -0
- package/src/components/multi-select/react.tsx +39 -0
- package/src/components/nav/README.md +12 -10
- package/src/components/nav/element.ts +5 -5
- package/src/components/nav/engine.ts +21 -14
- package/src/components/nav/nav.css +102 -102
- package/src/components/nav/react.tsx +4 -4
- package/src/components/notification-bell/README.md +14 -0
- package/src/components/notification-bell/element.ts +48 -0
- package/src/components/notification-bell/engine.ts +202 -0
- package/src/components/notification-bell/notification-bell.css +126 -0
- package/src/components/notification-bell/react.tsx +57 -0
- package/src/components/orbit-menu/README.md +46 -0
- package/src/components/orbit-menu/element.ts +27 -0
- package/src/components/orbit-menu/engine.ts +179 -0
- package/src/components/orbit-menu/orbit-menu.css +129 -0
- package/src/components/orbit-menu/react.tsx +44 -0
- package/src/components/pagination/README.md +1 -1
- package/src/components/pagination/engine.ts +50 -4
- package/src/components/pagination/pagination.css +10 -5
- package/src/components/password-strength/README.md +17 -0
- package/src/components/password-strength/element.ts +22 -0
- package/src/components/password-strength/engine.ts +212 -0
- package/src/components/password-strength/password-strength.css +182 -0
- package/src/components/password-strength/react.tsx +36 -0
- package/src/components/password-strength/score.ts +62 -0
- package/src/components/pie-chart/README.md +48 -0
- package/src/components/pie-chart/element.ts +30 -0
- package/src/components/pie-chart/engine.ts +283 -0
- package/src/components/pie-chart/pie-chart.css +92 -0
- package/src/components/pie-chart/react.tsx +14 -0
- package/src/components/pill-button/README.md +46 -0
- package/src/components/pill-button/element.ts +16 -0
- package/src/components/pill-button/engine.ts +77 -0
- package/src/components/pill-button/pill-button.css +134 -0
- package/src/components/pill-button/react.tsx +32 -0
- package/src/components/popover/README.md +44 -0
- package/src/components/popover/element.ts +31 -0
- package/src/components/popover/engine.ts +114 -0
- package/src/components/popover/popover.css +24 -0
- package/src/components/popover/react.tsx +50 -0
- package/src/components/progress/README.md +17 -0
- package/src/components/progress/element.ts +30 -0
- package/src/components/progress/engine.ts +173 -0
- package/src/components/progress/progress.css +139 -0
- package/src/components/progress/react.tsx +35 -0
- package/src/components/questionnaire/README.md +32 -0
- package/src/components/questionnaire/element.ts +118 -0
- package/src/components/questionnaire/engine.ts +255 -0
- package/src/components/questionnaire/questionnaire.css +216 -0
- package/src/components/questionnaire/react.tsx +140 -0
- package/src/components/radar-chart/README.md +48 -0
- package/src/components/radar-chart/element.ts +30 -0
- package/src/components/radar-chart/engine.ts +327 -0
- package/src/components/radar-chart/radar-chart.css +72 -0
- package/src/components/radar-chart/react.tsx +14 -0
- package/src/components/radial-chart/README.md +48 -0
- package/src/components/radial-chart/element.ts +30 -0
- package/src/components/radial-chart/engine.ts +290 -0
- package/src/components/radial-chart/radial-chart.css +100 -0
- package/src/components/radial-chart/react.tsx +14 -0
- package/src/components/range-calendar/README.md +58 -0
- package/src/components/range-calendar/element.ts +42 -0
- package/src/components/range-calendar/engine.ts +390 -0
- package/src/components/range-calendar/range-calendar.css +185 -0
- package/src/components/range-calendar/react.tsx +56 -0
- package/src/components/range-slider/README.md +23 -0
- package/src/components/range-slider/element.ts +53 -0
- package/src/components/range-slider/engine.ts +316 -0
- package/src/components/range-slider/range-slider.css +191 -0
- package/src/components/range-slider/react.tsx +66 -0
- package/src/components/resizable-panel/README.md +61 -0
- package/src/components/resizable-panel/element.ts +36 -0
- package/src/components/resizable-panel/engine.ts +551 -0
- package/src/components/resizable-panel/react.tsx +74 -0
- package/src/components/resizable-panel/resizable-panel.css +242 -0
- package/src/components/rich-text-editor/README.md +57 -0
- package/src/components/rich-text-editor/element.ts +34 -0
- package/src/components/rich-text-editor/engine.ts +673 -0
- package/src/components/rich-text-editor/react.tsx +60 -0
- package/src/components/rich-text-editor/rich-text-editor.css +283 -0
- package/src/components/sankey-chart/README.md +48 -0
- package/src/components/sankey-chart/element.ts +31 -0
- package/src/components/sankey-chart/engine.ts +401 -0
- package/src/components/sankey-chart/react.tsx +14 -0
- package/src/components/sankey-chart/sankey-chart.css +60 -0
- package/src/components/scratch-card/README.md +15 -0
- package/src/components/scratch-card/element.ts +30 -0
- package/src/components/scratch-card/engine.ts +257 -0
- package/src/components/scratch-card/react.tsx +40 -0
- package/src/components/scratch-card/scratch-card.css +74 -0
- package/src/components/scroll-island/README.md +34 -0
- package/src/components/scroll-island/element.ts +16 -0
- package/src/components/scroll-island/engine.ts +252 -0
- package/src/components/scroll-island/react.tsx +26 -0
- package/src/components/scroll-island/scroll-island.css +110 -0
- package/src/components/select/README.md +34 -0
- package/src/components/select/element.ts +26 -0
- package/src/components/select/engine.ts +267 -0
- package/src/components/select/react.tsx +33 -0
- package/src/components/select/select.css +135 -0
- package/src/components/skeleton/README.md +15 -0
- package/src/components/skeleton/element.ts +32 -0
- package/src/components/skeleton/engine.ts +104 -0
- package/src/components/skeleton/react.tsx +41 -0
- package/src/components/skeleton/skeleton.css +107 -0
- package/src/components/split-view/README.md +50 -0
- package/src/components/split-view/element.ts +34 -0
- package/src/components/split-view/engine.ts +114 -0
- package/src/components/split-view/react.tsx +63 -0
- package/src/components/split-view/split-view.css +18 -0
- package/src/components/star-rating/README.md +14 -0
- package/src/components/star-rating/element.ts +26 -0
- package/src/components/star-rating/engine.ts +217 -0
- package/src/components/star-rating/react.tsx +33 -0
- package/src/components/star-rating/star-rating.css +79 -0
- package/src/components/stat-card/README.md +42 -0
- package/src/components/stat-card/element.ts +40 -0
- package/src/components/stat-card/engine.ts +283 -0
- package/src/components/stat-card/react.tsx +36 -0
- package/src/components/stat-card/stat-card.css +163 -0
- package/src/components/swipe-actions/README.md +19 -0
- package/src/components/swipe-actions/element.ts +26 -0
- package/src/components/swipe-actions/engine.ts +260 -0
- package/src/components/swipe-actions/react.tsx +80 -0
- package/src/components/swipe-actions/swipe-actions.css +104 -0
- package/src/components/tag-input/README.md +42 -0
- package/src/components/tag-input/element.ts +39 -0
- package/src/components/tag-input/engine.ts +313 -0
- package/src/components/tag-input/react.tsx +54 -0
- package/src/components/tag-input/tag-input.css +243 -0
- package/src/components/time-field/README.md +54 -0
- package/src/components/time-field/element.ts +38 -0
- package/src/components/time-field/engine.ts +110 -0
- package/src/components/time-field/react.tsx +45 -0
- package/src/components/time-field/time-field.css +87 -0
- package/src/components/timeline/README.md +57 -0
- package/src/components/timeline/element.ts +31 -0
- package/src/components/timeline/engine.ts +300 -0
- package/src/components/timeline/react.tsx +59 -0
- package/src/components/timeline/timeline.css +218 -0
- package/src/components/toolbar/README.md +46 -0
- package/src/components/toolbar/element.ts +30 -0
- package/src/components/toolbar/engine.ts +230 -0
- package/src/components/toolbar/react.tsx +34 -0
- package/src/components/toolbar/toolbar.css +121 -0
- package/src/components/tooltip/README.md +46 -0
- package/src/components/tooltip/element.ts +22 -0
- package/src/components/tooltip/engine.ts +288 -0
- package/src/components/tooltip/react.tsx +30 -0
- package/src/components/tooltip/tooltip.css +78 -0
- package/src/components/tree-view/README.md +61 -0
- package/src/components/tree-view/element.ts +32 -0
- package/src/components/tree-view/engine.ts +743 -0
- package/src/components/tree-view/react.tsx +82 -0
- package/src/components/tree-view/tree-view.css +288 -0
- package/src/components/virtual-list/README.md +81 -0
- package/src/components/virtual-list/element.ts +42 -0
- package/src/components/virtual-list/engine.ts +678 -0
- package/src/components/virtual-list/react.tsx +101 -0
- package/src/components/virtual-list/virtual-list.css +194 -0
- package/src/components/voice-dots/README.md +48 -0
- package/src/components/voice-dots/element.ts +45 -0
- package/src/components/voice-dots/engine.ts +243 -0
- package/src/components/voice-dots/react.tsx +52 -0
- package/src/components/voice-dots/voice-dots.css +69 -0
- package/src/core/chips.ts +196 -0
- package/src/core/color.ts +304 -0
- package/src/core/date-range.ts +67 -0
- package/src/core/dates.ts +11 -1
- package/src/core/files.ts +107 -0
- package/src/core/filter.ts +229 -0
- package/src/core/gsap.ts +80 -65
- package/src/core/listbox.ts +332 -0
- package/src/core/mark.ts +29 -0
- package/src/core/position.ts +89 -0
- package/src/core/relative-time.ts +77 -0
- package/src/core/segment-field.ts +373 -0
- package/src/core/segments.ts +153 -0
- package/src/core/tree.ts +116 -0
- package/src/core/virtual.ts +79 -0
- package/src/css/tokens.css +186 -182
- package/src/elements/index.ts +186 -0
- package/src/index.ts +88 -0
- package/src/motion/liquid.ts +14 -4
- package/src/motion/micro.ts +248 -0
- package/src/motion/sheet.ts +88 -65
- package/src/motion/surface.ts +9 -0
- package/src/react/index.ts +71 -1
- package/dist/chunk-IKQ5FODN.js +0 -1617
- package/dist/chunk-IKQ5FODN.js.map +0 -1
- package/dist/chunk-J6OCFNGU.js +0 -177
- package/dist/chunk-J6OCFNGU.js.map +0 -1
- package/dist/chunk-TFNRIZHU.js +0 -3977
- package/dist/chunk-TFNRIZHU.js.map +0 -1
- package/dist/chunk-XAKC2QY7.js.map +0 -1
- package/dist/reveal-jsX-uwUz.d.ts +0 -625
- package/dist/store-DYb3tTsZ.d.ts +0 -195
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { MkElement, defineElement, type OptionType } from '../../elements/base';
|
|
2
|
+
import { createRangeSlider, type RangeSliderInstance, type RangeValue } from './engine';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* <mk-range-slider class="mk-range-slider" min="0" max="100" step="5" label="Precio" value="[20,80]">
|
|
6
|
+
* <div data-mk-range-track><input type="range" data-mk-range-input value="20"><input type="range" data-mk-range-input value="80"></div>
|
|
7
|
+
* <output data-mk-range-output></output>
|
|
8
|
+
* </mk-range-slider>
|
|
9
|
+
* Empty, it makes its input(s). Emits `mk:input` while dragging and `mk:change` ({value, previous}) on commit.
|
|
10
|
+
* `el.formatValue = (n) => `${n} €`` formats the output and aria-valuetext.
|
|
11
|
+
*/
|
|
12
|
+
export class MkRangeSliderElement extends MkElement<RangeSliderInstance> {
|
|
13
|
+
static options: Record<string, OptionType> = { min: 'number', max: 'number', step: 'number', value: 'json', disabled: 'boolean', name: 'string', label: 'string' };
|
|
14
|
+
|
|
15
|
+
private format: ((value: number) => string) | undefined;
|
|
16
|
+
|
|
17
|
+
/** Formatter for the output and aria-valuetext (code, so a property rather than an attribute). */
|
|
18
|
+
get formatValue() {
|
|
19
|
+
return this.format;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
set formatValue(format: ((value: number) => string) | undefined) {
|
|
23
|
+
this.format = format;
|
|
24
|
+
this.rebuild(true);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
protected create(options: Record<string, unknown>) {
|
|
28
|
+
return createRangeSlider(this, { ...options, formatValue: this.format });
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/** Rebuild with the current attributes; keepValue keeps the edited value over the `value` attribute. */
|
|
32
|
+
private rebuild(keepValue: boolean) {
|
|
33
|
+
if (!this.isConnected || !this.instance) return;
|
|
34
|
+
const value = this.instance.value;
|
|
35
|
+
this.instance.destroy();
|
|
36
|
+
this.instance = this.create(keepValue ? { ...this.options, value } : { value, ...this.options });
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Bounds rebuild the engine but keep the edited value unless `value` is set.
|
|
40
|
+
attributeChangedCallback(_name: string, previous: string | null, next: string | null) {
|
|
41
|
+
if (previous !== next) this.rebuild(false);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
setValue(value: RangeValue, notify?: boolean) {
|
|
45
|
+
this.instance?.setValue(value, notify);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
get value() {
|
|
49
|
+
return this.instance?.value;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export const defineRangeSlider = () => defineElement('mk-range-slider', MkRangeSliderElement);
|
|
@@ -0,0 +1,316 @@
|
|
|
1
|
+
import { gsap, EASE, registerMotion } from '../../core/gsap';
|
|
2
|
+
import { reduced } from '../../core/reduced';
|
|
3
|
+
import { createScope } from '../../core/scope';
|
|
4
|
+
import { inert, type Instance } from '../../core/types';
|
|
5
|
+
import { press } from '../../motion/micro';
|
|
6
|
+
|
|
7
|
+
/*
|
|
8
|
+
* Range slider on native <input type="range"> (keyboard, touch and forms come
|
|
9
|
+
* from the platform). One input is a value, two are an interval whose thumbs
|
|
10
|
+
* never cross. `input` updates value and output at once; `change` commits (onChange /
|
|
11
|
+
* mk:change) only when the committed value differs. The native thumbs are
|
|
12
|
+
* transparent hit areas: the engine draws its own fill, thumbs and value
|
|
13
|
+
* bubbles and moves them with GSAP, tightly while dragging and with Melonmind's
|
|
14
|
+
* swing (.38,.49,0,1.5) for keys, presets, setValue and form reset. A grabbed
|
|
15
|
+
* thumb grows on the click curve and shows its value above it.
|
|
16
|
+
* Parts: [data-mk-range-input] (made when absent), [data-mk-range-track], [data-mk-range-output].
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
export type RangeValue = number | [number, number];
|
|
20
|
+
|
|
21
|
+
export interface RangeSliderOptions {
|
|
22
|
+
min?: number;
|
|
23
|
+
max?: number;
|
|
24
|
+
step?: number;
|
|
25
|
+
value?: RangeValue;
|
|
26
|
+
disabled?: boolean;
|
|
27
|
+
/** Form name; an interval submits name[0] and name[1]. */
|
|
28
|
+
name?: string;
|
|
29
|
+
/** Accessible name of the input(s). */
|
|
30
|
+
label?: string;
|
|
31
|
+
/** Text for the output and aria-valuetext. */
|
|
32
|
+
formatValue?: (value: number) => string;
|
|
33
|
+
onInput?: (value: RangeValue) => void;
|
|
34
|
+
onChange?: (value: RangeValue) => void;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export interface RangeSliderInstance extends Instance {
|
|
38
|
+
setValue(value: RangeValue, notify?: boolean): void;
|
|
39
|
+
readonly value: RangeValue;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const finite = (value: unknown): value is number => typeof value === 'number' && Number.isFinite(value);
|
|
43
|
+
const attrNumber = (input: HTMLInputElement | undefined, name: string) => {
|
|
44
|
+
const raw = input?.getAttribute(name);
|
|
45
|
+
return raw !== null && raw !== undefined && raw.trim() !== '' && Number.isFinite(Number(raw)) ? Number(raw) : undefined;
|
|
46
|
+
};
|
|
47
|
+
const decimals = (n: number) => {
|
|
48
|
+
const [, fraction = ''] = String(n).split('.');
|
|
49
|
+
return fraction.length;
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
export function createRangeSlider(root: HTMLElement | null, options: RangeSliderOptions = {}): RangeSliderInstance {
|
|
53
|
+
if (!root) return inert({ setValue() {}, value: 0 });
|
|
54
|
+
registerMotion();
|
|
55
|
+
const scope = createScope();
|
|
56
|
+
let inputs = [...root.querySelectorAll<HTMLInputElement>('input[data-mk-range-input]')].slice(0, 2);
|
|
57
|
+
if (!inputs.length) {
|
|
58
|
+
const count = Array.isArray(options.value) ? 2 : 1;
|
|
59
|
+
inputs = Array.from({ length: count }, () => {
|
|
60
|
+
const input = document.createElement('input');
|
|
61
|
+
input.type = 'range';
|
|
62
|
+
input.dataset.mkRangeInput = '';
|
|
63
|
+
root.append(input);
|
|
64
|
+
scope.add(() => input.remove());
|
|
65
|
+
return input;
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
const pair = inputs.length === 2;
|
|
69
|
+
const first = inputs[0];
|
|
70
|
+
const min = finite(options.min) ? options.min : attrNumber(first, 'min') ?? 0;
|
|
71
|
+
const maxOption = finite(options.max) ? options.max : attrNumber(first, 'max') ?? 100;
|
|
72
|
+
const max = Math.max(min, maxOption);
|
|
73
|
+
const stepOption = finite(options.step) ? options.step : attrNumber(first, 'step') ?? 1;
|
|
74
|
+
const step = stepOption > 0 ? stepOption : 1;
|
|
75
|
+
const places = Math.min(10, Math.max(decimals(step), decimals(min)));
|
|
76
|
+
const fix = (n: number) => Number(n.toFixed(places));
|
|
77
|
+
const upper = Math.min(max, fix(min + Math.floor((max - min) / step + 1e-9) * step));
|
|
78
|
+
const normalize = (n: number) => Math.max(min, Math.min(upper, fix(min + Math.round((n - min) / step) * step)));
|
|
79
|
+
const disabled = options.disabled ?? inputs.some((input) => input.disabled);
|
|
80
|
+
const output = root.querySelector<HTMLElement>('[data-mk-range-output]');
|
|
81
|
+
const format = (n: number) => (options.formatValue ? options.formatValue(n) : String(n));
|
|
82
|
+
const originals = inputs.map((input) => input.value);
|
|
83
|
+
|
|
84
|
+
const ordered = (list: number[]) => (pair ? [...list].sort((a, b) => a - b) : list);
|
|
85
|
+
const read = () => inputs.map((input) => (input.value.trim() !== '' && Number.isFinite(Number(input.value)) ? normalize(Number(input.value)) : NaN));
|
|
86
|
+
/**
|
|
87
|
+
* Starting values. The parser clamps value="500" to the input's own range (0–100 by
|
|
88
|
+
* default) before min/max are set here: an authored value that was only clamped wins.
|
|
89
|
+
*/
|
|
90
|
+
const authored = () => {
|
|
91
|
+
const current = read();
|
|
92
|
+
return inputs.map((input, i) => {
|
|
93
|
+
const written = attrNumber(input, 'value');
|
|
94
|
+
if (written === undefined) return current[i]!;
|
|
95
|
+
const low = attrNumber(input, 'min') ?? 0;
|
|
96
|
+
const high = Math.max(low, attrNumber(input, 'max') ?? 100);
|
|
97
|
+
return Number(input.value) === Math.min(high, Math.max(low, written)) ? normalize(written) : current[i]!;
|
|
98
|
+
});
|
|
99
|
+
};
|
|
100
|
+
const initial = (() => {
|
|
101
|
+
const wanted = options.value;
|
|
102
|
+
const fromOption = Array.isArray(wanted) ? wanted : finite(wanted) ? [wanted, wanted] : null;
|
|
103
|
+
const fromInputs = authored();
|
|
104
|
+
return inputs.map((_, i) => {
|
|
105
|
+
const candidate = fromOption?.[i];
|
|
106
|
+
if (finite(candidate)) return normalize(candidate);
|
|
107
|
+
return Number.isFinite(fromInputs[i]) ? fromInputs[i]! : i === 0 ? min : upper;
|
|
108
|
+
});
|
|
109
|
+
})();
|
|
110
|
+
let values = ordered(initial);
|
|
111
|
+
let committed = [...values];
|
|
112
|
+
|
|
113
|
+
// First registered, last undone: values return after min/max/step are authored again.
|
|
114
|
+
scope.add(() => inputs.forEach((input, i) => (input.value = originals[i]!)));
|
|
115
|
+
scope.attr(root, 'style', root.getAttribute('style'));
|
|
116
|
+
scope.attr(root, 'data-mk-ready', '');
|
|
117
|
+
scope.attr(root, 'data-mk-range', pair ? 'interval' : 'single');
|
|
118
|
+
inputs.forEach((input, i) => {
|
|
119
|
+
scope.attr(input, 'min', String(min));
|
|
120
|
+
scope.attr(input, 'max', String(upper));
|
|
121
|
+
scope.attr(input, 'step', String(step));
|
|
122
|
+
scope.attr(input, 'disabled', disabled ? '' : null);
|
|
123
|
+
if (pair) scope.attr(input, 'style', input.getAttribute('style'));
|
|
124
|
+
if (options.name) scope.attr(input, 'name', pair ? `${options.name}[${i}]` : options.name);
|
|
125
|
+
if (options.label) scope.attr(input, 'aria-label', pair ? `${options.label} · ${i === 0 ? 'mínimo' : 'máximo'}` : options.label);
|
|
126
|
+
});
|
|
127
|
+
if (output) {
|
|
128
|
+
const text = output.textContent;
|
|
129
|
+
// The inputs announce their own value; a live <output> would repeat every step of a drag.
|
|
130
|
+
scope.attr(output, 'aria-live', 'off');
|
|
131
|
+
scope.add(() => {
|
|
132
|
+
output.textContent = text;
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// The drawn fill, thumbs and bubbles (decorative; the inputs stay the control).
|
|
137
|
+
const track = root.querySelector<HTMLElement>('[data-mk-range-track]') ?? first!.parentElement ?? root;
|
|
138
|
+
const visual = document.createElement('span');
|
|
139
|
+
visual.dataset.mkRangeVisual = '';
|
|
140
|
+
visual.setAttribute('aria-hidden', 'true');
|
|
141
|
+
const fill = document.createElement('span');
|
|
142
|
+
fill.dataset.mkPart = 'fill';
|
|
143
|
+
visual.append(fill);
|
|
144
|
+
const thumbs = inputs.map(() => {
|
|
145
|
+
const thumb = document.createElement('span');
|
|
146
|
+
thumb.dataset.mkPart = 'thumb';
|
|
147
|
+
const bubble = document.createElement('span');
|
|
148
|
+
bubble.dataset.mkPart = 'bubble';
|
|
149
|
+
thumb.append(bubble);
|
|
150
|
+
visual.append(thumb);
|
|
151
|
+
return { thumb, bubble };
|
|
152
|
+
});
|
|
153
|
+
track.append(visual);
|
|
154
|
+
|
|
155
|
+
const span = upper - min || 1;
|
|
156
|
+
const ratio = (n: number) => (n - min) / span;
|
|
157
|
+
/** What is drawn (ratios); it trails `values` while gliding. */
|
|
158
|
+
const shown = { a: pair ? ratio(values[0]!) : 0, b: ratio(pair ? values[1]! : values[0]!) };
|
|
159
|
+
const draw = () => {
|
|
160
|
+
root.style.setProperty('--mk-range-start', shown.a.toFixed(4));
|
|
161
|
+
root.style.setProperty('--mk-range-end', shown.b.toFixed(4));
|
|
162
|
+
};
|
|
163
|
+
let glide: gsap.core.Tween | null = null;
|
|
164
|
+
const effects = new Set<gsap.core.Animation>();
|
|
165
|
+
const trackEffect = (animation: gsap.core.Animation | null) => {
|
|
166
|
+
if (!animation) return;
|
|
167
|
+
effects.add(animation);
|
|
168
|
+
// Finished ones leave as new ones arrive: the animation's own onComplete stays untouched.
|
|
169
|
+
for (const effect of effects) if (effect.totalProgress() === 1) effects.delete(effect);
|
|
170
|
+
};
|
|
171
|
+
scope.add(() => {
|
|
172
|
+
glide?.kill();
|
|
173
|
+
for (const effect of effects) effect.kill();
|
|
174
|
+
effects.clear();
|
|
175
|
+
gsap.killTweensOf(thumbs.flatMap(({ thumb, bubble }) => [thumb, bubble]));
|
|
176
|
+
visual.remove();
|
|
177
|
+
});
|
|
178
|
+
let dragging = -1;
|
|
179
|
+
const bubbleFor = (i: number, on: boolean) => {
|
|
180
|
+
const part = thumbs[i];
|
|
181
|
+
if (!part) return;
|
|
182
|
+
part.thumb.toggleAttribute('data-mk-active', on);
|
|
183
|
+
if (reduced()) {
|
|
184
|
+
part.bubble.style.opacity = on ? '1' : '';
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
187
|
+
gsap.killTweensOf(part.bubble);
|
|
188
|
+
if (on) trackEffect(gsap.fromTo(part.bubble, { opacity: 0, y: 4, scale: 0.9 }, { opacity: 1, y: 0, scale: 1, duration: 0.3, ease: EASE.pop }));
|
|
189
|
+
else trackEffect(gsap.to(part.bubble, { opacity: 0, y: 4, duration: 0.2, delay: 0.35, ease: 'power2.in', clearProps: 'transform' }));
|
|
190
|
+
};
|
|
191
|
+
const grow = (i: number, on: boolean) => {
|
|
192
|
+
const part = thumbs[i];
|
|
193
|
+
if (!part || reduced()) return;
|
|
194
|
+
trackEffect(gsap.to(part.thumb, { scale: on ? 1.22 : 1, duration: on ? 0.35 : 0.5, ease: EASE.click, overwrite: 'auto' }));
|
|
195
|
+
};
|
|
196
|
+
|
|
197
|
+
type Mode = 'set' | 'drag' | 'glide';
|
|
198
|
+
const current = (): RangeValue => (pair ? [values[0]!, values[1]!] : values[0]!);
|
|
199
|
+
const render = (mode: Mode = 'glide') => {
|
|
200
|
+
const target = { a: pair ? ratio(values[0]!) : 0, b: ratio(pair ? values[1]! : values[0]!) };
|
|
201
|
+
glide?.kill();
|
|
202
|
+
if (mode === 'set' || reduced()) {
|
|
203
|
+
Object.assign(shown, target);
|
|
204
|
+
draw();
|
|
205
|
+
} else glide = gsap.to(shown, { ...target, duration: mode === 'drag' ? 0.14 : 0.5, ease: mode === 'drag' ? 'power3.out' : EASE.swatch, onUpdate: draw });
|
|
206
|
+
inputs.forEach((input, i) => {
|
|
207
|
+
input.value = String(values[i]);
|
|
208
|
+
if (options.formatValue) scope.attr(input, 'aria-valuetext', format(values[i]!));
|
|
209
|
+
// The upper thumb stays reachable when both meet at the right end.
|
|
210
|
+
if (pair) input.style.zIndex = i === 0 && values[0]! >= upper ? '3' : '';
|
|
211
|
+
thumbs[i]!.bubble.textContent = format(values[i]!);
|
|
212
|
+
});
|
|
213
|
+
if (output) output.textContent = pair ? `${format(values[0]!)} – ${format(values[1]!)}` : format(values[0]!);
|
|
214
|
+
};
|
|
215
|
+
const same = (a: number[], b: number[]) => a.length === b.length && a.every((n, i) => n === b[i]);
|
|
216
|
+
const emit = (type: 'mk:input' | 'mk:change', previous?: RangeValue) =>
|
|
217
|
+
root.dispatchEvent(new CustomEvent(type, { bubbles: true, detail: type === 'mk:change' ? { value: current(), previous } : { value: current() } }));
|
|
218
|
+
const commit = (notify: boolean) => {
|
|
219
|
+
if (same(values, committed)) return;
|
|
220
|
+
const previous: RangeValue = pair ? [committed[0]!, committed[1]!] : committed[0]!;
|
|
221
|
+
committed = [...values];
|
|
222
|
+
if (!notify) return;
|
|
223
|
+
options.onChange?.(current());
|
|
224
|
+
emit('mk:change', previous);
|
|
225
|
+
};
|
|
226
|
+
/** The thumb `i` moved: keep it on the grid and behind/ahead of the other. */
|
|
227
|
+
const take = (i: number) => {
|
|
228
|
+
const raw = inputs[i]!.value;
|
|
229
|
+
const n = raw.trim() !== '' && Number.isFinite(Number(raw)) ? normalize(Number(raw)) : NaN;
|
|
230
|
+
if (!Number.isFinite(n)) return false;
|
|
231
|
+
let next = n;
|
|
232
|
+
if (pair) next = i === 0 ? Math.min(next, values[1]!) : Math.max(next, values[0]!);
|
|
233
|
+
const changed = next !== values[i];
|
|
234
|
+
values[i] = next;
|
|
235
|
+
return changed;
|
|
236
|
+
};
|
|
237
|
+
|
|
238
|
+
inputs.forEach((input, i) => {
|
|
239
|
+
// Inputs and the inputs of a destroyed instance are ignored by the scope.
|
|
240
|
+
scope.on(input, 'input', () => {
|
|
241
|
+
if (disabled) return render('set');
|
|
242
|
+
const changed = take(i);
|
|
243
|
+
render(dragging === i ? 'drag' : 'glide');
|
|
244
|
+
if (!changed) return;
|
|
245
|
+
// A key step nudges its thumb.
|
|
246
|
+
if (dragging !== i && !reduced()) trackEffect(gsap.fromTo(thumbs[i]!.thumb, { scale: 1.18 }, { scale: 1, duration: 0.45, ease: EASE.click }));
|
|
247
|
+
options.onInput?.(current());
|
|
248
|
+
emit('mk:input');
|
|
249
|
+
});
|
|
250
|
+
scope.on(input, 'change', () => {
|
|
251
|
+
if (disabled) return render('set');
|
|
252
|
+
take(i);
|
|
253
|
+
render(dragging === i ? 'drag' : 'glide');
|
|
254
|
+
const before = committed.join();
|
|
255
|
+
commit(true);
|
|
256
|
+
if (committed.join() !== before) trackEffect(press(output));
|
|
257
|
+
});
|
|
258
|
+
scope.on(input, 'pointerdown', () => {
|
|
259
|
+
if (disabled) return;
|
|
260
|
+
dragging = i;
|
|
261
|
+
grow(i, true);
|
|
262
|
+
bubbleFor(i, true);
|
|
263
|
+
const release = () => {
|
|
264
|
+
removeEventListener('pointerup', release);
|
|
265
|
+
removeEventListener('pointercancel', release);
|
|
266
|
+
if (dragging !== i) return;
|
|
267
|
+
dragging = -1;
|
|
268
|
+
grow(i, false);
|
|
269
|
+
if (document.activeElement !== input || !input.matches(':focus-visible')) bubbleFor(i, false);
|
|
270
|
+
};
|
|
271
|
+
addEventListener('pointerup', release);
|
|
272
|
+
addEventListener('pointercancel', release);
|
|
273
|
+
scope.add(release);
|
|
274
|
+
});
|
|
275
|
+
scope.on(input, 'focus', () => {
|
|
276
|
+
thumbs[i]!.thumb.toggleAttribute('data-mk-focus', input.matches(':focus-visible'));
|
|
277
|
+
if (input.matches(':focus-visible')) bubbleFor(i, true);
|
|
278
|
+
});
|
|
279
|
+
scope.on(input, 'blur', () => {
|
|
280
|
+
thumbs[i]!.thumb.removeAttribute('data-mk-focus');
|
|
281
|
+
if (dragging !== i) bubbleFor(i, false);
|
|
282
|
+
});
|
|
283
|
+
});
|
|
284
|
+
const form = first?.form;
|
|
285
|
+
if (form) {
|
|
286
|
+
scope.on(form, 'reset', (event) => {
|
|
287
|
+
// The native reset writes the default values after its listeners: read them next.
|
|
288
|
+
scope.timeout(() => {
|
|
289
|
+
if (event.defaultPrevented || scope.disposed) return;
|
|
290
|
+
const restored = read().map((n, i) => (Number.isFinite(n) ? n : i === 0 ? min : upper));
|
|
291
|
+
values = ordered(restored);
|
|
292
|
+
committed = [...values];
|
|
293
|
+
render('glide');
|
|
294
|
+
}, 0);
|
|
295
|
+
});
|
|
296
|
+
}
|
|
297
|
+
render('set');
|
|
298
|
+
|
|
299
|
+
return {
|
|
300
|
+
setValue(next, notify = true) {
|
|
301
|
+
if (scope.disposed) return;
|
|
302
|
+
const list = Array.isArray(next) ? next : [next, next];
|
|
303
|
+
const candidate = inputs.map((_, i) => (finite(list[i]) ? normalize(list[i]!) : values[i]!));
|
|
304
|
+
values = ordered(candidate);
|
|
305
|
+
// A controlled parent echoing the drag follows tightly; anything else glides.
|
|
306
|
+
render(dragging >= 0 ? 'drag' : 'glide');
|
|
307
|
+
// Silent sets (a controlled parent echoing a drag) leave the committed value alone,
|
|
308
|
+
// so the user's change still reports.
|
|
309
|
+
if (notify) commit(true);
|
|
310
|
+
},
|
|
311
|
+
get value() {
|
|
312
|
+
return current();
|
|
313
|
+
},
|
|
314
|
+
destroy: () => scope.dispose(),
|
|
315
|
+
};
|
|
316
|
+
}
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
/* range-slider — Melonmind line: a neutral track, the accent for the chosen span, white thumbs with a soft
|
|
2
|
+
shadow and a glass value bubble. The native thumbs stay as transparent hit areas; the engine draws and moves the rest. */
|
|
3
|
+
.mk-range-slider,
|
|
4
|
+
mk-range-slider {
|
|
5
|
+
--mk-range-thumb: 1.5rem;
|
|
6
|
+
--mk-range-height: 8px;
|
|
7
|
+
--mk-range-track: var(--mk-surface-4);
|
|
8
|
+
--mk-range-fill: var(--mk-accent);
|
|
9
|
+
display: grid;
|
|
10
|
+
grid-template-columns: minmax(0, 1fr) auto;
|
|
11
|
+
align-items: center;
|
|
12
|
+
gap: 1rem;
|
|
13
|
+
color: var(--mk-text);
|
|
14
|
+
font-family: var(--mk-font);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
:is(.mk-range-slider, mk-range-slider) [data-mk-range-track] {
|
|
18
|
+
position: relative;
|
|
19
|
+
display: grid;
|
|
20
|
+
align-items: center;
|
|
21
|
+
min-width: 0;
|
|
22
|
+
height: 2.75rem;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
:is(.mk-range-slider, mk-range-slider) [data-mk-range-visual] {
|
|
26
|
+
position: absolute;
|
|
27
|
+
inset: 0;
|
|
28
|
+
pointer-events: none;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/* Track */
|
|
32
|
+
:is(.mk-range-slider, mk-range-slider) [data-mk-range-visual]::before {
|
|
33
|
+
content: '';
|
|
34
|
+
position: absolute;
|
|
35
|
+
top: 50%;
|
|
36
|
+
inset-inline: 0;
|
|
37
|
+
height: var(--mk-range-height);
|
|
38
|
+
border-radius: var(--mk-radius-full);
|
|
39
|
+
background: var(--mk-range-track);
|
|
40
|
+
translate: 0 -50%;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/* The span between the thumb centres. */
|
|
44
|
+
:is(.mk-range-slider, mk-range-slider) [data-mk-part='fill'] {
|
|
45
|
+
position: absolute;
|
|
46
|
+
top: 50%;
|
|
47
|
+
left: calc((100% - var(--mk-range-thumb)) * var(--mk-range-start, 0) + var(--mk-range-thumb) / 2);
|
|
48
|
+
right: calc(100% - ((100% - var(--mk-range-thumb)) * var(--mk-range-end, 0) + var(--mk-range-thumb) / 2));
|
|
49
|
+
height: var(--mk-range-height);
|
|
50
|
+
border-radius: var(--mk-radius-full);
|
|
51
|
+
background: var(--mk-range-fill);
|
|
52
|
+
translate: 0 -50%;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
:is(.mk-range-slider, mk-range-slider)[data-mk-range='single'] [data-mk-part='fill'] {
|
|
56
|
+
left: 0;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
:is(.mk-range-slider, mk-range-slider) [data-mk-part='thumb'] {
|
|
60
|
+
position: absolute;
|
|
61
|
+
top: 50%;
|
|
62
|
+
left: calc((100% - var(--mk-range-thumb)) * var(--mk-range-start, 0) + var(--mk-range-thumb) / 2);
|
|
63
|
+
width: var(--mk-range-thumb);
|
|
64
|
+
height: var(--mk-range-thumb);
|
|
65
|
+
margin: calc(var(--mk-range-thumb) / -2) 0 0 calc(var(--mk-range-thumb) / -2);
|
|
66
|
+
border-radius: 50%;
|
|
67
|
+
background: var(--mk-surface-1);
|
|
68
|
+
box-shadow:
|
|
69
|
+
0 0 0 1px color-mix(in srgb, var(--mk-text) 8%, transparent),
|
|
70
|
+
0 2px 8px color-mix(in srgb, var(--mk-shadow-color) 22%, transparent);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/* Single value: its one thumb sits at the end of the fill. */
|
|
74
|
+
:is(.mk-range-slider, mk-range-slider) [data-mk-part='thumb']:last-child {
|
|
75
|
+
left: calc((100% - var(--mk-range-thumb)) * var(--mk-range-end, 0) + var(--mk-range-thumb) / 2);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
:is(.mk-range-slider, mk-range-slider) [data-mk-part='thumb']::after {
|
|
79
|
+
content: '';
|
|
80
|
+
position: absolute;
|
|
81
|
+
inset: 30%;
|
|
82
|
+
border-radius: 50%;
|
|
83
|
+
background: var(--mk-range-fill);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
:is(.mk-range-slider, mk-range-slider) [data-mk-part='thumb'][data-mk-focus] {
|
|
87
|
+
box-shadow:
|
|
88
|
+
0 0 0 4px color-mix(in srgb, var(--mk-accent) 35%, transparent),
|
|
89
|
+
0 2px 8px color-mix(in srgb, var(--mk-shadow-color) 22%, transparent);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
:is(.mk-range-slider, mk-range-slider) [data-mk-part='bubble'] {
|
|
93
|
+
position: absolute;
|
|
94
|
+
bottom: calc(100% + 10px);
|
|
95
|
+
left: 50%;
|
|
96
|
+
min-width: 2.5rem;
|
|
97
|
+
padding: 0.25rem 0.6rem;
|
|
98
|
+
width: max-content;
|
|
99
|
+
translate: -50% 0;
|
|
100
|
+
border-radius: var(--mk-radius-full);
|
|
101
|
+
background: var(--mk-glass-inverse);
|
|
102
|
+
box-shadow: var(--mk-glass-bevel);
|
|
103
|
+
backdrop-filter: blur(16px);
|
|
104
|
+
color: var(--mk-on-glass-inverse);
|
|
105
|
+
font-size: 0.8125rem;
|
|
106
|
+
font-weight: 500;
|
|
107
|
+
font-variant-numeric: tabular-nums;
|
|
108
|
+
text-align: center;
|
|
109
|
+
white-space: nowrap;
|
|
110
|
+
opacity: 0;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/* The native inputs: transparent, the same size, on top for pointer and keyboard. */
|
|
114
|
+
:is(.mk-range-slider, mk-range-slider) [data-mk-range-track] input[type='range'] {
|
|
115
|
+
position: absolute;
|
|
116
|
+
inset: 0;
|
|
117
|
+
z-index: 1;
|
|
118
|
+
width: 100%;
|
|
119
|
+
height: 100%;
|
|
120
|
+
margin: 0;
|
|
121
|
+
background: transparent;
|
|
122
|
+
appearance: none;
|
|
123
|
+
-webkit-appearance: none;
|
|
124
|
+
cursor: grab;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
:is(.mk-range-slider, mk-range-slider) [data-mk-range-track] input[type='range']:active {
|
|
128
|
+
cursor: grabbing;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
:is(.mk-range-slider, mk-range-slider)[data-mk-range='interval'] [data-mk-range-track] input[type='range'] {
|
|
132
|
+
pointer-events: none;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
:is(.mk-range-slider, mk-range-slider) input[type='range']::-webkit-slider-runnable-track {
|
|
136
|
+
height: 100%;
|
|
137
|
+
background: transparent;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
:is(.mk-range-slider, mk-range-slider) input[type='range']::-moz-range-track {
|
|
141
|
+
background: transparent;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
:is(.mk-range-slider, mk-range-slider) input[type='range']::-webkit-slider-thumb {
|
|
145
|
+
-webkit-appearance: none;
|
|
146
|
+
appearance: none;
|
|
147
|
+
width: var(--mk-range-thumb);
|
|
148
|
+
height: 2.75rem;
|
|
149
|
+
border: 0;
|
|
150
|
+
background: transparent;
|
|
151
|
+
pointer-events: auto;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
:is(.mk-range-slider, mk-range-slider) input[type='range']::-moz-range-thumb {
|
|
155
|
+
width: var(--mk-range-thumb);
|
|
156
|
+
height: 2.75rem;
|
|
157
|
+
border: 0;
|
|
158
|
+
border-radius: 0;
|
|
159
|
+
background: transparent;
|
|
160
|
+
pointer-events: auto;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
:is(.mk-range-slider, mk-range-slider) input[type='range']:focus-visible {
|
|
164
|
+
outline: none;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
:is(.mk-range-slider, mk-range-slider) input[type='range']:disabled {
|
|
168
|
+
cursor: not-allowed;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
:is(.mk-range-slider, mk-range-slider):has(input:disabled) [data-mk-range-visual] {
|
|
172
|
+
opacity: 0.45;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
:is(.mk-range-slider, mk-range-slider) [data-mk-range-output] {
|
|
176
|
+
min-width: 4ch;
|
|
177
|
+
padding: 0.4rem 0.85rem;
|
|
178
|
+
border-radius: var(--mk-radius-full);
|
|
179
|
+
background: var(--mk-surface-3);
|
|
180
|
+
font-size: 0.875rem;
|
|
181
|
+
font-weight: 500;
|
|
182
|
+
font-variant-numeric: tabular-nums;
|
|
183
|
+
text-align: center;
|
|
184
|
+
white-space: nowrap;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
@media (prefers-reduced-motion: reduce) {
|
|
188
|
+
:is(.mk-range-slider, mk-range-slider) [data-mk-part='thumb'][data-mk-active] [data-mk-part='bubble'] {
|
|
189
|
+
opacity: 1;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { useRef, type HTMLAttributes } from 'react';
|
|
3
|
+
import { useIsomorphicLayoutEffect, useLatest } from '../../react/use-recipe';
|
|
4
|
+
import { createRangeSlider, type RangeSliderInstance, type RangeSliderOptions, type RangeValue } from './engine';
|
|
5
|
+
|
|
6
|
+
export interface RangeSliderProps extends Omit<HTMLAttributes<HTMLDivElement>, 'onChange' | 'onInput' | 'defaultValue'>, RangeSliderOptions {
|
|
7
|
+
/** Uncontrolled start; an array makes an interval. */
|
|
8
|
+
defaultValue?: RangeValue;
|
|
9
|
+
/** Show the formatted value beside the track (default true). */
|
|
10
|
+
showOutput?: boolean;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* <RangeSlider min={0} max={100} defaultValue={[20, 80]} label="Precio" onChange={setRange} />.
|
|
15
|
+
* Controlled with `value`; otherwise the edited value survives rerenders and new bounds.
|
|
16
|
+
*/
|
|
17
|
+
export function RangeSlider({ min, max, step, value, defaultValue, disabled, name, label, formatValue, onInput, onChange, showOutput = true, className, ...props }: RangeSliderProps) {
|
|
18
|
+
const ref = useRef<HTMLDivElement>(null);
|
|
19
|
+
const instance = useRef<RangeSliderInstance | null>(null);
|
|
20
|
+
const edited = useRef<RangeValue | undefined>(undefined);
|
|
21
|
+
const input = useLatest(onInput);
|
|
22
|
+
const change = useLatest(onChange);
|
|
23
|
+
const format = useLatest(formatValue);
|
|
24
|
+
const formatted = formatValue !== undefined;
|
|
25
|
+
const start = value ?? defaultValue;
|
|
26
|
+
const count = Array.isArray(start) ? 2 : 1;
|
|
27
|
+
const current = useLatest(value);
|
|
28
|
+
useIsomorphicLayoutEffect(() => {
|
|
29
|
+
const created = createRangeSlider(ref.current, {
|
|
30
|
+
min,
|
|
31
|
+
max,
|
|
32
|
+
step,
|
|
33
|
+
disabled,
|
|
34
|
+
name,
|
|
35
|
+
label,
|
|
36
|
+
value: current.current ?? edited.current ?? start,
|
|
37
|
+
formatValue: formatted ? (n) => format.current?.(n) ?? String(n) : undefined,
|
|
38
|
+
onInput: (next) => input.current?.(next),
|
|
39
|
+
onChange: (next) => change.current?.(next),
|
|
40
|
+
});
|
|
41
|
+
instance.current = created;
|
|
42
|
+
return () => {
|
|
43
|
+
edited.current = created.value;
|
|
44
|
+
created.destroy();
|
|
45
|
+
instance.current = null;
|
|
46
|
+
};
|
|
47
|
+
// `start` only seeds the first creation; edits and `value` take over after it.
|
|
48
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
49
|
+
}, [min, max, step, disabled, name, label, formatted, count]);
|
|
50
|
+
const valueKey = JSON.stringify(value);
|
|
51
|
+
useIsomorphicLayoutEffect(() => {
|
|
52
|
+
if (value !== undefined) instance.current?.setValue(value, false);
|
|
53
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
54
|
+
}, [valueKey]);
|
|
55
|
+
const initial = Array.isArray(start) ? start : [start ?? min ?? 0];
|
|
56
|
+
return (
|
|
57
|
+
<div {...props} ref={ref} className={['mk-range-slider', className].filter(Boolean).join(' ')}>
|
|
58
|
+
<div data-mk-range-track="">
|
|
59
|
+
{initial.map((n, i) => (
|
|
60
|
+
<input key={i} type="range" data-mk-range-input="" defaultValue={n} />
|
|
61
|
+
))}
|
|
62
|
+
</div>
|
|
63
|
+
{showOutput && <output data-mk-range-output="" />}
|
|
64
|
+
</div>
|
|
65
|
+
);
|
|
66
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# resizable-panel
|
|
2
|
+
|
|
3
|
+
Paneles redimensionables con divisores arrastrables, uno al lado del otro o apilados. Cada divisor es una línea fina con un asa que crece y toma el color de acento al pasar el puntero, al enfocarlo y al arrastrarlo; mientras se mueve, una burbuja muestra los dos tamaños («32 % · 68 %»). Funciona con mouse, lápiz y pantalla táctil.
|
|
4
|
+
|
|
5
|
+
Al llegar al límite de un panel, el arrastre cede solo una cuarta parte del camino (efecto goma) y al soltarlo vuelve con un resorte (.45 s, curva swing). Un panel plegable arrastrado muy por debajo de su mínimo se cierra de golpe (.3 s, curva swing) y se vuelve a abrir si lo arrastras de regreso, con Intro sobre su divisor o con los botones del divisor (`handleButtons`). Doble clic en un divisor devuelve los tamaños iniciales (.45 s, curva signature). Los tamaños se pueden recordar entre visitas (`storageKey`).
|
|
6
|
+
|
|
7
|
+
## HTML / Astro
|
|
8
|
+
|
|
9
|
+
```html
|
|
10
|
+
<mk-resizable-panel class="mk-resizable-panel" sizes="22,48,30" collapsible="2" handle-buttons storage-key="correo">
|
|
11
|
+
<aside data-label="Carpetas" data-min="15">…</aside>
|
|
12
|
+
<main data-label="Mensajes">…</main>
|
|
13
|
+
<section data-label="Vista previa" data-min="20">…</section>
|
|
14
|
+
</mk-resizable-panel>
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Cada panel puede llevar `data-size`, `data-min`, `data-max` (en %), `data-collapsible` y `data-label`. Conviene poner el relleno (`padding`) en un elemento interior: un panel cerrado no conserva el suyo.
|
|
18
|
+
|
|
19
|
+
## React
|
|
20
|
+
|
|
21
|
+
```tsx
|
|
22
|
+
const panels = useRef<ResizablePanelHandle>(null);
|
|
23
|
+
|
|
24
|
+
<ResizablePanel ref={panels} sizes={[25, 75]} collapsible={[0]} handleButtons onResizeEnd={guardar} style={{ height: 420 }}>
|
|
25
|
+
<aside>…</aside>
|
|
26
|
+
<main>…</main>
|
|
27
|
+
</ResizablePanel>;
|
|
28
|
+
|
|
29
|
+
panels.current?.toggle(0);
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Unos `sizes` nuevos se deslizan hasta ahí; agregar o quitar un panel reinicia el componente.
|
|
33
|
+
|
|
34
|
+
## Opciones
|
|
35
|
+
|
|
36
|
+
| Opción | Atributo | Por defecto | Qué hace |
|
|
37
|
+
|---|---|---|---|
|
|
38
|
+
| `orientation` | `orientation` | `'horizontal'` | `'horizontal'`: lado a lado; `'vertical'`: apilados. |
|
|
39
|
+
| `sizes` | `sizes` (lista con comas) | `data-size`, si no, iguales | Tamaños en %; los que falten se reparten lo que sobra. |
|
|
40
|
+
| `min` / `max` | `min` / `max` | `10` / `100` | Límites en % (los `data-min` y `data-max` de cada panel mandan). |
|
|
41
|
+
| `collapsible` | `collapsible` (índices) | `[]` | Paneles que se cierran al arrastrarlos más allá de la mitad de su mínimo. |
|
|
42
|
+
| `collapsed` | `collapsed` (índices) | `[]` | Paneles cerrados al empezar. |
|
|
43
|
+
| `step` | `step` | `5` | Lo que mueven las flechas (Mayús: el cuádruple). |
|
|
44
|
+
| `storageKey` | `storage-key` | — | Recordar tamaños y paneles cerrados en `localStorage`. |
|
|
45
|
+
| `handleButtons` | `handle-buttons` | `false` | Botones en los divisores para cerrar y abrir los paneles de al lado. |
|
|
46
|
+
| `labels` | `labels` (lista) | `data-label` | Nombres de los paneles para las etiquetas de los divisores. |
|
|
47
|
+
| `onResize(sizes)` | — | — | Mientras cambia (una vez por cuadro). |
|
|
48
|
+
| `onResizeEnd(sizes)` | — | — | Al terminar un arrastre, una tecla o una animación. |
|
|
49
|
+
| `onCollapse(index, closed)` | — | — | Un panel se cerró o se abrió. |
|
|
50
|
+
|
|
51
|
+
## API y eventos
|
|
52
|
+
|
|
53
|
+
`setSizes(sizes, animate = true)`, `collapse(index)`, `expand(index)`, `toggle(index)`, `reset()`, `setOrientation(orientation)`, `sizes`, `collapsed`, `orientation`, `destroy()`. Eventos `mk:resize` con `{ sizes }` y `mk:collapse` con `{ index, collapsed }`.
|
|
54
|
+
|
|
55
|
+
## Teclado
|
|
56
|
+
|
|
57
|
+
Sobre un divisor: ← → (o ↑ ↓ si están apilados) mueven el 5 %, con Mayús el 20 %; Inicio y Fin llevan el panel a sus límites; Intro cierra o abre el panel plegable de al lado. En un límite, el asa se sacude un poco.
|
|
58
|
+
|
|
59
|
+
## Accesibilidad
|
|
60
|
+
|
|
61
|
+
Patrón *window splitter* de WAI-ARIA: cada divisor es un `separator` enfocable con `aria-valuenow`, `aria-valuemin`, `aria-valuemax`, un texto («40 %, cerrado»), `aria-controls` apuntando al panel anterior y una etiqueta con los nombres de los dos paneles. Un panel cerrado queda oculto también para los lectores de pantalla, y un estado discreto anuncia cada cierre y apertura. Con movimiento reducido los cambios son inmediatos.
|