@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,69 @@
|
|
|
1
|
+
/* voice-dots — round dots in the text colour (or the chart colours) that stretch into pills with the voice.
|
|
2
|
+
The row is tall enough for the tallest pill, so the layout around it never moves. */
|
|
3
|
+
.mk-voice-dots,
|
|
4
|
+
mk-voice-dots {
|
|
5
|
+
--mk-voice-size: 12px;
|
|
6
|
+
--mk-voice-dot: var(--mk-text);
|
|
7
|
+
display: inline-grid;
|
|
8
|
+
place-items: center;
|
|
9
|
+
min-width: calc(var(--mk-voice-size) * 8);
|
|
10
|
+
height: calc(var(--mk-voice-size) * 4.6);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
[data-mk-voice-row] {
|
|
14
|
+
display: inline-flex;
|
|
15
|
+
align-items: center;
|
|
16
|
+
gap: calc(var(--mk-voice-size) * 0.9);
|
|
17
|
+
height: 100%;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
[data-mk-voice-dot] {
|
|
21
|
+
display: block;
|
|
22
|
+
width: var(--mk-voice-size);
|
|
23
|
+
height: var(--mk-voice-size);
|
|
24
|
+
border-radius: var(--mk-radius-full);
|
|
25
|
+
background: var(--mk-voice-dot);
|
|
26
|
+
transition:
|
|
27
|
+
background-color 0.3s ease,
|
|
28
|
+
opacity 0.3s ease;
|
|
29
|
+
will-change: transform, height;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
:is(.mk-voice-dots, mk-voice-dots)[data-mk-state='idle'] [data-mk-voice-dot] {
|
|
33
|
+
opacity: 0.55;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
:is(.mk-voice-dots, mk-voice-dots)[data-mk-colorful] [data-mk-voice-dot='1'] {
|
|
37
|
+
--mk-voice-dot: var(--mk-chart-1, #2a78d6);
|
|
38
|
+
}
|
|
39
|
+
:is(.mk-voice-dots, mk-voice-dots)[data-mk-colorful] [data-mk-voice-dot='2'] {
|
|
40
|
+
--mk-voice-dot: var(--mk-chart-8, #e34948);
|
|
41
|
+
}
|
|
42
|
+
:is(.mk-voice-dots, mk-voice-dots)[data-mk-colorful] [data-mk-voice-dot='3'] {
|
|
43
|
+
--mk-voice-dot: var(--mk-chart-4, #eda100);
|
|
44
|
+
}
|
|
45
|
+
:is(.mk-voice-dots, mk-voice-dots)[data-mk-colorful] [data-mk-voice-dot='4'] {
|
|
46
|
+
--mk-voice-dot: var(--mk-chart-3, #1baf7a);
|
|
47
|
+
}
|
|
48
|
+
:is(.mk-voice-dots, mk-voice-dots)[data-mk-colorful] [data-mk-voice-dot='5'] {
|
|
49
|
+
--mk-voice-dot: var(--mk-chart-7, #6250d6);
|
|
50
|
+
}
|
|
51
|
+
:is(.mk-voice-dots, mk-voice-dots)[data-mk-colorful] [data-mk-voice-dot='6'] {
|
|
52
|
+
--mk-voice-dot: var(--mk-chart-2, #eb6834);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/* Without motion the state shows in the dots' colour. */
|
|
56
|
+
@media (prefers-reduced-motion: reduce) {
|
|
57
|
+
:is(.mk-voice-dots, mk-voice-dots)[data-mk-state='listening'] [data-mk-voice-dot] {
|
|
58
|
+
--mk-voice-dot: var(--mk-accent);
|
|
59
|
+
}
|
|
60
|
+
:is(.mk-voice-dots, mk-voice-dots)[data-mk-state='thinking'] [data-mk-voice-dot] {
|
|
61
|
+
opacity: 0.75;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
@media (forced-colors: active) {
|
|
66
|
+
[data-mk-voice-dot] {
|
|
67
|
+
background: CanvasText;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
import { gsap, EASE, Flip, registerMotion } from './gsap';
|
|
2
|
+
import { reduced } from './reduced';
|
|
3
|
+
import { createScope } from './scope';
|
|
4
|
+
import { TIMING } from './timing';
|
|
5
|
+
import { ringPulse, shake } from '../motion/micro';
|
|
6
|
+
|
|
7
|
+
/*
|
|
8
|
+
* Chips (multi-select, tag-input): removable chips in a list. A chip that
|
|
9
|
+
* arrives pops in (scale .6 → 1, blur 6 → 0, pop curve) while the others glide
|
|
10
|
+
* to make room (Flip, layout spring); one that leaves folds away (its width,
|
|
11
|
+
* padding and margin to 0 out of a blur, .22 s) and the rest close the gap.
|
|
12
|
+
* Keyboard: from the field, ← reaches the last chip; ← / → move between chips,
|
|
13
|
+
* Delete or Backspace removes the focused one, → past the last returns to the
|
|
14
|
+
* field. arm() rings a chip that the next Backspace will remove; flash()
|
|
15
|
+
* shakes and rings one (a duplicate, a value already chosen).
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
export interface ChipItem {
|
|
19
|
+
value: string;
|
|
20
|
+
label: string;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface ChipsOptions {
|
|
24
|
+
/** A chip's remove button wants out. */
|
|
25
|
+
onRemove: (value: string) => void;
|
|
26
|
+
/** The keyboard left the chips towards the field. */
|
|
27
|
+
onExit?: () => void;
|
|
28
|
+
/** Accessible name of a chip's remove button (default 'Quitar <label>'). */
|
|
29
|
+
removeLabel?: (item: ChipItem) => string;
|
|
30
|
+
disabled?: boolean;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export interface Chips {
|
|
34
|
+
readonly element: HTMLUListElement;
|
|
35
|
+
/** Show these chips in this order; arrivals pop in and departures fold away when `animate`. */
|
|
36
|
+
set(items: ChipItem[], animate?: boolean): void;
|
|
37
|
+
/** Ring a chip as the next to go (null: none). */
|
|
38
|
+
arm(value: string | null): void;
|
|
39
|
+
readonly armed: string | null;
|
|
40
|
+
/** Shake and ring a chip. */
|
|
41
|
+
flash(value: string): void;
|
|
42
|
+
/** Focus the last chip (from the field). False when there are none. */
|
|
43
|
+
focusLast(): boolean;
|
|
44
|
+
destroy(): void;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const CLOSE = '<svg aria-hidden="true" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round"><path d="M7 7l10 10M17 7L7 17"/></svg>';
|
|
48
|
+
|
|
49
|
+
export function createChips(options: ChipsOptions): Chips {
|
|
50
|
+
registerMotion();
|
|
51
|
+
const scope = createScope();
|
|
52
|
+
const list = document.createElement('ul');
|
|
53
|
+
list.dataset.mkChips = '';
|
|
54
|
+
list.setAttribute('role', 'list');
|
|
55
|
+
const effects = new Set<gsap.core.Animation>();
|
|
56
|
+
const keep = (animation: gsap.core.Animation | null) => {
|
|
57
|
+
if (!animation) return;
|
|
58
|
+
effects.add(animation);
|
|
59
|
+
// Finished ones leave as new ones arrive: the animation's own onComplete stays untouched.
|
|
60
|
+
for (const effect of effects) if (effect.totalProgress() === 1) effects.delete(effect);
|
|
61
|
+
};
|
|
62
|
+
scope.add(() => {
|
|
63
|
+
for (const effect of effects) effect.kill();
|
|
64
|
+
effects.clear();
|
|
65
|
+
list.remove();
|
|
66
|
+
});
|
|
67
|
+
let armed: string | null = null;
|
|
68
|
+
|
|
69
|
+
const chipOf = (value: string) => [...list.querySelectorAll<HTMLElement>('[data-mk-chip]')].find((chip) => chip.dataset.value === value && !chip.hasAttribute('data-mk-leaving')) ?? null;
|
|
70
|
+
const present = () => [...list.querySelectorAll<HTMLElement>('[data-mk-chip]:not([data-mk-leaving])')];
|
|
71
|
+
|
|
72
|
+
const make = (item: ChipItem) => {
|
|
73
|
+
const chip = document.createElement('li');
|
|
74
|
+
chip.dataset.mkChip = '';
|
|
75
|
+
chip.dataset.value = item.value;
|
|
76
|
+
chip.tabIndex = -1;
|
|
77
|
+
const label = document.createElement('span');
|
|
78
|
+
label.dataset.mkChipLabel = '';
|
|
79
|
+
label.textContent = item.label;
|
|
80
|
+
chip.append(label);
|
|
81
|
+
if (!options.disabled) {
|
|
82
|
+
const remove = document.createElement('button');
|
|
83
|
+
remove.type = 'button';
|
|
84
|
+
remove.tabIndex = -1;
|
|
85
|
+
remove.dataset.mkChipRemove = '';
|
|
86
|
+
remove.setAttribute('aria-label', options.removeLabel ? options.removeLabel(item) : `Quitar ${item.label}`);
|
|
87
|
+
remove.innerHTML = CLOSE;
|
|
88
|
+
chip.append(remove);
|
|
89
|
+
}
|
|
90
|
+
return chip;
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
const leave = (chip: HTMLElement, animate: boolean) => {
|
|
94
|
+
chip.setAttribute('data-mk-leaving', '');
|
|
95
|
+
if (!animate || reduced()) return chip.remove();
|
|
96
|
+
gsap.killTweensOf(chip);
|
|
97
|
+
keep(
|
|
98
|
+
gsap.to(chip, {
|
|
99
|
+
width: 0,
|
|
100
|
+
paddingLeft: 0,
|
|
101
|
+
paddingRight: 0,
|
|
102
|
+
marginLeft: 0,
|
|
103
|
+
marginRight: 0,
|
|
104
|
+
opacity: 0,
|
|
105
|
+
scale: 0.8,
|
|
106
|
+
filter: 'blur(6px)',
|
|
107
|
+
duration: 0.22,
|
|
108
|
+
ease: 'power2.in',
|
|
109
|
+
onComplete: () => chip.remove(),
|
|
110
|
+
}),
|
|
111
|
+
);
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
const set = (items: ChipItem[], animate = true) => {
|
|
115
|
+
const wanted = new Set(items.map((item) => item.value));
|
|
116
|
+
const staying = present().filter((chip) => wanted.has(chip.dataset.value!));
|
|
117
|
+
const state = animate && !reduced() && staying.length ? Flip.getState(staying) : null;
|
|
118
|
+
for (const chip of present()) if (!wanted.has(chip.dataset.value!)) leave(chip, animate);
|
|
119
|
+
const arriving: HTMLElement[] = [];
|
|
120
|
+
let previous: HTMLElement | null = null;
|
|
121
|
+
for (const item of items) {
|
|
122
|
+
let chip = chipOf(item.value);
|
|
123
|
+
if (!chip) {
|
|
124
|
+
chip = make(item);
|
|
125
|
+
arriving.push(chip);
|
|
126
|
+
} else chip.querySelector<HTMLElement>('[data-mk-chip-label]')!.textContent = item.label;
|
|
127
|
+
// Keep the given order; leaving chips stay where they were while they fold.
|
|
128
|
+
if (previous) previous.after(chip);
|
|
129
|
+
else list.prepend(chip);
|
|
130
|
+
previous = chip;
|
|
131
|
+
}
|
|
132
|
+
if (armed && !wanted.has(armed)) armed = null;
|
|
133
|
+
if (state) keep(Flip.from(state, { targets: staying, duration: TIMING.layoutTravel, ease: EASE.layoutReflow }));
|
|
134
|
+
if (animate && arriving.length && !reduced()) {
|
|
135
|
+
keep(gsap.fromTo(arriving, { scale: 0.6, opacity: 0, filter: 'blur(6px)' }, { scale: 1, opacity: 1, filter: 'blur(0px)', duration: 0.3, stagger: 0.04, ease: EASE.pop, clearProps: 'scale,opacity,filter,transform' }));
|
|
136
|
+
}
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
const arm = (value: string | null) => {
|
|
140
|
+
armed = value;
|
|
141
|
+
for (const chip of present()) chip.toggleAttribute('data-mk-armed', chip.dataset.value === value);
|
|
142
|
+
const chip = value ? chipOf(value) : null;
|
|
143
|
+
if (chip && !reduced()) keep(gsap.fromTo(chip, { scale: 1.08 }, { scale: 1, duration: 0.3, ease: EASE.click, clearProps: 'scale' }));
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
scope.on(list, 'click', (event) => {
|
|
147
|
+
const remove = (event.target as Element | null)?.closest('[data-mk-chip-remove]');
|
|
148
|
+
const chip = remove?.closest<HTMLElement>('[data-mk-chip]');
|
|
149
|
+
if (chip && !chip.hasAttribute('data-mk-leaving')) options.onRemove(chip.dataset.value!);
|
|
150
|
+
});
|
|
151
|
+
scope.on(list, 'keydown', (event) => {
|
|
152
|
+
const key = event as KeyboardEvent;
|
|
153
|
+
const chip = (key.target as Element | null)?.closest<HTMLElement>('[data-mk-chip]');
|
|
154
|
+
if (!chip) return;
|
|
155
|
+
const chips = present();
|
|
156
|
+
const index = chips.indexOf(chip);
|
|
157
|
+
if (key.key === 'ArrowLeft') {
|
|
158
|
+
key.preventDefault();
|
|
159
|
+
chips[Math.max(0, index - 1)]?.focus();
|
|
160
|
+
} else if (key.key === 'ArrowRight') {
|
|
161
|
+
key.preventDefault();
|
|
162
|
+
if (index >= chips.length - 1) options.onExit?.();
|
|
163
|
+
else chips[index + 1]?.focus();
|
|
164
|
+
} else if ((key.key === 'Delete' || key.key === 'Backspace') && !options.disabled) {
|
|
165
|
+
key.preventDefault();
|
|
166
|
+
const next = chips[index + 1] ?? chips[index - 1] ?? null;
|
|
167
|
+
options.onRemove(chip.dataset.value!);
|
|
168
|
+
if (next && next !== chip && next.isConnected && !next.hasAttribute('data-mk-leaving')) next.focus();
|
|
169
|
+
else options.onExit?.();
|
|
170
|
+
} else if (key.key === 'Escape' || key.key === 'Tab') {
|
|
171
|
+
if (key.key === 'Escape') key.preventDefault();
|
|
172
|
+
if (key.key === 'Escape') options.onExit?.();
|
|
173
|
+
}
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
return {
|
|
177
|
+
element: list,
|
|
178
|
+
set,
|
|
179
|
+
arm,
|
|
180
|
+
get armed() {
|
|
181
|
+
return armed;
|
|
182
|
+
},
|
|
183
|
+
flash(value) {
|
|
184
|
+
const chip = chipOf(value);
|
|
185
|
+
if (!chip) return;
|
|
186
|
+
keep(shake(chip, 3));
|
|
187
|
+
keep(ringPulse(chip));
|
|
188
|
+
},
|
|
189
|
+
focusLast() {
|
|
190
|
+
const chips = present();
|
|
191
|
+
chips[chips.length - 1]?.focus();
|
|
192
|
+
return chips.length > 0;
|
|
193
|
+
},
|
|
194
|
+
destroy: () => scope.dispose(),
|
|
195
|
+
};
|
|
196
|
+
}
|
|
@@ -0,0 +1,304 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Colour model for the colour components. HSVA is the working space (h 0–360,
|
|
3
|
+
* s and v 0–100, a 0–1): the saturation × brightness area and the hue slider
|
|
4
|
+
* map onto it directly. Parses #rgb, #rgba, #rrggbb, #rrggbbaa, rgb()/rgba()
|
|
5
|
+
* and hsl()/hsla() (comma or space syntax, optional "/ alpha"); formats hex,
|
|
6
|
+
* rgb or hsl (alpha only when below 1). A grey keeps the hue it is given, so
|
|
7
|
+
* dragging through grey never resets the hue.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
export interface Hsva {
|
|
11
|
+
/** 0–360. */
|
|
12
|
+
h: number;
|
|
13
|
+
/** 0–100. */
|
|
14
|
+
s: number;
|
|
15
|
+
/** 0–100. */
|
|
16
|
+
v: number;
|
|
17
|
+
/** 0–1. */
|
|
18
|
+
a: number;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export interface Rgba {
|
|
22
|
+
/** 0–255. */
|
|
23
|
+
r: number;
|
|
24
|
+
g: number;
|
|
25
|
+
b: number;
|
|
26
|
+
a: number;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export interface Hsla {
|
|
30
|
+
h: number;
|
|
31
|
+
/** 0–100. */
|
|
32
|
+
s: number;
|
|
33
|
+
l: number;
|
|
34
|
+
a: number;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export type ColorFormat = 'hex' | 'rgb' | 'hsl';
|
|
38
|
+
export type ColorChannel = 'hue' | 'saturation' | 'brightness' | 'lightness' | 'red' | 'green' | 'blue' | 'alpha';
|
|
39
|
+
|
|
40
|
+
export const CHANNEL_LABELS: Record<ColorChannel, string> = {
|
|
41
|
+
hue: 'Tono',
|
|
42
|
+
saturation: 'Saturación',
|
|
43
|
+
brightness: 'Brillo',
|
|
44
|
+
lightness: 'Luminosidad',
|
|
45
|
+
red: 'Rojo',
|
|
46
|
+
green: 'Verde',
|
|
47
|
+
blue: 'Azul',
|
|
48
|
+
alpha: 'Opacidad',
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
export const CHANNEL_RANGE: Record<ColorChannel, [number, number]> = {
|
|
52
|
+
hue: [0, 360],
|
|
53
|
+
saturation: [0, 100],
|
|
54
|
+
brightness: [0, 100],
|
|
55
|
+
lightness: [0, 100],
|
|
56
|
+
red: [0, 255],
|
|
57
|
+
green: [0, 255],
|
|
58
|
+
blue: [0, 255],
|
|
59
|
+
alpha: [0, 1],
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
const clamp = (n: number, min: number, max: number) => Math.min(max, Math.max(min, Number.isFinite(n) ? n : min));
|
|
63
|
+
/** Two decimals: enough for a lossless 8-bit round trip, without float noise (31.999…). */
|
|
64
|
+
const tidy = (n: number) => Math.round(n * 100) / 100;
|
|
65
|
+
const wrapHue = (h: number) => {
|
|
66
|
+
const hue = ((h % 360) + 360) % 360;
|
|
67
|
+
return hue === 0 && h > 0 ? 360 : hue;
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
const hsva = (h: number, s: number, v: number, a: number): Hsva => ({
|
|
71
|
+
h: tidy(clamp(h, 0, 360)),
|
|
72
|
+
s: tidy(clamp(s, 0, 100)),
|
|
73
|
+
v: tidy(clamp(v, 0, 100)),
|
|
74
|
+
a: Math.round(clamp(a, 0, 1) * 1000) / 1000,
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
/** HSVA → RGBA (channels 0–255, unrounded). */
|
|
78
|
+
export function hsvToRgb({ h, s, v, a }: Hsva): Rgba {
|
|
79
|
+
const sat = s / 100;
|
|
80
|
+
const val = v / 100;
|
|
81
|
+
const f = (n: number) => {
|
|
82
|
+
const k = (n + (h % 360) / 60) % 6;
|
|
83
|
+
return val - val * sat * Math.max(0, Math.min(k, 4 - k, 1));
|
|
84
|
+
};
|
|
85
|
+
return { r: f(5) * 255, g: f(3) * 255, b: f(1) * 255, a };
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/** RGBA → HSVA; a grey keeps `hue` (and black keeps `saturation`) when given. */
|
|
89
|
+
export function rgbToHsv({ r, g, b, a }: Rgba, hue?: number, saturation?: number): Hsva {
|
|
90
|
+
const [red, green, blue] = [r / 255, g / 255, b / 255];
|
|
91
|
+
const max = Math.max(red, green, blue);
|
|
92
|
+
const min = Math.min(red, green, blue);
|
|
93
|
+
const delta = max - min;
|
|
94
|
+
let h = hue ?? 0;
|
|
95
|
+
if (delta > 0) {
|
|
96
|
+
if (max === red) h = 60 * (((green - blue) / delta) % 6);
|
|
97
|
+
else if (max === green) h = 60 * ((blue - red) / delta + 2);
|
|
98
|
+
else h = 60 * ((red - green) / delta + 4);
|
|
99
|
+
if (h < 0) h += 360;
|
|
100
|
+
}
|
|
101
|
+
const s = max === 0 ? (saturation ?? 0) : (delta / max) * 100;
|
|
102
|
+
return hsva(h, s, max * 100, a);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export function hsvToHsl({ h, s, v, a }: Hsva): Hsla {
|
|
106
|
+
const sat = s / 100;
|
|
107
|
+
const val = v / 100;
|
|
108
|
+
const l = val * (1 - sat / 2);
|
|
109
|
+
const sl = l === 0 || l === 1 ? 0 : (val - l) / Math.min(l, 1 - l);
|
|
110
|
+
return { h, s: sl * 100, l: l * 100, a };
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
export function hslToHsv({ h, s, l, a }: Hsla, saturation?: number): Hsva {
|
|
114
|
+
const sat = s / 100;
|
|
115
|
+
const light = l / 100;
|
|
116
|
+
const v = light + sat * Math.min(light, 1 - light);
|
|
117
|
+
const sv = v === 0 ? (saturation ?? 0) / 100 : 2 * (1 - light / v);
|
|
118
|
+
return hsva(h, sv * 100, v * 100, a);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
const number = (text: string, scale: number) => {
|
|
122
|
+
const value = Number.parseFloat(text);
|
|
123
|
+
if (!Number.isFinite(value)) return Number.NaN;
|
|
124
|
+
return text.trim().endsWith('%') ? (value / 100) * scale : value;
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
const alphaOf = (text: string | undefined) => (text === undefined ? 1 : clamp(number(text, 1), 0, 1));
|
|
128
|
+
|
|
129
|
+
const angle = (text: string) => {
|
|
130
|
+
const value = Number.parseFloat(text);
|
|
131
|
+
if (!Number.isFinite(value)) return Number.NaN;
|
|
132
|
+
if (/turn$/i.test(text)) return value * 360;
|
|
133
|
+
if (/grad$/i.test(text)) return value * 0.9;
|
|
134
|
+
if (/rad$/i.test(text)) return (value * 180) / Math.PI;
|
|
135
|
+
return value;
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
/** "rgb(1, 2, 3)", "rgb(1 2 3 / 50%)" → its parts, or null. */
|
|
139
|
+
function args(text: string, name: string): string[] | null {
|
|
140
|
+
const match = new RegExp(`^${name}a?\\(\\s*([^)]*)\\)$`, 'i').exec(text);
|
|
141
|
+
if (!match) return null;
|
|
142
|
+
const body = match[1]!.trim();
|
|
143
|
+
const [main, slash] = body.split('/');
|
|
144
|
+
const parts = main!.includes(',') ? main!.split(',').map((part) => part.trim()) : main!.trim().split(/\s+/);
|
|
145
|
+
if (slash !== undefined) {
|
|
146
|
+
if (main!.includes(',') || parts.length !== 3) return null;
|
|
147
|
+
parts.push(slash.trim());
|
|
148
|
+
}
|
|
149
|
+
if (parts.length < 3 || parts.length > 4 || parts.some((part) => !part)) return null;
|
|
150
|
+
return parts;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Any colour the components accept, as HSVA; null when the text is not one.
|
|
155
|
+
* `hue` is kept for greys (no hue of their own).
|
|
156
|
+
*/
|
|
157
|
+
export function parseColor(input: string | null | undefined, hue?: number): Hsva | null {
|
|
158
|
+
const text = (input ?? '').trim().toLowerCase();
|
|
159
|
+
if (!text) return null;
|
|
160
|
+
const hex = /^#?([0-9a-f]{3,4}|[0-9a-f]{6}|[0-9a-f]{8})$/.exec(text);
|
|
161
|
+
if (hex) {
|
|
162
|
+
let digits = hex[1]!;
|
|
163
|
+
if (digits.length <= 4) digits = [...digits].map((c) => c + c).join('');
|
|
164
|
+
const channel = (i: number) => Number.parseInt(digits.slice(i, i + 2), 16);
|
|
165
|
+
return rgbToHsv({ r: channel(0), g: channel(2), b: channel(4), a: digits.length === 8 ? channel(6) / 255 : 1 }, hue);
|
|
166
|
+
}
|
|
167
|
+
const rgb = args(text, 'rgb');
|
|
168
|
+
if (rgb) {
|
|
169
|
+
const [r, g, b] = rgb.slice(0, 3).map((part) => number(part, 255));
|
|
170
|
+
const a = alphaOf(rgb[3]);
|
|
171
|
+
if (![r, g, b, a].every(Number.isFinite)) return null;
|
|
172
|
+
return rgbToHsv({ r: clamp(r!, 0, 255), g: clamp(g!, 0, 255), b: clamp(b!, 0, 255), a }, hue);
|
|
173
|
+
}
|
|
174
|
+
const hsl = args(text, 'hsl');
|
|
175
|
+
if (hsl) {
|
|
176
|
+
const h = angle(hsl[0]!);
|
|
177
|
+
const s = Number.parseFloat(hsl[1]!);
|
|
178
|
+
const l = Number.parseFloat(hsl[2]!);
|
|
179
|
+
const a = alphaOf(hsl[3]);
|
|
180
|
+
if (![h, s, l, a].every(Number.isFinite)) return null;
|
|
181
|
+
const color = hslToHsv({ h: wrapHue(h), s: clamp(s, 0, 100), l: clamp(l, 0, 100), a });
|
|
182
|
+
return color.s === 0 && hue !== undefined ? { ...color, h: hue } : color;
|
|
183
|
+
}
|
|
184
|
+
return null;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
const DEFAULT: Hsva = { h: 0, s: 100, v: 100, a: 1 };
|
|
188
|
+
|
|
189
|
+
/** A colour option (text or HSVA) as HSVA; `fallback` (default red) when it cannot be read. */
|
|
190
|
+
export function toHsva(input: string | Hsva | null | undefined, fallback: Hsva = DEFAULT): Hsva {
|
|
191
|
+
if (input && typeof input === 'object') return hsva(input.h, input.s, input.v, input.a ?? 1);
|
|
192
|
+
return parseColor(input, fallback.h) ?? { ...fallback };
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
const byte = (n: number) => Math.round(clamp(n, 0, 255));
|
|
196
|
+
const hex2 = (n: number) => byte(n).toString(16).padStart(2, '0');
|
|
197
|
+
const short = (n: number) => String(Math.round(n * 100) / 100);
|
|
198
|
+
|
|
199
|
+
/** #rrggbb, or #rrggbbaa when translucent. */
|
|
200
|
+
export function toHex(color: Hsva): string {
|
|
201
|
+
const { r, g, b, a } = hsvToRgb(color);
|
|
202
|
+
const alpha = byte(a * 255);
|
|
203
|
+
return `#${hex2(r)}${hex2(g)}${hex2(b)}${alpha < 255 ? hex2(alpha) : ''}`;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/** The colour as CSS text: hex, "rgb(255 136 0 / 0.5)" or "hsl(32 100% 50%)". */
|
|
207
|
+
export function formatColor(color: Hsva, format: ColorFormat = 'hex'): string {
|
|
208
|
+
if (format === 'rgb') {
|
|
209
|
+
const { r, g, b, a } = hsvToRgb(color);
|
|
210
|
+
return `rgb(${byte(r)} ${byte(g)} ${byte(b)}${a < 1 ? ` / ${short(a)}` : ''})`;
|
|
211
|
+
}
|
|
212
|
+
if (format === 'hsl') {
|
|
213
|
+
const { h, s, l, a } = hsvToHsl(color);
|
|
214
|
+
return `hsl(${Math.round(h) % 360} ${Math.round(s)}% ${Math.round(l)}%${a < 1 ? ` / ${short(a)}` : ''})`;
|
|
215
|
+
}
|
|
216
|
+
return toHex(color);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/** Same colour on screen (same 8-bit hex). */
|
|
220
|
+
export const sameColor = (a: Hsva, b: Hsva) => toHex(a) === toHex(b);
|
|
221
|
+
|
|
222
|
+
/** Relative luminance (WCAG), 0–1, ignoring alpha. */
|
|
223
|
+
export function luminance(color: Hsva): number {
|
|
224
|
+
const { r, g, b } = hsvToRgb(color);
|
|
225
|
+
const linear = (n: number) => {
|
|
226
|
+
const c = n / 255;
|
|
227
|
+
return c <= 0.03928 ? c / 12.92 : ((c + 0.055) / 1.055) ** 2.4;
|
|
228
|
+
};
|
|
229
|
+
return 0.2126 * linear(r) + 0.7152 * linear(g) + 0.0722 * linear(b);
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/** One channel's value in its own range (CHANNEL_RANGE). */
|
|
233
|
+
export function getChannel(color: Hsva, channel: ColorChannel): number {
|
|
234
|
+
switch (channel) {
|
|
235
|
+
case 'hue':
|
|
236
|
+
return color.h;
|
|
237
|
+
case 'saturation':
|
|
238
|
+
return color.s;
|
|
239
|
+
case 'brightness':
|
|
240
|
+
return color.v;
|
|
241
|
+
case 'alpha':
|
|
242
|
+
return color.a;
|
|
243
|
+
case 'lightness':
|
|
244
|
+
return tidy(hsvToHsl(color).l);
|
|
245
|
+
default: {
|
|
246
|
+
const rgb = hsvToRgb(color);
|
|
247
|
+
return byte(channel === 'red' ? rgb.r : channel === 'green' ? rgb.g : rgb.b);
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
/** A copy with one channel changed (clamped to its range); the hue survives greys. */
|
|
253
|
+
export function setChannel(color: Hsva, channel: ColorChannel, value: number): Hsva {
|
|
254
|
+
const [min, max] = CHANNEL_RANGE[channel];
|
|
255
|
+
const next = clamp(value, min, max);
|
|
256
|
+
switch (channel) {
|
|
257
|
+
case 'hue':
|
|
258
|
+
return hsva(next, color.s, color.v, color.a);
|
|
259
|
+
case 'saturation':
|
|
260
|
+
return hsva(color.h, next, color.v, color.a);
|
|
261
|
+
case 'brightness':
|
|
262
|
+
return hsva(color.h, color.s, next, color.a);
|
|
263
|
+
case 'alpha':
|
|
264
|
+
return hsva(color.h, color.s, color.v, next);
|
|
265
|
+
case 'lightness': {
|
|
266
|
+
const hsl = hsvToHsl(color);
|
|
267
|
+
return { ...hslToHsv({ ...hsl, l: next }, color.s), h: color.h };
|
|
268
|
+
}
|
|
269
|
+
default: {
|
|
270
|
+
const rgb = hsvToRgb(color);
|
|
271
|
+
const key = channel === 'red' ? 'r' : channel === 'green' ? 'g' : 'b';
|
|
272
|
+
return rgbToHsv({ ...rgb, [key]: next }, color.h, color.s);
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
/** What a channel's value reads as: "210°", "64 %", "136". */
|
|
278
|
+
export function channelText(channel: ColorChannel, value: number, format?: Intl.NumberFormat): string {
|
|
279
|
+
const shown = (n: number) => (format ? format.format(n) : String(Math.round(n)));
|
|
280
|
+
if (channel === 'hue') return `${shown(value)}°`;
|
|
281
|
+
if (channel === 'alpha') return `${shown(value * 100)} %`;
|
|
282
|
+
if (channel === 'red' || channel === 'green' || channel === 'blue') return shown(value);
|
|
283
|
+
return `${shown(value)} %`;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
const rgbText = (color: Hsva) => formatColor(color, 'rgb');
|
|
287
|
+
|
|
288
|
+
/** A slider track for `channel` at `color`: the channel's whole range, the other channels fixed. */
|
|
289
|
+
export function channelGradient(color: Hsva, channel: ColorChannel, direction = 'to right'): string {
|
|
290
|
+
const opaque = { ...color, a: 1 };
|
|
291
|
+
let stops: string[];
|
|
292
|
+
if (channel === 'hue') stops = [0, 60, 120, 180, 240, 300, 360].map((h) => rgbText({ h, s: 100, v: 100, a: 1 }));
|
|
293
|
+
else if (channel === 'alpha') stops = [rgbText({ ...opaque, a: 0 }), rgbText(opaque)];
|
|
294
|
+
else if (channel === 'lightness') stops = [0, 50, 100].map((l) => rgbText(setChannel(opaque, 'lightness', l)));
|
|
295
|
+
else {
|
|
296
|
+
const [min, max] = CHANNEL_RANGE[channel];
|
|
297
|
+
stops = [setChannel(opaque, channel, min), setChannel(opaque, channel, max)].map(rgbText);
|
|
298
|
+
}
|
|
299
|
+
return `linear-gradient(${direction}, ${stops.join(', ')})`;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
/** The saturation (x) × brightness (y) square at hue `h`. */
|
|
303
|
+
export const areaBackground = (h: number) =>
|
|
304
|
+
`linear-gradient(to top, #000, transparent), linear-gradient(to right, #fff, hsl(${Math.round(h) % 360} 100% 50%))`;
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { addDays, dayKey, sameDay, toDate, type DateInput } from './dates';
|
|
2
|
+
|
|
3
|
+
/*
|
|
4
|
+
* Date ranges on local days (range-calendar, date-range-picker): reading a
|
|
5
|
+
* {start, end} option, the day rules (min, max, unavailable days) and the
|
|
6
|
+
* two-click pick — the first click fixes an anchor, the second closes the
|
|
7
|
+
* range in either direction; crossing an unavailable day restarts at the
|
|
8
|
+
* clicked day.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
export interface DateRange {
|
|
12
|
+
start: Date;
|
|
13
|
+
end: Date;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface DayRules {
|
|
17
|
+
min?: Date | null;
|
|
18
|
+
max?: Date | null;
|
|
19
|
+
isUnavailable?: (day: Date) => boolean;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const valid = (input: DateInput | null | undefined) => {
|
|
23
|
+
if (input === null || input === undefined || input === '') return null;
|
|
24
|
+
const date = toDate(input, new Date(Number.NaN));
|
|
25
|
+
return Number.isNaN(date.getTime()) ? null : date;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
/** An ordered range from {start, end} (Dates or 'YYYY-MM-DD'); null when either end is missing or unreadable. */
|
|
29
|
+
export function toRange(value: { start?: DateInput | null; end?: DateInput | null } | null | undefined): DateRange | null {
|
|
30
|
+
if (!value) return null;
|
|
31
|
+
const start = valid(value.start);
|
|
32
|
+
const end = valid(value.end);
|
|
33
|
+
if (!start || !end) return null;
|
|
34
|
+
return start <= end ? { start, end } : { start: end, end: start };
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export const rangeKeys = (range: DateRange) => ({ start: dayKey(range.start), end: dayKey(range.end) });
|
|
38
|
+
|
|
39
|
+
export const inRange = (day: Date, range: DateRange | null | undefined) => !!range && day >= range.start && day <= range.end;
|
|
40
|
+
|
|
41
|
+
/** Days from start to end, both included. */
|
|
42
|
+
export const rangeLength = (range: DateRange) =>
|
|
43
|
+
Math.round((Date.UTC(range.end.getFullYear(), range.end.getMonth(), range.end.getDate()) - Date.UTC(range.start.getFullYear(), range.start.getMonth(), range.start.getDate())) / 86400000) + 1;
|
|
44
|
+
|
|
45
|
+
export function isPickable(day: Date, rules: DayRules = {}): boolean {
|
|
46
|
+
if (rules.min && day < rules.min) return false;
|
|
47
|
+
if (rules.max && day > rules.max) return false;
|
|
48
|
+
return !rules.isUnavailable?.(day);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/** Every day of the range can be picked. */
|
|
52
|
+
export function rangeIsPickable(range: DateRange, rules: DayRules = {}): boolean {
|
|
53
|
+
for (let day = range.start; day <= range.end; day = addDays(day, 1)) if (!isPickable(day, rules)) return false;
|
|
54
|
+
return true;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* One click. Without an anchor, `day` becomes it. With one, the range closes
|
|
59
|
+
* (ordered); if it would cross a day that cannot be picked, `day` becomes the
|
|
60
|
+
* new anchor instead. Clicking the anchor again makes a one-day range.
|
|
61
|
+
*/
|
|
62
|
+
export function pickDay(anchor: Date | null, day: Date, rules: DayRules = {}): { anchor: Date | null; range: DateRange | null } {
|
|
63
|
+
if (!anchor) return { anchor: day, range: null };
|
|
64
|
+
const range = anchor <= day ? { start: anchor, end: day } : { start: day, end: anchor };
|
|
65
|
+
if (!sameDay(anchor, day) && !rangeIsPickable(range, rules)) return { anchor: day, range: null };
|
|
66
|
+
return { anchor: null, range };
|
|
67
|
+
}
|
package/src/core/dates.ts
CHANGED
|
@@ -83,7 +83,17 @@ export function safeFormat(locale: string | undefined, options: Intl.DateTimeFor
|
|
|
83
83
|
}
|
|
84
84
|
}
|
|
85
85
|
|
|
86
|
-
|
|
86
|
+
/** Numbers in the language of the page around `element`: "4,6" and "1204" on a Spanish page, "4.6" and "1,204" in English. */
|
|
87
|
+
export function numberFormat(element: Element, options: Intl.NumberFormatOptions = {}): Intl.NumberFormat {
|
|
88
|
+
const locale = element.closest('[lang]')?.getAttribute('lang') || undefined;
|
|
89
|
+
try {
|
|
90
|
+
return new Intl.NumberFormat(locale, options);
|
|
91
|
+
} catch {
|
|
92
|
+
return new Intl.NumberFormat(undefined, options);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export const capitalize =(text: string) => text.charAt(0).toLocaleUpperCase() + text.slice(1);
|
|
87
97
|
|
|
88
98
|
/** Options given as JSON text (data-mk-* attributes through autoInit) become values. */
|
|
89
99
|
export function fromJson<T>(value: T | string | undefined): T | undefined {
|