|
1 | 1 | 'use client'
|
2 | 2 |
|
3 |
| -import { useCallback } from 'react' |
4 | 3 | import { flushSync } from 'react-dom'
|
5 |
| -import { atom } from 'jotai' |
6 | 4 | import { useTheme } from 'next-themes'
|
7 | 5 | import { tv } from 'tailwind-variants'
|
8 | 6 |
|
9 | 7 | import { useIsClient } from '~/hooks/common/use-is-client'
|
10 |
| -import { isUndefined } from '~/lib/_' |
11 |
| -import { jotaiStore } from '~/lib/store' |
| 8 | +import { transitionViewIfSupported } from '~/lib/dom' |
12 | 9 |
|
13 | 10 | const styles = tv({
|
14 | 11 | base: 'rounded-inherit inline-flex h-[32px] w-[32px] items-center justify-center border-0 text-current',
|
@@ -88,16 +85,9 @@ const DarkIcon = () => {
|
88 | 85 | )
|
89 | 86 | }
|
90 | 87 |
|
91 |
| -const mousePositionAtom = atom({ x: 0, y: 0 }) |
92 | 88 | export const ThemeSwitcher = () => {
|
93 |
| - const handleClient: React.MouseEventHandler = useCallback((e) => { |
94 |
| - jotaiStore.set(mousePositionAtom, { |
95 |
| - x: e.clientX, |
96 |
| - y: e.clientY, |
97 |
| - }) |
98 |
| - }, []) |
99 | 89 | return (
|
100 |
| - <div className="relative inline-block" onClick={handleClient}> |
| 90 | + <div className="relative inline-block"> |
101 | 91 | <ThemeIndicator />
|
102 | 92 | <ButtonGroup />
|
103 | 93 | </div>
|
@@ -125,48 +115,9 @@ const ButtonGroup = () => {
|
125 | 115 | const { setTheme } = useTheme()
|
126 | 116 |
|
127 | 117 | const buildThemeTransition = (theme: 'light' | 'dark' | 'system') => {
|
128 |
| - if ( |
129 |
| - !('startViewTransition' in document) || |
130 |
| - window.matchMedia(`(prefers-reduced-motion: reduce)`).matches |
131 |
| - ) { |
132 |
| - setTheme(theme) |
133 |
| - return |
134 |
| - } |
135 |
| - |
136 |
| - const $document = document.documentElement |
137 |
| - |
138 |
| - const mousePosition = jotaiStore.get(mousePositionAtom) |
139 |
| - const { x, y } = mousePosition |
140 |
| - |
141 |
| - if (isUndefined(x) && isUndefined(y)) return |
142 |
| - |
143 |
| - const endRadius = Math.hypot( |
144 |
| - Math.max(x, window.innerWidth - x), |
145 |
| - Math.max(y, window.innerHeight - y), |
146 |
| - ) |
147 |
| - |
148 |
| - document |
149 |
| - .startViewTransition(() => { |
150 |
| - flushSync(() => setTheme(theme)) |
151 |
| - }) |
152 |
| - ?.ready.then(() => { |
153 |
| - if (mousePosition.x === 0) return |
154 |
| - const clipPath = [ |
155 |
| - `circle(0px at ${x}px ${y}px)`, |
156 |
| - `circle(${endRadius}px at ${x}px ${y}px)`, |
157 |
| - ] |
158 |
| - |
159 |
| - $document.animate( |
160 |
| - { |
161 |
| - clipPath, |
162 |
| - }, |
163 |
| - { |
164 |
| - duration: 300, |
165 |
| - easing: 'ease-in', |
166 |
| - pseudoElement: '::view-transition-new(root)', |
167 |
| - }, |
168 |
| - ) |
169 |
| - }) |
| 118 | + transitionViewIfSupported(() => { |
| 119 | + flushSync(() => setTheme(theme)) |
| 120 | + }) |
170 | 121 | }
|
171 | 122 |
|
172 | 123 | return (
|
|
0 commit comments