Merge remote-tracking branch 'origin/react-player' into develop
This commit is contained in:
commit
70f83844d1
|
@ -48,6 +48,7 @@
|
|||
"react-blurhash": "^0.3.0",
|
||||
"react-dom": "18.2.0",
|
||||
"react-imgix": "^9.7.0",
|
||||
"react-player": "^2.12.0",
|
||||
"typescript": "5.0.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
|
|
@ -4,9 +4,11 @@ import {
|
|||
extractIdFromFirstTag,
|
||||
extractInnerHtml,
|
||||
} from '@/utils/html.utils'
|
||||
import { convertToIframe } from '@/utils/string.utils'
|
||||
import { HeadingElementsRef } from '@/utils/ui.utils'
|
||||
import { Typography } from '@acid-info/lsd-react'
|
||||
import styled from '@emotion/styled'
|
||||
import ReactPlayer from 'react-player'
|
||||
import { LPE } from '../../types/lpe.types'
|
||||
import { PostImageRatio } from '../Post/Post'
|
||||
import { ArticleImageBlockWrapper } from './Article.ImageBlockWrapper'
|
||||
|
@ -43,6 +45,55 @@ export const RenderArticleBlock = ({
|
|||
/>
|
||||
)
|
||||
}
|
||||
case 'p': {
|
||||
const isIframeRegex = /<iframe[^>]*>(?:<\/iframe>|[^]*?<\/iframe>)/
|
||||
const isIframe = isIframeRegex.test(block.text)
|
||||
|
||||
const isYoutubeRegex =
|
||||
/^(https?\:\/\/)?((www\.)?youtube\.com|youtu\.?be)\/.+$/
|
||||
|
||||
const isYoutube = isYoutubeRegex.test(block.text)
|
||||
const youtubeLink = block.text.match(isYoutubeRegex) ?? []
|
||||
|
||||
const isSimplecastRegex =
|
||||
/^https?:\/\/([a-zA-Z0-9-]+\.)*simplecast\.com\/[^?\s]+(\?[\s\S]*)?$/
|
||||
|
||||
const isSimplecast = isSimplecastRegex.test(block.text)
|
||||
const simplecastLink = block.text.match(isSimplecastRegex) ?? []
|
||||
|
||||
// const episodeId = extractUUIDFromEpisode(simplecastLink[0] ?? '')
|
||||
|
||||
// let audioSrc = ''
|
||||
|
||||
// if (isSimplecast) {
|
||||
// fetch(
|
||||
// `https://api.simplecast.com/episodes/audio/bc313c16-82e9-439a-8e0c-af59833d22d7`,
|
||||
// )
|
||||
// .then((response) => response.json())
|
||||
// .then((data) => console.log(data))
|
||||
// }
|
||||
|
||||
return isIframe ? (
|
||||
<IframeContainer dangerouslySetInnerHTML={{ __html: block.text }} />
|
||||
) : isYoutube ? (
|
||||
<ReactPlayer url={youtubeLink[0]} />
|
||||
) : isSimplecast ? (
|
||||
<IframeContainer
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: convertToIframe(simplecastLink[0] ?? ''),
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<Paragraph
|
||||
variant="body1"
|
||||
component={block.tagName as any}
|
||||
genericFontFamily="sans-serif"
|
||||
className={extractClassFromFirstTag(block.html) || ''}
|
||||
id={extractIdFromFirstTag(block.html) || `p-${block.order}`}
|
||||
dangerouslySetInnerHTML={{ __html: extractInnerHtml(block.html) }}
|
||||
/>
|
||||
)
|
||||
}
|
||||
default:
|
||||
return (
|
||||
<Paragraph
|
||||
|
@ -67,3 +118,21 @@ const Paragraph = styled(Typography)`
|
|||
line-height: var(--lsd-h6-lineHeight);
|
||||
}
|
||||
`
|
||||
|
||||
const IframeContainer = styled.div`
|
||||
position: relative;
|
||||
padding-bottom: 56.25%;
|
||||
padding-top: 30px;
|
||||
height: 0;
|
||||
overflow: hidden;
|
||||
|
||||
& > iframe,
|
||||
& > object,
|
||||
& > embed {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
`
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
.audioPlayer > input[type='range'] {
|
||||
width: 100%;
|
||||
accent-color: black;
|
||||
}
|
||||
|
||||
.audioPlayer > input[type='range']:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.audioPlayer > input[type='range']::-webkit-slider-runnable-track {
|
||||
width: 100%;
|
||||
height: 8px;
|
||||
cursor: grabbing;
|
||||
}
|
||||
|
||||
.audioPlayer > input[type='range']::-webkit-slider-thumb {
|
||||
-webkit-appearance: none;
|
||||
appearance: none;
|
||||
margin-top: -4px;
|
||||
}
|
|
@ -0,0 +1,273 @@
|
|||
import ReactPlayer from 'react-player'
|
||||
import styled from '@emotion/styled'
|
||||
import { useRef, useState } from 'react'
|
||||
import { PlayIcon } from '../Icons/PlayIcon'
|
||||
import { PauseIcon } from '../Icons/PauseIcon'
|
||||
import { VolumeIcon } from '../Icons/VolumeIcon'
|
||||
import styles from './GlobalAudioPlayer.module.css'
|
||||
import { convertSecToMinAndSec } from '@/utils/string.utils'
|
||||
import { Typography } from '@acid-info/lsd-react'
|
||||
|
||||
type StateProps = {
|
||||
url: string | null
|
||||
pip: boolean
|
||||
playing: boolean
|
||||
playedSeconds: number
|
||||
controls: boolean
|
||||
light: boolean
|
||||
volume: number
|
||||
muted: boolean
|
||||
played: number
|
||||
loaded: number
|
||||
duration: number
|
||||
playbackRate: number
|
||||
loop: boolean
|
||||
seeking: boolean
|
||||
}
|
||||
|
||||
const TEMP_URL =
|
||||
'https://cdn.simplecast.com/audio/b54c0885-7c72-415d-b032-7d294b78d785/episodes/30d4e2f5-4434-419c-8fc1-a76e4b367e20/audio/3c8eb229-3f34-45a4-84f1-ce1d6bd65922/default_tc.mp3'
|
||||
|
||||
export default function GlobalAudioPlayer() {
|
||||
const ref = useRef<ReactPlayer>(null)
|
||||
const [state, setState] = useState<StateProps>({
|
||||
url: TEMP_URL,
|
||||
pip: false,
|
||||
playing: false,
|
||||
playedSeconds: 0,
|
||||
controls: false,
|
||||
light: false,
|
||||
volume: 0.8,
|
||||
muted: false,
|
||||
played: 0,
|
||||
loaded: 0,
|
||||
duration: 0,
|
||||
playbackRate: 1.0,
|
||||
loop: false,
|
||||
seeking: false,
|
||||
})
|
||||
const [showVolume, setShowVolume] = useState(false)
|
||||
|
||||
// const handleLoad = (url: string) => {
|
||||
// setState((prev) => ({ ...prev, url, played: 0, loaded: 0, pip: false }))
|
||||
// }
|
||||
|
||||
const handlePlay = () => {
|
||||
setState((prev) => ({ ...prev, playing: true }))
|
||||
}
|
||||
|
||||
const handlePlayPause = () => {
|
||||
setState((prev) => ({ ...prev, playing: !state.playing }))
|
||||
}
|
||||
|
||||
// const handleStop = () => {
|
||||
// setState((prev) => ({ ...prev, url: null, playing: false }))
|
||||
// }
|
||||
|
||||
const handleEnded = () => {
|
||||
setState((prev) => ({ ...prev, playing: prev.loop }))
|
||||
}
|
||||
|
||||
const handleVolumeChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
setState((prev) => ({ ...prev, volume: parseFloat(e.target.value) }))
|
||||
}
|
||||
|
||||
// const handleToggleMuted = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
// setState((prev) => ({ ...prev, muted: !state.muted }))
|
||||
// }
|
||||
|
||||
// const handleSetPlaybackRate = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
// setState((prev) => ({ ...prev, playbackRate: parseFloat(e.target.value) }))
|
||||
// }
|
||||
|
||||
const handlePause = () => {
|
||||
setState((prev) => ({ ...prev, playing: false }))
|
||||
}
|
||||
|
||||
const handleSeekMouseDown = (
|
||||
e: React.MouseEvent<HTMLInputElement, MouseEvent>,
|
||||
) => {
|
||||
setState((prev) => ({ ...prev, seeking: true }))
|
||||
}
|
||||
|
||||
const handleSeekChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
setState((prev) => ({ ...prev, played: parseFloat(e.target.value) }))
|
||||
}
|
||||
|
||||
const handleSeekMouseUp = (
|
||||
e: React.MouseEvent<HTMLInputElement, MouseEvent>,
|
||||
) => {
|
||||
setState((prev) => ({ ...prev, seeking: false }))
|
||||
const target = e.target as HTMLInputElement
|
||||
ref.current?.seekTo(parseFloat(target?.value))
|
||||
}
|
||||
|
||||
const handleDuration = (duration: number) => {
|
||||
setState((prev) => ({ ...prev, duration }))
|
||||
}
|
||||
|
||||
const handleOnPlaybackRateChange = (
|
||||
e: React.ChangeEvent<HTMLInputElement>,
|
||||
) => {
|
||||
setState((prev) => ({ ...prev, playbackRate: parseFloat(e.target.value) }))
|
||||
}
|
||||
|
||||
const handleProgress = (newState: { playedSeconds: number }) => {
|
||||
setState((prev) => ({ ...prev, playedSeconds: newState.playedSeconds }))
|
||||
}
|
||||
|
||||
return (
|
||||
<Container>
|
||||
<AudioPlayer>
|
||||
<Buttons>
|
||||
<Row>
|
||||
<PlayPause onClick={handlePlayPause}>
|
||||
{state.playing ? <PauseIcon /> : <PlayIcon />}
|
||||
</PlayPause>
|
||||
<TimeContainer>
|
||||
<Time variant="body3">
|
||||
{convertSecToMinAndSec(state.playedSeconds)}
|
||||
</Time>
|
||||
<Typography variant="body3">/</Typography>
|
||||
<Time variant="body3">
|
||||
{convertSecToMinAndSec(state.duration)}
|
||||
</Time>
|
||||
</TimeContainer>
|
||||
</Row>
|
||||
|
||||
<VolumeContainer onClick={() => setShowVolume((prev) => !prev)}>
|
||||
{showVolume && (
|
||||
<VolumeGauge>
|
||||
<input
|
||||
type="range"
|
||||
min={0}
|
||||
max={1}
|
||||
step="any"
|
||||
value={state.volume}
|
||||
onChange={handleVolumeChange}
|
||||
/>
|
||||
</VolumeGauge>
|
||||
)}
|
||||
<VolumeIcon />
|
||||
</VolumeContainer>
|
||||
</Buttons>
|
||||
|
||||
{/* <button onClick={handleStop}>Stop</button> */}
|
||||
|
||||
<Seek className={styles.audioPlayer}>
|
||||
<input
|
||||
type="range"
|
||||
min={0}
|
||||
max={0.999999}
|
||||
step="any"
|
||||
value={state.played}
|
||||
onMouseDown={handleSeekMouseDown}
|
||||
onChange={handleSeekChange}
|
||||
onMouseUp={handleSeekMouseUp}
|
||||
/>
|
||||
</Seek>
|
||||
</AudioPlayer>
|
||||
|
||||
<ReactPlayer
|
||||
ref={ref}
|
||||
style={{ display: 'none' }}
|
||||
url={state.url ?? TEMP_URL}
|
||||
width="100%"
|
||||
height="100%"
|
||||
pip={state.pip}
|
||||
playing={state.playing}
|
||||
controls={state.controls}
|
||||
light={state.light}
|
||||
loop={state.loop}
|
||||
playbackRate={state.playbackRate}
|
||||
volume={state.volume}
|
||||
muted={state.muted}
|
||||
onReady={() => console.log('onReady')}
|
||||
onStart={() => console.log('onStart')}
|
||||
onPlay={handlePlay}
|
||||
onPause={handlePause}
|
||||
onBuffer={() => console.log('onBuffer')}
|
||||
onPlaybackRateChange={handleOnPlaybackRateChange}
|
||||
onSeek={(e) => console.log('onSeek', e)}
|
||||
onEnded={handleEnded}
|
||||
onError={(e) => console.log('onError', e)}
|
||||
onDuration={handleDuration}
|
||||
onProgress={handleProgress}
|
||||
/>
|
||||
<RightMenu></RightMenu>
|
||||
</Container>
|
||||
)
|
||||
}
|
||||
|
||||
const Container = styled.div`
|
||||
width: 100vw;
|
||||
height: 80px;
|
||||
padding: 22px 16px;
|
||||
background: rgb(var(--lsd-surface-primary));
|
||||
position: fixed;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
border-top: 1px solid rgb(var(--lsd-border-primary));
|
||||
box-sizing: border-box;
|
||||
`
|
||||
|
||||
const Buttons = styled.div`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
width: 100%;
|
||||
`
|
||||
|
||||
const VolumeContainer = styled.div`
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
position: relative;
|
||||
align-items: center;
|
||||
`
|
||||
|
||||
const Seek = styled.div`
|
||||
display: flex;
|
||||
width: 100%;
|
||||
`
|
||||
|
||||
const VolumeGauge = styled.div`
|
||||
position: absolute;
|
||||
top: -30px;
|
||||
`
|
||||
|
||||
const AudioPlayer = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
width: 60%;
|
||||
`
|
||||
|
||||
const RightMenu = styled.div`
|
||||
width: 40%;
|
||||
`
|
||||
|
||||
const PlayPause = styled.button`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border: none;
|
||||
background: none;
|
||||
margin-right: 8px;
|
||||
`
|
||||
|
||||
const Row = styled.div`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
white-space: pre-wrap;
|
||||
`
|
||||
|
||||
const TimeContainer = styled(Row)`
|
||||
gap: 8px;
|
||||
`
|
||||
|
||||
const Time = styled(Typography)`
|
||||
width: 32px;
|
||||
`
|
|
@ -0,0 +1 @@
|
|||
export { default as GlobalAudioPlayer } from './GlobalAudioPlayer'
|
|
@ -0,0 +1,22 @@
|
|||
import { LsdIcon } from '@acid-info/lsd-react'
|
||||
|
||||
export const PauseIcon = LsdIcon(
|
||||
(props) => (
|
||||
<svg
|
||||
width="20px"
|
||||
height="20px"
|
||||
version="1.1"
|
||||
viewBox="0 0 512 512"
|
||||
xmlSpace="preserve"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlnsXlink="http://www.w3.org/1999/xlink"
|
||||
{...props}
|
||||
>
|
||||
<g>
|
||||
<path d="M224,435.8V76.1c0-6.7-5.4-12.1-12.2-12.1h-71.6c-6.8,0-12.2,5.4-12.2,12.1v359.7c0,6.7,5.4,12.2,12.2,12.2h71.6 C218.6,448,224,442.6,224,435.8z" />
|
||||
<path d="M371.8,64h-71.6c-6.7,0-12.2,5.4-12.2,12.1v359.7c0,6.7,5.4,12.2,12.2,12.2h71.6c6.7,0,12.2-5.4,12.2-12.2V76.1 C384,69.4,378.6,64,371.8,64z" />
|
||||
</g>
|
||||
</svg>
|
||||
),
|
||||
{ filled: true },
|
||||
)
|
|
@ -0,0 +1 @@
|
|||
export * from './PauseIcon'
|
|
@ -0,0 +1,20 @@
|
|||
import { LsdIcon } from '@acid-info/lsd-react'
|
||||
|
||||
export const PlayIcon = LsdIcon(
|
||||
(props) => (
|
||||
<svg
|
||||
width="20"
|
||||
height="20"
|
||||
viewBox="0 0 20 20"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
{...props}
|
||||
>
|
||||
<path
|
||||
d="M6.6665 15.8346V4.16797L15.8332 10.0013L6.6665 15.8346Z"
|
||||
fill="black"
|
||||
/>
|
||||
</svg>
|
||||
),
|
||||
{ filled: true },
|
||||
)
|
|
@ -0,0 +1 @@
|
|||
export * from './PlayIcon'
|
|
@ -0,0 +1,27 @@
|
|||
import { LsdIcon } from '@acid-info/lsd-react'
|
||||
|
||||
export const VolumeIcon = LsdIcon(
|
||||
(props) => (
|
||||
<svg
|
||||
width="16"
|
||||
height="16"
|
||||
viewBox="0 0 16 16"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
{...props}
|
||||
>
|
||||
<g clip-path="url(#clip0_228_17463)">
|
||||
<path
|
||||
d="M2 5.99901V9.99901H4.66667L8 13.3323V2.66568L4.66667 5.99901H2ZM11 7.99901C11 6.81901 10.32 5.80568 9.33333 5.31234V10.679C10.32 10.1923 11 9.17901 11 7.99901ZM9.33333 2.15234V3.52568C11.26 4.09901 12.6667 5.88568 12.6667 7.99901C12.6667 10.1123 11.26 11.899 9.33333 12.4723V13.8457C12.0067 13.239 14 10.8523 14 7.99901C14 5.14568 12.0067 2.75901 9.33333 2.15234Z"
|
||||
fill="black"
|
||||
/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_228_17463">
|
||||
<rect width="16" height="16" fill="white" />
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
),
|
||||
{ filled: true },
|
||||
)
|
|
@ -0,0 +1 @@
|
|||
export * from './VolumeIcon'
|
|
@ -8,6 +8,7 @@ import type { AppProps } from 'next/app'
|
|||
import Head from 'next/head'
|
||||
import { ReactNode } from 'react'
|
||||
import { LSDThemeProvider } from '../containers/LSDThemeProvider'
|
||||
import { GlobalAudioPlayer } from '@/components/GlobalAudioPlayer'
|
||||
|
||||
type NextLayoutComponentType<P = {}> = NextComponentType<
|
||||
NextPageContext,
|
||||
|
@ -94,6 +95,7 @@ export default function App({ Component, pageProps }: AppLayoutProps) {
|
|||
<SearchBarProvider>
|
||||
{getLayout(<Component {...pageProps} />)}
|
||||
</SearchBarProvider>
|
||||
<GlobalAudioPlayer />
|
||||
</LSDThemeProvider>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
|
|
@ -24,3 +24,25 @@ export const shuffle = (array: any[]) => {
|
|||
}
|
||||
|
||||
export const unique = (arr: any[]) => Array.from(new Set(arr))
|
||||
|
||||
export const getAudioSourceFromSimplecastPlayer = async (url: string) => {
|
||||
const myHeaders = new Headers()
|
||||
myHeaders.append(
|
||||
'Authorization',
|
||||
'Bearer eyJhcGlfa2V5IjoiMzg3OTdhY2Y5N2NmZjgzZjQxNGI5ODNiN2E2MjY3NmQifQ==',
|
||||
)
|
||||
|
||||
const requestOptions = {
|
||||
method: 'GET',
|
||||
headers: myHeaders,
|
||||
}
|
||||
|
||||
const result = await fetch(
|
||||
`https://api.simplecast.com/episodes/${url}`,
|
||||
requestOptions,
|
||||
)
|
||||
|
||||
const data = await result.json()
|
||||
console.log(data)
|
||||
return data
|
||||
}
|
||||
|
|
|
@ -41,3 +41,27 @@ export const calcReadingTime = (text: string): number => {
|
|||
const numberOfWords = text.split(/\s/g).length
|
||||
return Math.ceil(numberOfWords / wordsPerMinute)
|
||||
}
|
||||
|
||||
export function convertSecToMinAndSec(totalSeconds: number) {
|
||||
// Convert seconds to minutes and seconds
|
||||
const minutes = Math.floor(totalSeconds / 60)
|
||||
const seconds = Math.floor(totalSeconds % 60)
|
||||
|
||||
// Ensure two digit format
|
||||
const formattedMinutes = String(minutes).padStart(2, '0')
|
||||
const formattedSeconds = String(seconds).padStart(2, '0')
|
||||
|
||||
return `${formattedMinutes}:${formattedSeconds}`
|
||||
}
|
||||
|
||||
export function extractUUIDFromEpisode(url: string) {
|
||||
const regex = /([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})/
|
||||
const match = url.match(regex)
|
||||
return match ? match[1] : null
|
||||
}
|
||||
|
||||
export function convertToIframe(url: string) {
|
||||
if (!url) return ''
|
||||
|
||||
return `<iframe height="200px" width="100%" frameborder="no" scrolling="no" seamless src="${url}"></iframe>`
|
||||
}
|
||||
|
|
|
@ -83,7 +83,6 @@ export function useIntersectionObserver(
|
|||
headings.forEach((heading) => {
|
||||
if (heading.isIntersecting && heading.target instanceof HTMLElement) {
|
||||
const targetId = heading.target.getAttribute('id')
|
||||
console.log(targetId)
|
||||
if (targetId) setActiveId(targetId)
|
||||
}
|
||||
})
|
||||
|
|
31
yarn.lock
31
yarn.lock
|
@ -2439,6 +2439,11 @@ defaults@^1.0.3:
|
|||
dependencies:
|
||||
clone "^1.0.2"
|
||||
|
||||
deepmerge@^4.0.0:
|
||||
version "4.3.1"
|
||||
resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.3.1.tgz#44b5f2147cd3b00d4b56137685966f26fd25dd4a"
|
||||
integrity sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==
|
||||
|
||||
define-lazy-prop@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz#3f7ae421129bcaaac9bc74905c98a0009ec9ee7f"
|
||||
|
@ -3939,6 +3944,11 @@ locate-path@^5.0.0:
|
|||
dependencies:
|
||||
p-locate "^4.1.0"
|
||||
|
||||
load-script@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/load-script/-/load-script-1.0.0.tgz#0491939e0bee5643ee494a7e3da3d2bac70c6ca4"
|
||||
integrity sha512-kPEjMFtZvwL9TaZo0uZ2ml+Ye9HUMmPwbYRJ324qF9tqMejwykJ5ggTyvzmrbBeapCAbk98BSbTeovHEEP1uCA==
|
||||
|
||||
locate-path@^6.0.0:
|
||||
version "6.0.0"
|
||||
resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-6.0.0.tgz#55321eb309febbc59c4801d931a72452a681d286"
|
||||
|
@ -4033,6 +4043,11 @@ mdn-data@2.0.14:
|
|||
resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.14.tgz#7113fc4281917d63ce29b43446f701e68c25ba50"
|
||||
integrity sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==
|
||||
|
||||
memoize-one@^5.1.1:
|
||||
version "5.2.1"
|
||||
resolved "https://registry.yarnpkg.com/memoize-one/-/memoize-one-5.2.1.tgz#8337aa3c4335581839ec01c3d594090cebe8f00e"
|
||||
integrity sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q==
|
||||
|
||||
merge-stream@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60"
|
||||
|
@ -4616,6 +4631,11 @@ react-dom@18.2.0:
|
|||
loose-envify "^1.1.0"
|
||||
scheduler "^0.23.0"
|
||||
|
||||
react-fast-compare@^3.0.1:
|
||||
version "3.2.2"
|
||||
resolved "https://registry.yarnpkg.com/react-fast-compare/-/react-fast-compare-3.2.2.tgz#929a97a532304ce9fee4bcae44234f1ce2c21d49"
|
||||
integrity sha512-nsO+KSNgo1SbJqJEYRE9ERzo7YtYbou/OqjSQKxV7jcKox7+usiUVZOAC+XnDOABXggQTno0Y1CpVnuWEc1boQ==
|
||||
|
||||
react-imgix@^9.7.0:
|
||||
version "9.7.0"
|
||||
resolved "https://registry.yarnpkg.com/react-imgix/-/react-imgix-9.7.0.tgz#944f63693daf6524d07898aaf7d1cbbe59e5edca"
|
||||
|
@ -4642,6 +4662,17 @@ react-measure@^2.3.0:
|
|||
prop-types "^15.6.2"
|
||||
resize-observer-polyfill "^1.5.0"
|
||||
|
||||
react-player@^2.12.0:
|
||||
version "2.12.0"
|
||||
resolved "https://registry.yarnpkg.com/react-player/-/react-player-2.12.0.tgz#2fc05dbfec234c829292fbca563b544064bd14f0"
|
||||
integrity sha512-rymLRz/2GJJD+Wc01S7S+i9pGMFYnNmQibR2gVE3KmHJCBNN8BhPAlOPTGZtn1uKpJ6p4RPLlzPQ1OLreXd8gw==
|
||||
dependencies:
|
||||
deepmerge "^4.0.0"
|
||||
load-script "^1.0.0"
|
||||
memoize-one "^5.1.1"
|
||||
prop-types "^15.7.2"
|
||||
react-fast-compare "^3.0.1"
|
||||
|
||||
react-universal-interface@^0.6.2:
|
||||
version "0.6.2"
|
||||
resolved "https://registry.yarnpkg.com/react-universal-interface/-/react-universal-interface-0.6.2.tgz#5e8d438a01729a4dbbcbeeceb0b86be146fe2b3b"
|
||||
|
|
Loading…
Reference in New Issue