@remix-run/ui 0.1.2 → 0.3.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 (318) hide show
  1. package/README.md +9 -7
  2. package/dist/animation/demos/animation.demo.d.ts +5 -0
  3. package/dist/animation/demos/animation.demo.js +92 -0
  4. package/dist/animation/demos/animation.demo.js.map +1 -0
  5. package/dist/animation/demos/aspect-ratio.d.ts +2 -0
  6. package/dist/animation/demos/aspect-ratio.js +69 -0
  7. package/dist/animation/demos/aspect-ratio.js.map +1 -0
  8. package/dist/animation/demos/bouncy-switch.d.ts +2 -0
  9. package/dist/animation/demos/bouncy-switch.js +36 -0
  10. package/dist/animation/demos/bouncy-switch.js.map +1 -0
  11. package/dist/animation/demos/color-interpolation.d.ts +1 -0
  12. package/dist/animation/demos/color-interpolation.js +53 -0
  13. package/dist/animation/demos/color-interpolation.js.map +1 -0
  14. package/dist/animation/demos/cube.d.ts +2 -0
  15. package/dist/animation/demos/cube.js +86 -0
  16. package/dist/animation/demos/cube.js.map +1 -0
  17. package/dist/animation/demos/default-animate.d.ts +2 -0
  18. package/dist/animation/demos/default-animate.js +89 -0
  19. package/dist/animation/demos/default-animate.js.map +1 -0
  20. package/dist/animation/demos/drag-release.d.ts +24 -0
  21. package/dist/animation/demos/drag-release.js +96 -0
  22. package/dist/animation/demos/drag-release.js.map +1 -0
  23. package/dist/animation/demos/enter.d.ts +1 -0
  24. package/dist/animation/demos/enter.js +19 -0
  25. package/dist/animation/demos/enter.js.map +1 -0
  26. package/dist/animation/demos/exit.d.ts +2 -0
  27. package/dist/animation/demos/exit.js +58 -0
  28. package/dist/animation/demos/exit.js.map +1 -0
  29. package/dist/animation/demos/flip-toggle.d.ts +2 -0
  30. package/dist/animation/demos/flip-toggle.js +37 -0
  31. package/dist/animation/demos/flip-toggle.js.map +1 -0
  32. package/dist/animation/demos/hold-to-confirm.d.ts +13 -0
  33. package/dist/animation/demos/hold-to-confirm.js +211 -0
  34. package/dist/animation/demos/hold-to-confirm.js.map +1 -0
  35. package/dist/animation/demos/html-content.d.ts +2 -0
  36. package/dist/animation/demos/html-content.js +30 -0
  37. package/dist/animation/demos/html-content.js.map +1 -0
  38. package/dist/animation/demos/interruptible-keyframes.d.ts +1 -0
  39. package/dist/animation/demos/interruptible-keyframes.js +48 -0
  40. package/dist/animation/demos/interruptible-keyframes.js.map +1 -0
  41. package/dist/animation/demos/keyframes.d.ts +1 -0
  42. package/dist/animation/demos/keyframes.js +24 -0
  43. package/dist/animation/demos/keyframes.js.map +1 -0
  44. package/dist/animation/demos/material-ripple.d.ts +2 -0
  45. package/dist/animation/demos/material-ripple.js +127 -0
  46. package/dist/animation/demos/material-ripple.js.map +1 -0
  47. package/dist/animation/demos/mixin-presence-list.d.ts +2 -0
  48. package/dist/animation/demos/mixin-presence-list.js +88 -0
  49. package/dist/animation/demos/mixin-presence-list.js.map +1 -0
  50. package/dist/animation/demos/mixin-reclaim.d.ts +2 -0
  51. package/dist/animation/demos/mixin-reclaim.js +90 -0
  52. package/dist/animation/demos/mixin-reclaim.js.map +1 -0
  53. package/dist/animation/demos/multi-state-badge.d.ts +2 -0
  54. package/dist/animation/demos/multi-state-badge.js +231 -0
  55. package/dist/animation/demos/multi-state-badge.js.map +1 -0
  56. package/dist/animation/demos/press.d.ts +2 -0
  57. package/dist/animation/demos/press.js +63 -0
  58. package/dist/animation/demos/press.js.map +1 -0
  59. package/dist/animation/demos/reordering.d.ts +2 -0
  60. package/dist/animation/demos/reordering.js +50 -0
  61. package/dist/animation/demos/reordering.js.map +1 -0
  62. package/dist/animation/demos/rolling-square.d.ts +2 -0
  63. package/dist/animation/demos/rolling-square.js +42 -0
  64. package/dist/animation/demos/rolling-square.js.map +1 -0
  65. package/dist/animation/demos/rotate.d.ts +1 -0
  66. package/dist/animation/demos/rotate.js +18 -0
  67. package/dist/animation/demos/rotate.js.map +1 -0
  68. package/dist/animation/demos/shared-layout.d.ts +2 -0
  69. package/dist/animation/demos/shared-layout.js +114 -0
  70. package/dist/animation/demos/shared-layout.js.map +1 -0
  71. package/dist/animation/demos/spring.demo.d.ts +6 -0
  72. package/dist/animation/demos/spring.demo.js +304 -0
  73. package/dist/animation/demos/spring.demo.js.map +1 -0
  74. package/dist/animation/demos/transition-options.d.ts +1 -0
  75. package/dist/animation/demos/transition-options.js +21 -0
  76. package/dist/animation/demos/transition-options.js.map +1 -0
  77. package/dist/animation/spring.d.ts +24 -31
  78. package/dist/animation/spring.js +31 -8
  79. package/dist/animation/spring.js.map +1 -1
  80. package/dist/components/accordion/accordion.d.ts +6 -6
  81. package/dist/components/accordion/accordion.js.map +1 -1
  82. package/dist/components/accordion/demos/card.demo.d.ts +1 -0
  83. package/dist/components/accordion/demos/card.demo.js +1 -0
  84. package/dist/components/accordion/demos/card.demo.js.map +1 -1
  85. package/dist/components/accordion/demos/multiple.demo.d.ts +1 -0
  86. package/dist/components/accordion/demos/multiple.demo.js +1 -0
  87. package/dist/components/accordion/demos/multiple.demo.js.map +1 -1
  88. package/dist/components/accordion/demos/overview.demo.d.ts +1 -0
  89. package/dist/components/accordion/demos/overview.demo.js +1 -0
  90. package/dist/components/accordion/demos/overview.demo.js.map +1 -1
  91. package/dist/components/anchor/anchor.d.ts +8 -1
  92. package/dist/components/anchor/anchor.js +30 -11
  93. package/dist/components/anchor/anchor.js.map +1 -1
  94. package/dist/components/breadcrumbs/{demos/basic.demo.d.ts → basic.demo.d.ts} +1 -0
  95. package/dist/components/breadcrumbs/{demos/basic.demo.js → basic.demo.js} +1 -0
  96. package/dist/components/breadcrumbs/basic.demo.js.map +1 -0
  97. package/dist/components/breadcrumbs/breadcrumbs.d.ts +1 -1
  98. package/dist/components/breadcrumbs/{demos/separator.demo.d.ts → separator.demo.d.ts} +1 -0
  99. package/dist/components/breadcrumbs/{demos/separator.demo.js → separator.demo.js} +1 -0
  100. package/dist/components/breadcrumbs/separator.demo.js.map +1 -0
  101. package/dist/components/button/button.d.ts +4 -4
  102. package/dist/components/button/button.js +3 -1
  103. package/dist/components/button/button.js.map +1 -1
  104. package/dist/components/button/demos/aliases.demo.d.ts +1 -0
  105. package/dist/components/button/demos/aliases.demo.js +1 -0
  106. package/dist/components/button/demos/aliases.demo.js.map +1 -1
  107. package/dist/components/button/demos/basic.demo.d.ts +1 -0
  108. package/dist/components/button/demos/basic.demo.js +1 -0
  109. package/dist/components/button/demos/basic.demo.js.map +1 -1
  110. package/dist/components/button/demos/states.demo.d.ts +1 -0
  111. package/dist/components/button/demos/states.demo.js +1 -0
  112. package/dist/components/button/demos/states.demo.js.map +1 -1
  113. package/dist/components/combobox/combobox.d.ts +10 -10
  114. package/dist/components/combobox/combobox.js.map +1 -1
  115. package/dist/components/combobox/{demos/overview.demo.d.ts → overview.demo.d.ts} +1 -0
  116. package/dist/components/combobox/{demos/overview.demo.js → overview.demo.js} +1 -0
  117. package/dist/components/combobox/overview.demo.js.map +1 -0
  118. package/dist/components/listbox/listbox.d.ts +3 -3
  119. package/dist/components/listbox/listbox.js.map +1 -1
  120. package/dist/components/listbox/{demos/overview.demo.d.ts → overview.demo.d.ts} +1 -0
  121. package/dist/components/listbox/{demos/overview.demo.js → overview.demo.js} +1 -0
  122. package/dist/components/listbox/overview.demo.js.map +1 -0
  123. package/dist/components/menu/demos/bubbling.demo.d.ts +1 -0
  124. package/dist/components/menu/demos/bubbling.demo.js +1 -0
  125. package/dist/components/menu/demos/bubbling.demo.js.map +1 -1
  126. package/dist/components/menu/demos/context-trigger.demo.d.ts +7 -0
  127. package/dist/components/menu/demos/context-trigger.demo.js +87 -0
  128. package/dist/components/menu/demos/context-trigger.demo.js.map +1 -0
  129. package/dist/components/menu/demos/overview.demo.d.ts +1 -0
  130. package/dist/components/menu/demos/overview.demo.js +1 -0
  131. package/dist/components/menu/demos/overview.demo.js.map +1 -1
  132. package/dist/components/menu/menu.d.ts +15 -12
  133. package/dist/components/menu/menu.js +55 -0
  134. package/dist/components/menu/menu.js.map +1 -1
  135. package/dist/components/popover/{demos/overview.demo.d.ts → overview.demo.d.ts} +1 -0
  136. package/dist/components/popover/{demos/overview.demo.js → overview.demo.js} +1 -0
  137. package/dist/components/popover/overview.demo.js.map +1 -0
  138. package/dist/components/popover/popover.d.ts +7 -7
  139. package/dist/components/popover/popover.js +11 -4
  140. package/dist/components/popover/popover.js.map +1 -1
  141. package/dist/components/select/{demos/deconstructed.demo.d.ts → deconstructed.demo.d.ts} +1 -0
  142. package/dist/components/select/{demos/deconstructed.demo.js → deconstructed.demo.js} +1 -0
  143. package/dist/components/select/deconstructed.demo.js.map +1 -0
  144. package/dist/components/select/{demos/overview.demo.d.ts → overview.demo.d.ts} +1 -0
  145. package/dist/components/select/{demos/overview.demo.js → overview.demo.js} +1 -0
  146. package/dist/components/select/overview.demo.js.map +1 -0
  147. package/dist/components/select/select.d.ts +10 -10
  148. package/dist/components/select/select.js +1 -1
  149. package/dist/components/select/select.js.map +1 -1
  150. package/dist/components/tabs/tabs.d.ts +3 -3
  151. package/dist/index.d.ts +1 -1
  152. package/dist/index.js.map +1 -1
  153. package/dist/interactions/keydown/keydown.d.ts +1 -1
  154. package/dist/interactions/outside-click/outside-click-mixin.d.ts +2 -1
  155. package/dist/interactions/typeahead/typeahead-mixin.d.ts +3 -1
  156. package/dist/runtime/client-entries.d.ts +1 -2
  157. package/dist/runtime/client-entries.js.map +1 -1
  158. package/dist/runtime/component.d.ts +20 -17
  159. package/dist/runtime/component.js +15 -6
  160. package/dist/runtime/component.js.map +1 -1
  161. package/dist/runtime/core/attributes.d.ts +3 -1
  162. package/dist/runtime/core/attributes.js +19 -1
  163. package/dist/runtime/core/attributes.js.map +1 -1
  164. package/dist/runtime/core/props.js +6 -5
  165. package/dist/runtime/core/props.js.map +1 -1
  166. package/dist/runtime/demos/basic.demo.d.ts +6 -0
  167. package/dist/runtime/demos/basic.demo.js +16 -0
  168. package/dist/runtime/demos/basic.demo.js.map +1 -0
  169. package/dist/runtime/demos/controlled-uncontrolled-values.demo.d.ts +6 -0
  170. package/dist/runtime/demos/controlled-uncontrolled-values.demo.js +77 -0
  171. package/dist/runtime/demos/controlled-uncontrolled-values.demo.js.map +1 -0
  172. package/dist/runtime/demos/keyed-list.demo.d.ts +6 -0
  173. package/dist/runtime/demos/keyed-list.demo.js +62 -0
  174. package/dist/runtime/demos/keyed-list.demo.js.map +1 -0
  175. package/dist/runtime/demos/readme.demo.d.ts +5 -0
  176. package/dist/runtime/demos/readme.demo.js +396 -0
  177. package/dist/runtime/demos/readme.demo.js.map +1 -0
  178. package/dist/runtime/diff-dom.js +194 -21
  179. package/dist/runtime/diff-dom.js.map +1 -1
  180. package/dist/runtime/dom.d.ts +16 -9
  181. package/dist/runtime/frame.d.ts +7 -0
  182. package/dist/runtime/frame.js +176 -41
  183. package/dist/runtime/frame.js.map +1 -1
  184. package/dist/runtime/jsx.d.ts +3 -4
  185. package/dist/runtime/jsx.js.map +1 -1
  186. package/dist/runtime/mixins/link-mixin.d.ts +5 -1
  187. package/dist/runtime/mixins/link-mixin.js.map +1 -1
  188. package/dist/runtime/mixins/mixin.d.ts +2 -1
  189. package/dist/runtime/mixins/mixin.js.map +1 -1
  190. package/dist/runtime/mixins/ref-mixin.d.ts +2 -1
  191. package/dist/runtime/mixins/ref-mixin.js.map +1 -1
  192. package/dist/runtime/reconcile.d.ts +1 -1
  193. package/dist/runtime/reconcile.js +19 -5
  194. package/dist/runtime/reconcile.js.map +1 -1
  195. package/dist/runtime/scheduler.d.ts +10 -10
  196. package/dist/runtime/scheduler.js +1 -1
  197. package/dist/runtime/scheduler.js.map +1 -1
  198. package/dist/runtime/svg-attributes.js +1 -0
  199. package/dist/runtime/svg-attributes.js.map +1 -1
  200. package/dist/runtime/to-vnode.js +2 -2
  201. package/dist/runtime/to-vnode.js.map +1 -1
  202. package/dist/runtime/vnode.d.ts +6 -1
  203. package/dist/runtime/vnode.js +4 -0
  204. package/dist/runtime/vnode.js.map +1 -1
  205. package/dist/server/stream.d.ts +2 -0
  206. package/dist/server/stream.js +82 -11
  207. package/dist/server/stream.js.map +1 -1
  208. package/dist/style/css-mixin.d.ts +2 -1
  209. package/dist/style/css-mixin.js.map +1 -1
  210. package/dist/style/index.d.ts +1 -1
  211. package/dist/style/style.js +27 -10
  212. package/dist/style/style.js.map +1 -1
  213. package/dist/style/stylesheet.d.ts +13 -13
  214. package/dist/style/stylesheet.js.map +1 -1
  215. package/dist/theme/contract.d.ts +24 -222
  216. package/dist/theme/contract.js +2 -2
  217. package/dist/theme/contract.js.map +1 -1
  218. package/dist/theme/glyph-contract.d.ts +1 -3
  219. package/dist/theme/glyph-contract.js.map +1 -1
  220. package/dist/theme/presets/rmx-01/index.d.ts +4 -2
  221. package/dist/theme/presets/rmx-01/index.js.map +1 -1
  222. package/dist/utils/scroll-lock.d.ts +3 -3
  223. package/dist/utils/scroll-lock.js.map +1 -1
  224. package/package.json +4 -4
  225. package/src/animation/README.md +7 -5
  226. package/src/animation/demos/animation.demo.tsx +194 -0
  227. package/src/animation/demos/aspect-ratio.tsx +120 -0
  228. package/src/animation/demos/bouncy-switch.tsx +47 -0
  229. package/src/animation/demos/color-interpolation.tsx +73 -0
  230. package/src/animation/demos/cube.tsx +116 -0
  231. package/src/animation/demos/default-animate.tsx +129 -0
  232. package/src/animation/demos/drag-release.ts +118 -0
  233. package/src/animation/demos/enter.tsx +22 -0
  234. package/src/animation/demos/exit.tsx +79 -0
  235. package/src/animation/demos/flip-toggle.tsx +46 -0
  236. package/src/animation/demos/hold-to-confirm.tsx +317 -0
  237. package/src/animation/demos/html-content.tsx +41 -0
  238. package/src/animation/demos/interruptible-keyframes.tsx +62 -0
  239. package/src/animation/demos/keyframes.tsx +29 -0
  240. package/src/animation/demos/material-ripple.tsx +158 -0
  241. package/src/animation/demos/mixin-presence-list.tsx +121 -0
  242. package/src/animation/demos/mixin-reclaim.tsx +125 -0
  243. package/src/animation/demos/multi-state-badge.tsx +426 -0
  244. package/src/animation/demos/press.tsx +67 -0
  245. package/src/animation/demos/reordering.tsx +65 -0
  246. package/src/animation/demos/rolling-square.tsx +57 -0
  247. package/src/animation/demos/rotate.tsx +21 -0
  248. package/src/animation/demos/shared-layout.tsx +211 -0
  249. package/src/animation/demos/spring.demo.tsx +385 -0
  250. package/src/animation/demos/transition-options.tsx +24 -0
  251. package/src/animation/spring.ts +43 -9
  252. package/src/components/accordion/accordion.tsx +7 -5
  253. package/src/components/accordion/demos/card.demo.tsx +1 -0
  254. package/src/components/accordion/demos/multiple.demo.tsx +1 -0
  255. package/src/components/accordion/demos/overview.demo.tsx +1 -0
  256. package/src/components/anchor/README.md +11 -3
  257. package/src/components/anchor/anchor.ts +54 -13
  258. package/src/components/breadcrumbs/{demos/basic.demo.tsx → basic.demo.tsx} +1 -0
  259. package/src/components/breadcrumbs/breadcrumbs.tsx +1 -1
  260. package/src/components/breadcrumbs/{demos/separator.demo.tsx → separator.demo.tsx} +1 -0
  261. package/src/components/button/button.tsx +30 -18
  262. package/src/components/button/demos/aliases.demo.tsx +1 -0
  263. package/src/components/button/demos/basic.demo.tsx +1 -0
  264. package/src/components/button/demos/states.demo.tsx +1 -0
  265. package/src/components/combobox/README.md +1 -1
  266. package/src/components/combobox/combobox.tsx +30 -12
  267. package/src/components/combobox/{demos/overview.demo.tsx → overview.demo.tsx} +1 -0
  268. package/src/components/glyph/glyph.tsx +2 -2
  269. package/src/components/listbox/listbox.ts +88 -83
  270. package/src/components/listbox/{demos/overview.demo.tsx → overview.demo.tsx} +1 -0
  271. package/src/components/menu/README.md +26 -2
  272. package/src/components/menu/demos/bubbling.demo.tsx +1 -0
  273. package/src/components/menu/demos/context-trigger.demo.tsx +133 -0
  274. package/src/components/menu/demos/overview.demo.tsx +1 -0
  275. package/src/components/menu/menu.tsx +100 -16
  276. package/src/components/popover/{demos/overview.demo.tsx → overview.demo.tsx} +1 -0
  277. package/src/components/popover/popover.ts +101 -81
  278. package/src/components/select/{demos/deconstructed.demo.tsx → deconstructed.demo.tsx} +1 -0
  279. package/src/components/select/{demos/overview.demo.tsx → overview.demo.tsx} +1 -0
  280. package/src/components/select/select.tsx +27 -10
  281. package/src/index.ts +1 -0
  282. package/src/runtime/client-entries.ts +1 -5
  283. package/src/runtime/component.ts +37 -15
  284. package/src/runtime/core/attributes.ts +21 -0
  285. package/src/runtime/core/props.ts +6 -4
  286. package/src/runtime/demos/basic.demo.tsx +21 -0
  287. package/src/runtime/demos/controlled-uncontrolled-values.demo.tsx +177 -0
  288. package/src/runtime/demos/keyed-list.demo.tsx +94 -0
  289. package/src/runtime/demos/readme.demo.tsx +702 -0
  290. package/src/runtime/diff-dom.ts +270 -20
  291. package/src/runtime/dom.ts +16 -9
  292. package/src/runtime/frame.ts +218 -41
  293. package/src/runtime/jsx.ts +7 -11
  294. package/src/runtime/mixins/link-mixin.ts +88 -86
  295. package/src/runtime/mixins/mixin.ts +9 -1
  296. package/src/runtime/mixins/ref-mixin.ts +4 -5
  297. package/src/runtime/reconcile.ts +23 -14
  298. package/src/runtime/scheduler.ts +20 -20
  299. package/src/runtime/svg-attributes.ts +1 -0
  300. package/src/runtime/to-vnode.ts +2 -2
  301. package/src/runtime/vnode.ts +10 -0
  302. package/src/server/README.md +4 -2
  303. package/src/server/stream.ts +84 -10
  304. package/src/style/css-mixin.ts +6 -1
  305. package/src/style/index.ts +1 -2
  306. package/src/style/style.ts +29 -10
  307. package/src/style/stylesheet.ts +14 -2
  308. package/src/theme/contract.ts +37 -5
  309. package/src/theme/glyph-contract.ts +3 -1
  310. package/src/theme/presets/rmx-01/index.ts +4 -2
  311. package/src/utils/scroll-lock.ts +7 -3
  312. package/dist/components/breadcrumbs/demos/basic.demo.js.map +0 -1
  313. package/dist/components/breadcrumbs/demos/separator.demo.js.map +0 -1
  314. package/dist/components/combobox/demos/overview.demo.js.map +0 -1
  315. package/dist/components/listbox/demos/overview.demo.js.map +0 -1
  316. package/dist/components/popover/demos/overview.demo.js.map +0 -1
  317. package/dist/components/select/demos/deconstructed.demo.js.map +0 -1
  318. package/dist/components/select/demos/overview.demo.js.map +0 -1
@@ -0,0 +1,29 @@
1
+ import { css } from '@remix-run/ui'
2
+
3
+ export function Keyframes() {
4
+ return () => (
5
+ <div mix={[css({ padding: 60 })]}>
6
+ <div
7
+ mix={[
8
+ css({
9
+ width: 100,
10
+ height: 100,
11
+ backgroundColor: '#888',
12
+ borderRadius: 5,
13
+ // 2s animation + 1s delay = 3s total cycle
14
+ // Original times [0, 0.2, 0.5, 0.8, 1] scaled to first 66.67% of animation
15
+ '@keyframes box-animation': {
16
+ '0%': { transform: 'scale(1) rotate(0deg)', borderRadius: '0%' },
17
+ '13.33%': { transform: 'scale(2) rotate(0deg)', borderRadius: '0%' },
18
+ '33.33%': { transform: 'scale(2) rotate(180deg)', borderRadius: '50%' },
19
+ '53.33%': { transform: 'scale(1) rotate(180deg)', borderRadius: '50%' },
20
+ '66.67%': { transform: 'scale(1) rotate(0deg)', borderRadius: '0%' },
21
+ '100%': { transform: 'scale(1) rotate(0deg)', borderRadius: '0%' },
22
+ },
23
+ animation: 'box-animation 3s ease-in-out infinite',
24
+ }),
25
+ ]}
26
+ />
27
+ </div>
28
+ )
29
+ }
@@ -0,0 +1,158 @@
1
+ import type { Handle } from '@remix-run/ui'
2
+ import { css, on, ref } from '@remix-run/ui'
3
+
4
+ import { animateEntrance, animateExit } from '@remix-run/ui/animation'
5
+
6
+ type Ripple = {
7
+ id: number
8
+ x: number
9
+ y: number
10
+ size: number
11
+ }
12
+
13
+ export function MaterialRipple(handle: Handle) {
14
+ let ripples: Ripple[] = []
15
+ let idCounter = 0
16
+ let buttonEl: HTMLButtonElement | null = null
17
+
18
+ function createRipple(originX: number, originY: number) {
19
+ if (!buttonEl) return
20
+
21
+ let rect = buttonEl.getBoundingClientRect()
22
+ let localX = originX - rect.left
23
+ let localY = originY - rect.top
24
+ let dx = Math.max(localX, rect.width - localX)
25
+ let dy = Math.max(localY, rect.height - localY)
26
+ let radius = Math.sqrt(dx * dx + dy * dy)
27
+ let size = radius * 2
28
+
29
+ let id = ++idCounter
30
+ ripples = [...ripples, { id, x: localX, y: localY, size }]
31
+ handle.update()
32
+ }
33
+
34
+ function removeAllRipples() {
35
+ if (ripples.length > 0) {
36
+ ripples = []
37
+ handle.update()
38
+ }
39
+ }
40
+
41
+ function createCenteredRipple() {
42
+ if (!buttonEl) return
43
+ let rect = buttonEl.getBoundingClientRect()
44
+ createRipple(rect.left + rect.width / 2, rect.top + rect.height / 2)
45
+ }
46
+
47
+ return () => (
48
+ <button
49
+ mix={[
50
+ ref((el) => {
51
+ buttonEl = el
52
+ }),
53
+ css({
54
+ position: 'relative',
55
+ display: 'inline-flex',
56
+ alignItems: 'center',
57
+ justifyContent: 'center',
58
+ padding: '10px 20px',
59
+ borderRadius: 4,
60
+ textTransform: 'uppercase',
61
+ backgroundColor: 'transparent',
62
+ color: '#7c3aed',
63
+ border: '1px solid #7c3aed',
64
+ userSelect: 'none',
65
+ cursor: 'pointer',
66
+ overflow: 'hidden',
67
+ letterSpacing: '0.2px',
68
+ WebkitTapHighlightColor: 'transparent',
69
+ transition: 'border-color 200ms linear, background-color 200ms linear',
70
+ '&:hover': {
71
+ borderColor: '#6d28d9',
72
+ backgroundColor: '#7c3aed20',
73
+ },
74
+ '&:focus-visible': {
75
+ outline: '2px solid #7c3aed80',
76
+ outlineOffset: 2,
77
+ },
78
+ }),
79
+ on('pointerdown', (event) => {
80
+ if (event.isPrimary === false) return
81
+ createRipple(event.clientX, event.clientY)
82
+ }),
83
+ on('pointerup', removeAllRipples),
84
+ on('pointercancel', removeAllRipples),
85
+ on('pointerleave', removeAllRipples),
86
+ on('keydown', (event) => {
87
+ if (!(event.key === 'Enter' || event.key === ' ') || event.repeat) return
88
+ event.preventDefault()
89
+ createCenteredRipple()
90
+ }),
91
+ on('keyup', (event) => {
92
+ if (!(event.key === 'Enter' || event.key === ' ')) return
93
+ event.preventDefault()
94
+ removeAllRipples()
95
+ }),
96
+ on('blur', removeAllRipples),
97
+ ]}
98
+ >
99
+ Click me
100
+ <span
101
+ aria-hidden="true"
102
+ mix={[
103
+ css({
104
+ position: 'absolute',
105
+ inset: 0,
106
+ overflow: 'hidden',
107
+ borderRadius: 'inherit',
108
+ pointerEvents: 'none',
109
+ }),
110
+ ]}
111
+ >
112
+ {ripples.map((ripple) => (
113
+ // Outer span: handles exit (fade out)
114
+ <span
115
+ key={ripple.id}
116
+ mix={[
117
+ css({
118
+ position: 'absolute',
119
+ borderRadius: '50%',
120
+ }),
121
+ animateExit({
122
+ opacity: 0,
123
+ duration: 550,
124
+ easing: 'ease-out',
125
+ }),
126
+ ]}
127
+ style={{
128
+ width: ripple.size,
129
+ height: ripple.size,
130
+ left: ripple.x - ripple.size / 2,
131
+ top: ripple.y - ripple.size / 2,
132
+ }}
133
+ >
134
+ {/* Inner span: handles enter (scale) so it doesn't get reversed when removed */}
135
+ <span
136
+ mix={[
137
+ css({
138
+ display: 'block',
139
+ width: '100%',
140
+ height: '100%',
141
+ borderRadius: 'inherit',
142
+ backgroundColor: 'currentColor',
143
+ opacity: 0.4,
144
+ }),
145
+ animateEntrance({
146
+ opacity: 0,
147
+ transform: 'scale(0)',
148
+ duration: 300,
149
+ easing: 'ease-out',
150
+ }),
151
+ ]}
152
+ />
153
+ </span>
154
+ ))}
155
+ </span>
156
+ </button>
157
+ )
158
+ }
@@ -0,0 +1,121 @@
1
+ import type { Handle } from '@remix-run/ui'
2
+ import { css, on } from '@remix-run/ui'
3
+
4
+ import { animateEntrance, animateExit } from '@remix-run/ui/animation'
5
+
6
+ let nextId = 1
7
+ function createItem() {
8
+ return { id: nextId++, label: `Row ${nextId - 1}` }
9
+ }
10
+
11
+ export function MixinPresenceList(handle: Handle) {
12
+ let items = [createItem(), createItem(), createItem()]
13
+
14
+ return () => (
15
+ <div
16
+ mix={[
17
+ css({
18
+ display: 'flex',
19
+ flexDirection: 'column',
20
+ gap: 10,
21
+ width: 220,
22
+ }),
23
+ ]}
24
+ >
25
+ <div mix={[css({ display: 'flex', gap: 8 })]}>
26
+ <button
27
+ mix={[
28
+ css({
29
+ flex: 1,
30
+ padding: '8px 10px',
31
+ border: 'none',
32
+ borderRadius: 6,
33
+ backgroundColor: '#0ea5e9',
34
+ color: 'white',
35
+ cursor: 'pointer',
36
+ '&:hover': { backgroundColor: '#0284c7' },
37
+ }),
38
+ on('click', () => {
39
+ items.unshift(createItem())
40
+ handle.update()
41
+ }),
42
+ ]}
43
+ >
44
+ Add
45
+ </button>
46
+ <button
47
+ mix={[
48
+ css({
49
+ flex: 1,
50
+ padding: '8px 10px',
51
+ border: 'none',
52
+ borderRadius: 6,
53
+ backgroundColor: '#ef4444',
54
+ color: 'white',
55
+ cursor: 'pointer',
56
+ '&:hover': { backgroundColor: '#dc2626' },
57
+ }),
58
+ on('click', () => {
59
+ items = items.slice(0, Math.max(0, items.length - 1))
60
+ handle.update()
61
+ }),
62
+ ]}
63
+ >
64
+ Remove
65
+ </button>
66
+ </div>
67
+ {items.map((item) => (
68
+ <div
69
+ key={String(item.id)}
70
+ mix={[
71
+ animateEntrance({
72
+ opacity: 0,
73
+ transform: 'translateY(-10px) scale(0.98)',
74
+ duration: 220,
75
+ easing: 'ease-out',
76
+ }),
77
+ animateExit({
78
+ opacity: 0,
79
+ transform: 'translateY(10px) scale(0.98)',
80
+ duration: 180,
81
+ easing: 'ease-in',
82
+ }),
83
+ css({
84
+ display: 'flex',
85
+ alignItems: 'center',
86
+ justifyContent: 'space-between',
87
+ padding: '10px 12px',
88
+ borderRadius: 8,
89
+ backgroundColor: '#f8fafc',
90
+ color: '#334155',
91
+ border: '1px solid #e2e8f0',
92
+ }),
93
+ ]}
94
+ >
95
+ <span>{item.label}</span>
96
+ <button
97
+ mix={[
98
+ css({
99
+ width: 24,
100
+ height: 24,
101
+ padding: 0,
102
+ border: 'none',
103
+ borderRadius: 4,
104
+ backgroundColor: 'transparent',
105
+ color: '#64748b',
106
+ cursor: 'pointer',
107
+ '&:hover': { backgroundColor: '#e2e8f0' },
108
+ }),
109
+ on('click', () => {
110
+ items = items.filter((entry) => entry.id !== item.id)
111
+ handle.update()
112
+ }),
113
+ ]}
114
+ >
115
+ ×
116
+ </button>
117
+ </div>
118
+ ))}
119
+ </div>
120
+ )
121
+ }
@@ -0,0 +1,125 @@
1
+ import type { Handle } from '@remix-run/ui'
2
+ import { css, on } from '@remix-run/ui'
3
+
4
+ import { animateEntrance, animateExit } from '@remix-run/ui/animation'
5
+
6
+ export function MixinReclaim(handle: Handle) {
7
+ let visible = true
8
+ let interruptTimer: number | undefined
9
+
10
+ function clearInterruptTimer() {
11
+ if (interruptTimer === undefined) return
12
+ window.clearTimeout(interruptTimer)
13
+ interruptTimer = undefined
14
+ }
15
+
16
+ function scheduleInterrupt() {
17
+ clearInterruptTimer()
18
+ visible = false
19
+ handle.update()
20
+ interruptTimer = window.setTimeout(() => {
21
+ visible = true
22
+ handle.update()
23
+ interruptTimer = undefined
24
+ }, 140)
25
+ }
26
+
27
+ return () => (
28
+ <div mix={[css({ display: 'flex', flexDirection: 'column', gap: 12, width: 240 })]}>
29
+ <div mix={[css({ display: 'flex', gap: 8 })]}>
30
+ <button
31
+ mix={[
32
+ css({
33
+ flex: 1,
34
+ padding: '8px 10px',
35
+ border: 'none',
36
+ borderRadius: 6,
37
+ backgroundColor: '#10b981',
38
+ color: 'white',
39
+ cursor: 'pointer',
40
+ '&:hover': { backgroundColor: '#059669' },
41
+ }),
42
+ on('click', () => {
43
+ clearInterruptTimer()
44
+ visible = true
45
+ handle.update()
46
+ }),
47
+ ]}
48
+ >
49
+ Show
50
+ </button>
51
+ <button
52
+ mix={[
53
+ css({
54
+ flex: 1,
55
+ padding: '8px 10px',
56
+ border: 'none',
57
+ borderRadius: 6,
58
+ backgroundColor: '#f59e0b',
59
+ color: 'white',
60
+ cursor: 'pointer',
61
+ '&:hover': { backgroundColor: '#d97706' },
62
+ }),
63
+ on('click', scheduleInterrupt),
64
+ ]}
65
+ >
66
+ Interrupt
67
+ </button>
68
+ </div>
69
+
70
+ <button
71
+ mix={[
72
+ css({
73
+ padding: '8px 10px',
74
+ border: 'none',
75
+ borderRadius: 6,
76
+ backgroundColor: '#ef4444',
77
+ color: 'white',
78
+ cursor: 'pointer',
79
+ '&:hover': { backgroundColor: '#dc2626' },
80
+ }),
81
+ on('click', () => {
82
+ clearInterruptTimer()
83
+ visible = false
84
+ handle.update()
85
+ }),
86
+ ]}
87
+ >
88
+ Hide
89
+ </button>
90
+
91
+ <div mix={[css({ minHeight: 100, display: 'grid', placeItems: 'center' })]}>
92
+ {visible && (
93
+ <div
94
+ key="reclaim-card"
95
+ mix={[
96
+ animateEntrance({
97
+ opacity: 0,
98
+ transform: 'translateY(12px) scale(0.94)',
99
+ duration: 260,
100
+ easing: 'ease-out',
101
+ }),
102
+ animateExit({
103
+ opacity: 0,
104
+ transform: 'translateY(-12px) scale(0.94)',
105
+ duration: 260,
106
+ easing: 'ease-in',
107
+ }),
108
+ css({
109
+ width: 200,
110
+ padding: '14px 16px',
111
+ borderRadius: 10,
112
+ background: 'linear-gradient(135deg, #7c3aed, #3b82f6)',
113
+ color: 'white',
114
+ textAlign: 'center',
115
+ fontWeight: 600,
116
+ }),
117
+ ]}
118
+ >
119
+ Reclaim Me Mid-Exit
120
+ </div>
121
+ )}
122
+ </div>
123
+ </div>
124
+ )
125
+ }