mirror of
https://github.com/GiriNeko/YesPlayMusic.git
synced 2025-12-17 13:48:02 +00:00
feat: updates
This commit is contained in:
parent
884f3df41a
commit
c6c59b2cd9
84 changed files with 3531 additions and 2616 deletions
|
|
@ -1,6 +1,4 @@
|
|||
import useUserAlbums, {
|
||||
useMutationLikeAAlbum,
|
||||
} from '@/web/api/hooks/useUserAlbums'
|
||||
import useUserAlbums, { useMutationLikeAAlbum } from '@/web/api/hooks/useUserAlbums'
|
||||
import contextMenus, { closeContextMenu } from '@/web/states/contextMenus'
|
||||
import player from '@/web/states/player'
|
||||
import { AnimatePresence } from 'framer-motion'
|
||||
|
|
@ -14,16 +12,15 @@ import BasicContextMenu from './BasicContextMenu'
|
|||
const AlbumContextMenu = () => {
|
||||
const { t } = useTranslation()
|
||||
|
||||
const { cursorPosition, type, dataSourceID, target, options } =
|
||||
useSnapshot(contextMenus)
|
||||
const { cursorPosition, type, dataSourceID, target, options } = useSnapshot(contextMenus)
|
||||
const likeAAlbum = useMutationLikeAAlbum()
|
||||
const [, copyToClipboard] = useCopyToClipboard()
|
||||
|
||||
const { data: likedAlbums } = useUserAlbums()
|
||||
const addToLibraryLabel = useMemo(() => {
|
||||
return likedAlbums?.data?.find(a => a.id === Number(dataSourceID))
|
||||
? 'Remove from Library'
|
||||
: 'Add to Library'
|
||||
? t`context-menu.remove-from-library`
|
||||
: t`context-menu.add-to-library`
|
||||
}, [dataSourceID, likedAlbums?.data])
|
||||
|
||||
return (
|
||||
|
|
@ -82,19 +79,15 @@ const AlbumContextMenu = () => {
|
|||
type: 'item',
|
||||
label: t`context-menu.copy-netease-link`,
|
||||
onClick: () => {
|
||||
copyToClipboard(
|
||||
`https://music.163.com/#/album?id=${dataSourceID}`
|
||||
)
|
||||
copyToClipboard(`https://music.163.com/#/album?id=${dataSourceID}`)
|
||||
toast.success(t`toasts.copied`)
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'item',
|
||||
label: 'Copy YPM Link',
|
||||
label: t`context-menu.copy-r3play-link`,
|
||||
onClick: () => {
|
||||
copyToClipboard(
|
||||
`${window.location.origin}/album/${dataSourceID}`
|
||||
)
|
||||
copyToClipboard(`${window.location.origin}/album/${dataSourceID}`)
|
||||
toast.success(t`toasts.copied`)
|
||||
},
|
||||
},
|
||||
|
|
|
|||
|
|
@ -4,6 +4,8 @@ import useLockMainScroll from '@/web/hooks/useLockMainScroll'
|
|||
import useMeasure from 'react-use-measure'
|
||||
import { ContextMenuItem } from './MenuItem'
|
||||
import MenuPanel from './MenuPanel'
|
||||
import { createPortal } from 'react-dom'
|
||||
import { ContextMenuPosition } from './types'
|
||||
|
||||
const BasicContextMenu = ({
|
||||
onClose,
|
||||
|
|
@ -19,15 +21,14 @@ const BasicContextMenu = ({
|
|||
cursorPosition: { x: number; y: number }
|
||||
options?: {
|
||||
useCursorPosition?: boolean
|
||||
fixedPosition?: `${'top' | 'bottom'}-${'left' | 'right'}`
|
||||
} | null
|
||||
classNames?: string
|
||||
}) => {
|
||||
const menuRef = useRef<HTMLDivElement>(null)
|
||||
const [measureRef, menu] = useMeasure()
|
||||
|
||||
const [position, setPosition] = useState<{ x: number; y: number } | null>(
|
||||
null
|
||||
)
|
||||
const [position, setPosition] = useState<ContextMenuPosition | null>(null)
|
||||
|
||||
useClickAway(menuRef, onClose)
|
||||
useLockMainScroll(!!position)
|
||||
|
|
@ -43,6 +44,22 @@ const BasicContextMenu = ({
|
|||
y: bottomY + menu.height < window.innerHeight ? bottomY : topY,
|
||||
}
|
||||
setPosition(position)
|
||||
} else if (options?.fixedPosition) {
|
||||
const [vertical, horizontal] = options.fixedPosition.split('-') as [
|
||||
'top' | 'bottom',
|
||||
'left' | 'right'
|
||||
]
|
||||
const button = target.getBoundingClientRect()
|
||||
const leftX = button.x
|
||||
const rightX = button.x - menu.width + button.width
|
||||
const bottomY = button.y + button.height + 8
|
||||
const topY = button.y - menu.height - 8
|
||||
const position: ContextMenuPosition = {
|
||||
x: horizontal === 'left' ? leftX : rightX,
|
||||
y: vertical === 'bottom' ? bottomY : topY,
|
||||
transformOrigin: `origin-${options.fixedPosition}`,
|
||||
}
|
||||
setPosition(position)
|
||||
} else {
|
||||
const button = target.getBoundingClientRect()
|
||||
const leftX = button.x
|
||||
|
|
@ -57,7 +74,7 @@ const BasicContextMenu = ({
|
|||
}
|
||||
}, [target, menu, options?.useCursorPosition, cursorPosition])
|
||||
|
||||
return (
|
||||
return createPortal(
|
||||
<>
|
||||
<MenuPanel
|
||||
position={{ x: 99999, y: 99999 }}
|
||||
|
|
@ -78,7 +95,8 @@ const BasicContextMenu = ({
|
|||
classNames={classNames}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
</>,
|
||||
document.body
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,13 +1,7 @@
|
|||
import { css, cx } from '@emotion/css'
|
||||
import { ForwardedRef, forwardRef, useRef, useState } from 'react'
|
||||
import Icon from '../Icon'
|
||||
|
||||
export interface ContextMenuItem {
|
||||
type: 'item' | 'submenu' | 'divider'
|
||||
label?: string
|
||||
onClick?: (e: MouseEvent) => void
|
||||
items?: ContextMenuItem[]
|
||||
}
|
||||
import { ContextMenuItem } from './types'
|
||||
|
||||
const MenuItem = ({
|
||||
item,
|
||||
|
|
@ -63,7 +57,7 @@ const MenuItem = ({
|
|||
onSubmenuClose()
|
||||
}}
|
||||
className={cx(
|
||||
'relative',
|
||||
'relative cursor-default',
|
||||
className,
|
||||
css`
|
||||
padding-right: 9px;
|
||||
|
|
|
|||
|
|
@ -7,14 +7,11 @@ import {
|
|||
useState,
|
||||
} from 'react'
|
||||
import { motion } from 'framer-motion'
|
||||
import MenuItem, { ContextMenuItem } from './MenuItem'
|
||||
import MenuItem from './MenuItem'
|
||||
import { ContextMenuItem, ContextMenuPosition } from './types'
|
||||
|
||||
interface PanelProps {
|
||||
position: {
|
||||
x: number
|
||||
y: number
|
||||
transformOrigin?: `origin-${'top' | 'bottom'}-${'left' | 'right'}`
|
||||
}
|
||||
position: ContextMenuPosition
|
||||
items: ContextMenuItem[]
|
||||
onClose: (e: MouseEvent) => void
|
||||
forMeasure?: boolean
|
||||
|
|
@ -36,33 +33,33 @@ const MenuPanel = forwardRef(
|
|||
|
||||
return (
|
||||
// Container (to add padding for submenus)
|
||||
<motion.div
|
||||
initial={{ opacity: 0, scale: forMeasure ? 1 : 0.96 }}
|
||||
animate={{
|
||||
opacity: 1,
|
||||
scale: 1,
|
||||
transition: {
|
||||
duration: 0.1,
|
||||
},
|
||||
}}
|
||||
exit={{ opacity: 0, scale: 0.96 }}
|
||||
transition={{ duration: 0.2 }}
|
||||
<div
|
||||
ref={ref}
|
||||
className={cx(
|
||||
'fixed',
|
||||
position.transformOrigin || 'origin-top-left',
|
||||
isSubmenu ? 'submenu z-20 px-1' : 'z-10'
|
||||
'fixed select-none',
|
||||
isSubmenu ? 'submenu z-30 px-1' : 'z-20'
|
||||
)}
|
||||
style={{ left: position.x, top: position.y }}
|
||||
>
|
||||
{/* The real panel */}
|
||||
<div
|
||||
<motion.div
|
||||
initial={{ opacity: 0, scale: forMeasure ? 1 : 0.96 }}
|
||||
animate={{
|
||||
opacity: 1,
|
||||
scale: 1,
|
||||
transition: {
|
||||
duration: 0.1,
|
||||
},
|
||||
}}
|
||||
exit={{ opacity: 0, scale: 0.96 }}
|
||||
transition={{ duration: 0.2 }}
|
||||
className={cx(
|
||||
'rounded-12 border border-white/[.06] bg-gray-900/95 p-px py-2.5 shadow-xl outline outline-1 outline-black backdrop-blur-3xl',
|
||||
css`
|
||||
min-width: 200px;
|
||||
`,
|
||||
classNames
|
||||
classNames,
|
||||
position.transformOrigin || 'origin-top-left'
|
||||
)}
|
||||
>
|
||||
{items.map((item, index) => (
|
||||
|
|
@ -76,7 +73,7 @@ const MenuPanel = forwardRef(
|
|||
className={isSubmenu ? 'submenu' : ''}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</motion.div>
|
||||
|
||||
{/* Submenu */}
|
||||
<SubMenu
|
||||
|
|
@ -86,7 +83,7 @@ const MenuPanel = forwardRef(
|
|||
itemRect={submenuProps?.itemRect}
|
||||
onClose={onClose}
|
||||
/>
|
||||
</motion.div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
)
|
||||
|
|
|
|||
12
packages/web/components/ContextMenus/types.ts
Normal file
12
packages/web/components/ContextMenus/types.ts
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
export interface ContextMenuPosition {
|
||||
x: number
|
||||
y: number
|
||||
transformOrigin?: `origin-${'top' | 'bottom'}-${'left' | 'right'}`
|
||||
}
|
||||
|
||||
export interface ContextMenuItem {
|
||||
type: 'item' | 'submenu' | 'divider'
|
||||
label?: string
|
||||
onClick?: (e: MouseEvent) => void
|
||||
items?: ContextMenuItem[]
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue