@remix-run/ui 0.2.0 → 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 (232) hide show
  1. package/dist/animation/demos/animation.demo.d.ts +5 -0
  2. package/dist/animation/demos/animation.demo.js +92 -0
  3. package/dist/animation/demos/animation.demo.js.map +1 -0
  4. package/dist/animation/demos/aspect-ratio.d.ts +2 -0
  5. package/dist/animation/demos/aspect-ratio.js +69 -0
  6. package/dist/animation/demos/aspect-ratio.js.map +1 -0
  7. package/dist/animation/demos/bouncy-switch.d.ts +2 -0
  8. package/dist/animation/demos/bouncy-switch.js +36 -0
  9. package/dist/animation/demos/bouncy-switch.js.map +1 -0
  10. package/dist/animation/demos/color-interpolation.d.ts +1 -0
  11. package/dist/animation/demos/color-interpolation.js +53 -0
  12. package/dist/animation/demos/color-interpolation.js.map +1 -0
  13. package/dist/animation/demos/cube.d.ts +2 -0
  14. package/dist/animation/demos/cube.js +86 -0
  15. package/dist/animation/demos/cube.js.map +1 -0
  16. package/dist/animation/demos/default-animate.d.ts +2 -0
  17. package/dist/animation/demos/default-animate.js +89 -0
  18. package/dist/animation/demos/default-animate.js.map +1 -0
  19. package/dist/animation/demos/drag-release.d.ts +24 -0
  20. package/dist/animation/demos/drag-release.js +96 -0
  21. package/dist/animation/demos/drag-release.js.map +1 -0
  22. package/dist/animation/demos/enter.d.ts +1 -0
  23. package/dist/animation/demos/enter.js +19 -0
  24. package/dist/animation/demos/enter.js.map +1 -0
  25. package/dist/animation/demos/exit.d.ts +2 -0
  26. package/dist/animation/demos/exit.js +58 -0
  27. package/dist/animation/demos/exit.js.map +1 -0
  28. package/dist/animation/demos/flip-toggle.d.ts +2 -0
  29. package/dist/animation/demos/flip-toggle.js +37 -0
  30. package/dist/animation/demos/flip-toggle.js.map +1 -0
  31. package/dist/animation/demos/hold-to-confirm.d.ts +13 -0
  32. package/dist/animation/demos/hold-to-confirm.js +211 -0
  33. package/dist/animation/demos/hold-to-confirm.js.map +1 -0
  34. package/dist/animation/demos/html-content.d.ts +2 -0
  35. package/dist/animation/demos/html-content.js +30 -0
  36. package/dist/animation/demos/html-content.js.map +1 -0
  37. package/dist/animation/demos/interruptible-keyframes.d.ts +1 -0
  38. package/dist/animation/demos/interruptible-keyframes.js +48 -0
  39. package/dist/animation/demos/interruptible-keyframes.js.map +1 -0
  40. package/dist/animation/demos/keyframes.d.ts +1 -0
  41. package/dist/animation/demos/keyframes.js +24 -0
  42. package/dist/animation/demos/keyframes.js.map +1 -0
  43. package/dist/animation/demos/material-ripple.d.ts +2 -0
  44. package/dist/animation/demos/material-ripple.js +127 -0
  45. package/dist/animation/demos/material-ripple.js.map +1 -0
  46. package/dist/animation/demos/mixin-presence-list.d.ts +2 -0
  47. package/dist/animation/demos/mixin-presence-list.js +88 -0
  48. package/dist/animation/demos/mixin-presence-list.js.map +1 -0
  49. package/dist/animation/demos/mixin-reclaim.d.ts +2 -0
  50. package/dist/animation/demos/mixin-reclaim.js +90 -0
  51. package/dist/animation/demos/mixin-reclaim.js.map +1 -0
  52. package/dist/animation/demos/multi-state-badge.d.ts +2 -0
  53. package/dist/animation/demos/multi-state-badge.js +231 -0
  54. package/dist/animation/demos/multi-state-badge.js.map +1 -0
  55. package/dist/animation/demos/press.d.ts +2 -0
  56. package/dist/animation/demos/press.js +63 -0
  57. package/dist/animation/demos/press.js.map +1 -0
  58. package/dist/animation/demos/reordering.d.ts +2 -0
  59. package/dist/animation/demos/reordering.js +50 -0
  60. package/dist/animation/demos/reordering.js.map +1 -0
  61. package/dist/animation/demos/rolling-square.d.ts +2 -0
  62. package/dist/animation/demos/rolling-square.js +42 -0
  63. package/dist/animation/demos/rolling-square.js.map +1 -0
  64. package/dist/animation/demos/rotate.d.ts +1 -0
  65. package/dist/animation/demos/rotate.js +18 -0
  66. package/dist/animation/demos/rotate.js.map +1 -0
  67. package/dist/animation/demos/shared-layout.d.ts +2 -0
  68. package/dist/animation/demos/shared-layout.js +114 -0
  69. package/dist/animation/demos/shared-layout.js.map +1 -0
  70. package/dist/animation/demos/spring.demo.d.ts +6 -0
  71. package/dist/animation/demos/spring.demo.js +304 -0
  72. package/dist/animation/demos/spring.demo.js.map +1 -0
  73. package/dist/animation/demos/transition-options.d.ts +1 -0
  74. package/dist/animation/demos/transition-options.js +21 -0
  75. package/dist/animation/demos/transition-options.js.map +1 -0
  76. package/dist/animation/spring.d.ts +22 -0
  77. package/dist/animation/spring.js +22 -0
  78. package/dist/animation/spring.js.map +1 -1
  79. package/dist/components/accordion/demos/card.demo.d.ts +1 -0
  80. package/dist/components/accordion/demos/card.demo.js +1 -0
  81. package/dist/components/accordion/demos/card.demo.js.map +1 -1
  82. package/dist/components/accordion/demos/multiple.demo.d.ts +1 -0
  83. package/dist/components/accordion/demos/multiple.demo.js +1 -0
  84. package/dist/components/accordion/demos/multiple.demo.js.map +1 -1
  85. package/dist/components/accordion/demos/overview.demo.d.ts +1 -0
  86. package/dist/components/accordion/demos/overview.demo.js +1 -0
  87. package/dist/components/accordion/demos/overview.demo.js.map +1 -1
  88. package/dist/components/anchor/anchor.d.ts +8 -1
  89. package/dist/components/anchor/anchor.js +30 -11
  90. package/dist/components/anchor/anchor.js.map +1 -1
  91. package/dist/components/breadcrumbs/{demos/basic.demo.d.ts → basic.demo.d.ts} +1 -0
  92. package/dist/components/breadcrumbs/{demos/basic.demo.js → basic.demo.js} +1 -0
  93. package/dist/components/breadcrumbs/basic.demo.js.map +1 -0
  94. package/dist/components/breadcrumbs/{demos/separator.demo.d.ts → separator.demo.d.ts} +1 -0
  95. package/dist/components/breadcrumbs/{demos/separator.demo.js → separator.demo.js} +1 -0
  96. package/dist/components/breadcrumbs/separator.demo.js.map +1 -0
  97. package/dist/components/button/demos/aliases.demo.d.ts +1 -0
  98. package/dist/components/button/demos/aliases.demo.js +1 -0
  99. package/dist/components/button/demos/aliases.demo.js.map +1 -1
  100. package/dist/components/button/demos/basic.demo.d.ts +1 -0
  101. package/dist/components/button/demos/basic.demo.js +1 -0
  102. package/dist/components/button/demos/basic.demo.js.map +1 -1
  103. package/dist/components/button/demos/states.demo.d.ts +1 -0
  104. package/dist/components/button/demos/states.demo.js +1 -0
  105. package/dist/components/button/demos/states.demo.js.map +1 -1
  106. package/dist/components/combobox/{demos/overview.demo.d.ts → overview.demo.d.ts} +1 -0
  107. package/dist/components/combobox/{demos/overview.demo.js → overview.demo.js} +1 -0
  108. package/dist/components/combobox/overview.demo.js.map +1 -0
  109. package/dist/components/listbox/{demos/overview.demo.d.ts → overview.demo.d.ts} +1 -0
  110. package/dist/components/listbox/{demos/overview.demo.js → overview.demo.js} +1 -0
  111. package/dist/components/listbox/overview.demo.js.map +1 -0
  112. package/dist/components/menu/demos/bubbling.demo.d.ts +1 -0
  113. package/dist/components/menu/demos/bubbling.demo.js +1 -0
  114. package/dist/components/menu/demos/bubbling.demo.js.map +1 -1
  115. package/dist/components/menu/demos/context-trigger.demo.d.ts +7 -0
  116. package/dist/components/menu/demos/context-trigger.demo.js +87 -0
  117. package/dist/components/menu/demos/context-trigger.demo.js.map +1 -0
  118. package/dist/components/menu/demos/overview.demo.d.ts +1 -0
  119. package/dist/components/menu/demos/overview.demo.js +1 -0
  120. package/dist/components/menu/demos/overview.demo.js.map +1 -1
  121. package/dist/components/menu/menu.d.ts +3 -0
  122. package/dist/components/menu/menu.js +55 -0
  123. package/dist/components/menu/menu.js.map +1 -1
  124. package/dist/components/popover/{demos/overview.demo.d.ts → overview.demo.d.ts} +1 -0
  125. package/dist/components/popover/{demos/overview.demo.js → overview.demo.js} +1 -0
  126. package/dist/components/popover/overview.demo.js.map +1 -0
  127. package/dist/components/popover/popover.d.ts +2 -2
  128. package/dist/components/popover/popover.js +11 -4
  129. package/dist/components/popover/popover.js.map +1 -1
  130. package/dist/components/select/{demos/deconstructed.demo.d.ts → deconstructed.demo.d.ts} +1 -0
  131. package/dist/components/select/{demos/deconstructed.demo.js → deconstructed.demo.js} +1 -0
  132. package/dist/components/select/deconstructed.demo.js.map +1 -0
  133. package/dist/components/select/{demos/overview.demo.d.ts → overview.demo.d.ts} +1 -0
  134. package/dist/components/select/{demos/overview.demo.js → overview.demo.js} +1 -0
  135. package/dist/components/select/overview.demo.js.map +1 -0
  136. package/dist/components/select/select.js +1 -1
  137. package/dist/components/select/select.js.map +1 -1
  138. package/dist/runtime/client-entries.d.ts +1 -2
  139. package/dist/runtime/client-entries.js.map +1 -1
  140. package/dist/runtime/component.d.ts +2 -2
  141. package/dist/runtime/component.js +1 -1
  142. package/dist/runtime/component.js.map +1 -1
  143. package/dist/runtime/demos/basic.demo.d.ts +6 -0
  144. package/dist/runtime/demos/basic.demo.js +16 -0
  145. package/dist/runtime/demos/basic.demo.js.map +1 -0
  146. package/dist/runtime/demos/controlled-uncontrolled-values.demo.d.ts +6 -0
  147. package/dist/runtime/demos/controlled-uncontrolled-values.demo.js +77 -0
  148. package/dist/runtime/demos/controlled-uncontrolled-values.demo.js.map +1 -0
  149. package/dist/runtime/demos/keyed-list.demo.d.ts +6 -0
  150. package/dist/runtime/demos/keyed-list.demo.js +62 -0
  151. package/dist/runtime/demos/keyed-list.demo.js.map +1 -0
  152. package/dist/runtime/demos/readme.demo.d.ts +5 -0
  153. package/dist/runtime/demos/readme.demo.js +396 -0
  154. package/dist/runtime/demos/readme.demo.js.map +1 -0
  155. package/dist/runtime/diff-dom.js +194 -21
  156. package/dist/runtime/diff-dom.js.map +1 -1
  157. package/dist/runtime/frame.d.ts +7 -0
  158. package/dist/runtime/frame.js +176 -41
  159. package/dist/runtime/frame.js.map +1 -1
  160. package/dist/runtime/jsx.d.ts +2 -3
  161. package/dist/runtime/jsx.js.map +1 -1
  162. package/dist/server/stream.js +3 -2
  163. package/dist/server/stream.js.map +1 -1
  164. package/dist/style/style.js +27 -10
  165. package/dist/style/style.js.map +1 -1
  166. package/package.json +3 -3
  167. package/src/animation/demos/animation.demo.tsx +194 -0
  168. package/src/animation/demos/aspect-ratio.tsx +120 -0
  169. package/src/animation/demos/bouncy-switch.tsx +47 -0
  170. package/src/animation/demos/color-interpolation.tsx +73 -0
  171. package/src/animation/demos/cube.tsx +116 -0
  172. package/src/animation/demos/default-animate.tsx +129 -0
  173. package/src/animation/demos/drag-release.ts +118 -0
  174. package/src/animation/demos/enter.tsx +22 -0
  175. package/src/animation/demos/exit.tsx +79 -0
  176. package/src/animation/demos/flip-toggle.tsx +46 -0
  177. package/src/animation/demos/hold-to-confirm.tsx +317 -0
  178. package/src/animation/demos/html-content.tsx +41 -0
  179. package/src/animation/demos/interruptible-keyframes.tsx +62 -0
  180. package/src/animation/demos/keyframes.tsx +29 -0
  181. package/src/animation/demos/material-ripple.tsx +158 -0
  182. package/src/animation/demos/mixin-presence-list.tsx +121 -0
  183. package/src/animation/demos/mixin-reclaim.tsx +125 -0
  184. package/src/animation/demos/multi-state-badge.tsx +426 -0
  185. package/src/animation/demos/press.tsx +67 -0
  186. package/src/animation/demos/reordering.tsx +65 -0
  187. package/src/animation/demos/rolling-square.tsx +57 -0
  188. package/src/animation/demos/rotate.tsx +21 -0
  189. package/src/animation/demos/shared-layout.tsx +211 -0
  190. package/src/animation/demos/spring.demo.tsx +385 -0
  191. package/src/animation/demos/transition-options.tsx +24 -0
  192. package/src/animation/spring.ts +22 -0
  193. package/src/components/accordion/demos/card.demo.tsx +1 -0
  194. package/src/components/accordion/demos/multiple.demo.tsx +1 -0
  195. package/src/components/accordion/demos/overview.demo.tsx +1 -0
  196. package/src/components/anchor/README.md +11 -3
  197. package/src/components/anchor/anchor.ts +53 -12
  198. package/src/components/breadcrumbs/{demos/basic.demo.tsx → basic.demo.tsx} +1 -0
  199. package/src/components/breadcrumbs/{demos/separator.demo.tsx → separator.demo.tsx} +1 -0
  200. package/src/components/button/demos/aliases.demo.tsx +1 -0
  201. package/src/components/button/demos/basic.demo.tsx +1 -0
  202. package/src/components/button/demos/states.demo.tsx +1 -0
  203. package/src/components/combobox/{demos/overview.demo.tsx → overview.demo.tsx} +1 -0
  204. package/src/components/listbox/{demos/overview.demo.tsx → overview.demo.tsx} +1 -0
  205. package/src/components/menu/README.md +26 -2
  206. package/src/components/menu/demos/bubbling.demo.tsx +1 -0
  207. package/src/components/menu/demos/context-trigger.demo.tsx +133 -0
  208. package/src/components/menu/demos/overview.demo.tsx +1 -0
  209. package/src/components/menu/menu.tsx +73 -1
  210. package/src/components/popover/{demos/overview.demo.tsx → overview.demo.tsx} +1 -0
  211. package/src/components/popover/popover.ts +18 -9
  212. package/src/components/select/{demos/deconstructed.demo.tsx → deconstructed.demo.tsx} +1 -0
  213. package/src/components/select/{demos/overview.demo.tsx → overview.demo.tsx} +1 -0
  214. package/src/components/select/select.tsx +1 -1
  215. package/src/runtime/client-entries.ts +1 -5
  216. package/src/runtime/component.ts +3 -3
  217. package/src/runtime/demos/basic.demo.tsx +21 -0
  218. package/src/runtime/demos/controlled-uncontrolled-values.demo.tsx +177 -0
  219. package/src/runtime/demos/keyed-list.demo.tsx +94 -0
  220. package/src/runtime/demos/readme.demo.tsx +702 -0
  221. package/src/runtime/diff-dom.ts +270 -20
  222. package/src/runtime/frame.ts +218 -41
  223. package/src/runtime/jsx.ts +6 -10
  224. package/src/server/stream.ts +7 -2
  225. package/src/style/style.ts +29 -10
  226. package/dist/components/breadcrumbs/demos/basic.demo.js.map +0 -1
  227. package/dist/components/breadcrumbs/demos/separator.demo.js.map +0 -1
  228. package/dist/components/combobox/demos/overview.demo.js.map +0 -1
  229. package/dist/components/listbox/demos/overview.demo.js.map +0 -1
  230. package/dist/components/popover/demos/overview.demo.js.map +0 -1
  231. package/dist/components/select/demos/deconstructed.demo.js.map +0 -1
  232. package/dist/components/select/demos/overview.demo.js.map +0 -1
@@ -0,0 +1,67 @@
1
+ import { createMixin, css, on, type Handle } from '@remix-run/ui'
2
+ import { spring } from '@remix-run/ui/animation'
3
+
4
+ export function Press(handle: Handle) {
5
+ let pressed = false
6
+
7
+ function setPressed(nextPressed: boolean) {
8
+ if (pressed === nextPressed) return
9
+ pressed = nextPressed
10
+ handle.update()
11
+ }
12
+
13
+ return () => (
14
+ <div
15
+ tabIndex={0}
16
+ mix={[
17
+ css({
18
+ width: 100,
19
+ height: 100,
20
+ backgroundColor: '#9911ff',
21
+ borderRadius: 5,
22
+ transition: `transform ${spring()}`,
23
+ '&:focus': {
24
+ outline: '4px solid rgba(0,120,255,0.7)',
25
+ outlineOffset: 1,
26
+ },
27
+ '&:hover, &:focus': {
28
+ transform: pressed ? 'scale(0.8)' : 'scale(1.2)',
29
+ },
30
+ // or use default browser :active but lose keyboard "down" press states
31
+ // '&:active': {
32
+ // transform: 'scale(0.8)',
33
+ // },
34
+ }),
35
+ onPressDown(() => {
36
+ setPressed(true)
37
+ }),
38
+ onPressUp(() => {
39
+ setPressed(false)
40
+ }),
41
+ ]}
42
+ />
43
+ )
44
+ }
45
+
46
+ const onPressDown = createMixin<HTMLElement, [handler: () => void]>(() => (handler) => [
47
+ on('pointerdown', (event) => {
48
+ if (event.isPrimary === false) return
49
+ handler()
50
+ }),
51
+ on('keydown', (event) => {
52
+ if (!(event.key === 'Enter' || event.key === ' ') || event.repeat) return
53
+ event.preventDefault()
54
+ handler()
55
+ }),
56
+ ])
57
+
58
+ const onPressUp = createMixin<HTMLElement, [handler: () => void]>(() => (handler) => [
59
+ on('pointerup', handler),
60
+ on('pointerleave', handler),
61
+ on('keyup', (event) => {
62
+ if (!(event.key === 'Enter' || event.key === ' ')) return
63
+ event.preventDefault()
64
+ handler()
65
+ }),
66
+ on('blur', handler),
67
+ ])
@@ -0,0 +1,65 @@
1
+ import { css, type Handle } from '@remix-run/ui'
2
+ import { animateLayout, spring } from '@remix-run/ui/animation'
3
+
4
+ const initialOrder = ['#ff0088', '#dd00ee', '#9911ff', '#0d63f8']
5
+
6
+ function shuffle<T>(array: T[]): T[] {
7
+ let result = [...array]
8
+ for (let i = result.length - 1; i > 0; i--) {
9
+ let j = Math.floor(Math.random() * (i + 1))
10
+ ;[result[i], result[j]] = [result[j], result[i]]
11
+ }
12
+ return result
13
+ }
14
+
15
+ export function Reordering(handle: Handle) {
16
+ let order = initialOrder
17
+
18
+ function scheduleNextShuffle() {
19
+ setTimeout(() => {
20
+ if (handle.signal.aborted) return
21
+ order = shuffle(order)
22
+ handle.update()
23
+ scheduleNextShuffle()
24
+ }, 1000)
25
+ }
26
+
27
+ scheduleNextShuffle()
28
+
29
+ return () => (
30
+ <ul
31
+ mix={[
32
+ css({
33
+ listStyle: 'none',
34
+ padding: 0,
35
+ margin: 0,
36
+ position: 'relative',
37
+ display: 'flex',
38
+ flexWrap: 'wrap',
39
+ gap: 10,
40
+ width: 220,
41
+ flexDirection: 'row',
42
+ justifyContent: 'center',
43
+ alignItems: 'center',
44
+ }),
45
+ ]}
46
+ >
47
+ {order.map((backgroundColor) => (
48
+ <li
49
+ key={backgroundColor}
50
+ mix={[
51
+ css({
52
+ width: 100,
53
+ height: 100,
54
+ borderRadius: 10,
55
+ }),
56
+ animateLayout({
57
+ ...spring({ duration: 600, bounce: 0.2 }),
58
+ }),
59
+ ]}
60
+ style={{ backgroundColor }}
61
+ />
62
+ ))}
63
+ </ul>
64
+ )
65
+ }
@@ -0,0 +1,57 @@
1
+ import { type Handle } from '@remix-run/ui'
2
+ import { css, on } from '@remix-run/ui'
3
+
4
+ import { spring } from '@remix-run/ui/animation'
5
+
6
+ export function RollingSquare(handle: Handle) {
7
+ let toggled = false
8
+
9
+ return () => (
10
+ <div
11
+ mix={[
12
+ css({
13
+ display: 'flex',
14
+ flexDirection: 'column',
15
+ alignItems: 'center',
16
+ justifyContent: 'center',
17
+ gap: '20px',
18
+ minWidth: '300px',
19
+ }),
20
+ ]}
21
+ >
22
+ <div
23
+ mix={[
24
+ css({
25
+ width: '80px',
26
+ height: '80px',
27
+ backgroundColor: '#8df0cc',
28
+ borderRadius: '10px',
29
+ transition: `transform ${spring({ duration: 500, bounce: 0.5 })}`,
30
+ }),
31
+ ]}
32
+ style={{
33
+ transform: toggled ? 'translateX(100%) rotate(180deg)' : 'translateX(-100%)',
34
+ }}
35
+ />
36
+ <button
37
+ mix={[
38
+ css({
39
+ backgroundColor: '#8df0cc',
40
+ color: '#0f1115',
41
+ borderRadius: '5px',
42
+ padding: '10px',
43
+ margin: '10px',
44
+ border: 'none',
45
+ cursor: 'pointer',
46
+ }),
47
+ on('click', () => {
48
+ toggled = !toggled
49
+ handle.update()
50
+ }),
51
+ ]}
52
+ >
53
+ Toggle position
54
+ </button>
55
+ </div>
56
+ )
57
+ }
@@ -0,0 +1,21 @@
1
+ import { css } from '@remix-run/ui'
2
+
3
+ export function Rotate() {
4
+ return () => (
5
+ <div
6
+ mix={[
7
+ css({
8
+ width: 100,
9
+ height: 100,
10
+ backgroundColor: '#ff0088',
11
+ borderRadius: 5,
12
+ '@keyframes rotate-demo': {
13
+ '0%': { transform: 'rotate(0deg)' },
14
+ '100%': { transform: 'rotate(360deg)' },
15
+ },
16
+ animation: `rotate-demo 1s ease-in-out 1`,
17
+ }),
18
+ ]}
19
+ />
20
+ )
21
+ }
@@ -0,0 +1,211 @@
1
+ import { css, on, type Handle, type RemixNode } from '@remix-run/ui'
2
+ import { animateEntrance, animateExit } from '@remix-run/ui/animation'
3
+
4
+ const ease = 'cubic-bezier(0.26, 0.02, 0.23, 0.94)'
5
+
6
+ function OverlapExample(handle: Handle<{ state: boolean }>) {
7
+ let shouldAnimate = false
8
+ handle.queueTask(() => {
9
+ shouldAnimate = true
10
+ })
11
+
12
+ return () => {
13
+ let { state } = handle.props
14
+ let animationMix = [
15
+ animateExit({
16
+ opacity: 0,
17
+ transform: 'scale(0.8)',
18
+ duration: 300,
19
+ easing: ease,
20
+ }),
21
+ ]
22
+ if (shouldAnimate) {
23
+ animationMix.unshift(
24
+ animateEntrance({
25
+ opacity: 0,
26
+ transform: 'scale(0.6)',
27
+ duration: 300,
28
+ easing: ease,
29
+ }),
30
+ )
31
+ }
32
+
33
+ return (
34
+ <div
35
+ // grid layout so children render in the same position
36
+ mix={[css({ display: 'grid', width: 80, height: 80, '& > *': { gridArea: '1 / 1' } })]}
37
+ >
38
+ {state ? (
39
+ <div key="filled" mix={animationMix}>
40
+ <Circle filled>
41
+ <FilledIcon />
42
+ </Circle>
43
+ </div>
44
+ ) : (
45
+ <div key="outline" mix={animationMix}>
46
+ <Circle>
47
+ <OutlineIcon />
48
+ </Circle>
49
+ </div>
50
+ )}
51
+ </div>
52
+ )
53
+ }
54
+ }
55
+
56
+ function WaitExample(handle: Handle<{ state: boolean }>) {
57
+ let shouldAnimate = false
58
+ handle.queueTask(() => {
59
+ shouldAnimate = true
60
+ })
61
+
62
+ return () => {
63
+ let { state } = handle.props
64
+ let animationMix = [
65
+ animateExit({
66
+ opacity: 0,
67
+ transform: 'scale(0.8)',
68
+ duration: 300,
69
+ easing: ease,
70
+ }),
71
+ ]
72
+ if (shouldAnimate) {
73
+ animationMix.unshift(
74
+ animateEntrance({
75
+ opacity: 0,
76
+ transform: 'scale(0.6)',
77
+ duration: 300,
78
+ easing: ease,
79
+ delay: 300,
80
+ }),
81
+ )
82
+ }
83
+
84
+ return (
85
+ <div
86
+ // grid layout so children render in the same position
87
+ mix={[css({ display: 'grid', width: 80, height: 80, '& > *': { gridArea: '1 / 1' } })]}
88
+ >
89
+ {state ? (
90
+ <div key="filled" mix={animationMix}>
91
+ <Circle filled>
92
+ <FilledIcon />
93
+ </Circle>
94
+ </div>
95
+ ) : (
96
+ <div key="outline" mix={animationMix}>
97
+ <Circle>
98
+ <OutlineIcon />
99
+ </Circle>
100
+ </div>
101
+ )}
102
+ </div>
103
+ )
104
+ }
105
+ }
106
+
107
+ export function SharedLayout(handle: Handle) {
108
+ let state = true
109
+
110
+ return () => (
111
+ <div
112
+ mix={[
113
+ css({
114
+ display: 'flex',
115
+ flexDirection: 'column',
116
+ alignItems: 'center',
117
+ gap: 16,
118
+ }),
119
+ ]}
120
+ >
121
+ <div mix={[css({ display: 'flex', gap: 16 })]}>
122
+ <OverlapExample state={state} />
123
+ <WaitExample state={state} />
124
+ </div>
125
+ <button
126
+ mix={[
127
+ css({
128
+ backgroundColor: '#0f1115',
129
+ color: '#f5f5f5',
130
+ border: 'none',
131
+ borderRadius: 8,
132
+ padding: '12px 32px',
133
+ fontSize: 14,
134
+ fontWeight: 500,
135
+ cursor: 'pointer',
136
+ transition: 'transform 100ms ease-in-out',
137
+ '&:active': {
138
+ transform: 'scale(0.95)',
139
+ },
140
+ }),
141
+ on('click', () => {
142
+ state = !state
143
+ handle.update()
144
+ }),
145
+ ]}
146
+ >
147
+ Switch
148
+ </button>
149
+ </div>
150
+ )
151
+ }
152
+
153
+ function Circle(handle: Handle<{ filled?: boolean; children: RemixNode }>) {
154
+ return () => (
155
+ <div
156
+ mix={[
157
+ css({
158
+ width: 80,
159
+ height: 80,
160
+ borderRadius: '50%',
161
+ display: 'flex',
162
+ alignItems: 'center',
163
+ justifyContent: 'center',
164
+ boxSizing: 'border-box',
165
+ backgroundColor: handle.props.filled ? '#0f1115' : 'transparent',
166
+ color: handle.props.filled ? '#f5f5f5' : '#0f1115',
167
+ border: handle.props.filled ? '2px solid #0f1115' : '2px solid #0f1115',
168
+ }),
169
+ ]}
170
+ >
171
+ {handle.props.children}
172
+ </div>
173
+ )
174
+ }
175
+
176
+ function FilledIcon() {
177
+ return () => (
178
+ <svg
179
+ width="24"
180
+ height="24"
181
+ viewBox="0 0 24 24"
182
+ fill="none"
183
+ stroke="currentColor"
184
+ strokeWidth="2"
185
+ strokeLinecap="round"
186
+ strokeLinejoin="round"
187
+ >
188
+ <path d="M21 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h6" />
189
+ <path d="m21 3-9 9" />
190
+ <path d="M15 3h6v6" />
191
+ </svg>
192
+ )
193
+ }
194
+
195
+ function OutlineIcon() {
196
+ return () => (
197
+ <svg
198
+ width="24"
199
+ height="24"
200
+ viewBox="0 0 24 24"
201
+ fill="none"
202
+ stroke="currentColor"
203
+ strokeWidth="2"
204
+ strokeLinecap="round"
205
+ strokeLinejoin="round"
206
+ >
207
+ <rect x="3" y="3" width="18" height="18" rx="2" />
208
+ <path d="M9 12h6" />
209
+ </svg>
210
+ )
211
+ }