@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,121 @@
|
|
|
1
|
+
/* toolbar — a dark rail of round tools; a light blob sits under the chosen one and stretches like slime to the next.
|
|
2
|
+
The label beside a tool is a small dark pill. In the dark theme the rail turns light and the blob dark. */
|
|
3
|
+
.mk-toolbar,
|
|
4
|
+
mk-toolbar {
|
|
5
|
+
--mk-toolbar-rail: color-mix(in srgb, var(--mk-text) 86%, var(--mk-bg));
|
|
6
|
+
--mk-toolbar-on-rail: var(--mk-bg);
|
|
7
|
+
--mk-toolbar-blob: var(--mk-bg);
|
|
8
|
+
--mk-toolbar-on-blob: var(--mk-text);
|
|
9
|
+
--mk-toolbar-tool: 3.5rem;
|
|
10
|
+
position: relative;
|
|
11
|
+
display: inline-flex;
|
|
12
|
+
width: max-content;
|
|
13
|
+
max-width: 100%;
|
|
14
|
+
flex-direction: column;
|
|
15
|
+
gap: 0.25rem;
|
|
16
|
+
padding: 0.5rem;
|
|
17
|
+
border-radius: var(--mk-radius-full);
|
|
18
|
+
background: var(--mk-toolbar-rail);
|
|
19
|
+
box-shadow: 0 10px 30px -12px var(--mk-shadow-color, rgb(0 0 0 / 0.35));
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
:is(.mk-toolbar, mk-toolbar)[data-mk-orientation='horizontal'] {
|
|
23
|
+
flex-direction: row;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
:is(.mk-toolbar, mk-toolbar) > button {
|
|
27
|
+
position: relative;
|
|
28
|
+
z-index: 1;
|
|
29
|
+
display: grid;
|
|
30
|
+
place-items: center;
|
|
31
|
+
width: var(--mk-toolbar-tool);
|
|
32
|
+
height: var(--mk-toolbar-tool);
|
|
33
|
+
padding: 0;
|
|
34
|
+
border: 0;
|
|
35
|
+
border-radius: var(--mk-radius-full);
|
|
36
|
+
background: transparent;
|
|
37
|
+
color: var(--mk-toolbar-on-rail);
|
|
38
|
+
cursor: pointer;
|
|
39
|
+
transition: color 0.2s ease;
|
|
40
|
+
-webkit-tap-highlight-color: transparent;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
:is(.mk-toolbar, mk-toolbar) > button svg {
|
|
44
|
+
width: 1.5rem;
|
|
45
|
+
height: 1.5rem;
|
|
46
|
+
transition: transform 0.35s var(--mk-ease-swatch);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/* The chosen tool's icon turns dark as the blob arrives under it. */
|
|
50
|
+
:is(.mk-toolbar, mk-toolbar) > button[aria-pressed='true'] {
|
|
51
|
+
color: var(--mk-toolbar-on-blob);
|
|
52
|
+
transition-delay: 0.12s;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
@media (hover: hover) {
|
|
56
|
+
:is(.mk-toolbar, mk-toolbar) > button:hover svg {
|
|
57
|
+
transform: scale(1.1);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
:is(.mk-toolbar, mk-toolbar) > button:focus-visible {
|
|
62
|
+
outline: 2px solid var(--mk-accent);
|
|
63
|
+
outline-offset: 2px;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
:is(.mk-toolbar, mk-toolbar) > button:disabled {
|
|
67
|
+
cursor: not-allowed;
|
|
68
|
+
opacity: 0.4;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
[data-mk-toolbar-blob] {
|
|
72
|
+
position: absolute;
|
|
73
|
+
top: 0;
|
|
74
|
+
left: 0;
|
|
75
|
+
width: var(--mk-toolbar-tool);
|
|
76
|
+
height: var(--mk-toolbar-tool);
|
|
77
|
+
border-radius: var(--mk-radius-full);
|
|
78
|
+
background: var(--mk-toolbar-blob);
|
|
79
|
+
pointer-events: none;
|
|
80
|
+
will-change: transform, height, width;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
[data-mk-toolbar-tip] {
|
|
84
|
+
position: absolute;
|
|
85
|
+
top: 0;
|
|
86
|
+
left: calc(100% + 0.75rem);
|
|
87
|
+
z-index: 2;
|
|
88
|
+
padding: 0.4rem 0.9rem;
|
|
89
|
+
border-radius: var(--mk-radius-full);
|
|
90
|
+
background: var(--mk-toolbar-rail);
|
|
91
|
+
color: var(--mk-toolbar-on-rail);
|
|
92
|
+
font: 500 0.875rem/1.2 var(--mk-font);
|
|
93
|
+
white-space: nowrap;
|
|
94
|
+
pointer-events: none;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
:is(.mk-toolbar, mk-toolbar)[data-mk-orientation='horizontal'] [data-mk-toolbar-tip] {
|
|
98
|
+
top: auto;
|
|
99
|
+
bottom: calc(100% + 0.75rem);
|
|
100
|
+
left: 0;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
[data-mk-toolbar-tip][hidden] {
|
|
104
|
+
display: none;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
@media (prefers-reduced-motion: reduce) {
|
|
108
|
+
:is(.mk-toolbar, mk-toolbar) > button,
|
|
109
|
+
:is(.mk-toolbar, mk-toolbar) > button svg {
|
|
110
|
+
transition: none;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
@media (forced-colors: active) {
|
|
115
|
+
[data-mk-toolbar-blob] {
|
|
116
|
+
background: Highlight;
|
|
117
|
+
}
|
|
118
|
+
:is(.mk-toolbar, mk-toolbar) > button[aria-pressed='true'] {
|
|
119
|
+
color: HighlightText;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# tooltip
|
|
2
|
+
|
|
3
|
+
Descripción breve junto a su disparador. Aparece cuando el puntero se detiene sobre él (400 ms), en cuanto el foco del teclado llega (al momento) o con una pulsación larga en pantallas táctiles (500 ms). Toma el lado preferido, cambia al opuesto si no cabe, se mantiene dentro de la ventana y apunta con una flecha al centro del disparador. Vive en la capa superior (API `popover`), así que ningún `overflow` lo recorta.
|
|
4
|
+
|
|
5
|
+
Movimiento (línea Melon Mind): sale de la flecha con un pop (escala .85, 4 px hacia el disparador, blur 4 → 0, .22 s) y se va en .12 s. Si pasas a otro tooltip en menos de .4 s, aparece al instante con un fundido simple («caliente»). El texto nuevo cambia con un fundido.
|
|
6
|
+
|
|
7
|
+
## HTML / Astro
|
|
8
|
+
|
|
9
|
+
```html
|
|
10
|
+
<mk-tooltip class="mk-tooltip" content="Copiar enlace">
|
|
11
|
+
<button class="mk-button mk-button--icon" aria-label="Copiar">⧉</button>
|
|
12
|
+
</mk-tooltip>
|
|
13
|
+
|
|
14
|
+
<!-- Sin content usa el title del disparador (y lo quita, para que no salga el del navegador). -->
|
|
15
|
+
<mk-tooltip placement="right"><button title="Ajustes">⚙</button></mk-tooltip>
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## React
|
|
19
|
+
|
|
20
|
+
```tsx
|
|
21
|
+
<Tooltip content={copied ? 'Copiado' : 'Copiar enlace'}>
|
|
22
|
+
<button onClick={copy}>⧉</button>
|
|
23
|
+
</Tooltip>
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
El primer elemento enfocable dentro es el disparador (o `[data-mk-tooltip-trigger]`). Si no hay ninguno, el propio contenedor recibe `tabindex="0"`.
|
|
27
|
+
|
|
28
|
+
## Opciones
|
|
29
|
+
|
|
30
|
+
| Opción | Atributo | Por defecto | Qué hace |
|
|
31
|
+
|---|---|---|---|
|
|
32
|
+
| `content` | `content` | el `title` del disparador | Texto del tooltip. |
|
|
33
|
+
| `placement` | `placement` | `'top'` | `top`, `bottom`, `left`, `right` o `auto` (el lado con más espacio). |
|
|
34
|
+
| `openDelay` | `open-delay` | `400` | Espera al señalar, en ms. |
|
|
35
|
+
| `closeDelay` | `close-delay` | `100` | Espera al salir, en ms. |
|
|
36
|
+
| `interactive` | `interactive` | `false` | El puntero puede entrar en el tooltip (enlaces, texto seleccionable). |
|
|
37
|
+
| `arrow` | `arrow` | `true` | Flecha hacia el disparador. |
|
|
38
|
+
| `onOpenChange(open)` | — | — | Al abrirse o cerrarse. |
|
|
39
|
+
|
|
40
|
+
## API y eventos
|
|
41
|
+
|
|
42
|
+
`open()`, `close()`, `setContent(texto)` (un texto vacío lo oculta), `isOpen`, `destroy()`. Evento `mk:open-change` con `{ open }`.
|
|
43
|
+
|
|
44
|
+
## Accesibilidad
|
|
45
|
+
|
|
46
|
+
`role="tooltip"` y `aria-describedby` en el disparador. Escape lo oculta sin mover el foco (WCAG 1.4.13). Con movimiento reducido aparece y desaparece sin animación. Variable CSS: `--mk-tooltip-bg` (cristal invertido).
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { MkElement, defineElement, type OptionType } from '../../elements/base';
|
|
2
|
+
import { createTooltip, type TooltipInstance } from './engine';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* <mk-tooltip class="mk-tooltip" content="Copiar enlace"><button>…</button></mk-tooltip>
|
|
6
|
+
* Without `content`, the trigger's title. Emits mk:open-change ({open}).
|
|
7
|
+
*/
|
|
8
|
+
export class MkTooltipElement extends MkElement<TooltipInstance> {
|
|
9
|
+
static options: Record<string, OptionType> = { content: 'string', placement: 'string', openDelay: 'number', closeDelay: 'number', interactive: 'boolean', arrow: 'boolean' };
|
|
10
|
+
|
|
11
|
+
protected create(options: Record<string, unknown>) {
|
|
12
|
+
return createTooltip(this, options);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
attributeChangedCallback(name: string, previous: string | null, next: string | null) {
|
|
16
|
+
if (previous === next || !this.instance) return;
|
|
17
|
+
if (name === 'content') this.instance.setContent(next ?? '');
|
|
18
|
+
else super.attributeChangedCallback(name, previous, next);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export const defineTooltip = () => defineElement('mk-tooltip', MkTooltipElement);
|
|
@@ -0,0 +1,288 @@
|
|
|
1
|
+
import { gsap, EASE, registerMotion } from '../../core/gsap';
|
|
2
|
+
import { requestFrame } from '../../core/frame';
|
|
3
|
+
import { reduced } from '../../core/reduced';
|
|
4
|
+
import { createScope } from '../../core/scope';
|
|
5
|
+
import { inert, type Instance } from '../../core/types';
|
|
6
|
+
import { originOf, place, type Placement } from '../../core/position';
|
|
7
|
+
|
|
8
|
+
/*
|
|
9
|
+
* Tooltip: a short description beside its trigger, shown when the pointer
|
|
10
|
+
* rests on it (400 ms), when keyboard focus reaches it (at once) or on a long
|
|
11
|
+
* press (500 ms, touch). It takes the preferred side, flips when that side has
|
|
12
|
+
* no room, stays inside the viewport and points an arrow at the trigger's
|
|
13
|
+
* centre; it lives in the top layer (popover API) so no overflow clips it.
|
|
14
|
+
* Motion (Melon Mind): it pops from the arrow (scale .85, 4 px toward the
|
|
15
|
+
* trigger, blur 4 → 0, .22 s, pop curve) and fades out in .12 s; moving to
|
|
16
|
+
* another tooltip within .4 s shows it at once with a plain fade (warm), and
|
|
17
|
+
* new text cross-fades. Escape hides it (WCAG 1.4.13); with `interactive` the
|
|
18
|
+
* pointer can move onto it. The trigger is described by it (aria-describedby).
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
export interface TooltipOptions {
|
|
22
|
+
/** Text of the bubble (default: the trigger's title, which is removed so the browser's own does not show). */
|
|
23
|
+
content?: string;
|
|
24
|
+
/** Preferred side (default 'top'); 'auto' takes the roomiest. */
|
|
25
|
+
placement?: Placement;
|
|
26
|
+
/** Hover delay in ms (default 400). Focus shows it at once. */
|
|
27
|
+
openDelay?: number;
|
|
28
|
+
/** Delay before hiding when the pointer leaves, in ms (default 100). */
|
|
29
|
+
closeDelay?: number;
|
|
30
|
+
/** The pointer can move onto the bubble (links, selectable text). */
|
|
31
|
+
interactive?: boolean;
|
|
32
|
+
/** Arrow pointing at the trigger (default true). */
|
|
33
|
+
arrow?: boolean;
|
|
34
|
+
onOpenChange?: (open: boolean) => void;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export interface TooltipInstance extends Instance {
|
|
38
|
+
open(): void;
|
|
39
|
+
close(): void;
|
|
40
|
+
/** New text; an open tooltip cross-fades to it. Empty text hides it. */
|
|
41
|
+
setContent(text: string): void;
|
|
42
|
+
readonly isOpen: boolean;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const FOCUSABLE = 'a[href], button, input, select, textarea, summary, [tabindex]:not([tabindex="-1"]), [contenteditable="true"]';
|
|
46
|
+
/** A tooltip shown within this long after another one hid skips the delay and the pop. */
|
|
47
|
+
const WARM = 400;
|
|
48
|
+
let sequence = 0;
|
|
49
|
+
let current: { hide(instant: boolean): void } | null = null;
|
|
50
|
+
let lastHidden = -Infinity;
|
|
51
|
+
const now = () => (typeof performance !== 'undefined' ? performance.now() : Date.now());
|
|
52
|
+
|
|
53
|
+
export function createTooltip(root: HTMLElement | null, options: TooltipOptions = {}): TooltipInstance {
|
|
54
|
+
if (!root) return inert({ open() {}, close() {}, setContent() {}, isOpen: false });
|
|
55
|
+
registerMotion();
|
|
56
|
+
const scope = createScope();
|
|
57
|
+
const trigger = root.querySelector<HTMLElement>('[data-mk-tooltip-trigger]') ?? (root.matches(FOCUSABLE) ? root : root.querySelector<HTMLElement>(FOCUSABLE)) ?? root;
|
|
58
|
+
const title = trigger.getAttribute('title');
|
|
59
|
+
if (title !== null) scope.attr(trigger, 'title', null);
|
|
60
|
+
let text = options.content ?? title ?? '';
|
|
61
|
+
// Keyboard users must be able to reach what the tooltip describes.
|
|
62
|
+
if (!trigger.matches(FOCUSABLE)) scope.attr(trigger, 'tabindex', '0');
|
|
63
|
+
const openDelay = Number.isFinite(options.openDelay) ? Math.max(0, options.openDelay!) : 400;
|
|
64
|
+
const closeDelay = Number.isFinite(options.closeDelay) ? Math.max(0, options.closeDelay!) : 100;
|
|
65
|
+
const placement = options.placement ?? 'top';
|
|
66
|
+
const withArrow = options.arrow !== false;
|
|
67
|
+
|
|
68
|
+
const bubble = document.createElement('div');
|
|
69
|
+
bubble.dataset.mkTooltipBubble = '';
|
|
70
|
+
bubble.setAttribute('role', 'tooltip');
|
|
71
|
+
bubble.id = `mk-tooltip-${++sequence}`;
|
|
72
|
+
if (options.interactive) bubble.dataset.mkInteractive = '';
|
|
73
|
+
const label = document.createElement('span');
|
|
74
|
+
label.dataset.mkTooltipText = '';
|
|
75
|
+
label.textContent = text;
|
|
76
|
+
bubble.append(label);
|
|
77
|
+
if (withArrow) {
|
|
78
|
+
const arrow = document.createElement('span');
|
|
79
|
+
arrow.dataset.mkTooltipArrow = '';
|
|
80
|
+
arrow.setAttribute('aria-hidden', 'true');
|
|
81
|
+
bubble.append(arrow);
|
|
82
|
+
}
|
|
83
|
+
// The top layer (popover API) escapes every overflow; without it the bubble hangs from <body>.
|
|
84
|
+
const topLayer = typeof bubble.showPopover === 'function';
|
|
85
|
+
if (topLayer) {
|
|
86
|
+
bubble.setAttribute('popover', 'manual');
|
|
87
|
+
root.append(bubble);
|
|
88
|
+
} else {
|
|
89
|
+
bubble.hidden = true;
|
|
90
|
+
document.body.append(bubble);
|
|
91
|
+
}
|
|
92
|
+
scope.add(() => bubble.remove());
|
|
93
|
+
scope.attr(trigger, 'aria-describedby', [trigger.getAttribute('aria-describedby'), bubble.id].filter(Boolean).join(' '));
|
|
94
|
+
scope.attr(root, 'data-mk-open', null);
|
|
95
|
+
scope.attr(root, 'data-mk-ready', '');
|
|
96
|
+
|
|
97
|
+
const effects = new Set<gsap.core.Animation>();
|
|
98
|
+
const keep = (animation: gsap.core.Animation | null) => {
|
|
99
|
+
if (!animation) return;
|
|
100
|
+
effects.add(animation);
|
|
101
|
+
// Finished ones leave as new ones arrive: the animation's own onComplete stays untouched.
|
|
102
|
+
for (const effect of effects) if (effect.totalProgress() === 1) effects.delete(effect);
|
|
103
|
+
};
|
|
104
|
+
let motion: gsap.core.Animation | null = null;
|
|
105
|
+
scope.add(() => {
|
|
106
|
+
motion?.kill();
|
|
107
|
+
for (const effect of effects) effect.kill();
|
|
108
|
+
effects.clear();
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
let isOpen = false;
|
|
112
|
+
let timer: ReturnType<typeof setTimeout> | undefined;
|
|
113
|
+
const cancel = () => {
|
|
114
|
+
clearTimeout(timer);
|
|
115
|
+
timer = undefined;
|
|
116
|
+
};
|
|
117
|
+
const later = (fn: () => void, ms: number) => {
|
|
118
|
+
cancel();
|
|
119
|
+
timer = setTimeout(fn, ms);
|
|
120
|
+
};
|
|
121
|
+
scope.add(cancel);
|
|
122
|
+
|
|
123
|
+
const shown = () => (topLayer ? bubble.matches(':popover-open') : !bubble.hidden);
|
|
124
|
+
const reveal = () => {
|
|
125
|
+
if (shown()) return;
|
|
126
|
+
if (topLayer) bubble.showPopover();
|
|
127
|
+
else bubble.hidden = false;
|
|
128
|
+
};
|
|
129
|
+
const conceal = () => {
|
|
130
|
+
if (!shown()) return;
|
|
131
|
+
if (topLayer) bubble.hidePopover();
|
|
132
|
+
else bubble.hidden = true;
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
const position = () => {
|
|
136
|
+
const anchor = trigger.getBoundingClientRect();
|
|
137
|
+
const box = bubble.getBoundingClientRect();
|
|
138
|
+
const placed = place(anchor, { width: box.width, height: box.height }, { placement, gap: withArrow ? 10 : 6 });
|
|
139
|
+
bubble.style.left = `${placed.x}px`;
|
|
140
|
+
bubble.style.top = `${placed.y}px`;
|
|
141
|
+
bubble.dataset.mkSide = placed.side;
|
|
142
|
+
bubble.style.setProperty('--mk-tooltip-arrow', `${placed.arrow}px`);
|
|
143
|
+
return placed;
|
|
144
|
+
};
|
|
145
|
+
let pending: (() => void) | null = null;
|
|
146
|
+
const follow = () => {
|
|
147
|
+
if (!isOpen || pending) return;
|
|
148
|
+
pending = requestFrame(() => {
|
|
149
|
+
pending = null;
|
|
150
|
+
if (isOpen) position();
|
|
151
|
+
});
|
|
152
|
+
};
|
|
153
|
+
scope.add(() => pending?.());
|
|
154
|
+
|
|
155
|
+
const announce = (open: boolean) => {
|
|
156
|
+
if (open) root.setAttribute('data-mk-open', '');
|
|
157
|
+
else root.removeAttribute('data-mk-open');
|
|
158
|
+
options.onOpenChange?.(open);
|
|
159
|
+
root.dispatchEvent(new CustomEvent('mk:open-change', { bubbles: true, detail: { open } }));
|
|
160
|
+
};
|
|
161
|
+
|
|
162
|
+
const controller = {
|
|
163
|
+
hide: (instant: boolean) => hide(instant),
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
const show = () => {
|
|
167
|
+
cancel();
|
|
168
|
+
if (isOpen || scope.disposed || !text) return;
|
|
169
|
+
const warm = current !== null || now() - lastHidden < WARM;
|
|
170
|
+
if (current && current !== controller) current.hide(true);
|
|
171
|
+
current = controller;
|
|
172
|
+
isOpen = true;
|
|
173
|
+
motion?.kill();
|
|
174
|
+
reveal();
|
|
175
|
+
const placed = position();
|
|
176
|
+
if (!reduced()) {
|
|
177
|
+
if (warm) motion = gsap.fromTo(bubble, { opacity: 0 }, { opacity: 1, duration: 0.1, ease: 'power1.out', clearProps: 'opacity' });
|
|
178
|
+
else {
|
|
179
|
+
const toward = 4;
|
|
180
|
+
const offset = placed.side === 'top' ? { x: 0, y: toward } : placed.side === 'bottom' ? { x: 0, y: -toward } : placed.side === 'left' ? { x: toward, y: 0 } : { x: -toward, y: 0 };
|
|
181
|
+
gsap.set(bubble, { transformOrigin: originOf(placed) });
|
|
182
|
+
motion = gsap.fromTo(
|
|
183
|
+
bubble,
|
|
184
|
+
{ opacity: 0, scale: 0.85, filter: 'blur(4px)', ...offset },
|
|
185
|
+
{ opacity: 1, scale: 1, filter: 'blur(0px)', x: 0, y: 0, duration: 0.22, ease: EASE.pop, clearProps: 'opacity,scale,filter,transform,transformOrigin' },
|
|
186
|
+
);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
announce(true);
|
|
190
|
+
};
|
|
191
|
+
|
|
192
|
+
const hide = (instant = false) => {
|
|
193
|
+
cancel();
|
|
194
|
+
if (!isOpen) return;
|
|
195
|
+
isOpen = false;
|
|
196
|
+
if (current === controller) current = null;
|
|
197
|
+
lastHidden = now();
|
|
198
|
+
motion?.kill();
|
|
199
|
+
const finish = () => {
|
|
200
|
+
conceal();
|
|
201
|
+
gsap.set(bubble, { clearProps: 'opacity,scale,filter,transform,transformOrigin' });
|
|
202
|
+
};
|
|
203
|
+
if (instant || reduced()) finish();
|
|
204
|
+
else motion = gsap.to(bubble, { opacity: 0, scale: 0.96, duration: 0.12, ease: 'power2.in', onComplete: finish });
|
|
205
|
+
announce(false);
|
|
206
|
+
};
|
|
207
|
+
scope.add(() => {
|
|
208
|
+
if (current === controller) current = null;
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
/** The last input: focus after a click is not keyboard focus. */
|
|
212
|
+
let pointerInput = false;
|
|
213
|
+
scope.on(document, 'pointerdown', () => (pointerInput = true), true);
|
|
214
|
+
scope.on(document, 'keydown', () => (pointerInput = false), true);
|
|
215
|
+
|
|
216
|
+
scope.on(trigger, 'pointerenter', (event) => {
|
|
217
|
+
if ((event as PointerEvent).pointerType === 'touch') return;
|
|
218
|
+
later(show, current || now() - lastHidden < WARM ? 0 : openDelay);
|
|
219
|
+
});
|
|
220
|
+
scope.on(trigger, 'pointerleave', (event) => {
|
|
221
|
+
if ((event as PointerEvent).pointerType === 'touch') return;
|
|
222
|
+
later(() => hide(), closeDelay);
|
|
223
|
+
});
|
|
224
|
+
// Touch: a long press shows it for a moment.
|
|
225
|
+
scope.on(trigger, 'pointerdown', (event) => {
|
|
226
|
+
if ((event as PointerEvent).pointerType === 'touch') later(show, 500);
|
|
227
|
+
});
|
|
228
|
+
for (const type of ['pointerup', 'pointercancel']) {
|
|
229
|
+
scope.on(trigger, type, (event) => {
|
|
230
|
+
if ((event as PointerEvent).pointerType !== 'touch') return;
|
|
231
|
+
if (isOpen) later(() => hide(), 1500);
|
|
232
|
+
else cancel();
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
if (options.interactive) {
|
|
236
|
+
scope.on(bubble, 'pointerenter', cancel);
|
|
237
|
+
scope.on(bubble, 'pointerleave', () => later(() => hide(), closeDelay));
|
|
238
|
+
}
|
|
239
|
+
scope.on(trigger, 'focusin', () => {
|
|
240
|
+
if (!pointerInput) show();
|
|
241
|
+
});
|
|
242
|
+
scope.on(trigger, 'focusout', (event) => {
|
|
243
|
+
if (options.interactive && bubble.contains((event as FocusEvent).relatedTarget as Node | null)) return;
|
|
244
|
+
hide();
|
|
245
|
+
});
|
|
246
|
+
scope.on(document, 'keydown', (event) => {
|
|
247
|
+
if ((event as KeyboardEvent).key === 'Escape' && isOpen) hide();
|
|
248
|
+
});
|
|
249
|
+
scope.on(window, 'resize', follow);
|
|
250
|
+
scope.on(window, 'scroll', follow, { capture: true, passive: true });
|
|
251
|
+
scope.add(() => {
|
|
252
|
+
if (isOpen) conceal();
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
return {
|
|
256
|
+
open: show,
|
|
257
|
+
close: () => hide(),
|
|
258
|
+
setContent(next) {
|
|
259
|
+
if (scope.disposed) return;
|
|
260
|
+
text = next;
|
|
261
|
+
if (!text) {
|
|
262
|
+
label.textContent = '';
|
|
263
|
+
hide();
|
|
264
|
+
return;
|
|
265
|
+
}
|
|
266
|
+
if (!isOpen || reduced() || label.textContent === text) {
|
|
267
|
+
label.textContent = text;
|
|
268
|
+
if (isOpen) position();
|
|
269
|
+
return;
|
|
270
|
+
}
|
|
271
|
+
gsap.killTweensOf(label);
|
|
272
|
+
keep(
|
|
273
|
+
gsap
|
|
274
|
+
.timeline()
|
|
275
|
+
.to(label, { opacity: 0, filter: 'blur(3px)', duration: 0.1, ease: 'power1.in' })
|
|
276
|
+
.call(() => {
|
|
277
|
+
label.textContent = text;
|
|
278
|
+
if (isOpen) position();
|
|
279
|
+
})
|
|
280
|
+
.to(label, { opacity: 1, filter: 'blur(0px)', duration: 0.16, ease: 'power1.out', clearProps: 'opacity,filter' }),
|
|
281
|
+
);
|
|
282
|
+
},
|
|
283
|
+
get isOpen() {
|
|
284
|
+
return isOpen;
|
|
285
|
+
},
|
|
286
|
+
destroy: () => scope.dispose(),
|
|
287
|
+
};
|
|
288
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { useRef, type HTMLAttributes } from 'react';
|
|
3
|
+
import { useIsomorphicLayoutEffect, useLatest } from '../../react/use-recipe';
|
|
4
|
+
import { createTooltip, type TooltipInstance, type TooltipOptions } from './engine';
|
|
5
|
+
|
|
6
|
+
export interface TooltipProps extends HTMLAttributes<HTMLSpanElement>, TooltipOptions {}
|
|
7
|
+
|
|
8
|
+
/** <Tooltip content="Copiar enlace"><button>…</button></Tooltip>: the first focusable child is the trigger. */
|
|
9
|
+
export function Tooltip({ content, placement, openDelay, closeDelay, interactive, arrow, onOpenChange, className, children, ...props }: TooltipProps) {
|
|
10
|
+
const ref = useRef<HTMLSpanElement>(null);
|
|
11
|
+
const instance = useRef<TooltipInstance | null>(null);
|
|
12
|
+
const change = useLatest(onOpenChange);
|
|
13
|
+
useIsomorphicLayoutEffect(() => {
|
|
14
|
+
const created = createTooltip(ref.current, { content, placement, openDelay, closeDelay, interactive, arrow, onOpenChange: (open) => change.current?.(open) });
|
|
15
|
+
instance.current = created;
|
|
16
|
+
return () => {
|
|
17
|
+
created.destroy();
|
|
18
|
+
instance.current = null;
|
|
19
|
+
};
|
|
20
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
21
|
+
}, [placement, openDelay, closeDelay, interactive, arrow]);
|
|
22
|
+
useIsomorphicLayoutEffect(() => {
|
|
23
|
+
instance.current?.setContent(content ?? '');
|
|
24
|
+
}, [content]);
|
|
25
|
+
return (
|
|
26
|
+
<span {...props} ref={ref} className={['mk-tooltip', className].filter(Boolean).join(' ')}>
|
|
27
|
+
{children}
|
|
28
|
+
</span>
|
|
29
|
+
);
|
|
30
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/* tooltip — Melon Mind's inverse glass in a compact 8 px bubble: .8125rem text, at most 18rem wide, a 12 × 6 px arrow
|
|
2
|
+
that points at the trigger's centre. It lives in the top layer, so the reset below undoes the popover UA styles. */
|
|
3
|
+
.mk-tooltip,
|
|
4
|
+
mk-tooltip {
|
|
5
|
+
display: inline-flex;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
[data-mk-tooltip-bubble] {
|
|
9
|
+
--mk-tooltip-bg: var(--mk-glass-inverse, rgb(24 24 24 / 0.9));
|
|
10
|
+
position: fixed;
|
|
11
|
+
inset: auto;
|
|
12
|
+
z-index: 2147483000;
|
|
13
|
+
box-sizing: border-box;
|
|
14
|
+
width: max-content;
|
|
15
|
+
max-width: min(18rem, calc(100vw - 16px));
|
|
16
|
+
margin: 0;
|
|
17
|
+
padding: 0.4rem 0.65rem;
|
|
18
|
+
border: 0;
|
|
19
|
+
border-radius: 8px;
|
|
20
|
+
overflow: visible;
|
|
21
|
+
background: var(--mk-tooltip-bg);
|
|
22
|
+
color: var(--mk-on-glass-inverse, #fff);
|
|
23
|
+
box-shadow: 0 8px 24px -10px color-mix(in srgb, var(--mk-shadow-color, #000) 45%, transparent);
|
|
24
|
+
font: 500 0.8125rem/1.35 var(--mk-font, system-ui, sans-serif);
|
|
25
|
+
text-align: start;
|
|
26
|
+
overflow-wrap: anywhere;
|
|
27
|
+
pointer-events: none;
|
|
28
|
+
-webkit-backdrop-filter: blur(12px);
|
|
29
|
+
backdrop-filter: blur(12px);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
[data-mk-tooltip-bubble][data-mk-interactive] {
|
|
33
|
+
pointer-events: auto;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
[data-mk-tooltip-bubble][hidden] {
|
|
37
|
+
display: none;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/* A triangle outside the bubble's edge (no overlap, so the translucent glass is not doubled). */
|
|
41
|
+
[data-mk-tooltip-arrow] {
|
|
42
|
+
position: absolute;
|
|
43
|
+
background: var(--mk-tooltip-bg);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
[data-mk-tooltip-bubble][data-mk-side='top'] > [data-mk-tooltip-arrow],
|
|
47
|
+
[data-mk-tooltip-bubble][data-mk-side='bottom'] > [data-mk-tooltip-arrow] {
|
|
48
|
+
left: calc(var(--mk-tooltip-arrow, 50%) - 6px);
|
|
49
|
+
width: 12px;
|
|
50
|
+
height: 6px;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
[data-mk-tooltip-bubble][data-mk-side='top'] > [data-mk-tooltip-arrow] {
|
|
54
|
+
top: 100%;
|
|
55
|
+
clip-path: polygon(0 0, 100% 0, 50% 100%);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
[data-mk-tooltip-bubble][data-mk-side='bottom'] > [data-mk-tooltip-arrow] {
|
|
59
|
+
bottom: 100%;
|
|
60
|
+
clip-path: polygon(50% 0, 100% 100%, 0 100%);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
[data-mk-tooltip-bubble][data-mk-side='left'] > [data-mk-tooltip-arrow],
|
|
64
|
+
[data-mk-tooltip-bubble][data-mk-side='right'] > [data-mk-tooltip-arrow] {
|
|
65
|
+
top: calc(var(--mk-tooltip-arrow, 50%) - 6px);
|
|
66
|
+
width: 6px;
|
|
67
|
+
height: 12px;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
[data-mk-tooltip-bubble][data-mk-side='left'] > [data-mk-tooltip-arrow] {
|
|
71
|
+
left: 100%;
|
|
72
|
+
clip-path: polygon(0 0, 100% 50%, 0 100%);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
[data-mk-tooltip-bubble][data-mk-side='right'] > [data-mk-tooltip-arrow] {
|
|
76
|
+
right: 100%;
|
|
77
|
+
clip-path: polygon(100% 0, 100% 100%, 0 50%);
|
|
78
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# tree-view
|
|
2
|
+
|
|
3
|
+
Árbol jerárquico con nodos expandibles, checkboxes y búsqueda. Una rama se abre por su altura (.32 s, curva signature) mientras sus hijos entran deslizándose desde un blur, 25 ms entre uno y otro, y su flecha gira; al cerrarse hace lo mismo al revés. La fila seleccionada va sobre una píldora que se desliza hasta cada nueva elección (.3 s, curva swing).
|
|
4
|
+
|
|
5
|
+
Con `checkable`, las casillas tienen tres estados: la palomita se dibuja sola y la casilla hace pop; una rama con solo algunos hijos marcados muestra un guion. Marcar una rama marca todo lo que tiene dentro. La búsqueda resalta las letras que coinciden (sin importar tildes ni mayúsculas), abre las ramas que las contienen y oculta lo demás; al borrarla vuelve lo que estaba abierto. `loadChildren()` llena una rama la primera vez que se abre (mientras tanto, una fila «Cargando…»).
|
|
6
|
+
|
|
7
|
+
## HTML / Astro
|
|
8
|
+
|
|
9
|
+
```html
|
|
10
|
+
<mk-tree-view class="mk-tree-view" checkable search="Filtrar carpetas" expanded="docs"
|
|
11
|
+
nodes='[{"id":"docs","label":"Documentos","icon":"📁","children":[
|
|
12
|
+
{"id":"cv","label":"Currículum.pdf","icon":"📄"},
|
|
13
|
+
{"id":"facturas","label":"Facturas","icon":"📁","children":[{"id":"f1","label":"Enero"},{"id":"f2","label":"Febrero"}]}]},
|
|
14
|
+
{"id":"fotos","label":"Fotos","icon":"🖼️","children":[{"id":"playa","label":"Playa"}]}]'></mk-tree-view>
|
|
15
|
+
|
|
16
|
+
<script>
|
|
17
|
+
const tree = document.querySelector('mk-tree-view');
|
|
18
|
+
tree.addEventListener('mk:check', (event) => console.log(event.detail.checked));
|
|
19
|
+
tree.addEventListener('mk:select', (event) => console.log(event.detail.node));
|
|
20
|
+
</script>
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
`expanded="all"` abre todas las ramas; `search` sin texto activa la búsqueda con su texto por defecto.
|
|
24
|
+
|
|
25
|
+
## React
|
|
26
|
+
|
|
27
|
+
```tsx
|
|
28
|
+
const tree = useRef<TreeViewHandle>(null);
|
|
29
|
+
|
|
30
|
+
<TreeView ref={tree} nodes={carpetas} checkable checked={ids} onCheck={setIds} search expanded={['docs']} />;
|
|
31
|
+
|
|
32
|
+
tree.current?.expandAll();
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Opciones
|
|
36
|
+
|
|
37
|
+
| Opción | Atributo | Por defecto | Qué hace |
|
|
38
|
+
|---|---|---|---|
|
|
39
|
+
| `nodes` | `nodes` (JSON) | `[]` | `{ id, label, children?, icon?, disabled?, hasChildren?, data? }[]`. |
|
|
40
|
+
| `checkable` | `checkable` | `false` | Casillas de tres estados. |
|
|
41
|
+
| `checked` | `checked` (lista con comas) | `[]` | Ids marcados (una rama marca lo que tiene dentro). |
|
|
42
|
+
| `expanded` | `expanded` (lista o `all`) | `[]` | Ramas abiertas al empezar; `true` abre todas. |
|
|
43
|
+
| `selected` | `selected` | — | Id seleccionado (sin casillas). |
|
|
44
|
+
| `search` | `search` | `false` | Campo de búsqueda; un texto es su marcador. |
|
|
45
|
+
| `label` | `label` | `'Árbol'` | Nombre accesible. |
|
|
46
|
+
| `loadChildren(node)` | — | — | Hijos de un nodo con `hasChildren`, la primera vez que se abre. |
|
|
47
|
+
| `renderLabel(node, element, query)` | — | — | Dibujar la etiqueta a mano (lo usan `json-viewer` y `file-explorer`). |
|
|
48
|
+
| `searchText(node)` | — | la etiqueta | El texto donde busca la búsqueda. |
|
|
49
|
+
| `onSelect(node)`, `onCheck(ids)`, `onExpand(node, abierto)` | — | — | Cada cambio. |
|
|
50
|
+
|
|
51
|
+
## API y eventos
|
|
52
|
+
|
|
53
|
+
`expand(id)`, `collapse(id)`, `toggle(id)`, `expandAll()`, `collapseAll()`, `reveal(id)` (abre lo que tiene encima y lo muestra), `select(id, notify = false)`, `setChecked(ids)`, `filter(texto)`, `setNodes(nodes)`, `focusItem(id)`, `selected`, `checked`, `expanded`, `element`, `destroy()`. Eventos `mk:select` con `{ id, node }`, `mk:check` con `{ checked }` y `mk:expand` con `{ id, expanded }`.
|
|
54
|
+
|
|
55
|
+
## Teclado
|
|
56
|
+
|
|
57
|
+
↑ ↓ mueven, → abre o entra, ← cierra o sube, Inicio y Fin van a los extremos, `*` abre las ramas hermanas, Intro selecciona, Espacio marca (o selecciona) y escribir salta a la etiqueta que empieza así. Desde la búsqueda, ↓ entra al árbol.
|
|
58
|
+
|
|
59
|
+
## Accesibilidad
|
|
60
|
+
|
|
61
|
+
Patrón de árbol de WAI-ARIA: `role="tree"` con `treeitem` que llevan `aria-level`, `aria-setsize`, `aria-posinset`, `aria-expanded`, `aria-selected` o `aria-checked` (`mixed` para el guion), y un solo elemento tabulable (tabindex móvil). Un estado discreto anuncia las coincidencias y cada casilla marcada. Con movimiento reducido las ramas se abren y la píldora salta sin animación.
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { MkElement, defineElement, type OptionType } from '../../elements/base';
|
|
2
|
+
import { createTreeView, type TreeNode, type TreeViewInstance } from './engine';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* <mk-tree-view class="mk-tree-view" checkable search
|
|
6
|
+
* nodes='[{"id":"a","label":"Proyectos","children":[{"id":"a1","label":"Melon"}]}]'></mk-tree-view>
|
|
7
|
+
* Emits mk:select ({id, node}), mk:check ({checked}), mk:expand ({id, expanded}).
|
|
8
|
+
*/
|
|
9
|
+
export class MkTreeViewElement extends MkElement<TreeViewInstance> {
|
|
10
|
+
static options: Record<string, OptionType> = { nodes: 'json', checkable: 'boolean', checked: 'list', expanded: 'list', selected: 'string', search: 'string', label: 'string', icons: 'boolean', draggable: 'boolean' };
|
|
11
|
+
|
|
12
|
+
protected create(options: Record<string, unknown>) {
|
|
13
|
+
// search="" (present without text) turns the field on with its default placeholder.
|
|
14
|
+
const search = this.hasAttribute('search') ? (this.getAttribute('search') || true) : undefined;
|
|
15
|
+
const expanded = this.getAttribute('expanded') === 'all' ? true : options.expanded;
|
|
16
|
+
return createTreeView(this, { ...options, search, expanded } as Parameters<typeof createTreeView>[1]);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
attributeChangedCallback(name: string, previous: string | null, next: string | null) {
|
|
20
|
+
if (previous === next || !this.instance) return;
|
|
21
|
+
if (name === 'nodes') {
|
|
22
|
+
try {
|
|
23
|
+
this.instance.setNodes(JSON.parse(next ?? '[]') as TreeNode[]);
|
|
24
|
+
} catch {
|
|
25
|
+
// Malformed JSON: keep the current nodes.
|
|
26
|
+
}
|
|
27
|
+
} else if (name === 'selected') this.instance.select(next);
|
|
28
|
+
else super.attributeChangedCallback(name, previous, next);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export const defineTreeView = () => defineElement('mk-tree-view', MkTreeViewElement);
|