@yewdiel21/motion-kit 0.9.1 → 0.14.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.
Files changed (458) hide show
  1. package/README.md +72 -0
  2. package/dist/auto/index.d.ts +72 -3
  3. package/dist/auto/index.js +139 -3
  4. package/dist/auto/index.js.map +1 -1
  5. package/dist/{chunk-BFC27F2G.js → chunk-6P4P7NU6.js} +2 -218
  6. package/dist/chunk-6P4P7NU6.js.map +1 -0
  7. package/dist/chunk-CU6SXW3E.js +20334 -0
  8. package/dist/chunk-CU6SXW3E.js.map +1 -0
  9. package/dist/{chunk-XDWBVFHQ.js → chunk-EY4BJKC4.js} +1203 -29
  10. package/dist/chunk-EY4BJKC4.js.map +1 -0
  11. package/dist/chunk-QP4QJOEN.js +72 -0
  12. package/dist/chunk-QP4QJOEN.js.map +1 -0
  13. package/dist/css/components/activity-feed.css +240 -0
  14. package/dist/css/components/attachment.css +266 -0
  15. package/dist/css/components/breadcrumb.css +110 -0
  16. package/dist/css/components/calendar.css +159 -0
  17. package/dist/css/components/chart-tooltip.css +168 -0
  18. package/dist/css/components/chart.css +296 -0
  19. package/dist/css/components/code-block.css +264 -0
  20. package/dist/css/components/color-area.css +82 -0
  21. package/dist/css/components/color-field.css +67 -0
  22. package/dist/css/components/color-picker.css +131 -0
  23. package/dist/css/components/color-slider.css +113 -0
  24. package/dist/css/components/command-search.css +252 -0
  25. package/dist/css/components/context-menu.css +23 -0
  26. package/dist/css/components/credit-card.css +288 -0
  27. package/dist/css/components/data-table.css +591 -0
  28. package/dist/css/components/date-range-picker.css +168 -0
  29. package/dist/css/components/date-time-picker.css +178 -0
  30. package/dist/css/components/drawer.css +58 -0
  31. package/dist/css/components/editable-table.css +201 -0
  32. package/dist/css/components/empty-state.css +61 -0
  33. package/dist/css/components/file-explorer.css +633 -0
  34. package/dist/css/components/filter-builder.css +232 -0
  35. package/dist/css/components/hold-confirm.css +87 -0
  36. package/dist/css/components/hover-card.css +20 -0
  37. package/dist/css/components/image-preview.css +195 -0
  38. package/dist/css/components/input-otp.css +10 -0
  39. package/dist/css/components/json-viewer.css +272 -0
  40. package/dist/css/components/like-button.css +78 -0
  41. package/dist/css/components/link-preview.css +275 -0
  42. package/dist/css/components/loading-button.css +77 -0
  43. package/dist/css/components/multi-select.css +267 -0
  44. package/dist/css/components/notification-bell.css +127 -0
  45. package/dist/css/components/pagination.css +11 -0
  46. package/dist/css/components/password-strength.css +183 -0
  47. package/dist/css/components/popover.css +25 -0
  48. package/dist/css/components/progress.css +140 -0
  49. package/dist/css/components/quantity.css +16 -0
  50. package/dist/css/components/questionnaire.css +217 -0
  51. package/dist/css/components/range-calendar.css +186 -0
  52. package/dist/css/components/range-slider.css +192 -0
  53. package/dist/css/components/resizable-panel.css +243 -0
  54. package/dist/css/components/rich-text-editor.css +284 -0
  55. package/dist/css/components/scratch-card.css +75 -0
  56. package/dist/css/components/skeleton.css +108 -0
  57. package/dist/css/components/slot-picker.css +126 -0
  58. package/dist/css/components/split-view.css +19 -0
  59. package/dist/css/components/star-rating.css +80 -0
  60. package/dist/css/components/stat-card.css +164 -0
  61. package/dist/css/components/studio-dock.css +35 -0
  62. package/dist/css/components/swipe-actions.css +105 -0
  63. package/dist/css/components/tag-input.css +244 -0
  64. package/dist/css/components/time-field.css +88 -0
  65. package/dist/css/components/timeline.css +219 -0
  66. package/dist/css/components/tooltip.css +79 -0
  67. package/dist/css/components/trailer.css +37 -0
  68. package/dist/css/components/tree-view.css +210 -0
  69. package/dist/css/components/virtual-list.css +195 -0
  70. package/dist/css/controls.css +65 -0
  71. package/dist/css/tokens.css +185 -181
  72. package/dist/elements/index.d.ts +742 -5
  73. package/dist/elements/index.js +1527 -8
  74. package/dist/elements/index.js.map +1 -1
  75. package/dist/{engine-GDGwQnJE.d.ts → engine-Cn6IhE3z.d.ts} +1 -1
  76. package/dist/engine-QECQnGz1.d.ts +33 -0
  77. package/dist/index.css +10113 -181
  78. package/dist/index.d.ts +89 -5
  79. package/dist/index.js +285 -9
  80. package/dist/index.js.map +1 -1
  81. package/dist/react/index.d.ts +678 -23
  82. package/dist/react/index.js +2389 -7
  83. package/dist/react/index.js.map +1 -1
  84. package/dist/{reduced-B98f8LPs.d.ts → reduced-BBUusld6.d.ts} +206 -2
  85. package/dist/reveal-DEvp6U6T.d.ts +2381 -0
  86. package/dist/{store-pHiUvxP8.d.ts → store-DjkTBX7f.d.ts} +2 -2
  87. package/package.json +1 -1
  88. package/registry.json +583 -104
  89. package/skills/motion-kit/SKILL.md +209 -189
  90. package/src/auto/auto-init.ts +136 -0
  91. package/src/components/activity-feed/README.md +56 -0
  92. package/src/components/activity-feed/activity-feed.css +239 -0
  93. package/src/components/activity-feed/element.ts +36 -0
  94. package/src/components/activity-feed/engine.ts +351 -0
  95. package/src/components/activity-feed/react.tsx +32 -0
  96. package/src/components/attachment/README.md +27 -0
  97. package/src/components/attachment/attachment.css +265 -0
  98. package/src/components/attachment/element.ts +50 -0
  99. package/src/components/attachment/engine.ts +469 -0
  100. package/src/components/attachment/react.tsx +65 -0
  101. package/src/components/breadcrumb/README.md +38 -0
  102. package/src/components/breadcrumb/breadcrumb.css +109 -0
  103. package/src/components/breadcrumb/element.ts +27 -0
  104. package/src/components/breadcrumb/engine.ts +249 -0
  105. package/src/components/breadcrumb/react.tsx +30 -0
  106. package/src/components/calendar/README.md +82 -0
  107. package/src/components/calendar/calendar.css +158 -0
  108. package/src/components/calendar/element.ts +27 -0
  109. package/src/components/calendar/engine.ts +412 -0
  110. package/src/components/calendar/react.tsx +69 -0
  111. package/src/components/chart/README.md +24 -0
  112. package/src/components/chart/chart.css +295 -0
  113. package/src/components/chart/element.ts +41 -0
  114. package/src/components/chart/engine.ts +811 -0
  115. package/src/components/chart/react.tsx +50 -0
  116. package/src/components/chart-tooltip/README.md +53 -0
  117. package/src/components/chart-tooltip/chart-tooltip.css +167 -0
  118. package/src/components/chart-tooltip/element.ts +37 -0
  119. package/src/components/chart-tooltip/engine.ts +442 -0
  120. package/src/components/chart-tooltip/react.tsx +47 -0
  121. package/src/components/code-block/README.md +19 -0
  122. package/src/components/code-block/code-block.css +263 -0
  123. package/src/components/code-block/element.ts +34 -0
  124. package/src/components/code-block/engine.ts +254 -0
  125. package/src/components/code-block/highlight.ts +244 -0
  126. package/src/components/code-block/react.tsx +94 -0
  127. package/src/components/color-area/README.md +50 -0
  128. package/src/components/color-area/color-area.css +81 -0
  129. package/src/components/color-area/element.ts +26 -0
  130. package/src/components/color-area/engine.ts +246 -0
  131. package/src/components/color-area/react.tsx +42 -0
  132. package/src/components/color-field/README.md +43 -0
  133. package/src/components/color-field/color-field.css +66 -0
  134. package/src/components/color-field/element.ts +26 -0
  135. package/src/components/color-field/engine.ts +174 -0
  136. package/src/components/color-field/react.tsx +42 -0
  137. package/src/components/color-picker/README.md +45 -0
  138. package/src/components/color-picker/color-picker.css +130 -0
  139. package/src/components/color-picker/element.ts +45 -0
  140. package/src/components/color-picker/engine.ts +314 -0
  141. package/src/components/color-picker/react.tsx +47 -0
  142. package/src/components/color-slider/README.md +55 -0
  143. package/src/components/color-slider/color-slider.css +112 -0
  144. package/src/components/color-slider/element.ts +26 -0
  145. package/src/components/color-slider/engine.ts +234 -0
  146. package/src/components/color-slider/react.tsx +45 -0
  147. package/src/components/command-search/README.md +15 -0
  148. package/src/components/command-search/command-search.css +251 -0
  149. package/src/components/command-search/element.ts +36 -0
  150. package/src/components/command-search/engine.ts +356 -0
  151. package/src/components/command-search/match.ts +53 -0
  152. package/src/components/command-search/react.tsx +66 -0
  153. package/src/components/context-menu/README.md +31 -0
  154. package/src/components/context-menu/context-menu.css +22 -0
  155. package/src/components/context-menu/element.ts +40 -0
  156. package/src/components/context-menu/engine.ts +153 -0
  157. package/src/components/context-menu/react.tsx +57 -0
  158. package/src/components/credit-card/README.md +20 -0
  159. package/src/components/credit-card/card.ts +77 -0
  160. package/src/components/credit-card/credit-card.css +287 -0
  161. package/src/components/credit-card/element.ts +33 -0
  162. package/src/components/credit-card/engine.ts +410 -0
  163. package/src/components/credit-card/react.tsx +36 -0
  164. package/src/components/data-table/README.md +83 -0
  165. package/src/components/data-table/data-table.css +590 -0
  166. package/src/components/data-table/element.ts +54 -0
  167. package/src/components/data-table/engine.ts +1059 -0
  168. package/src/components/data-table/react.tsx +94 -0
  169. package/src/components/date-range-picker/README.md +51 -0
  170. package/src/components/date-range-picker/date-range-picker.css +167 -0
  171. package/src/components/date-range-picker/element.ts +52 -0
  172. package/src/components/date-range-picker/engine.ts +352 -0
  173. package/src/components/date-range-picker/react.tsx +57 -0
  174. package/src/components/date-time-picker/README.md +48 -0
  175. package/src/components/date-time-picker/date-time-picker.css +177 -0
  176. package/src/components/date-time-picker/element.ts +47 -0
  177. package/src/components/date-time-picker/engine.ts +350 -0
  178. package/src/components/date-time-picker/react.tsx +54 -0
  179. package/src/components/dialog/README.md +103 -102
  180. package/src/components/dialog/element.ts +123 -118
  181. package/src/components/dialog/engine.ts +449 -432
  182. package/src/components/drawer/README.md +28 -0
  183. package/src/components/drawer/drawer.css +57 -0
  184. package/src/components/drawer/element.ts +21 -0
  185. package/src/components/drawer/engine.ts +56 -0
  186. package/src/components/drawer/react.tsx +85 -0
  187. package/src/components/editable-table/README.md +67 -0
  188. package/src/components/editable-table/editable-table.css +200 -0
  189. package/src/components/editable-table/element.ts +45 -0
  190. package/src/components/editable-table/engine.ts +775 -0
  191. package/src/components/editable-table/react.tsx +79 -0
  192. package/src/components/empty-state/README.md +41 -0
  193. package/src/components/empty-state/element.ts +23 -0
  194. package/src/components/empty-state/empty-state.css +60 -0
  195. package/src/components/empty-state/engine.ts +232 -0
  196. package/src/components/empty-state/react.tsx +34 -0
  197. package/src/components/file-explorer/README.md +67 -0
  198. package/src/components/file-explorer/element.ts +39 -0
  199. package/src/components/file-explorer/engine.ts +1307 -0
  200. package/src/components/file-explorer/file-explorer.css +632 -0
  201. package/src/components/file-explorer/react.tsx +77 -0
  202. package/src/components/filter-builder/README.md +47 -0
  203. package/src/components/filter-builder/element.ts +34 -0
  204. package/src/components/filter-builder/engine.ts +365 -0
  205. package/src/components/filter-builder/filter-builder.css +231 -0
  206. package/src/components/filter-builder/react.tsx +36 -0
  207. package/src/components/hold-confirm/README.md +13 -0
  208. package/src/components/hold-confirm/element.ts +24 -0
  209. package/src/components/hold-confirm/engine.ts +216 -0
  210. package/src/components/hold-confirm/hold-confirm.css +86 -0
  211. package/src/components/hold-confirm/react.tsx +28 -0
  212. package/src/components/hover-card/README.md +20 -0
  213. package/src/components/hover-card/element.ts +8 -0
  214. package/src/components/hover-card/engine.ts +131 -0
  215. package/src/components/hover-card/hover-card.css +19 -0
  216. package/src/components/hover-card/react.tsx +19 -0
  217. package/src/components/image-preview/README.md +16 -0
  218. package/src/components/image-preview/element.ts +26 -0
  219. package/src/components/image-preview/engine.ts +325 -0
  220. package/src/components/image-preview/image-preview.css +194 -0
  221. package/src/components/image-preview/react.tsx +43 -0
  222. package/src/components/input-otp/README.md +18 -0
  223. package/src/components/input-otp/element.ts +14 -0
  224. package/src/components/input-otp/engine.ts +106 -0
  225. package/src/components/input-otp/input-otp.css +9 -0
  226. package/src/components/input-otp/react.tsx +21 -0
  227. package/src/components/json-viewer/README.md +56 -0
  228. package/src/components/json-viewer/element.ts +37 -0
  229. package/src/components/json-viewer/engine.ts +440 -0
  230. package/src/components/json-viewer/json-viewer.css +271 -0
  231. package/src/components/json-viewer/react.tsx +56 -0
  232. package/src/components/like-button/README.md +13 -0
  233. package/src/components/like-button/element.ts +47 -0
  234. package/src/components/like-button/engine.ts +145 -0
  235. package/src/components/like-button/like-button.css +77 -0
  236. package/src/components/like-button/react.tsx +39 -0
  237. package/src/components/link-preview/README.md +19 -0
  238. package/src/components/link-preview/element.ts +29 -0
  239. package/src/components/link-preview/engine.ts +192 -0
  240. package/src/components/link-preview/link-preview.css +274 -0
  241. package/src/components/link-preview/react.tsx +101 -0
  242. package/src/components/loading-button/README.md +45 -0
  243. package/src/components/loading-button/element.ts +34 -0
  244. package/src/components/loading-button/engine.ts +201 -0
  245. package/src/components/loading-button/loading-button.css +76 -0
  246. package/src/components/loading-button/react.tsx +43 -0
  247. package/src/components/multi-select/README.md +48 -0
  248. package/src/components/multi-select/element.ts +28 -0
  249. package/src/components/multi-select/engine.ts +294 -0
  250. package/src/components/multi-select/multi-select.css +266 -0
  251. package/src/components/multi-select/react.tsx +39 -0
  252. package/src/components/notification-bell/README.md +14 -0
  253. package/src/components/notification-bell/element.ts +48 -0
  254. package/src/components/notification-bell/engine.ts +202 -0
  255. package/src/components/notification-bell/notification-bell.css +126 -0
  256. package/src/components/notification-bell/react.tsx +57 -0
  257. package/src/components/pagination/README.md +17 -0
  258. package/src/components/pagination/element.ts +14 -0
  259. package/src/components/pagination/engine.ts +102 -0
  260. package/src/components/pagination/pagination.css +10 -0
  261. package/src/components/pagination/react.tsx +22 -0
  262. package/src/components/password-strength/README.md +17 -0
  263. package/src/components/password-strength/element.ts +22 -0
  264. package/src/components/password-strength/engine.ts +212 -0
  265. package/src/components/password-strength/password-strength.css +182 -0
  266. package/src/components/password-strength/react.tsx +36 -0
  267. package/src/components/password-strength/score.ts +62 -0
  268. package/src/components/popover/README.md +44 -0
  269. package/src/components/popover/element.ts +31 -0
  270. package/src/components/popover/engine.ts +114 -0
  271. package/src/components/popover/popover.css +24 -0
  272. package/src/components/popover/react.tsx +50 -0
  273. package/src/components/progress/README.md +17 -0
  274. package/src/components/progress/element.ts +30 -0
  275. package/src/components/progress/engine.ts +173 -0
  276. package/src/components/progress/progress.css +139 -0
  277. package/src/components/progress/react.tsx +35 -0
  278. package/src/components/quantity/README.md +20 -0
  279. package/src/components/quantity/element.ts +14 -0
  280. package/src/components/quantity/engine.ts +85 -0
  281. package/src/components/quantity/quantity.css +15 -0
  282. package/src/components/quantity/react.tsx +21 -0
  283. package/src/components/questionnaire/README.md +32 -0
  284. package/src/components/questionnaire/element.ts +118 -0
  285. package/src/components/questionnaire/engine.ts +255 -0
  286. package/src/components/questionnaire/questionnaire.css +216 -0
  287. package/src/components/questionnaire/react.tsx +140 -0
  288. package/src/components/range-calendar/README.md +58 -0
  289. package/src/components/range-calendar/element.ts +42 -0
  290. package/src/components/range-calendar/engine.ts +390 -0
  291. package/src/components/range-calendar/range-calendar.css +185 -0
  292. package/src/components/range-calendar/react.tsx +56 -0
  293. package/src/components/range-slider/README.md +23 -0
  294. package/src/components/range-slider/element.ts +53 -0
  295. package/src/components/range-slider/engine.ts +316 -0
  296. package/src/components/range-slider/range-slider.css +191 -0
  297. package/src/components/range-slider/react.tsx +66 -0
  298. package/src/components/resizable-panel/README.md +61 -0
  299. package/src/components/resizable-panel/element.ts +36 -0
  300. package/src/components/resizable-panel/engine.ts +551 -0
  301. package/src/components/resizable-panel/react.tsx +74 -0
  302. package/src/components/resizable-panel/resizable-panel.css +242 -0
  303. package/src/components/rich-text-editor/README.md +57 -0
  304. package/src/components/rich-text-editor/element.ts +34 -0
  305. package/src/components/rich-text-editor/engine.ts +673 -0
  306. package/src/components/rich-text-editor/react.tsx +60 -0
  307. package/src/components/rich-text-editor/rich-text-editor.css +283 -0
  308. package/src/components/scratch-card/README.md +15 -0
  309. package/src/components/scratch-card/element.ts +30 -0
  310. package/src/components/scratch-card/engine.ts +257 -0
  311. package/src/components/scratch-card/react.tsx +40 -0
  312. package/src/components/scratch-card/scratch-card.css +74 -0
  313. package/src/components/skeleton/README.md +15 -0
  314. package/src/components/skeleton/element.ts +32 -0
  315. package/src/components/skeleton/engine.ts +104 -0
  316. package/src/components/skeleton/react.tsx +41 -0
  317. package/src/components/skeleton/skeleton.css +107 -0
  318. package/src/components/slot-picker/README.md +68 -0
  319. package/src/components/slot-picker/element.ts +27 -0
  320. package/src/components/slot-picker/engine.ts +412 -0
  321. package/src/components/slot-picker/react.tsx +76 -0
  322. package/src/components/slot-picker/slot-picker.css +125 -0
  323. package/src/components/split-view/README.md +50 -0
  324. package/src/components/split-view/element.ts +34 -0
  325. package/src/components/split-view/engine.ts +114 -0
  326. package/src/components/split-view/react.tsx +63 -0
  327. package/src/components/split-view/split-view.css +18 -0
  328. package/src/components/star-rating/README.md +14 -0
  329. package/src/components/star-rating/element.ts +26 -0
  330. package/src/components/star-rating/engine.ts +217 -0
  331. package/src/components/star-rating/react.tsx +33 -0
  332. package/src/components/star-rating/star-rating.css +79 -0
  333. package/src/components/stat-card/README.md +42 -0
  334. package/src/components/stat-card/element.ts +40 -0
  335. package/src/components/stat-card/engine.ts +283 -0
  336. package/src/components/stat-card/react.tsx +36 -0
  337. package/src/components/stat-card/stat-card.css +163 -0
  338. package/src/components/studio-dock/README.md +44 -0
  339. package/src/components/studio-dock/element.ts +11 -0
  340. package/src/components/studio-dock/engine.ts +212 -0
  341. package/src/components/studio-dock/react.tsx +23 -0
  342. package/src/components/studio-dock/studio-dock.css +34 -0
  343. package/src/components/swipe-actions/README.md +19 -0
  344. package/src/components/swipe-actions/element.ts +26 -0
  345. package/src/components/swipe-actions/engine.ts +260 -0
  346. package/src/components/swipe-actions/react.tsx +80 -0
  347. package/src/components/swipe-actions/swipe-actions.css +104 -0
  348. package/src/components/tag-input/README.md +42 -0
  349. package/src/components/tag-input/element.ts +39 -0
  350. package/src/components/tag-input/engine.ts +313 -0
  351. package/src/components/tag-input/react.tsx +54 -0
  352. package/src/components/tag-input/tag-input.css +243 -0
  353. package/src/components/time-field/README.md +54 -0
  354. package/src/components/time-field/element.ts +38 -0
  355. package/src/components/time-field/engine.ts +110 -0
  356. package/src/components/time-field/react.tsx +45 -0
  357. package/src/components/time-field/time-field.css +87 -0
  358. package/src/components/timeline/README.md +57 -0
  359. package/src/components/timeline/element.ts +31 -0
  360. package/src/components/timeline/engine.ts +300 -0
  361. package/src/components/timeline/react.tsx +59 -0
  362. package/src/components/timeline/timeline.css +218 -0
  363. package/src/components/tooltip/README.md +46 -0
  364. package/src/components/tooltip/element.ts +22 -0
  365. package/src/components/tooltip/engine.ts +288 -0
  366. package/src/components/tooltip/react.tsx +30 -0
  367. package/src/components/tooltip/tooltip.css +78 -0
  368. package/src/components/trailer/README.md +28 -0
  369. package/src/components/trailer/element.ts +13 -0
  370. package/src/components/trailer/engine.ts +189 -0
  371. package/src/components/trailer/react.tsx +16 -0
  372. package/src/components/trailer/trailer.css +36 -0
  373. package/src/components/tree-view/README.md +61 -0
  374. package/src/components/tree-view/element.ts +32 -0
  375. package/src/components/tree-view/engine.ts +569 -0
  376. package/src/components/tree-view/react.tsx +78 -0
  377. package/src/components/tree-view/tree-view.css +209 -0
  378. package/src/components/virtual-list/README.md +81 -0
  379. package/src/components/virtual-list/element.ts +42 -0
  380. package/src/components/virtual-list/engine.ts +678 -0
  381. package/src/components/virtual-list/react.tsx +101 -0
  382. package/src/components/virtual-list/virtual-list.css +194 -0
  383. package/src/core/chips.ts +196 -0
  384. package/src/core/color.ts +304 -0
  385. package/src/core/date-range.ts +67 -0
  386. package/src/core/dates.ts +108 -0
  387. package/src/core/files.ts +107 -0
  388. package/src/core/filter.ts +229 -0
  389. package/src/core/gsap.ts +80 -65
  390. package/src/core/listbox.ts +332 -0
  391. package/src/core/mark.ts +29 -0
  392. package/src/core/position.ts +89 -0
  393. package/src/core/relative-time.ts +77 -0
  394. package/src/core/segment-field.ts +373 -0
  395. package/src/core/segments.ts +153 -0
  396. package/src/core/tree.ts +116 -0
  397. package/src/core/virtual.ts +79 -0
  398. package/src/css/controls/README.md +14 -0
  399. package/src/css/controls/status.css +63 -0
  400. package/src/css/tokens.css +186 -182
  401. package/src/elements/index.ts +171 -0
  402. package/src/index.ts +97 -0
  403. package/src/motion/micro.ts +248 -0
  404. package/src/motion/sheet.ts +88 -65
  405. package/src/motion/surface.ts +9 -0
  406. package/src/react/index.ts +78 -0
  407. package/src/react/use-action-button.ts +6 -0
  408. package/src/react/use-editorial-reveal.ts +7 -0
  409. package/src/react/use-film-hero.ts +6 -0
  410. package/src/react/use-font-cycle.ts +7 -0
  411. package/src/react/use-image-zoom.ts +5 -0
  412. package/src/react/use-joined-cta.ts +5 -0
  413. package/src/react/use-local-clock.ts +5 -0
  414. package/src/react/use-scroll-dock.ts +5 -0
  415. package/src/react/use-sticker.ts +5 -0
  416. package/src/react/use-studio-dock.ts +8 -0
  417. package/src/react/use-thumbnail-nav.ts +7 -0
  418. package/src/react/use-trailer.ts +6 -0
  419. package/src/react/use-weight-hover.ts +5 -0
  420. package/src/recipes/action-button/README.md +31 -0
  421. package/src/recipes/action-button/action-button.css +113 -0
  422. package/src/recipes/action-button/create-action-button.ts +56 -0
  423. package/src/recipes/editorial-reveal/README.md +35 -0
  424. package/src/recipes/editorial-reveal/create-editorial-reveal.ts +91 -0
  425. package/src/recipes/editorial-reveal/editorial-reveal.css +17 -0
  426. package/src/recipes/film-hero/README.md +23 -0
  427. package/src/recipes/film-hero/create-film-hero.ts +65 -0
  428. package/src/recipes/film-hero/film-hero.css +29 -0
  429. package/src/recipes/font-cycle/README.md +11 -0
  430. package/src/recipes/font-cycle/create-font-cycle.ts +126 -0
  431. package/src/recipes/font-cycle/font-cycle.css +36 -0
  432. package/src/recipes/image-zoom/README.md +16 -0
  433. package/src/recipes/image-zoom/create-image-zoom.ts +27 -0
  434. package/src/recipes/image-zoom/image-zoom.css +7 -0
  435. package/src/recipes/joined-cta/README.md +13 -0
  436. package/src/recipes/joined-cta/create-joined-cta.ts +22 -0
  437. package/src/recipes/joined-cta/joined-cta.css +17 -0
  438. package/src/recipes/local-clock/README.md +31 -0
  439. package/src/recipes/local-clock/create-local-clock.ts +61 -0
  440. package/src/recipes/scroll-dock/README.md +46 -0
  441. package/src/recipes/scroll-dock/create-scroll-dock.ts +72 -0
  442. package/src/recipes/scroll-dock/scroll-dock.css +70 -0
  443. package/src/recipes/sticker/README.md +59 -0
  444. package/src/recipes/sticker/create-sticker.ts +490 -0
  445. package/src/recipes/sticker/sticker.css +112 -0
  446. package/src/recipes/thumbnail-nav/README.md +16 -0
  447. package/src/recipes/thumbnail-nav/create-thumbnail-nav.ts +165 -0
  448. package/src/recipes/thumbnail-nav/thumbnail-nav.css +108 -0
  449. package/src/recipes/weight-hover/README.md +37 -0
  450. package/src/recipes/weight-hover/create-weight-hover.ts +136 -0
  451. package/src/recipes/weight-hover/weight-hover.css +7 -0
  452. package/dist/chunk-BFC27F2G.js.map +0 -1
  453. package/dist/chunk-J6OCFNGU.js +0 -177
  454. package/dist/chunk-J6OCFNGU.js.map +0 -1
  455. package/dist/chunk-JQMJQSXD.js +0 -2374
  456. package/dist/chunk-JQMJQSXD.js.map +0 -1
  457. package/dist/chunk-XDWBVFHQ.js.map +0 -1
  458. package/dist/reveal-C0hCDcB_.d.ts +0 -415
@@ -1,432 +1,449 @@
1
- import { gsap, EASE, registerMotion } from '../../core/gsap';
2
- import { reduced } from '../../core/reduced';
3
- import { createScope } from '../../core/scope';
4
- import { TIMING } from '../../core/timing';
5
- import { inert, warnOnce, type Cleanup, type Instance } from '../../core/types';
6
- import { MORPH_STYLES, clearStyles } from '../../core/styles';
7
- import { anchorSurface, clearAnchor } from '../../motion/anchor';
8
- import { attachDragDismiss } from '../../motion/drag-dismiss';
9
- import {
10
- attachFloating,
11
- dockWindow,
12
- floatingAvailable,
13
- forgetFloat,
14
- liftWindow,
15
- savedFloat,
16
- unplaceWindow,
17
- } from '../../motion/floating';
18
- import { isMorphing, morphCloseWithGhost, morphOpen, releaseShared, showOrigin, stopMorph } from '../../motion/morph';
19
- import { pushUnderlay, releaseUnderlay, sheetClose, sheetOpen } from '../../motion/sheet';
20
- import { closeModal, fadeBackdrop, openModal } from '../../motion/surface';
21
-
22
- /*
23
- * Dialog on a native <dialog> (focus trap, Escape, top layer): the
24
- * control that opened it turns into it and it folds back into that control
25
- * (morph or liquid) over a backdrop tinted on the signature curve. It eases to
26
- * new heights (auto-resize), can be dragged away (> 100 px) and, on desktop,
27
- * lifted into a floating window by its handle. Parts: [data-mk-part] header,
28
- * title, description, body, footer, close, handle.
29
- */
30
-
31
- export type DialogVariant = 'morph' | 'liquid' | 'sheet';
32
- export type DialogPlacement = 'center' | 'anchor' | 'side';
33
-
34
- export interface DialogLabels {
35
- close?: string;
36
- float?: string;
37
- dock?: string;
38
- }
39
-
40
- export interface DialogOptions {
41
- /** morph (default), liquid, or sheet: rises from the bottom and pushes [data-mk-underlay] back. */
42
- variant?: DialogVariant;
43
- placement?: DialogPlacement;
44
- /** Tinted backdrop; false leaves it transparent (menus). */
45
- backdrop?: boolean;
46
- closeOnBackdrop?: boolean;
47
- /** Drag (or wheel) down to dismiss. */
48
- drag?: boolean;
49
- /** A key turns on the floating window (desktop) and remembers its place. */
50
- float?: string | false;
51
- autoResize?: boolean;
52
- /** `[data-mk-open="<id>"]` buttons open it: true uses the dialog's id; a string names another id; false: none. */
53
- triggers?: boolean | string;
54
- /** Modal (default) or a non-modal dialog. */
55
- modal?: boolean;
56
- labels?: DialogLabels;
57
- /** Grow the content with the surface (default). Menus turn it off and cascade their items instead. */
58
- animateContent?: boolean;
59
- onOpen?: () => void;
60
- /** After it closed, whatever closed it. */
61
- onClose?: () => void;
62
- }
63
-
64
- export interface DialogInstance extends Instance {
65
- /** `origin`: the control it grows from; omitted, the focused control; null, none (scale-in). */
66
- open(origin?: HTMLElement | null): void;
67
- close(): void;
68
- toggle(origin?: HTMLElement | null): void;
69
- float(): void;
70
- dock(): void;
71
- readonly isOpen: boolean;
72
- readonly isFloating: boolean;
73
- readonly element: HTMLElement;
74
- }
75
-
76
- const DEFAULT_LABELS: Required<DialogLabels> = { close: 'Cerrar', float: 'Ventana flotante', dock: 'Volver a su sitio' };
77
- const FIELDS = '[autofocus], input:not([type="hidden"]):not(:disabled), textarea:not(:disabled), select:not(:disabled)';
78
- const CORNERS = ['tl', 'tr', 'bl', 'br'] as const;
79
- let uid = 0;
80
-
81
- const part = (dialog: HTMLElement, name: string) => dialog.querySelector<HTMLElement>(`[data-mk-part="${name}"]`);
82
-
83
- export function createDialog(dialog: HTMLElement | null, options: DialogOptions = {}): DialogInstance {
84
- if (!dialog) {
85
- warnOnce('dialog: missing <dialog> element');
86
- return inert({ open() {}, close() {}, toggle() {}, float() {}, dock() {}, isOpen: false, isFloating: false, element: null as unknown as HTMLElement });
87
- }
88
- registerMotion();
89
- const {
90
- variant = 'morph',
91
- placement = 'center',
92
- backdrop = true,
93
- closeOnBackdrop = true,
94
- drag = true,
95
- float: floatKey = false,
96
- autoResize = true,
97
- triggers = true,
98
- modal = true,
99
- } = options;
100
- const labels = { ...DEFAULT_LABELS, ...options.labels };
101
- const liquid = variant === 'liquid';
102
- const sheet = variant === 'sheet';
103
- const scope = createScope();
104
- /** Effects that live while it is open. */
105
- let session: Cleanup[] = [];
106
- let opener: HTMLElement | null = null;
107
- let isOpen = false;
108
- let closing = false;
109
- let floating = false;
110
- /**
111
- * Lifting and docking swap modal ↔ popover: the native `close` events they cause
112
- * (queued, so they arrive later) are not the person closing it.
113
- */
114
- let ownCloses = 0;
115
- const closeNatively = () => {
116
- if (!dialog.hasAttribute('open')) return;
117
- ownCloses += 1;
118
- closeModal(dialog as HTMLDialogElement);
119
- };
120
- let destroyed = false;
121
- /** destroy() is under way: the page behind comes back at once. */
122
- let tearingDown = false;
123
-
124
- if (placement !== 'center') scope.attr(dialog, 'data-mk-placement', placement);
125
- if (!backdrop) scope.attr(dialog, 'data-mk-backdrop', 'none');
126
- scope.attr(dialog, 'data-mk-variant', variant);
127
- const title = part(dialog, 'title');
128
- if (title && !dialog.hasAttribute('aria-label') && !dialog.hasAttribute('aria-labelledby')) {
129
- if (!title.id) scope.attr(title, 'id', `mk-dialog-title-${++uid}`);
130
- scope.attr(dialog, 'aria-labelledby', title.id);
131
- }
132
- const description = part(dialog, 'description');
133
- if (description && !dialog.hasAttribute('aria-describedby')) {
134
- if (!description.id) scope.attr(description, 'id', `mk-dialog-description-${++uid}`);
135
- scope.attr(dialog, 'aria-describedby', description.id);
136
- }
137
- const closeButton = part(dialog, 'close');
138
- if (closeButton && !closeButton.hasAttribute('aria-label') && !closeButton.textContent?.trim()) {
139
- scope.attr(closeButton, 'aria-label', labels.close);
140
- }
141
- const handle = floatKey ? part(dialog, 'handle') : null;
142
- if (handle) {
143
- scope.attr(handle, 'data-mk-no-drag', '');
144
- scope.attr(handle, 'aria-pressed', 'false');
145
- scope.attr(handle, 'aria-label', labels.float);
146
- }
147
- const corners: HTMLElement[] = [];
148
- if (floatKey) {
149
- for (const corner of CORNERS) {
150
- const element = document.createElement('span');
151
- element.className = 'mk-float-corner';
152
- element.dataset.mkFloatCorner = corner;
153
- element.dataset.mkNoDrag = '';
154
- element.setAttribute('aria-hidden', 'true');
155
- dialog.append(element);
156
- corners.push(element);
157
- }
158
- scope.add(() => corners.forEach((corner) => corner.remove()));
159
- }
160
-
161
- const emit = (type: string, cancelable = false, detail: Record<string, unknown> = {}) =>
162
- dialog.dispatchEvent(new CustomEvent(type, { bubbles: true, cancelable, detail: { dialog, ...detail } }));
163
-
164
- const endSession = () => {
165
- for (const cleanup of session.splice(0).reverse()) cleanup();
166
- };
167
-
168
- const settleFloating = () => {
169
- if (!floating) return;
170
- floating = false;
171
- if (typeof dialog.hidePopover === 'function' && dialog.hasAttribute('popover')) {
172
- try {
173
- dialog.hidePopover();
174
- } catch {
175
- // Already hidden.
176
- }
177
- }
178
- dialog.removeAttribute('popover');
179
- unplaceWindow(dialog);
180
- if (handle) {
181
- handle.setAttribute('aria-pressed', 'false');
182
- handle.setAttribute('aria-label', labels.float);
183
- }
184
- };
185
-
186
- /** Closed without our animation (owner, form[method=dialog], destroy). */
187
- const closedQuietly = () => {
188
- stopMorph(dialog);
189
- endSession();
190
- isOpen = false;
191
- closing = false;
192
- showOrigin(opener);
193
- releaseShared(opener);
194
- emit('mk:closed');
195
- options.onClose?.();
196
- };
197
-
198
- const lift = () => {
199
- if (!floatKey || floating || !isOpen || !floatingAvailable()) return;
200
- const box = dialog.getBoundingClientRect();
201
- const rect = savedFloat(floatKey) ?? { top: box.top, left: box.left, width: box.width, height: box.height };
202
- const focused = document.activeElement;
203
- // Its backdrop leaves as the window lifts off the page.
204
- const backdropStyle = getComputedStyle(dialog, '::backdrop');
205
- const veil = document.createElement('div');
206
- veil.setAttribute('aria-hidden', 'true');
207
- Object.assign(veil.style, {
208
- position: 'fixed',
209
- inset: '0',
210
- background: backdropStyle.background || backdropStyle.backgroundColor,
211
- backdropFilter: backdropStyle.backdropFilter,
212
- pointerEvents: 'none',
213
- });
214
- closeNatively();
215
- document.body.append(veil);
216
- dialog.setAttribute('popover', 'manual');
217
- if (typeof dialog.showPopover === 'function') dialog.showPopover();
218
- else openModal(dialog, false);
219
- liftWindow(dialog, rect);
220
- gsap.to(veil, { opacity: 0, duration: reduced() ? 0 : TIMING.float.shadow, ease: EASE.signature, onComplete: () => veil.remove() });
221
- floating = true;
222
- if (handle) {
223
- handle.setAttribute('aria-pressed', 'true');
224
- handle.setAttribute('aria-label', labels.dock);
225
- }
226
- showOrigin(opener, { fade: true });
227
- if (focused instanceof HTMLElement && dialog.contains(focused)) focused.focus();
228
- };
229
-
230
- const dock = () => {
231
- if (!floatKey || !floating) return;
232
- const box = dialog.getBoundingClientRect();
233
- const focused = document.activeElement;
234
- floating = false;
235
- if (typeof dialog.hidePopover === 'function') dialog.hidePopover();
236
- dialog.removeAttribute('popover');
237
- closeNatively();
238
- openModal(dialog, modal);
239
- dockWindow(dialog, { top: box.top, left: box.left, width: box.width, height: box.height });
240
- if (backdrop) void fadeBackdrop(dialog, 'in');
241
- forgetFloat(floatKey);
242
- if (handle) {
243
- handle.setAttribute('aria-pressed', 'false');
244
- handle.setAttribute('aria-label', labels.float);
245
- }
246
- if (focused instanceof HTMLElement && dialog.contains(focused)) focused.focus();
247
- };
248
-
249
- const startSession = () => {
250
- if (sheet) {
251
- const behind = [...document.querySelectorAll<HTMLElement>('[data-mk-underlay]')];
252
- pushUnderlay(behind);
253
- const root = document.documentElement;
254
- const overflow = root.style.overflow;
255
- root.style.overflow = 'hidden';
256
- session.push(() => {
257
- root.style.overflow = overflow;
258
- if (root.getAttribute('style') === '') root.removeAttribute('style');
259
- releaseUnderlay(behind, !tearingDown);
260
- });
261
- }
262
- if (autoResize && typeof ResizeObserver === 'function') {
263
- let last = dialog.offsetHeight;
264
- let resizing: gsap.core.Tween | null = null;
265
- const observer = new ResizeObserver(() => {
266
- if (resizing?.isActive()) return;
267
- const next = dialog.offsetHeight;
268
- const previous = last;
269
- last = next;
270
- // A floating window's size is the person's, not its content's.
271
- if (floating || Math.abs(next - previous) < 2 || !previous || isMorphing(dialog) || reduced()) return;
272
- resizing = gsap.fromTo(
273
- dialog,
274
- { height: previous },
275
- {
276
- height: next,
277
- duration: TIMING.dialog.autoResize,
278
- ease: EASE.signature,
279
- clearProps: 'height',
280
- onComplete: () => {
281
- last = dialog.offsetHeight;
282
- },
283
- },
284
- );
285
- });
286
- observer.observe(dialog);
287
- session.push(() => {
288
- observer.disconnect();
289
- resizing?.kill();
290
- clearStyles([dialog], ['height']);
291
- });
292
- }
293
- if (drag) {
294
- session.push(
295
- attachDragDismiss(dialog, {
296
- canDrag: () => !isMorphing(dialog) && !floating && !closing && isOpen,
297
- onDismiss: () => close(true),
298
- }),
299
- );
300
- }
301
- if (placement === 'anchor') {
302
- const onResize = () => {
303
- if (isOpen && !floating) anchorSurface(dialog, opener?.isConnected ? opener.getBoundingClientRect() : null);
304
- };
305
- addEventListener('resize', onResize);
306
- session.push(() => {
307
- removeEventListener('resize', onResize);
308
- clearAnchor(dialog);
309
- });
310
- }
311
- if (floatKey && handle) {
312
- session.push(attachFloating(dialog, { key: floatKey, handle, corners, isFloating: () => floating, float: lift, dock }));
313
- const onResize = () => {
314
- if (floating && !floatingAvailable()) dock();
315
- };
316
- addEventListener('resize', onResize);
317
- session.push(() => removeEventListener('resize', onResize));
318
- }
319
- };
320
-
321
- const open = (origin?: HTMLElement | null) => {
322
- if (destroyed || isOpen) return;
323
- const active = document.activeElement;
324
- // undefined: the focused control (usually the one clicked); null: no opener at all.
325
- opener = origin !== undefined ? origin : active instanceof HTMLElement && active !== document.body && !dialog.contains(active) ? active : null;
326
- closing = false;
327
- isOpen = true;
328
- openModal(dialog, modal);
329
- if (placement === 'anchor') anchorSurface(dialog, opener?.getBoundingClientRect());
330
- if (backdrop && modal) void fadeBackdrop(dialog, 'in');
331
- if (sheet) sheetOpen(dialog);
332
- else morphOpen(dialog, opener, { hideOrigin: true, liquid, content: options.animateContent === false ? ':scope > :not(*)' : undefined });
333
- startSession();
334
- // Forms start at their first field (after showModal: a closed dialog cannot take focus).
335
- dialog.querySelector<HTMLElement>(FIELDS)?.focus({ preventScroll: true });
336
- emit('mk:open', false, { origin: opener });
337
- options.onOpen?.();
338
- };
339
-
340
- /** Animated close: the dialog closes at once while an inert copy folds into the opener. */
341
- const close = (inPlace = false) => {
342
- if (destroyed || !isOpen || closing) return;
343
- if (!emit('mk:close', true, { inPlace })) return;
344
- closing = true;
345
- // The copy is measured first: the session cleanup clears an anchored place.
346
- if (sheet) sheetClose(dialog, { inPlace });
347
- else morphCloseWithGhost(dialog, opener, { inPlace, liquid });
348
- endSession();
349
- if (floating) settleFloating();
350
- if (dialog.hasAttribute('open')) closeModal(dialog as HTMLDialogElement);
351
- isOpen = false;
352
- closing = false;
353
- if (opener?.isConnected) opener.focus();
354
- emit('mk:closed');
355
- options.onClose?.();
356
- };
357
-
358
- scope.on(dialog, 'cancel', (event) => {
359
- event.preventDefault();
360
- close();
361
- });
362
- scope.on(dialog, 'close', () => {
363
- if (ownCloses > 0) {
364
- ownCloses -= 1;
365
- return;
366
- }
367
- // A native close we did not start (form method="dialog", dialog.close()).
368
- if (!isOpen || closing) return;
369
- closedQuietly();
370
- });
371
- scope.on(dialog, 'keydown', (event) => {
372
- // A floating (non-modal) window still closes with Escape.
373
- if ((event as KeyboardEvent).key === 'Escape' && floating) {
374
- event.preventDefault();
375
- close();
376
- }
377
- });
378
- scope.on(dialog, 'click', (event) => {
379
- const target = event.target as Element | null;
380
- if (target?.closest('[data-mk-part="close"], [data-mk-close]') && dialog.contains(target)) {
381
- close();
382
- return;
383
- }
384
- if (target !== dialog || !closeOnBackdrop || floating) return;
385
- const mouse = event as MouseEvent;
386
- const box = dialog.getBoundingClientRect();
387
- if (mouse.clientX < box.left || mouse.clientX > box.right || mouse.clientY < box.top || mouse.clientY > box.bottom) close();
388
- });
389
- const triggerId = triggers === true ? dialog.id : triggers || '';
390
- if (triggerId) {
391
- const selector = `[data-mk-open="${typeof CSS !== 'undefined' && CSS.escape ? CSS.escape(triggerId) : triggerId}"]`;
392
- scope.on(document, 'click', (event) => {
393
- const trigger = (event.target as Element | null)?.closest<HTMLElement>(selector);
394
- if (!trigger || dialog.contains(trigger)) return;
395
- event.preventDefault();
396
- if (isOpen) close();
397
- else open(trigger);
398
- });
399
- }
400
- dialog.dataset.mkReady = '';
401
-
402
- return {
403
- open,
404
- close: () => close(),
405
- toggle: (origin?: HTMLElement | null) => (isOpen ? close() : open(origin)),
406
- float: lift,
407
- dock,
408
- get isOpen() {
409
- return isOpen;
410
- },
411
- get isFloating() {
412
- return floating;
413
- },
414
- element: dialog,
415
- destroy() {
416
- if (destroyed) return;
417
- tearingDown = true;
418
- if (isOpen) {
419
- settleFloating();
420
- closing = true;
421
- if (dialog.hasAttribute('open')) closeModal(dialog as HTMLDialogElement);
422
- closedQuietly();
423
- }
424
- destroyed = true;
425
- endSession();
426
- gsap.killTweensOf(dialog);
427
- clearStyles([dialog], [...MORPH_STYLES, 'height']);
428
- scope.dispose();
429
- delete dialog.dataset.mkReady;
430
- },
431
- };
432
- }
1
+ import { gsap, EASE, registerMotion } from '../../core/gsap';
2
+ import { reduced } from '../../core/reduced';
3
+ import { createScope } from '../../core/scope';
4
+ import { TIMING } from '../../core/timing';
5
+ import { inert, warnOnce, type Cleanup, type Instance } from '../../core/types';
6
+ import { MORPH_STYLES, clearStyles } from '../../core/styles';
7
+ import { anchorSurface, clearAnchor } from '../../motion/anchor';
8
+ import { attachDragDismiss } from '../../motion/drag-dismiss';
9
+ import {
10
+ attachFloating,
11
+ dockWindow,
12
+ floatingAvailable,
13
+ forgetFloat,
14
+ liftWindow,
15
+ savedFloat,
16
+ unplaceWindow,
17
+ } from '../../motion/floating';
18
+ import { isMorphing, morphCloseWithGhost, morphOpen, releaseShared, showOrigin, stopMorph } from '../../motion/morph';
19
+ import { pushUnderlay, releaseUnderlay, sheetClose, sheetOpen, slideClose, slideOpen } from '../../motion/sheet';
20
+ import { closeModal, fadeBackdrop, openModal } from '../../motion/surface';
21
+
22
+ /*
23
+ * Dialog on a native <dialog> (focus trap, Escape, top layer): the
24
+ * control that opened it turns into it and it folds back into that control
25
+ * (morph or liquid) over a backdrop tinted on the signature curve. It eases to
26
+ * new heights (auto-resize), can be dragged away (> 100 px) and, on desktop,
27
+ * lifted into a floating window by its handle. Parts: [data-mk-part] header,
28
+ * title, description, body, footer, close, handle.
29
+ */
30
+
31
+ /** drawer: slides in from its side (data-mk-drawer="left|right", see createDrawer). */
32
+ export type DialogVariant = 'morph' | 'liquid' | 'sheet' | 'drawer';
33
+ export type DialogPlacement = 'center' | 'anchor' | 'side';
34
+
35
+ export interface DialogLabels {
36
+ close?: string;
37
+ float?: string;
38
+ dock?: string;
39
+ }
40
+
41
+ export interface DialogOptions {
42
+ /** morph (default), liquid, or sheet: rises from the bottom and pushes [data-mk-underlay] back. */
43
+ variant?: DialogVariant;
44
+ placement?: DialogPlacement;
45
+ /** placement 'anchor': the box to place it against, read on open and resize (default: the opener's). */
46
+ anchor?: () => DOMRect | null | undefined;
47
+ /** Tinted backdrop; false leaves it transparent (menus). */
48
+ backdrop?: boolean;
49
+ closeOnBackdrop?: boolean;
50
+ /** Drag (or wheel) down to dismiss. */
51
+ drag?: boolean;
52
+ /** A key turns on the floating window (desktop) and remembers its place. */
53
+ float?: string | false;
54
+ autoResize?: boolean;
55
+ /** `[data-mk-open="<id>"]` buttons open it: true uses the dialog's id; a string names another id; false: none. */
56
+ triggers?: boolean | string;
57
+ /** Modal (default) or a non-modal dialog. */
58
+ modal?: boolean;
59
+ labels?: DialogLabels;
60
+ /** Grow the content with the surface (default). Menus turn it off and cascade their items instead. */
61
+ animateContent?: boolean;
62
+ onOpen?: () => void;
63
+ /** After it closed, whatever closed it. */
64
+ onClose?: () => void;
65
+ }
66
+
67
+ export interface DialogInstance extends Instance {
68
+ /** `origin`: the control it grows from; omitted, the focused control; null, none (scale-in). */
69
+ open(origin?: HTMLElement | null): void;
70
+ close(): void;
71
+ toggle(origin?: HTMLElement | null): void;
72
+ float(): void;
73
+ dock(): void;
74
+ readonly isOpen: boolean;
75
+ readonly isFloating: boolean;
76
+ readonly element: HTMLElement;
77
+ }
78
+
79
+ const DEFAULT_LABELS: Required<DialogLabels> = { close: 'Cerrar', float: 'Ventana flotante', dock: 'Volver a su sitio' };
80
+ const FIELDS = '[autofocus], input:not([type="hidden"]):not(:disabled), textarea:not(:disabled), select:not(:disabled)';
81
+ const CORNERS = ['tl', 'tr', 'bl', 'br'] as const;
82
+ let uid = 0;
83
+
84
+ const part = (dialog: HTMLElement, name: string) => dialog.querySelector<HTMLElement>(`[data-mk-part="${name}"]`);
85
+
86
+ export function createDialog(dialog: HTMLElement | null, options: DialogOptions = {}): DialogInstance {
87
+ if (!dialog) {
88
+ warnOnce('dialog: missing <dialog> element');
89
+ return inert({ open() {}, close() {}, toggle() {}, float() {}, dock() {}, isOpen: false, isFloating: false, element: null as unknown as HTMLElement });
90
+ }
91
+ registerMotion();
92
+ const {
93
+ variant = 'morph',
94
+ placement = 'center',
95
+ backdrop = true,
96
+ closeOnBackdrop = true,
97
+ drag = true,
98
+ float: floatKey = false,
99
+ autoResize = true,
100
+ triggers = true,
101
+ modal = true,
102
+ } = options;
103
+ const labels = { ...DEFAULT_LABELS, ...options.labels };
104
+ const liquid = variant === 'liquid';
105
+ const sheet = variant === 'sheet';
106
+ const drawer = variant === 'drawer';
107
+ const scope = createScope();
108
+ /** Effects that live while it is open. */
109
+ let session: Cleanup[] = [];
110
+ let opener: HTMLElement | null = null;
111
+ let isOpen = false;
112
+ let closing = false;
113
+ let floating = false;
114
+ /**
115
+ * Lifting and docking swap modal ↔ popover: the native `close` events they cause
116
+ * (queued, so they arrive later) are not the person closing it.
117
+ */
118
+ let ownCloses = 0;
119
+ const closeNatively = () => {
120
+ if (!dialog.hasAttribute('open')) return;
121
+ ownCloses += 1;
122
+ closeModal(dialog as HTMLDialogElement);
123
+ };
124
+ let destroyed = false;
125
+ /** destroy() is under way: the page behind comes back at once. */
126
+ let tearingDown = false;
127
+
128
+ if (placement !== 'center') scope.attr(dialog, 'data-mk-placement', placement);
129
+ if (!backdrop) scope.attr(dialog, 'data-mk-backdrop', 'none');
130
+ scope.attr(dialog, 'data-mk-variant', variant);
131
+ const title = part(dialog, 'title');
132
+ if (title && !dialog.hasAttribute('aria-label') && !dialog.hasAttribute('aria-labelledby')) {
133
+ if (!title.id) scope.attr(title, 'id', `mk-dialog-title-${++uid}`);
134
+ scope.attr(dialog, 'aria-labelledby', title.id);
135
+ }
136
+ const description = part(dialog, 'description');
137
+ if (description && !dialog.hasAttribute('aria-describedby')) {
138
+ if (!description.id) scope.attr(description, 'id', `mk-dialog-description-${++uid}`);
139
+ scope.attr(dialog, 'aria-describedby', description.id);
140
+ }
141
+ const closeButton = part(dialog, 'close');
142
+ if (closeButton && !closeButton.hasAttribute('aria-label') && !closeButton.textContent?.trim()) {
143
+ scope.attr(closeButton, 'aria-label', labels.close);
144
+ }
145
+ const handle = floatKey ? part(dialog, 'handle') : null;
146
+ if (handle) {
147
+ scope.attr(handle, 'data-mk-no-drag', '');
148
+ scope.attr(handle, 'aria-pressed', 'false');
149
+ scope.attr(handle, 'aria-label', labels.float);
150
+ }
151
+ const corners: HTMLElement[] = [];
152
+ if (floatKey) {
153
+ for (const corner of CORNERS) {
154
+ const element = document.createElement('span');
155
+ element.className = 'mk-float-corner';
156
+ element.dataset.mkFloatCorner = corner;
157
+ element.dataset.mkNoDrag = '';
158
+ element.setAttribute('aria-hidden', 'true');
159
+ dialog.append(element);
160
+ corners.push(element);
161
+ }
162
+ scope.add(() => corners.forEach((corner) => corner.remove()));
163
+ }
164
+
165
+ const emit = (type: string, cancelable = false, detail: Record<string, unknown> = {}) =>
166
+ dialog.dispatchEvent(new CustomEvent(type, { bubbles: true, cancelable, detail: { dialog, ...detail } }));
167
+
168
+ const endSession = () => {
169
+ for (const cleanup of session.splice(0).reverse()) cleanup();
170
+ };
171
+
172
+ const settleFloating = () => {
173
+ if (!floating) return;
174
+ floating = false;
175
+ if (typeof dialog.hidePopover === 'function' && dialog.hasAttribute('popover')) {
176
+ try {
177
+ dialog.hidePopover();
178
+ } catch {
179
+ // Already hidden.
180
+ }
181
+ }
182
+ dialog.removeAttribute('popover');
183
+ unplaceWindow(dialog);
184
+ if (handle) {
185
+ handle.setAttribute('aria-pressed', 'false');
186
+ handle.setAttribute('aria-label', labels.float);
187
+ }
188
+ };
189
+
190
+ /** Closed without our animation (owner, form[method=dialog], destroy). */
191
+ const closedQuietly = () => {
192
+ stopMorph(dialog);
193
+ endSession();
194
+ isOpen = false;
195
+ closing = false;
196
+ showOrigin(opener);
197
+ releaseShared(opener);
198
+ emit('mk:closed');
199
+ options.onClose?.();
200
+ };
201
+
202
+ const lift = () => {
203
+ if (!floatKey || floating || !isOpen || !floatingAvailable()) return;
204
+ const box = dialog.getBoundingClientRect();
205
+ const rect = savedFloat(floatKey) ?? { top: box.top, left: box.left, width: box.width, height: box.height };
206
+ const focused = document.activeElement;
207
+ // Its backdrop leaves as the window lifts off the page.
208
+ const backdropStyle = getComputedStyle(dialog, '::backdrop');
209
+ const veil = document.createElement('div');
210
+ veil.setAttribute('aria-hidden', 'true');
211
+ Object.assign(veil.style, {
212
+ position: 'fixed',
213
+ inset: '0',
214
+ background: backdropStyle.background || backdropStyle.backgroundColor,
215
+ backdropFilter: backdropStyle.backdropFilter,
216
+ pointerEvents: 'none',
217
+ });
218
+ closeNatively();
219
+ document.body.append(veil);
220
+ dialog.setAttribute('popover', 'manual');
221
+ if (typeof dialog.showPopover === 'function') dialog.showPopover();
222
+ else openModal(dialog, false);
223
+ liftWindow(dialog, rect);
224
+ gsap.to(veil, { opacity: 0, duration: reduced() ? 0 : TIMING.float.shadow, ease: EASE.signature, onComplete: () => veil.remove() });
225
+ floating = true;
226
+ if (handle) {
227
+ handle.setAttribute('aria-pressed', 'true');
228
+ handle.setAttribute('aria-label', labels.dock);
229
+ }
230
+ showOrigin(opener, { fade: true });
231
+ if (focused instanceof HTMLElement && dialog.contains(focused)) focused.focus();
232
+ };
233
+
234
+ const dock = () => {
235
+ if (!floatKey || !floating) return;
236
+ const box = dialog.getBoundingClientRect();
237
+ const focused = document.activeElement;
238
+ floating = false;
239
+ if (typeof dialog.hidePopover === 'function') dialog.hidePopover();
240
+ dialog.removeAttribute('popover');
241
+ closeNatively();
242
+ openModal(dialog, modal);
243
+ dockWindow(dialog, { top: box.top, left: box.left, width: box.width, height: box.height });
244
+ if (backdrop) void fadeBackdrop(dialog, 'in');
245
+ forgetFloat(floatKey);
246
+ if (handle) {
247
+ handle.setAttribute('aria-pressed', 'false');
248
+ handle.setAttribute('aria-label', labels.float);
249
+ }
250
+ if (focused instanceof HTMLElement && dialog.contains(focused)) focused.focus();
251
+ };
252
+
253
+ const anchorBox = () => (options.anchor ? options.anchor() : opener?.isConnected ? opener.getBoundingClientRect() : null);
254
+
255
+ const startSession = () => {
256
+ if (drawer && modal) {
257
+ const root = document.documentElement;
258
+ const overflow = root.style.overflow;
259
+ root.style.overflow = 'hidden';
260
+ session.push(() => {
261
+ root.style.overflow = overflow;
262
+ if (root.getAttribute('style') === '') root.removeAttribute('style');
263
+ });
264
+ }
265
+ if (sheet) {
266
+ const behind = [...document.querySelectorAll<HTMLElement>('[data-mk-underlay]')];
267
+ pushUnderlay(behind);
268
+ const root = document.documentElement;
269
+ const overflow = root.style.overflow;
270
+ root.style.overflow = 'hidden';
271
+ session.push(() => {
272
+ root.style.overflow = overflow;
273
+ if (root.getAttribute('style') === '') root.removeAttribute('style');
274
+ releaseUnderlay(behind, !tearingDown);
275
+ });
276
+ }
277
+ if (autoResize && typeof ResizeObserver === 'function') {
278
+ let last = dialog.offsetHeight;
279
+ let resizing: gsap.core.Tween | null = null;
280
+ const observer = new ResizeObserver(() => {
281
+ if (resizing?.isActive()) return;
282
+ const next = dialog.offsetHeight;
283
+ const previous = last;
284
+ last = next;
285
+ // A floating window's size is the person's, not its content's.
286
+ if (floating || Math.abs(next - previous) < 2 || !previous || isMorphing(dialog) || reduced()) return;
287
+ resizing = gsap.fromTo(
288
+ dialog,
289
+ { height: previous },
290
+ {
291
+ height: next,
292
+ duration: TIMING.dialog.autoResize,
293
+ ease: EASE.signature,
294
+ clearProps: 'height',
295
+ onComplete: () => {
296
+ last = dialog.offsetHeight;
297
+ },
298
+ },
299
+ );
300
+ });
301
+ observer.observe(dialog);
302
+ session.push(() => {
303
+ observer.disconnect();
304
+ resizing?.kill();
305
+ clearStyles([dialog], ['height']);
306
+ });
307
+ }
308
+ if (drag) {
309
+ session.push(
310
+ attachDragDismiss(dialog, {
311
+ canDrag: () => !isMorphing(dialog) && !floating && !closing && isOpen,
312
+ onDismiss: () => close(true),
313
+ }),
314
+ );
315
+ }
316
+ if (placement === 'anchor') {
317
+ const onResize = () => {
318
+ if (isOpen && !floating) anchorSurface(dialog, anchorBox());
319
+ };
320
+ addEventListener('resize', onResize);
321
+ session.push(() => {
322
+ removeEventListener('resize', onResize);
323
+ clearAnchor(dialog);
324
+ });
325
+ }
326
+ if (floatKey && handle) {
327
+ session.push(attachFloating(dialog, { key: floatKey, handle, corners, isFloating: () => floating, float: lift, dock }));
328
+ const onResize = () => {
329
+ if (floating && !floatingAvailable()) dock();
330
+ };
331
+ addEventListener('resize', onResize);
332
+ session.push(() => removeEventListener('resize', onResize));
333
+ }
334
+ };
335
+
336
+ const open = (origin?: HTMLElement | null) => {
337
+ if (destroyed || isOpen) return;
338
+ const active = document.activeElement;
339
+ // undefined: the focused control (usually the one clicked); null: no opener at all.
340
+ opener = origin !== undefined ? origin : active instanceof HTMLElement && active !== document.body && !dialog.contains(active) ? active : null;
341
+ closing = false;
342
+ isOpen = true;
343
+ openModal(dialog, modal);
344
+ if (placement === 'anchor') anchorSurface(dialog, anchorBox());
345
+ if (backdrop && modal) void fadeBackdrop(dialog, 'in');
346
+ if (sheet) sheetOpen(dialog);
347
+ else if (drawer) slideOpen(dialog);
348
+ else morphOpen(dialog, opener, { hideOrigin: true, liquid, content: options.animateContent === false ? ':scope > :not(*)' : undefined });
349
+ startSession();
350
+ // Forms start at their first field (after showModal: a closed dialog cannot take focus).
351
+ dialog.querySelector<HTMLElement>(FIELDS)?.focus({ preventScroll: true });
352
+ emit('mk:open', false, { origin: opener });
353
+ options.onOpen?.();
354
+ };
355
+
356
+ /** Animated close: the dialog closes at once while an inert copy folds into the opener. */
357
+ const close = (inPlace = false) => {
358
+ if (destroyed || !isOpen || closing) return;
359
+ if (!emit('mk:close', true, { inPlace })) return;
360
+ closing = true;
361
+ // The copy is measured first: the session cleanup clears an anchored place.
362
+ if (sheet) sheetClose(dialog, { inPlace });
363
+ else if (drawer) slideClose(dialog);
364
+ else morphCloseWithGhost(dialog, opener, { inPlace, liquid });
365
+ endSession();
366
+ if (floating) settleFloating();
367
+ if (dialog.hasAttribute('open')) closeModal(dialog as HTMLDialogElement);
368
+ isOpen = false;
369
+ closing = false;
370
+ if (opener?.isConnected) opener.focus();
371
+ emit('mk:closed');
372
+ options.onClose?.();
373
+ };
374
+
375
+ scope.on(dialog, 'cancel', (event) => {
376
+ event.preventDefault();
377
+ close();
378
+ });
379
+ scope.on(dialog, 'close', () => {
380
+ if (ownCloses > 0) {
381
+ ownCloses -= 1;
382
+ return;
383
+ }
384
+ // A native close we did not start (form method="dialog", dialog.close()).
385
+ if (!isOpen || closing) return;
386
+ closedQuietly();
387
+ });
388
+ scope.on(dialog, 'keydown', (event) => {
389
+ // A floating (non-modal) window still closes with Escape.
390
+ if ((event as KeyboardEvent).key === 'Escape' && floating) {
391
+ event.preventDefault();
392
+ close();
393
+ }
394
+ });
395
+ scope.on(dialog, 'click', (event) => {
396
+ const target = event.target as Element | null;
397
+ if (target?.closest('[data-mk-part="close"], [data-mk-close]') && dialog.contains(target)) {
398
+ close();
399
+ return;
400
+ }
401
+ if (target !== dialog || !closeOnBackdrop || floating) return;
402
+ const mouse = event as MouseEvent;
403
+ const box = dialog.getBoundingClientRect();
404
+ if (mouse.clientX < box.left || mouse.clientX > box.right || mouse.clientY < box.top || mouse.clientY > box.bottom) close();
405
+ });
406
+ const triggerId = triggers === true ? dialog.id : triggers || '';
407
+ if (triggerId) {
408
+ const selector = `[data-mk-open="${typeof CSS !== 'undefined' && CSS.escape ? CSS.escape(triggerId) : triggerId}"]`;
409
+ scope.on(document, 'click', (event) => {
410
+ const trigger = (event.target as Element | null)?.closest<HTMLElement>(selector);
411
+ if (!trigger || dialog.contains(trigger)) return;
412
+ event.preventDefault();
413
+ if (isOpen) close();
414
+ else open(trigger);
415
+ });
416
+ }
417
+ dialog.dataset.mkReady = '';
418
+
419
+ return {
420
+ open,
421
+ close: () => close(),
422
+ toggle: (origin?: HTMLElement | null) => (isOpen ? close() : open(origin)),
423
+ float: lift,
424
+ dock,
425
+ get isOpen() {
426
+ return isOpen;
427
+ },
428
+ get isFloating() {
429
+ return floating;
430
+ },
431
+ element: dialog,
432
+ destroy() {
433
+ if (destroyed) return;
434
+ tearingDown = true;
435
+ if (isOpen) {
436
+ settleFloating();
437
+ closing = true;
438
+ if (dialog.hasAttribute('open')) closeModal(dialog as HTMLDialogElement);
439
+ closedQuietly();
440
+ }
441
+ destroyed = true;
442
+ endSession();
443
+ gsap.killTweensOf(dialog);
444
+ clearStyles([dialog], [...MORPH_STYLES, 'height']);
445
+ scope.dispose();
446
+ delete dialog.dataset.mkReady;
447
+ },
448
+ };
449
+ }