perf: optimize theme styles
This commit is contained in:
parent
bcdd435e45
commit
2d051fedd7
22 changed files with 105 additions and 72 deletions
|
@ -74,7 +74,6 @@
|
|||
"idb-keyval": "^6.2.1",
|
||||
"lucide-react": "^0.453.0",
|
||||
"nanoid": "^5.0.7",
|
||||
"next-themes": "^0.3.0",
|
||||
"react": "^18.3.1",
|
||||
"react-dom": "^18.3.1",
|
||||
"react-hook-form": "^7.53.0",
|
||||
|
|
|
@ -98,9 +98,6 @@ importers:
|
|||
nanoid:
|
||||
specifier: ^5.0.7
|
||||
version: 5.0.7
|
||||
next-themes:
|
||||
specifier: ^0.3.0
|
||||
version: 0.3.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||
react:
|
||||
specifier: ^18.3.1
|
||||
version: 18.3.1
|
||||
|
@ -4949,12 +4946,6 @@ packages:
|
|||
resolution: {integrity: sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg==}
|
||||
engines: {node: '>= 0.4.0'}
|
||||
|
||||
next-themes@0.3.0:
|
||||
resolution: {integrity: sha512-/QHIrsYpd6Kfk7xakK4svpDI5mmXP0gfvCoJdGpZQ2TOrQZmsW0QxjaiLn8wbIKjtm4BTSqLoix4lxYYOnLJ/w==}
|
||||
peerDependencies:
|
||||
react: ^16.8 || ^17 || ^18
|
||||
react-dom: ^16.8 || ^17 || ^18
|
||||
|
||||
nice-try@1.0.5:
|
||||
resolution: {integrity: sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==}
|
||||
|
||||
|
@ -12482,11 +12473,6 @@ snapshots:
|
|||
|
||||
netmask@2.0.2: {}
|
||||
|
||||
next-themes@0.3.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1):
|
||||
dependencies:
|
||||
react: 18.3.1
|
||||
react-dom: 18.3.1(react@18.3.1)
|
||||
|
||||
nice-try@1.0.5: {}
|
||||
|
||||
no-case@3.0.4:
|
||||
|
|
|
@ -15,6 +15,7 @@ import { AnimatePresence, motion } from 'framer-motion'
|
|||
import DanmakuContainer from './components/DanmakuContainer'
|
||||
import DanmakuDomain from '@/domain/Danmaku'
|
||||
import AppStatusDomain from '@/domain/AppStatus'
|
||||
import { cn } from '@/utils'
|
||||
|
||||
/**
|
||||
* Fix requestAnimationFrame error in jest
|
||||
|
@ -63,24 +64,29 @@ export default function App() {
|
|||
|
||||
return (
|
||||
appStatusLoadIsFinished && (
|
||||
<>
|
||||
<AppMain className={userInfo?.themeMode}>
|
||||
<div id="app" className={cn('contents', userInfo?.themeMode)}>
|
||||
<AppMain>
|
||||
<Header />
|
||||
<Main />
|
||||
<Footer />
|
||||
<AnimatePresence>
|
||||
{notUserInfo && (
|
||||
<motion.div initial={{ opacity: 1 }} exit={{ opacity: 0 }} transition={{ duration: 0.3 }}>
|
||||
<motion.div
|
||||
className="contents"
|
||||
initial={{ opacity: 1 }}
|
||||
exit={{ opacity: 0 }}
|
||||
transition={{ duration: 0.3 }}
|
||||
>
|
||||
<Setup></Setup>
|
||||
</motion.div>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
<Toaster richColors offset="70px" visibleToasts={1} position="top-center"></Toaster>
|
||||
</AppMain>
|
||||
<AppButton className={userInfo?.themeMode}></AppButton>
|
||||
<AppButton></AppButton>
|
||||
|
||||
<DanmakuContainer ref={danmakuContainerRef} className={userInfo?.themeMode} />
|
||||
</>
|
||||
<DanmakuContainer ref={danmakuContainerRef} />
|
||||
</div>
|
||||
)
|
||||
)
|
||||
}
|
||||
|
|
|
@ -10,7 +10,7 @@ export interface EmojiButtonProps {
|
|||
onSelect?: (value: string) => void
|
||||
}
|
||||
|
||||
const emojiGroups = chunk([...EMOJI_LIST], 8)
|
||||
const emojiGroups = chunk([...EMOJI_LIST], 6)
|
||||
|
||||
// BUG: https://github.com/radix-ui/primitives/pull/2433
|
||||
// BUG https://github.com/radix-ui/primitives/issues/1666
|
||||
|
@ -34,16 +34,19 @@ const EmojiButton: FC<EmojiButtonProps> = ({ onSelect }) => {
|
|||
<SmileIcon size={20} />
|
||||
</Button>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent className="z-infinity w-72 rounded-xl px-0" onCloseAutoFocus={handleCloseAutoFocus}>
|
||||
<ScrollArea className="size-72 px-3">
|
||||
<PopoverContent
|
||||
className="z-infinity w-64 overflow-hidden rounded-xl p-0 dark:bg-slate-900"
|
||||
onCloseAutoFocus={handleCloseAutoFocus}
|
||||
>
|
||||
<ScrollArea className="size-64 p-1">
|
||||
{emojiGroups.map((group, index) => {
|
||||
return (
|
||||
<div key={index} className="grid grid-cols-8">
|
||||
<div key={index} className="grid grid-cols-6">
|
||||
{group.map((emoji, index) => (
|
||||
<Button
|
||||
key={index}
|
||||
size="sm"
|
||||
className="text-base"
|
||||
size="icon"
|
||||
className="text-xl"
|
||||
variant="ghost"
|
||||
onClick={() => handleSelect(emoji)}
|
||||
>
|
||||
|
|
|
@ -33,8 +33,8 @@ const LikeButton: FC<LikeButtonProps> & { Icon: FC<LikeButtonIconProps> } = ({
|
|||
onClick={handleClick}
|
||||
variant="secondary"
|
||||
className={cn(
|
||||
'grid items-center overflow-hidden rounded-full leading-none transition-all select-none dark:bg-slate-200',
|
||||
checked ? 'text-orange-500' : 'text-slate-500',
|
||||
'grid items-center overflow-hidden rounded-full leading-none transition-all select-none dark:bg-slate-600',
|
||||
checked ? 'text-orange-500' : 'text-slate-500 dark:text-slate-100',
|
||||
count ? 'grid-cols-[auto_1fr] gap-x-1' : 'grid-cols-[auto_0fr] gap-x-0'
|
||||
)}
|
||||
size="xs"
|
||||
|
|
|
@ -12,7 +12,7 @@ const MessageList: FC<MessageListProps> = ({ children }) => {
|
|||
const [scrollParentRef, setScrollParentRef] = useState<HTMLDivElement | null>(null)
|
||||
|
||||
return (
|
||||
<ScrollArea ref={setScrollParentRef}>
|
||||
<ScrollArea ref={setScrollParentRef} className="dark:bg-slate-900">
|
||||
<Virtuoso
|
||||
defaultItemHeight={108}
|
||||
followOutput={(isAtBottom: boolean) => (isAtBottom ? 'smooth' : 'auto')}
|
||||
|
|
|
@ -13,10 +13,7 @@ export interface PromptItemProps {
|
|||
const PromptItem: FC<PromptItemProps> = ({ data, className }) => {
|
||||
return (
|
||||
<div className={cn('flex justify-center py-1 px-4 ', className)}>
|
||||
<Badge
|
||||
variant="secondary"
|
||||
className="gap-x-2 rounded-full px-2 font-medium text-slate-400 dark:bg-slate-600 dark:text-slate-50"
|
||||
>
|
||||
<Badge variant="secondary" className="gap-x-2 rounded-full px-2 font-medium text-slate-400 dark:bg-slate-800">
|
||||
<Avatar className="size-4">
|
||||
<AvatarImage src={data.userAvatar} className="size-full" alt="avatar" />
|
||||
<AvatarFallback>{data.username.at(0)}</AvatarFallback>
|
||||
|
|
|
@ -2,7 +2,7 @@ import React from 'react'
|
|||
import { createRoot } from 'react-dom/client'
|
||||
import { Remesh } from 'remesh'
|
||||
import { RemeshRoot, RemeshScope } from 'remesh-react'
|
||||
import { RemeshLogger } from 'remesh-logger'
|
||||
// import { RemeshLogger } from 'remesh-logger'
|
||||
import { defineContentScript } from 'wxt/sandbox'
|
||||
import { createShadowRootUi } from 'wxt/client'
|
||||
|
||||
|
@ -15,8 +15,8 @@ import { ToastImpl } from '@/domain/impls/Toast'
|
|||
import { PeerRoomImpl } from '@/domain/impls/PeerRoom2'
|
||||
import '@/assets/styles/tailwind.css'
|
||||
import '@/assets/styles/sonner.css'
|
||||
import { createElement } from '@/utils'
|
||||
import NotificationDomain from '@/domain/Notification'
|
||||
import { createElement } from '@/utils'
|
||||
|
||||
export default defineContentScript({
|
||||
cssInjectionMode: 'ui',
|
||||
|
@ -24,6 +24,13 @@ export default defineContentScript({
|
|||
matches: ['https://*/*'],
|
||||
excludeMatches: ['*://localhost/*', '*://127.0.0.1/*', '*://*.csdn.net/*', '*://*.csdn.com/*'],
|
||||
async main(ctx) {
|
||||
window.CSS.registerProperty({
|
||||
name: '--shimmer-angle',
|
||||
syntax: '<angle>',
|
||||
inherits: false,
|
||||
initialValue: '0deg'
|
||||
})
|
||||
|
||||
const store = Remesh.store({
|
||||
externs: [
|
||||
LocalStorageImpl,
|
||||
|
@ -45,9 +52,8 @@ export default defineContentScript({
|
|||
mode: 'open',
|
||||
isolateEvents: ['keyup', 'keydown', 'keypress'],
|
||||
onMount: (container) => {
|
||||
const app = createElement('<div id="app"></div>')
|
||||
const app = createElement('<div id="root"></div>')
|
||||
container.append(app)
|
||||
|
||||
const root = createRoot(app)
|
||||
root.render(
|
||||
<React.StrictMode>
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { type FC, useState, type MouseEvent, useLayoutEffect, useEffect } from 'react'
|
||||
import { type FC, useState, type MouseEvent, useEffect } from 'react'
|
||||
import { SettingsIcon, MoonIcon, SunIcon, HandIcon } from 'lucide-react'
|
||||
import { motion, AnimatePresence } from 'framer-motion'
|
||||
|
||||
|
@ -19,6 +19,7 @@ import AppStatusDomain from '@/domain/AppStatus'
|
|||
import { getDay } from 'date-fns'
|
||||
import { messenger } from '@/messenger'
|
||||
import useDarg from '@/hooks/useDarg'
|
||||
import useWindowResize from '@/hooks/useWindowResize'
|
||||
|
||||
export interface AppButtonProps {
|
||||
className?: string
|
||||
|
@ -47,21 +48,17 @@ const AppButton: FC<AppButtonProps> = ({ className }) => {
|
|||
} = useDarg({
|
||||
initX: appPosition.x,
|
||||
initY: appPosition.y,
|
||||
minX: 44,
|
||||
maxX: window.innerWidth - 44,
|
||||
minX: 50,
|
||||
maxX: window.innerWidth - 50,
|
||||
maxY: window.innerHeight - 22,
|
||||
minY: window.innerHeight / 2
|
||||
})
|
||||
|
||||
useEffect(() => {
|
||||
const handler = () => {
|
||||
send(appStatusDomain.command.UpdatePositionCommand({ x: window.innerWidth - 44, y: window.innerHeight - 22 }))
|
||||
}
|
||||
window.addEventListener('resize', handler)
|
||||
return () => window.removeEventListener('resize', handler)
|
||||
}, [])
|
||||
useWindowResize(({ width, height }) => {
|
||||
send(appStatusDomain.command.UpdatePositionCommand({ x: width - 50, y: height - 22 }))
|
||||
})
|
||||
|
||||
useLayoutEffect(() => {
|
||||
useEffect(() => {
|
||||
send(appStatusDomain.command.UpdatePositionCommand({ x, y }))
|
||||
}, [x, y])
|
||||
|
||||
|
@ -116,7 +113,7 @@ const AppButton: FC<AppButtonProps> = ({ className }) => {
|
|||
className={cn(
|
||||
'absolute grid grid-rows-[repeat(2,minmax(0,2.5rem))] w-full justify-center items-center transition-all duration-500',
|
||||
isDarkMode ? 'top-0' : '-top-10',
|
||||
isDarkMode ? 'bg-slate-800 text-white' : 'bg-white text-orange-400'
|
||||
isDarkMode ? 'bg-slate-950 text-white' : 'bg-white text-orange-400'
|
||||
)}
|
||||
>
|
||||
<MoonIcon size={20} />
|
||||
|
@ -136,7 +133,7 @@ const AppButton: FC<AppButtonProps> = ({ className }) => {
|
|||
<Button
|
||||
onClick={handleToggleApp}
|
||||
onContextMenu={handleToggleMenu}
|
||||
className="relative z-20 size-11 rounded-full p-0 text-xs shadow-lg shadow-slate-500/50"
|
||||
className="relative z-20 size-11 rounded-full p-0 text-xs shadow-lg shadow-slate-500/50 after:absolute after:-inset-0.5 after:z-10 after:animate-[shimmer_2s_linear_infinite] after:rounded-full after:bg-[conic-gradient(from_var(--shimmer-angle),theme(colors.slate.500)_0%,theme(colors.white)_10%,theme(colors.slate.500)_20%)]"
|
||||
>
|
||||
<AnimatePresence>
|
||||
{hasUnreadQuery && (
|
||||
|
@ -145,7 +142,7 @@ const AppButton: FC<AppButtonProps> = ({ className }) => {
|
|||
animate={{ opacity: 1 }}
|
||||
exit={{ opacity: 0 }}
|
||||
transition={{ duration: 0.1 }}
|
||||
className="absolute -right-1 -top-1 flex size-5 items-center justify-center"
|
||||
className="absolute -right-1 -top-1 z-30 flex size-5 items-center justify-center"
|
||||
>
|
||||
<span
|
||||
className={cn('absolute inline-flex size-full animate-ping rounded-full opacity-75', 'bg-orange-400')}
|
||||
|
@ -154,7 +151,8 @@ const AppButton: FC<AppButtonProps> = ({ className }) => {
|
|||
</motion.div>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
<DayLogo className="max-h-full max-w-full"></DayLogo>
|
||||
|
||||
<DayLogo className="relative z-20 max-h-full max-w-full overflow-hidden"></DayLogo>
|
||||
</Button>
|
||||
</div>
|
||||
)
|
||||
|
|
|
@ -4,7 +4,7 @@ import { motion, AnimatePresence } from 'framer-motion'
|
|||
import AppStatusDomain from '@/domain/AppStatus'
|
||||
import { useRemeshDomain, useRemeshQuery } from 'remesh-react'
|
||||
import { cn } from '@/utils'
|
||||
import { useWindowSize } from 'react-use'
|
||||
import useWindowResize from '@/hooks/useWindowResize'
|
||||
|
||||
export interface AppMainProps {
|
||||
children?: ReactNode
|
||||
|
@ -16,9 +16,9 @@ const AppMain: FC<AppMainProps> = ({ children, className }) => {
|
|||
const appOpenStatus = useRemeshQuery(appStatusDomain.query.OpenQuery())
|
||||
const { x, y } = useRemeshQuery(appStatusDomain.query.PositionQuery())
|
||||
|
||||
const { width } = useWindowSize()
|
||||
const { width } = useWindowResize()
|
||||
|
||||
const isOnRightSide = x >= width / 2 + 44
|
||||
const isOnRightSide = x >= width / 2 + 50
|
||||
|
||||
const { size, setRef } = useResizable({
|
||||
initSize: Math.max(375, width / 6),
|
||||
|
@ -54,7 +54,7 @@ const AppMain: FC<AppMainProps> = ({ children, className }) => {
|
|||
<div
|
||||
ref={setRef}
|
||||
className={cn(
|
||||
'absolute inset-y-3 z-infinity w-1 dark:bg-slate-500 cursor-ew-resize rounded-xl bg-slate-100 opacity-0 shadow transition-opacity duration-200 ease-in hover:opacity-100',
|
||||
'absolute inset-y-3 z-infinity w-1 dark:bg-slate-600 cursor-ew-resize rounded-xl bg-slate-100 opacity-0 shadow transition-opacity duration-200 ease-in hover:opacity-100',
|
||||
isOnRightSide ? '-left-0.5' : '-right-0.5'
|
||||
)}
|
||||
></div>
|
||||
|
|
|
@ -15,7 +15,7 @@ import useTriggerAway from '@/hooks/useTriggerAway'
|
|||
import { ScrollArea } from '@/components/ui/ScrollArea'
|
||||
import { Virtuoso, VirtuosoHandle } from 'react-virtuoso'
|
||||
import UserInfoDomain from '@/domain/UserInfo'
|
||||
import { cn, getTextSimilarity } from '@/utils'
|
||||
import { cn, getRootNode, getTextSimilarity } from '@/utils'
|
||||
import { Avatar, AvatarFallback } from '@/components/ui/Avatar'
|
||||
import { AvatarImage } from '@radix-ui/react-avatar'
|
||||
import ToastDomain from '@/domain/Toast'
|
||||
|
@ -240,7 +240,7 @@ const Footer: FC = () => {
|
|||
})
|
||||
}
|
||||
|
||||
const root = document.querySelector(__NAME__)?.shadowRoot
|
||||
const root = getRootNode()
|
||||
|
||||
return (
|
||||
<div className="relative z-10 grid gap-y-2 rounded-b-xl px-4 pb-4 pt-2 before:pointer-events-none before:absolute before:inset-x-4 before:-top-2 before:h-2 before:bg-gradient-to-t before:from-slate-50 before:from-30% before:to-transparent dark:bg-slate-900 before:dark:from-slate-900">
|
||||
|
@ -248,8 +248,8 @@ const Footer: FC = () => {
|
|||
<Portal
|
||||
container={root}
|
||||
ref={shareAutoCompleteListRef}
|
||||
className="fixed z-infinity w-36 -translate-y-full rounded-lg border bg-popover text-popover-foreground shadow-md duration-300 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95"
|
||||
style={{ left: `min(${x}px, 100vw - 212px)`, top: `${y}px` }}
|
||||
className="fixed z-infinity w-36 -translate-y-full overflow-hidden rounded-lg border bg-popover text-popover-foreground shadow-md duration-300 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95"
|
||||
style={{ left: `min(${x}px, 100vw - 160px)`, top: `${y}px` }}
|
||||
>
|
||||
<ScrollArea className="max-h-[204px] min-h-9 p-1" ref={setScrollParentRef}>
|
||||
<Virtuoso
|
||||
|
|
|
@ -19,7 +19,7 @@ const Header: FC = () => {
|
|||
|
||||
return (
|
||||
<div className="z-10 grid h-12 grid-flow-col grid-cols-[theme('spacing.20')_auto_theme('spacing.20')] items-center justify-between rounded-t-xl bg-white px-4 backdrop-blur-lg dark:bg-slate-950">
|
||||
<Avatar className="size-8 dark:text-slate-50">
|
||||
<Avatar className="size-8">
|
||||
<AvatarImage src={siteInfo.icon} alt="favicon" />
|
||||
<AvatarFallback>
|
||||
<Globe2Icon size="100%" className="text-gray-400" />
|
||||
|
|
|
@ -16,7 +16,7 @@ function App() {
|
|||
<VersionLink></VersionLink>
|
||||
<Main>
|
||||
<ProfileForm></ProfileForm>
|
||||
<Toaster richColors position="top-center" />
|
||||
<Toaster richColors position="top-center" duration={1000000} />
|
||||
</Main>
|
||||
<BadgeList></BadgeList>
|
||||
</Layout>
|
||||
|
|
|
@ -82,3 +82,9 @@
|
|||
direction: ltr !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* @property --shimmer-angle {
|
||||
syntax: '<angle>';
|
||||
inherits: false;
|
||||
initial-value: 0deg;
|
||||
} */
|
||||
|
|
|
@ -113,7 +113,7 @@ const Markdown: FC<MarkdownProps> = ({ children = '', className }) => {
|
|||
)
|
||||
}}
|
||||
remarkPlugins={[remarkGfm, remarkBreaks]}
|
||||
className={cn(className, 'prose prose-sm prose-slate break-words dark:text-slate-400')}
|
||||
className={cn(className, 'prose prose-sm prose-slate break-words dark:text-slate-50')}
|
||||
>
|
||||
{children}
|
||||
</ReactMarkdown>
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import * as React from 'react'
|
||||
import * as PopoverPrimitive from '@radix-ui/react-popover'
|
||||
import { cn } from '@/utils/index'
|
||||
import { cn, getRootNode } from '@/utils'
|
||||
|
||||
const Popover = PopoverPrimitive.Root
|
||||
|
||||
|
@ -10,8 +10,7 @@ const PopoverContent = React.forwardRef<
|
|||
React.ElementRef<typeof PopoverPrimitive.Content>,
|
||||
React.ComponentPropsWithoutRef<typeof PopoverPrimitive.Content>
|
||||
>(({ className, align = 'center', sideOffset = 4, ...props }, ref) => {
|
||||
const root = document.querySelector(__NAME__)?.shadowRoot
|
||||
|
||||
const root = getRootNode()
|
||||
return (
|
||||
<PopoverPrimitive.Portal container={root}>
|
||||
<PopoverPrimitive.Content
|
||||
|
|
|
@ -7,10 +7,7 @@ const ScrollArea = React.forwardRef<
|
|||
React.ElementRef<typeof ScrollAreaPrimitive.Root>,
|
||||
React.ComponentPropsWithoutRef<typeof ScrollAreaPrimitive.Root> & { scrollLock?: boolean }
|
||||
>(({ className, children, scrollLock = true, ...props }, ref) => (
|
||||
<ScrollAreaPrimitive.Root
|
||||
className={cn('relative grid grid-rows-[1fr] overflow-hidden dark:bg-slate-900', className)}
|
||||
{...props}
|
||||
>
|
||||
<ScrollAreaPrimitive.Root className={cn('relative grid grid-rows-[1fr] overflow-hidden', className)} {...props}>
|
||||
<ScrollAreaPrimitive.Viewport
|
||||
ref={ref}
|
||||
className={cn('size-full rounded-[inherit]', scrollLock ? 'overscroll-none' : 'overscroll-auto')}
|
||||
|
|
|
@ -15,7 +15,7 @@ export interface AppStatus {
|
|||
export const defaultStatusState = {
|
||||
open: false,
|
||||
unread: 0,
|
||||
position: { x: window.innerWidth - 44, y: window.innerHeight - 22 }
|
||||
position: { x: window.innerWidth - 50, y: window.innerHeight - 22 }
|
||||
}
|
||||
|
||||
const AppStatusDomain = Remesh.domain({
|
||||
|
|
22
src/hooks/useWindowResize.ts
Normal file
22
src/hooks/useWindowResize.ts
Normal file
|
@ -0,0 +1,22 @@
|
|||
import { useEffect, useState } from 'react'
|
||||
|
||||
const useWindowResize = (callback?: ({ width, height }: { width: number; height: number }) => void) => {
|
||||
const [size, setSize] = useState({ width: window.innerWidth, height: window.innerHeight })
|
||||
|
||||
useEffect(() => {
|
||||
const handler = () => {
|
||||
const width = window.innerWidth
|
||||
const height = window.innerHeight
|
||||
setSize({ width, height })
|
||||
callback?.({ width, height })
|
||||
}
|
||||
window.addEventListener('resize', handler)
|
||||
return () => {
|
||||
window.removeEventListener('resize', handler)
|
||||
}
|
||||
}, [])
|
||||
|
||||
return size
|
||||
}
|
||||
|
||||
export default useWindowResize
|
5
src/utils/getRootNode.ts
Normal file
5
src/utils/getRootNode.ts
Normal file
|
@ -0,0 +1,5 @@
|
|||
export const getRootNode = () => {
|
||||
return document.querySelector(__NAME__)?.shadowRoot?.querySelector('#app') || document.body
|
||||
}
|
||||
|
||||
export default getRootNode
|
|
@ -13,3 +13,4 @@ export { default as generateRandomAvatar } from './generateRandomAvatar'
|
|||
export { default as generateRandomName } from './generateRandomName'
|
||||
export { default as getCursorPosition } from './getCursorPosition'
|
||||
export { default as getTextSimilarity } from './getTextSimilarity'
|
||||
export { default as getRootNode } from './getRootNode'
|
||||
|
|
|
@ -81,6 +81,14 @@ export default {
|
|||
transform: 'rotate(215deg) translateX(-500px)',
|
||||
opacity: '0'
|
||||
}
|
||||
},
|
||||
shimmer: {
|
||||
'0%': {
|
||||
'--shimmer-angle': '0deg'
|
||||
},
|
||||
'100%': {
|
||||
'--shimmer-angle': '360deg'
|
||||
}
|
||||
}
|
||||
},
|
||||
animation: {
|
||||
|
|
Loading…
Reference in a new issue