mirror of
https://github.com/GiriNeko/YesPlayMusic.git
synced 2025-12-18 06:07:48 +00:00
feat: updates
This commit is contained in:
parent
47e41dea9b
commit
ebebf2a733
160 changed files with 4148 additions and 2001 deletions
42
packages/web/components/New/TrackListHeader/Actions.tsx
Normal file
42
packages/web/components/New/TrackListHeader/Actions.tsx
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
import Icon from '../../Icon'
|
||||
|
||||
const Actions = ({
|
||||
onPlay,
|
||||
onLike,
|
||||
isLiked,
|
||||
}: {
|
||||
isLiked?: boolean
|
||||
onPlay: () => void
|
||||
onLike?: () => void
|
||||
}) => {
|
||||
return (
|
||||
<div className='mt-11 flex items-end justify-between lg:mt-4 lg:justify-start'>
|
||||
<div className='flex items-end'>
|
||||
{/* Menu */}
|
||||
<button className='mr-2.5 flex h-14 w-14 items-center justify-center rounded-full text-white/40 transition duration-400 hover:text-white/70 dark:bg-white/10 hover:dark:bg-white/30'>
|
||||
<Icon name='more' className='h-7 w-7' />
|
||||
</button>
|
||||
{/* Like */}
|
||||
{onLike && (
|
||||
<button
|
||||
onClick={() => onLike()}
|
||||
className='flex h-14 w-14 items-center justify-center rounded-full text-white/40 transition duration-400 hover:text-white/70 dark:bg-white/10 hover:dark:bg-white/30 lg:mr-2.5'
|
||||
>
|
||||
<Icon
|
||||
name={isLiked ? 'heart' : 'heart-outline'}
|
||||
className='h-7 w-7'
|
||||
/>
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
<button
|
||||
onClick={() => onPlay()}
|
||||
className='h-14 rounded-full px-10 text-18 font-medium text-white dark:bg-brand-700'
|
||||
>
|
||||
Play
|
||||
</button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default Actions
|
||||
33
packages/web/components/New/TrackListHeader/Cover.tsx
Normal file
33
packages/web/components/New/TrackListHeader/Cover.tsx
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
import { isIOS, isSafari, resizeImage } from '@/web/utils/common'
|
||||
import Image from '@/web/components/New/Image'
|
||||
import { memo, useEffect } from 'react'
|
||||
import useVideoCover from '@/web/hooks/useVideoCover'
|
||||
import { motion } from 'framer-motion'
|
||||
import { ease } from '@/web/utils/const'
|
||||
import useAppleMusicAlbum from '@/web/hooks/useAppleMusicAlbum'
|
||||
import uiStates from '@/web/states/uiStates'
|
||||
import VideoCover from './VideoCover'
|
||||
|
||||
const Cover = memo(
|
||||
({ cover, videoCover }: { cover?: string; videoCover?: string }) => {
|
||||
useEffect(() => {
|
||||
if (cover) uiStates.blurBackgroundImage = cover
|
||||
}, [cover])
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className='relative aspect-square w-full overflow-hidden rounded-24 '>
|
||||
<Image
|
||||
className='absolute inset-0'
|
||||
src={resizeImage(cover || '', 'lg')}
|
||||
/>
|
||||
|
||||
{videoCover && <VideoCover videoCover={videoCover} />}
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
)
|
||||
Cover.displayName = 'Cover'
|
||||
|
||||
export default Cover
|
||||
59
packages/web/components/New/TrackListHeader/Info.tsx
Normal file
59
packages/web/components/New/TrackListHeader/Info.tsx
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
import { formatDate } from '@/web/utils/common'
|
||||
import Icon from '@/web/components/Icon'
|
||||
import dayjs from 'dayjs'
|
||||
import { useNavigate } from 'react-router-dom'
|
||||
import useIsMobile from '@/web/hooks/useIsMobile'
|
||||
import { ReactNode } from 'react'
|
||||
|
||||
const Info = ({
|
||||
title,
|
||||
creatorName,
|
||||
creatorLink,
|
||||
description,
|
||||
extraInfo,
|
||||
}: {
|
||||
title?: string
|
||||
creatorName?: string
|
||||
creatorLink?: string
|
||||
description?: string
|
||||
extraInfo?: string | ReactNode
|
||||
}) => {
|
||||
const navigate = useNavigate()
|
||||
const isMobile = useIsMobile()
|
||||
|
||||
return (
|
||||
<div>
|
||||
{/* Title */}
|
||||
<div className='mt-2.5 text-28 font-semibold dark:text-white/80 lg:mt-0 lg:text-36 lg:font-medium'>
|
||||
{title}
|
||||
</div>
|
||||
|
||||
{/* Creator */}
|
||||
<div className='mt-2.5 lg:mt-6'>
|
||||
<span
|
||||
onClick={() => creatorLink && navigate(creatorLink)}
|
||||
className='text-24 font-medium transition-colors duration-300 dark:text-white/40 hover:dark:text-neutral-100 '
|
||||
>
|
||||
{creatorName}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
{/* Extra info */}
|
||||
<div className='mt-1 flex items-center text-12 font-medium dark:text-white/40 lg:text-14 lg:font-bold'>
|
||||
{extraInfo}
|
||||
</div>
|
||||
|
||||
{/* Description */}
|
||||
{!isMobile && (
|
||||
<div
|
||||
className='line-clamp-3 mt-6 whitespace-pre-wrap text-14 font-bold dark:text-white/40'
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: description || '',
|
||||
}}
|
||||
></div>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default Info
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
import React from 'react'
|
||||
import { ComponentStory, ComponentMeta } from '@storybook/react'
|
||||
import TrackListHeader from './TrackListHeader'
|
||||
|
||||
export default {
|
||||
title: 'Components/TrackListHeader',
|
||||
component: TrackListHeader,
|
||||
} as ComponentMeta<typeof TrackListHeader>
|
||||
|
||||
const Template: ComponentStory<typeof TrackListHeader> = args => (
|
||||
<div className='w-[calc(100vw_-_32px)] rounded-24 bg-[#F8F8F8] p-10 dark:bg-black'>
|
||||
<TrackListHeader />
|
||||
</div>
|
||||
)
|
||||
|
||||
export const Default = Template.bind({})
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
import { css, cx } from '@emotion/css'
|
||||
import Cover from './Cover'
|
||||
import Actions from './Actions'
|
||||
import Info from './Info'
|
||||
import React from 'react'
|
||||
|
||||
interface Props {
|
||||
className?: string
|
||||
title?: string
|
||||
creatorName?: string
|
||||
creatorLink?: string
|
||||
description?: string
|
||||
extraInfo?: string | React.ReactNode
|
||||
cover?: string
|
||||
videoCover?: string
|
||||
isLiked: boolean
|
||||
onPlay: () => void
|
||||
onLike?: () => void
|
||||
}
|
||||
|
||||
const TrackListHeader = ({
|
||||
className,
|
||||
title,
|
||||
creatorName,
|
||||
creatorLink,
|
||||
description,
|
||||
extraInfo,
|
||||
cover,
|
||||
videoCover,
|
||||
isLiked,
|
||||
onPlay,
|
||||
onLike,
|
||||
}: Props) => {
|
||||
return (
|
||||
<div
|
||||
className={cx(
|
||||
className,
|
||||
'mx-2.5 rounded-48 p-8 dark:bg-white/10',
|
||||
'lg:mx-0 lg:grid lg:grid-rows-1 lg:gap-10 lg:rounded-none lg:p-0 lg:dark:bg-transparent',
|
||||
css`
|
||||
grid-template-columns: 318px auto;
|
||||
`
|
||||
)}
|
||||
>
|
||||
<Cover {...{ cover, videoCover }} />
|
||||
|
||||
<div className='flex flex-col justify-between'>
|
||||
<Info
|
||||
{...{ title, creatorName, creatorLink, description, extraInfo }}
|
||||
/>
|
||||
<Actions {...{ onPlay, onLike, isLiked }} />
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const memoized = React.memo(TrackListHeader)
|
||||
memoized.displayName = 'TrackListHeader'
|
||||
|
||||
export default memoized
|
||||
51
packages/web/components/New/TrackListHeader/VideoCover.tsx
Normal file
51
packages/web/components/New/TrackListHeader/VideoCover.tsx
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
import { useEffect, useRef } from 'react'
|
||||
import Hls from 'hls.js'
|
||||
import { injectGlobal } from '@emotion/css'
|
||||
import { isIOS, isSafari } from '@/web/utils/common'
|
||||
import { motion } from 'framer-motion'
|
||||
|
||||
injectGlobal`
|
||||
.plyr__video-wrapper,
|
||||
.plyr--video {
|
||||
background-color: transparent !important;
|
||||
}
|
||||
`
|
||||
|
||||
const VideoCover = ({ videoCover }: { videoCover?: string }) => {
|
||||
const ref = useRef<HTMLVideoElement>(null)
|
||||
const hls = useRef<Hls>(new Hls())
|
||||
|
||||
useEffect(() => {
|
||||
if (videoCover && Hls.isSupported()) {
|
||||
const video = document.querySelector('#video-cover') as HTMLVideoElement
|
||||
hls.current.loadSource(videoCover)
|
||||
hls.current.attachMedia(video)
|
||||
}
|
||||
}, [videoCover])
|
||||
|
||||
return (
|
||||
<motion.div
|
||||
initial={{ opacity: isIOS ? 1 : 0 }}
|
||||
animate={{ opacity: 1 }}
|
||||
transition={{ duration: 0.6 }}
|
||||
className='absolute inset-0'
|
||||
>
|
||||
{isSafari ? (
|
||||
<video
|
||||
src={videoCover}
|
||||
className='h-full w-full'
|
||||
autoPlay
|
||||
loop
|
||||
muted
|
||||
playsInline
|
||||
></video>
|
||||
) : (
|
||||
<div className='aspect-square'>
|
||||
<video id='video-cover' ref={ref} autoPlay muted loop />
|
||||
</div>
|
||||
)}
|
||||
</motion.div>
|
||||
)
|
||||
}
|
||||
|
||||
export default VideoCover
|
||||
3
packages/web/components/New/TrackListHeader/index.tsx
Normal file
3
packages/web/components/New/TrackListHeader/index.tsx
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
import TrackListHeader from './TrackListHeader'
|
||||
|
||||
export default TrackListHeader
|
||||
Loading…
Add table
Add a link
Reference in a new issue