mirror of
https://github.com/zoriya/v10.git
synced 2026-08-15 02:14:06 +00:00
feat(ui): add toasted skin
This commit is contained in:
@@ -0,0 +1 @@
|
||||
22.19.0
|
||||
+3
-1
@@ -14,8 +14,10 @@
|
||||
],
|
||||
"words": [
|
||||
"antfu",
|
||||
"noto",
|
||||
"nums",
|
||||
"rahim"
|
||||
"rahim",
|
||||
"segoe"
|
||||
],
|
||||
"ignoreWords": [],
|
||||
"import": []
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<link rel="preconnect" href="https://rsms.me/" />
|
||||
<link rel="stylesheet" href="https://rsms.me/inter/inter.css" />
|
||||
</head>
|
||||
<body>
|
||||
<body class="bg-white text-stone-700 dark:bg-stone-900 dark:text-stone-200">
|
||||
<div id="root"></div>
|
||||
<script type="module" src="/src/main.tsx"></script>
|
||||
</body>
|
||||
|
||||
@@ -0,0 +1,149 @@
|
||||
import { MediaProvider, MediaSkinDefault, MediaSkinToasted, Video } from '@vjs-10/react';
|
||||
|
||||
import './globals.css';
|
||||
import { useCallback, useMemo, useState, type ChangeEventHandler } from 'react';
|
||||
|
||||
const skins = [{
|
||||
key: 'default',
|
||||
name: 'Frosted',
|
||||
component: MediaSkinDefault,
|
||||
}, {
|
||||
key: 'toasted',
|
||||
name: 'Toasted',
|
||||
component: MediaSkinToasted,
|
||||
}] as const;
|
||||
|
||||
type SkinKey = (typeof skins)[number]['key'];
|
||||
|
||||
const mediaSources = [{
|
||||
key: '1',
|
||||
name: 'Mux 1',
|
||||
value: 'https://stream.mux.com/a4nOgmxGWg6gULfcBbAa00gXyfcwPnAFldF8RdsNyk8M.m3u8'
|
||||
}, {
|
||||
key: '2',
|
||||
name: 'Mux 2',
|
||||
value: 'https://stream.mux.com/fXNzVtmtWuyz00xnSrJg4OJH6PyNo6D02UzmgeKGkP5YQ.m3u8'
|
||||
}, {
|
||||
key: '3',
|
||||
name: 'Mux 3',
|
||||
value: 'https://stream.mux.com/A3VXy02VoUinw01pwyomEO3bHnG4P32xzV7u1j1FSzjNg/high.mp4'
|
||||
}, {
|
||||
key: '4',
|
||||
name: 'Mux 4',
|
||||
value: 'https://stream.mux.com/lyrKpPcGfqyzeI00jZAfW6MvP6GNPrkML.m3u8'
|
||||
}] as const;
|
||||
|
||||
type MediaSourceKey = (typeof mediaSources)[number]['key'];
|
||||
|
||||
function getParam<T>(key: string, defaultValue: T): T {
|
||||
const params = new URLSearchParams(window.location.search);
|
||||
return params.get(key) as T || defaultValue;
|
||||
}
|
||||
function setParam(key: string, value: string) {
|
||||
const params = new URLSearchParams(window.location.search);
|
||||
params.set(key, value);
|
||||
const search = params.toString();
|
||||
const url = window.location.pathname + (search ? `?${search}` : '');
|
||||
window.history.replaceState(null, '', url);
|
||||
}
|
||||
|
||||
const DEFAULT_SKIN: SkinKey = 'toasted';
|
||||
const DEFAULT_MEDIA_SOURCE: MediaSourceKey = '1';
|
||||
|
||||
export default function App(): JSX.Element {
|
||||
const [skinKey, setSkinKey] = useState<SkinKey>(getParam('skin', DEFAULT_SKIN));
|
||||
const [mediaSourceKey, setMediaSourceKey] = useState<MediaSourceKey>(getParam('source', DEFAULT_MEDIA_SOURCE));
|
||||
|
||||
const mediaSource = useMemo(() => {
|
||||
let match = mediaSources.find(m => m.key === mediaSourceKey);
|
||||
if (!match) {
|
||||
match = mediaSources.find(m => m.key === DEFAULT_MEDIA_SOURCE)!;
|
||||
setMediaSourceKey(match.key);
|
||||
}
|
||||
return match.value;
|
||||
}, [mediaSourceKey]);
|
||||
|
||||
const Skin = useMemo(() => {
|
||||
let match = skins.find(s => s.key === skinKey);
|
||||
if (!match) {
|
||||
match = skins.find(s => s.key === DEFAULT_SKIN)!;
|
||||
setSkinKey(match.key);
|
||||
}
|
||||
return match.component;
|
||||
}, [skinKey]);
|
||||
|
||||
const onChangeSkin: ChangeEventHandler<HTMLSelectElement> = useCallback((event) => {
|
||||
const value = event.target.value as SkinKey;
|
||||
setSkinKey(value);
|
||||
setParam('skin', value);
|
||||
}, []);
|
||||
const onChangeMediaSource: ChangeEventHandler<HTMLSelectElement> = useCallback((event) => {
|
||||
const value = event.target.value as MediaSourceKey;
|
||||
setMediaSourceKey(value);
|
||||
setParam('source', value);
|
||||
}, []);
|
||||
|
||||
// Force a re-render on changes.
|
||||
const key = `${skinKey}-${mediaSourceKey}`;
|
||||
|
||||
const skinClassName = useMemo(() => {
|
||||
switch (skinKey) {
|
||||
case 'default':
|
||||
return 'rounded-4xl shadow shadow-lg shadow-black/15';
|
||||
case 'toasted':
|
||||
return 'rounded-lg shadow shadow-lg shadow-black/15';
|
||||
default:
|
||||
return '';
|
||||
}
|
||||
}, [skinKey]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<header className="fixed top-0 inset-x-0 bg-white dark:bg-stone-800 shadow shadow-black/10 after:h-px after:absolute after:inset-x-0 after:top-full after:bg-black/5">
|
||||
<div className='grid grid-cols-5 h-2' aria-hidden="true">
|
||||
<div className='bg-yellow-500'></div>
|
||||
<div className='bg-orange-500'></div>
|
||||
<div className='bg-red-500'></div>
|
||||
<div className='bg-purple-500'></div>
|
||||
<div className='bg-blue-500'></div>
|
||||
</div>
|
||||
|
||||
<div className="py-3 px-6 flex items-center justify-between">
|
||||
<div className="space-y-1">
|
||||
<h1 className="font-medium text-lg tracking-tight leading-tight dark:text-white">Playground</h1>
|
||||
<small className='block text-stone-400 text-sm'>Test out the various skins for Video.js.</small>
|
||||
</div>
|
||||
|
||||
<nav className='flex items-center gap-3'>
|
||||
<select value={mediaSourceKey} onChange={onChangeMediaSource}>
|
||||
{mediaSources.map(({ key, name }) => (
|
||||
<option key={key} value={key}>
|
||||
{name}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
<select value={skinKey} onChange={onChangeSkin}>
|
||||
{skins.map(({ key, name }) => (
|
||||
<option key={key} value={key}>
|
||||
{name}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</nav>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main className="min-h-screen flex justify-center items-center bg-radial bg-size-[16px_16px] from-stone-300 dark:from-stone-700 via-10% via-transparent to-transparent">
|
||||
<div className='w-full max-w-4xl mx-auto p-6'>
|
||||
<MediaProvider key={key}>
|
||||
<Skin className={skinClassName}>
|
||||
{/* @ts-ignore -- types are incorrect */}
|
||||
<Video src={mediaSource} />
|
||||
</Skin>
|
||||
</MediaProvider>
|
||||
</div>
|
||||
</main>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -4,18 +4,16 @@
|
||||
@source "../../../packages/react";
|
||||
|
||||
@theme {
|
||||
--font-sans:
|
||||
InterVariable, ui-sans-serif, system-ui, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol',
|
||||
'Noto Color Emoji';
|
||||
--font-sans: InterVariable, ui-sans-serif, system-ui, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji';
|
||||
}
|
||||
|
||||
.body {
|
||||
@apply antialiased font-[510] font-sans text-[0.8125rem] @7xl/root:text-[0.9375rem] leading-normal tracking-[-0.0125em];
|
||||
}
|
||||
|
||||
:root {
|
||||
font-feature-settings: 'cv01', 'ss01', 'ss03';
|
||||
}
|
||||
|
||||
/* Make hocus (hover + focus-visible) variants */
|
||||
@custom-variant hocus (&:is(:hover, :focus-visible));
|
||||
@custom-variant group-hocus (&:is(:hover, :focus-visible) &);
|
||||
@custom-variant peer-hocus (&:is(:hover, :focus-visible) ~ &);
|
||||
/* Make reduced-transparency variant */
|
||||
@custom-variant reduced-transparency @media (prefers-reduced-transparency: reduce);
|
||||
|
||||
@@ -1,38 +1,10 @@
|
||||
import ReactDOM from 'react-dom/client';
|
||||
|
||||
import { MediaProvider, MediaSkinDefault, Video } from '@vjs-10/react';
|
||||
|
||||
import { StrictMode } from 'react';
|
||||
import { createRoot } from 'react-dom/client';
|
||||
import App from './App';
|
||||
import './globals.css';
|
||||
|
||||
function DemoPlayer() {
|
||||
return (
|
||||
<div className="min-h-screen grid place-items-center px-6">
|
||||
<div className="space-y-12 w-full">
|
||||
<MediaProvider>
|
||||
<div className="w-full max-w-4xl mx-auto">
|
||||
<MediaSkinDefault className="rounded-4xl aspect-video">
|
||||
{/* @ts-ignore -- types are incorrect */}
|
||||
{/* <Video
|
||||
muted
|
||||
src="https://stream.mux.com/A3VXy02VoUinw01pwyomEO3bHnG4P32xzV7u1j1FSzjNg/high.mp4"
|
||||
/> */}
|
||||
{/* @ts-ignore -- types are incorrect */}
|
||||
<Video muted src="https://stream.mux.com/a4nOgmxGWg6gULfcBbAa00gXyfcwPnAFldF8RdsNyk8M.m3u8" />
|
||||
</MediaSkinDefault>
|
||||
</div>
|
||||
</MediaProvider>
|
||||
|
||||
<MediaProvider>
|
||||
<div className="w-full max-w-4xl mx-auto">
|
||||
<MediaSkinDefault className="aspect-video">
|
||||
{/* @ts-ignore -- types are incorrect */}
|
||||
<Video muted src="https://stream.mux.com/fXNzVtmtWuyz00xnSrJg4OJH6PyNo6D02UzmgeKGkP5YQ.m3u8" />
|
||||
</MediaSkinDefault>
|
||||
</div>
|
||||
</MediaProvider>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
ReactDOM.createRoot(document.getElementById('root')!).render(<DemoPlayer />);
|
||||
createRoot(document.getElementById('root')!).render(
|
||||
<StrictMode>
|
||||
<App />
|
||||
</StrictMode>,
|
||||
);
|
||||
|
||||
@@ -38,7 +38,6 @@
|
||||
"eslint": "^9.36.0",
|
||||
"eslint-plugin-format": "^1.0.2",
|
||||
"eslint-plugin-jsx-a11y": "^6.10.2",
|
||||
"eslint-plugin-react": "^7.37.5",
|
||||
"eslint-plugin-react-hooks": "^5.2.0",
|
||||
"eslint-plugin-react-refresh": "^0.4.21",
|
||||
"lint-staged": "^16.2.0",
|
||||
|
||||
@@ -46,12 +46,12 @@ export default function MediaSkinDefault({ children, className = '' }: SkinProps
|
||||
className={styles.TimeDisplay}
|
||||
/>
|
||||
|
||||
<TimeRange.Root className={styles.TimeRangeRoot}>
|
||||
<TimeRange.Track className={styles.TimeRangeTrack}>
|
||||
<TimeRange.Progress className={styles.TimeRangeProgress} />
|
||||
<TimeRange.Pointer className={styles.TimeRangePointer} />
|
||||
<TimeRange.Root className={styles.SliderRoot}>
|
||||
<TimeRange.Track className={styles.SliderTrack}>
|
||||
<TimeRange.Progress className={styles.SliderProgress} />
|
||||
<TimeRange.Pointer className={styles.SliderPointer} />
|
||||
</TimeRange.Track>
|
||||
<TimeRange.Thumb className={styles.TimeRangeThumb} />
|
||||
<TimeRange.Thumb className={styles.SliderThumb} />
|
||||
</TimeRange.Root>
|
||||
|
||||
<DurationDisplay className={styles.TimeDisplay} />
|
||||
@@ -66,12 +66,12 @@ export default function MediaSkinDefault({ children, className = '' }: SkinProps
|
||||
</MuteButton>
|
||||
</Popover.Trigger>
|
||||
<Popover.Positioner side="top" sideOffset={8}>
|
||||
<Popover.Popup className={styles.VolumePopup}>
|
||||
<VolumeRange.Root className={styles.VolumeRangeRoot} orientation="vertical">
|
||||
<VolumeRange.Track className={styles.VolumeRangeTrack}>
|
||||
<VolumeRange.Progress className={styles.VolumeRangeProgress} />
|
||||
<Popover.Popup className={styles.PopoverPopup}>
|
||||
<VolumeRange.Root className={styles.SliderRoot} orientation="vertical">
|
||||
<VolumeRange.Track className={styles.SliderTrack}>
|
||||
<VolumeRange.Progress className={styles.SliderProgress} />
|
||||
</VolumeRange.Track>
|
||||
<VolumeRange.Thumb className={styles.VolumeRangeThumb} />
|
||||
<VolumeRange.Thumb className={styles.SliderThumb} />
|
||||
</VolumeRange.Root>
|
||||
</Popover.Popup>
|
||||
</Popover.Positioner>
|
||||
|
||||
@@ -1,43 +1,12 @@
|
||||
// A (very crude) utility to merge class names
|
||||
// Usually I'd use something like `clsx` or `classnames` but this is ok for our simple use case.
|
||||
// It just makes the billions of Tailwind classes a little easier to read.
|
||||
const cn = (...classes: (string | undefined)[]): string => classes.filter(Boolean).join(' ');
|
||||
import type { MediaDefaultSkinStyles } from "./types";
|
||||
|
||||
export interface MediaDefaultSkinStyles {
|
||||
readonly MediaContainer: string;
|
||||
readonly Overlay: string;
|
||||
readonly Controls: string;
|
||||
readonly Button: string;
|
||||
readonly IconButton: string;
|
||||
readonly PlayButton: string;
|
||||
readonly PlayIcon: string;
|
||||
readonly PauseIcon: string;
|
||||
readonly VolumeButton: string;
|
||||
readonly VolumeHighIcon: string;
|
||||
readonly VolumeLowIcon: string;
|
||||
readonly VolumeOffIcon: string;
|
||||
readonly FullScreenButton: string;
|
||||
readonly FullScreenEnterIcon: string;
|
||||
readonly FullScreenExitIcon: string;
|
||||
readonly TimeControls: string;
|
||||
readonly TimeDisplay: string;
|
||||
readonly TimeRangeRoot: string;
|
||||
readonly TimeRangeTrack: string;
|
||||
readonly TimeRangeProgress: string;
|
||||
readonly TimeRangePointer: string;
|
||||
readonly TimeRangeThumb: string;
|
||||
readonly VolumePopup: string;
|
||||
readonly VolumeRangeRoot: string;
|
||||
readonly VolumeRangeTrack: string;
|
||||
readonly VolumeRangeProgress: string;
|
||||
readonly VolumeRangeThumb: string;
|
||||
}
|
||||
import { cn } from "../../utils/cn";
|
||||
|
||||
const styles: MediaDefaultSkinStyles = {
|
||||
MediaContainer: cn(
|
||||
'relative @container/root group/root overflow-clip',
|
||||
// Base typography
|
||||
'antialiased font-[510] font-sans text-[0.8125rem] @7xl/root:text-[0.9375rem] leading-normal tracking-[-0.0125em]',
|
||||
'text-sm',
|
||||
// Prevent rounded corners in fullscreen.
|
||||
'[&:fullscreen]:rounded-none [&:fullscreen]:[&_video]:h-full [&:fullscreen]:[&_video]:w-full',
|
||||
// Fancy borders.
|
||||
@@ -49,25 +18,26 @@ const styles: MediaDefaultSkinStyles = {
|
||||
Overlay: cn(
|
||||
'opacity-0 delay-500 rounded-[inherit] absolute inset-0 pointer-events-none z-10 bg-gradient-to-t from-black/50 via-black/20 to-transparent transition-opacity backdrop-saturate-150 backdrop-brightness-90',
|
||||
// Hide when playing (for now).
|
||||
// This is crude temporary logic, we’ll improve it later I guess with a [data-show-controls] attribute or something?
|
||||
// FIXME: This is crude temporary logic, we’ll improve it later I guess with a [data-show-controls] attribute or something?
|
||||
'has-[+.controls_[data-paused]]:opacity-100 has-[+.controls_[data-paused]]:delay-0',
|
||||
'group-hover/root:opacity-100 group-hover/root:delay-0'
|
||||
),
|
||||
Controls: cn(
|
||||
'controls', // Temporary className hook for above logic. Can be removed once have a proper selector as above.
|
||||
'controls', // FIXME: Temporary className hook for above logic in the overlay. Can be removed once have a proper way to handle controls visibility.
|
||||
'@container/controls absolute inset-x-3 bottom-3 rounded-full z-20 flex items-center p-1 ring ring-white/10 ring-inset gap-0.5 text-white text-shadow',
|
||||
'shadow-sm shadow-black/15',
|
||||
// Background
|
||||
'bg-white/10 backdrop-blur-3xl backdrop-saturate-150 backdrop-brightness-90',
|
||||
// Animation
|
||||
'transition will-change-transform origin-bottom ease-out',
|
||||
// Temporary hide/show logic
|
||||
// FIXME: Temporary hide/show logic
|
||||
'scale-90 opacity-0 delay-500',
|
||||
'has-[[data-paused]]:scale-100 has-[[data-paused]]:opacity-100 has-[[data-paused]]:delay-0',
|
||||
'group-hover/root:scale-100 group-hover/root:opacity-100 group-hover/root:delay-0',
|
||||
// Border to enhance contrast on lighter videos
|
||||
'after:absolute after:inset-0 after:ring after:rounded-[inherit] after:ring-black/15 after:pointer-events-none after:z-10',
|
||||
// Reduced transparency for users with preference
|
||||
// XXX: This requires a Tailwind custom variant (see 1 below)
|
||||
'reduced-transparency:bg-black/70 reduced-transparency:ring-black reduced-transparency:after:ring-white/20',
|
||||
// High contrast mode
|
||||
'contrast-more:bg-black/90 contrast-more:ring-black contrast-more:after:ring-white/20'
|
||||
@@ -77,7 +47,7 @@ const styles: MediaDefaultSkinStyles = {
|
||||
// Background/foreground
|
||||
'bg-transparent text-white/90',
|
||||
// Hover and focus states
|
||||
'hocus:no-underline hocus:bg-white/10 hocus:text-white',
|
||||
'hover:no-underline hover:bg-white/10 hover:text-white focus-visible:no-underline focus-visible:bg-white/10 focus-visible:text-white',
|
||||
// Focus state
|
||||
'-outline-offset-2 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-blue-500',
|
||||
// Disabled state
|
||||
@@ -128,39 +98,42 @@ const styles: MediaDefaultSkinStyles = {
|
||||
),
|
||||
TimeControls: cn('flex-1 flex items-center gap-3 px-1.5'),
|
||||
TimeDisplay: cn('tabular-nums text-shadow-2xs shadow-black/50'),
|
||||
TimeRangeRoot: cn('flex [&[data-orientation="horizontal"]]:h-5 [&[data-orientation="vertical"]]:w-5 [&[data-orientation="vertical"]]:h-20 items-center justify-center flex-1 group/slider relative'),
|
||||
TimeRangeTrack: cn('[&[data-orientation="horizontal"]]:h-1 [&[data-orientation="vertical"]]:w-1 w-full relative select-none rounded-full bg-white/20 ring-1 ring-black/5'),
|
||||
TimeRangeProgress: cn('bg-white rounded-[inherit]'),
|
||||
SliderRoot: cn(
|
||||
'flex items-center justify-center flex-1 group/slider relative',
|
||||
'[&[data-orientation="horizontal"]]:h-5 [&[data-orientation="horizontal"]]:min-w-20',
|
||||
'[&[data-orientation="vertical"]]:w-5 [&[data-orientation="vertical"]]:h-20',
|
||||
),
|
||||
SliderTrack: cn(
|
||||
'w-full relative select-none rounded-full bg-white/20 ring-1 ring-black/5',
|
||||
'[&[data-orientation="horizontal"]]:h-1',
|
||||
'[&[data-orientation="vertical"]]:w-1'
|
||||
),
|
||||
SliderProgress: cn('bg-white rounded-[inherit]'),
|
||||
// TODO: Work out what we want to do here.
|
||||
TimeRangePointer: cn('rounded-[inherit]'),
|
||||
TimeRangeThumb: cn(
|
||||
SliderPointer: cn('rounded-[inherit]'),
|
||||
SliderThumb: cn(
|
||||
'bg-white z-10 select-none ring ring-black/10 rounded-full shadow-sm shadow-black/15',
|
||||
'opacity-0 transition-[opacity,height,width] ease-in-out',
|
||||
'-outline-offset-2 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-blue-500',
|
||||
'group-hover/slider:opacity-100 group-focus-within/slider:opacity-100',
|
||||
'size-2.5 active:size-3 group-active/slider:size-3'
|
||||
'size-2.5 active:size-3 group-active/slider:size-3 hover:cursor-ew-resize'
|
||||
),
|
||||
VolumePopup: cn(
|
||||
PopoverPopup: cn(
|
||||
'relative z-30 px-2 py-4 rounded-2xl',
|
||||
'bg-white/10 backdrop-blur-3xl backdrop-saturate-150 backdrop-brightness-90',
|
||||
'ring ring-white/10 ring-inset shadow-sm shadow-black/15',
|
||||
// Border to enhance contrast on lighter videos
|
||||
'after:absolute after:inset-0 after:ring after:rounded-[inherit] after:ring-black/15 after:pointer-events-none after:z-10',
|
||||
// Reduced transparency for users with preference
|
||||
// XXX: This requires a Tailwind custom variant (see 1 below)
|
||||
'reduced-transparency:bg-black/70 reduced-transparency:ring-black reduced-transparency:after:ring-white/20',
|
||||
// High contrast mode
|
||||
'contrast-more:bg-black/90 contrast-more:ring-black contrast-more:after:ring-white/20'
|
||||
),
|
||||
VolumeRangeRoot: cn('flex [&[data-orientation="horizontal"]]:w-20 [&[data-orientation="horizontal"]]:h-5 [&[data-orientation="vertical"]]:w-5 [&[data-orientation="vertical"]]:h-20 items-center justify-center group/slider relative'),
|
||||
VolumeRangeTrack: cn('[&[data-orientation="horizontal"]]:h-1 [&[data-orientation="vertical"]]:w-1 w-full relative select-none rounded-full bg-white/20 ring-1 ring-black/5'),
|
||||
VolumeRangeProgress: cn('bg-white rounded-[inherit]'),
|
||||
VolumeRangeThumb: cn(
|
||||
'bg-white z-10 select-none ring ring-black/10 rounded-full shadow-sm shadow-black/15',
|
||||
'opacity-0 transition-[opacity,height,width] ease-in-out',
|
||||
'-outline-offset-2 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-blue-500',
|
||||
'group-hover/slider:opacity-100 group-focus-within/slider:opacity-100',
|
||||
'size-2.5 active:size-3 group-active/slider:size-3'
|
||||
),
|
||||
};
|
||||
|
||||
/*
|
||||
[1] @custom-variant reduced-transparency @media (prefers-reduced-transparency: reduce);
|
||||
*/
|
||||
|
||||
export default styles;
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
export interface MediaDefaultSkinStyles {
|
||||
readonly MediaContainer: string;
|
||||
readonly Overlay: string;
|
||||
readonly Controls: string;
|
||||
readonly Button: string;
|
||||
readonly IconButton: string;
|
||||
readonly PlayButton: string;
|
||||
readonly PlayIcon: string;
|
||||
readonly PauseIcon: string;
|
||||
readonly VolumeButton: string;
|
||||
readonly VolumeHighIcon: string;
|
||||
readonly VolumeLowIcon: string;
|
||||
readonly VolumeOffIcon: string;
|
||||
readonly FullScreenButton: string;
|
||||
readonly FullScreenEnterIcon: string;
|
||||
readonly FullScreenExitIcon: string;
|
||||
readonly TimeControls: string;
|
||||
readonly TimeDisplay: string;
|
||||
readonly SliderRoot: string;
|
||||
readonly SliderTrack: string;
|
||||
readonly SliderProgress: string;
|
||||
readonly SliderPointer: string;
|
||||
readonly SliderThumb: string;
|
||||
readonly PopoverPopup: string;
|
||||
}
|
||||
@@ -1 +1,2 @@
|
||||
export * from './default'
|
||||
export * from './default'
|
||||
export * from './toasted'
|
||||
@@ -0,0 +1,94 @@
|
||||
import type { PropsWithChildren } from 'react';
|
||||
|
||||
import {
|
||||
FullscreenEnterIcon,
|
||||
FullscreenExitIcon,
|
||||
PauseIcon,
|
||||
PlayIcon,
|
||||
VolumeHighIcon,
|
||||
VolumeLowIcon,
|
||||
VolumeOffIcon,
|
||||
} from '@vjs-10/react-icons';
|
||||
|
||||
import { CurrentTimeDisplay } from '../../components/CurrentTimeDisplay';
|
||||
import { DurationDisplay } from '../../components/DurationDisplay';
|
||||
import { FullscreenButton } from '../../components/FullscreenButton';
|
||||
import { MediaContainer } from '../../components/MediaContainer';
|
||||
import MuteButton from '../../components/MuteButton';
|
||||
import PlayButton from '../../components/PlayButton';
|
||||
import { TimeRange } from '../../components/TimeRange';
|
||||
import { VolumeRange } from '../../components/VolumeRange';
|
||||
import styles from './styles';
|
||||
|
||||
type SkinProps = PropsWithChildren<{
|
||||
className?: string;
|
||||
}>;
|
||||
|
||||
export default function MediaSkinDefault({ children, className = '' }: SkinProps): JSX.Element {
|
||||
return (
|
||||
<MediaContainer className={`${styles.MediaContainer} ${className}`}>
|
||||
{children}
|
||||
|
||||
<div className={styles.Controls} data-testid="media-controls">
|
||||
{/* <header className='py-2 px-4 text-shadow-sm text-shadow-black/10'>
|
||||
<h1 className="text-base font-medium">View From a Blue Moon</h1>
|
||||
<p className="text-stone-400">A story about Jon Jon Florence, a surfer from Hawaii.</p>
|
||||
</header> */}
|
||||
|
||||
<div className={styles.TimeSlider}>
|
||||
<TimeRange.Root className={styles.SliderRoot}>
|
||||
<TimeRange.Track className={styles.SliderTrack}>
|
||||
<TimeRange.Progress className={styles.SliderProgress} />
|
||||
<TimeRange.Pointer className={styles.SliderPointer} />
|
||||
</TimeRange.Track>
|
||||
<TimeRange.Thumb className={`${styles.SliderThumb} ${styles.TimeSliderThumb}`} />
|
||||
</TimeRange.Root>
|
||||
</div>
|
||||
|
||||
<div className={styles.ControlsRow}>
|
||||
<div className='flex items-center gap-3'>
|
||||
<PlayButton className={`${styles.Button} ${styles.IconButton} ${styles.PlayButton}`}>
|
||||
<PlayIcon className={styles.PlayIcon}></PlayIcon>
|
||||
<PauseIcon className={styles.PauseIcon}></PauseIcon>
|
||||
</PlayButton>
|
||||
|
||||
<div className="flex items-center gap-1">
|
||||
<CurrentTimeDisplay
|
||||
// Use showRemaining to show count down/remaining time
|
||||
// showRemaining
|
||||
className={styles.TimeDisplay}
|
||||
/>
|
||||
<span className='opacity-50'>/</span>
|
||||
<DurationDisplay className={`${styles.TimeDisplay} opacity-50`} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div className='flex items-center gap-0.5'>
|
||||
<div className={styles.VolumeControls}>
|
||||
<MuteButton className={`${styles.Button} ${styles.IconButton} ${styles.VolumeButton}`}>
|
||||
<VolumeHighIcon className={styles.VolumeHighIcon} />
|
||||
<VolumeLowIcon className={styles.VolumeLowIcon} />
|
||||
<VolumeOffIcon className={styles.VolumeOffIcon} />
|
||||
</MuteButton>
|
||||
|
||||
<div className={styles.VolumeSlider}>
|
||||
<VolumeRange.Root className={styles.SliderRoot}>
|
||||
<VolumeRange.Track className={styles.SliderTrack}>
|
||||
<VolumeRange.Progress className={styles.SliderProgress} />
|
||||
</VolumeRange.Track>
|
||||
<VolumeRange.Thumb className={styles.SliderThumb} />
|
||||
</VolumeRange.Root>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<FullscreenButton className={`${styles.Button} ${styles.IconButton} ${styles.FullScreenButton}`}>
|
||||
<FullscreenEnterIcon className={styles.FullScreenEnterIcon} />
|
||||
<FullscreenExitIcon className={styles.FullScreenExitIcon} />
|
||||
</FullscreenButton>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</MediaContainer>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1 @@
|
||||
export { default as MediaSkinToasted } from './MediaSkinToasted';
|
||||
@@ -0,0 +1,115 @@
|
||||
import type { MediaToastedSkinStyles } from "./types";
|
||||
import { cn } from "../../utils/cn";
|
||||
|
||||
const styles: MediaToastedSkinStyles = {
|
||||
MediaContainer: cn(
|
||||
'relative @container/root group/root overflow-clip bg-black',
|
||||
// Base typography
|
||||
'text-sm',
|
||||
// 'ring-1 ring-inset ring-black/10 dark:ring-white/10',
|
||||
'after:absolute after:inset-0 after:ring-black/10 after:ring-1 dark:after:ring-white/10 after:ring-inset after:z-10 after:pointer-events-none after:rounded-[inherit]',
|
||||
// Prevent rounded corners in fullscreen.
|
||||
'[&:fullscreen]:rounded-none [&:fullscreen]:[&_video]:h-full [&:fullscreen]:[&_video]:w-full',
|
||||
// Ensure the nested video inherits the radius.
|
||||
'[&_video]:rounded-[inherit] [&_video]:w-full [&_video]:h-auto',
|
||||
),
|
||||
Controls: cn(
|
||||
'@container/controls absolute inset-x-0 bottom-0 top-1/3 flex flex-col justify-end z-20 px-1 pb-1.5 text-white text-shadow',
|
||||
'shadow-sm shadow-black/15',
|
||||
// Background
|
||||
'bg-gradient-to-t from-stone-900/70 via-stone-900/60 via-35% to-transparent',
|
||||
// Animation
|
||||
'transition ease-in-out',
|
||||
// FIXME: Temporary hide/show logic
|
||||
'translate-y-full opacity-0 delay-500 pointer-events-none',
|
||||
'has-[[data-paused]]:translate-y-0 has-[[data-paused]]:opacity-100 has-[[data-paused]]:delay-0 has-[[data-paused]]:pointer-events-auto',
|
||||
'group-hover/root:translate-y-0 group-hover/root:opacity-100 group-hover/root:delay-0 group-hover/root:pointer-events-auto',
|
||||
),
|
||||
ControlsRow: cn('flex items-center justify-between px-1.5 pb-0.5'),
|
||||
Button: cn(
|
||||
'group/button cursor-pointer relative shrink-0 transition select-none p-2 rounded-md',
|
||||
// Background/foreground
|
||||
'bg-transparent text-white/90',
|
||||
// Hover and focus states
|
||||
'hover:no-underline hover:bg-white/15 hover:backdrop-blur-md hover:text-white focus-visible:no-underline focus-visible:bg-white/10 focus-visible:text-white',
|
||||
// Focus state
|
||||
'-outline-offset-2 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-amber-500',
|
||||
// Disabled state
|
||||
'aria-disabled:grayscale aria-disabled:opacity-50 aria-disabled:cursor-not-allowed',
|
||||
// Loading state
|
||||
'aria-busy:pointer-events-none aria-busy:cursor-not-allowed',
|
||||
// Expanded state
|
||||
'aria-expanded:bg-white/10 aria-expanded:text-white',
|
||||
// Pressed state
|
||||
'active:scale-95',
|
||||
),
|
||||
IconButton: cn(
|
||||
'grid [&_svg]:[grid-area:1/1]',
|
||||
'[&_svg]:shrink-0 [&_svg]:transition [&_svg]:duration-300 [&_svg]:ease-out [&_svg]:drop-shadow-[0_1px_0_var(--tw-shadow-color)] [&_svg]:shadow-black/20',
|
||||
),
|
||||
PlayButton: cn(
|
||||
'[&_.pause-icon]:opacity-100 [&[data-paused]_.pause-icon]:opacity-0',
|
||||
'[&_.play-icon]:opacity-0 [&[data-paused]_.play-icon]:opacity-100',
|
||||
),
|
||||
PlayIcon: cn('play-icon'),
|
||||
PauseIcon: cn('pause-icon'),
|
||||
VolumeControls: cn('flex items-center flex-row-reverse group/volume'),
|
||||
VolumeSlider: cn(
|
||||
'w-0 px-3 overflow-hidden pointer-events-none transition-[opacity,width] opacity-0 ease-out delay-500',
|
||||
'group-hover/volume:w-28 group-hover/volume:pointer-events-auto group-hover/volume:opacity-100 group-hover/volume:delay-0',
|
||||
'group-focus-within/volume:w-28 group-focus-within/volume:pointer-events-auto group-focus-within/volume:opacity-100 group-focus-within/volume:delay-0',
|
||||
),
|
||||
VolumeButton: cn(
|
||||
'[&_svg]:hidden',
|
||||
'[&[data-volume-level="high"]_.volume-high-icon]:inline',
|
||||
'[&[data-volume-level="medium"]_.volume-low-icon]:inline',
|
||||
'[&[data-volume-level="low"]_.volume-low-icon]:inline',
|
||||
'[&[data-volume-level="off"]_.volume-off-icon]:inline',
|
||||
),
|
||||
VolumeHighIcon: cn('volume-high-icon'),
|
||||
VolumeLowIcon: cn('volume-low-icon'),
|
||||
VolumeOffIcon: cn('volume-off-icon'),
|
||||
FullScreenButton: cn(
|
||||
'[&_.fullscreen-enter-icon]:opacity-100 [&[data-fullscreen]_.fullscreen-enter-icon]:opacity-0',
|
||||
'[&_.fullscreen-exit-icon]:opacity-0 [&[data-fullscreen]_.fullscreen-exit-icon]:opacity-100',
|
||||
'[&_path]:transition-transform ease-out',
|
||||
),
|
||||
FullScreenEnterIcon: cn(
|
||||
'fullscreen-enter-icon',
|
||||
'group-hover/button:[&_.arrow-1]:-translate-x-px group-hover/button:[&_.arrow-1]:-translate-y-px',
|
||||
'group-hover/button:[&_.arrow-2]:translate-x-px group-hover/button:[&_.arrow-2]:translate-y-px',
|
||||
),
|
||||
FullScreenExitIcon: cn(
|
||||
'fullscreen-exit-icon',
|
||||
'[&_.arrow-1]:-translate-x-px [&_.arrow-1]:-translate-y-px',
|
||||
'[&_.arrow-2]:translate-x-px [&_.arrow-2]:translate-y-px',
|
||||
'group-hover/button:[&_.arrow-1]:translate-0',
|
||||
'group-hover/button:[&_.arrow-2]:translate-0',
|
||||
),
|
||||
TimeSlider: cn('px-1.5'),
|
||||
TimeSliderThumb: cn(
|
||||
'opacity-0',
|
||||
'group-hover/slider:opacity-100 group-focus-within/slider:opacity-100',
|
||||
),
|
||||
TimeDisplay: cn('tabular-nums text-shadow-2xs shadow-black/50'),
|
||||
SliderRoot: cn(
|
||||
'flex items-center justify-center flex-1 group/slider relative',
|
||||
'[&[data-orientation="horizontal"]]:h-5 [&[data-orientation="horizontal"]]:min-w-20',
|
||||
'[&[data-orientation="vertical"]]:w-5 [&[data-orientation="vertical"]]:h-20',
|
||||
),
|
||||
SliderTrack: cn(
|
||||
'relative select-none rounded-full bg-white/25 backdrop-blur-sm backdrop-brightness-90 backdrop-saturate-150 shadow-sm shadow-black/10',
|
||||
'[&[data-orientation="horizontal"]]:w-full [&[data-orientation="horizontal"]]:h-1',
|
||||
'[&[data-orientation="vertical"]]:w-1',
|
||||
),
|
||||
SliderProgress: cn('bg-amber-500 rounded-[inherit]'),
|
||||
// TODO: Work out what we want to do here.
|
||||
SliderPointer: cn('rounded-[inherit]'),
|
||||
SliderThumb: cn(
|
||||
'bg-white z-10 select-none ring ring-black/10 rounded-full shadow-sm shadow-black/15 transition-[opacity,height,width] ease-in-out',
|
||||
'-outline-offset-2 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-amber-500',
|
||||
'size-3 active:size-3.5 group-active/slider:size-3.5 hover:cursor-ew-resize',
|
||||
),
|
||||
};
|
||||
|
||||
export default styles;
|
||||
@@ -0,0 +1,27 @@
|
||||
export interface MediaToastedSkinStyles {
|
||||
readonly MediaContainer: string;
|
||||
readonly Controls: string;
|
||||
readonly ControlsRow: string;
|
||||
readonly Button: string;
|
||||
readonly IconButton: string;
|
||||
readonly PlayButton: string;
|
||||
readonly PlayIcon: string;
|
||||
readonly PauseIcon: string;
|
||||
readonly VolumeControls: string;
|
||||
readonly VolumeSlider: string;
|
||||
readonly VolumeButton: string;
|
||||
readonly VolumeHighIcon: string;
|
||||
readonly VolumeLowIcon: string;
|
||||
readonly VolumeOffIcon: string;
|
||||
readonly FullScreenButton: string;
|
||||
readonly FullScreenEnterIcon: string;
|
||||
readonly FullScreenExitIcon: string;
|
||||
readonly TimeSlider: string;
|
||||
readonly TimeSliderThumb: string;
|
||||
readonly TimeDisplay: string;
|
||||
readonly SliderRoot: string;
|
||||
readonly SliderTrack: string;
|
||||
readonly SliderProgress: string;
|
||||
readonly SliderPointer: string;
|
||||
readonly SliderThumb: string;
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
// A (very crude) utility to merge class names
|
||||
// Usually I'd use something like `clsx` or `classnames` but this is ok for our simple use case.
|
||||
// It just makes the billions of Tailwind classes a little easier to read.
|
||||
export function cn(...classes: (string | undefined)[]): string {
|
||||
return classes.filter(Boolean).join(' ');
|
||||
}
|
||||
Generated
-188
@@ -35,9 +35,6 @@ importers:
|
||||
eslint-plugin-jsx-a11y:
|
||||
specifier: ^6.10.2
|
||||
version: 6.10.2(eslint@9.36.0(jiti@2.5.1))
|
||||
eslint-plugin-react:
|
||||
specifier: ^7.37.5
|
||||
version: 7.37.5(eslint@9.36.0(jiti@2.5.1))
|
||||
eslint-plugin-react-hooks:
|
||||
specifier: ^5.2.0
|
||||
version: 5.2.0(eslint@9.36.0(jiti@2.5.1))
|
||||
@@ -1547,10 +1544,6 @@ packages:
|
||||
resolution: {integrity: sha512-FmeCCAenzH0KH381SPT5FZmiA/TmpndpcaShhfgEN9eCVjnFBqq3l1xrI42y8+PPLI6hypzou4GXw00WHmPBLQ==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
array.prototype.findlast@1.2.5:
|
||||
resolution: {integrity: sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
array.prototype.flat@1.3.3:
|
||||
resolution: {integrity: sha512-rwG/ja1neyLqCuGZ5YYrznA62D4mZXg0i1cIskIUKSiqF3Cje9/wXAls9B9s1Wa2fomMsIv8czB8jZcPmxCXFg==}
|
||||
engines: {node: '>= 0.4'}
|
||||
@@ -1559,10 +1552,6 @@ packages:
|
||||
resolution: {integrity: sha512-Y7Wt51eKJSyi80hFrJCePGGNo5ktJCslFuboqJsbf57CCPcm5zztluPlc4/aD8sWsKvlwatezpV4U1efk8kpjg==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
array.prototype.tosorted@1.1.4:
|
||||
resolution: {integrity: sha512-p6Fx8B7b7ZhL/gmUsAy0D15WhvDccw3mnGNbZpi3pmeJdxtWsj2jEaI4Y6oo3XiHfzuSgPwKc04MYt6KgvC/wA==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
arraybuffer.prototype.slice@1.0.4:
|
||||
resolution: {integrity: sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==}
|
||||
engines: {node: '>= 0.4'}
|
||||
@@ -1840,10 +1829,6 @@ packages:
|
||||
resolution: {integrity: sha512-sSuxWU5j5SR9QQji/o2qMvqRNYRDOcBTgsJ/DeCf4iSN4gW+gNMXM7wFIP+fdXZxoNiAnHUTGjCr+TSWXdRDKg==}
|
||||
engines: {node: '>=0.3.1'}
|
||||
|
||||
doctrine@2.1.0:
|
||||
resolution: {integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
||||
dom-serializer@2.0.0:
|
||||
resolution: {integrity: sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==}
|
||||
|
||||
@@ -1913,10 +1898,6 @@ packages:
|
||||
resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
es-iterator-helpers@1.2.1:
|
||||
resolution: {integrity: sha512-uDn+FE1yrDzyC0pCo961B2IHbdM8y/ACZsKD4dG6WqrjV53BADjwa7D+1aom2rsNVfLyDgU/eigvlJGJ08OQ4w==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
es-object-atoms@1.1.1:
|
||||
resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==}
|
||||
engines: {node: '>= 0.4'}
|
||||
@@ -2142,12 +2123,6 @@ packages:
|
||||
typescript:
|
||||
optional: true
|
||||
|
||||
eslint-plugin-react@7.37.5:
|
||||
resolution: {integrity: sha512-Qteup0SqU15kdocexFNAJMvCJEfa2xUKNV4CC1xsVMrIIqEy3SQ/rqyxCWNzfrd3/ldy6HMlD2e0JDVpDg2qIA==}
|
||||
engines: {node: '>=4'}
|
||||
peerDependencies:
|
||||
eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7
|
||||
|
||||
eslint-plugin-regexp@2.10.0:
|
||||
resolution: {integrity: sha512-ovzQT8ESVn5oOe5a7gIDPD5v9bCSjIFJu57sVPDqgPRXicQzOnYfFN21WoQBQF18vrhT5o7UMKFwJQVVjyJ0ng==}
|
||||
engines: {node: ^18 || >=20}
|
||||
@@ -2495,10 +2470,6 @@ packages:
|
||||
resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
is-core-module@2.16.1:
|
||||
resolution: {integrity: sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
is-data-view@1.0.2:
|
||||
resolution: {integrity: sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==}
|
||||
engines: {node: '>= 0.4'}
|
||||
@@ -2591,10 +2562,6 @@ packages:
|
||||
isexe@2.0.0:
|
||||
resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==}
|
||||
|
||||
iterator.prototype@1.1.5:
|
||||
resolution: {integrity: sha512-H0dkQoCa3b2VEeKQBOxFph+JAbcrQdE7KC0UkqwpLmv2EC4P41QXP+rqo9wYodACiG5/WM5s9oDApTU8utwj9g==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
jiti@2.5.1:
|
||||
resolution: {integrity: sha512-twQoecYPiVA5K/h6SxtORw/Bs3ar+mLUtoPSc7iMXzQzK8d7eJ/R09wmTwAjiamETn1cXYPGfNnu7DMoHgu12w==}
|
||||
hasBin: true
|
||||
@@ -2988,10 +2955,6 @@ packages:
|
||||
nth-check@2.1.1:
|
||||
resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==}
|
||||
|
||||
object-assign@4.1.1:
|
||||
resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
||||
object-deep-merge@1.0.5:
|
||||
resolution: {integrity: sha512-3DioFgOzetbxbeUq8pB2NunXo8V0n4EvqsWM/cJoI6IA9zghd7cl/2pBOuWRf4dlvA+fcg5ugFMZaN2/RuoaGg==}
|
||||
|
||||
@@ -3007,10 +2970,6 @@ packages:
|
||||
resolution: {integrity: sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
object.entries@1.1.9:
|
||||
resolution: {integrity: sha512-8u/hfXFRBD1O0hPUjioLhoWFHRmt6tKA4/vZPyckBr18l1KE9uHrFaFaUi8MDRTpi4uak2goyPTSNJLXX2k2Hw==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
object.fromentries@2.0.8:
|
||||
resolution: {integrity: sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==}
|
||||
engines: {node: '>= 0.4'}
|
||||
@@ -3071,9 +3030,6 @@ packages:
|
||||
resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==}
|
||||
engines: {node: '>=8'}
|
||||
|
||||
path-parse@1.0.7:
|
||||
resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==}
|
||||
|
||||
path-type@4.0.0:
|
||||
resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==}
|
||||
engines: {node: '>=8'}
|
||||
@@ -3140,9 +3096,6 @@ packages:
|
||||
engines: {node: '>=14'}
|
||||
hasBin: true
|
||||
|
||||
prop-types@15.8.1:
|
||||
resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==}
|
||||
|
||||
punycode@2.3.1:
|
||||
resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==}
|
||||
engines: {node: '>=6'}
|
||||
@@ -3158,9 +3111,6 @@ packages:
|
||||
peerDependencies:
|
||||
react: ^18.3.1
|
||||
|
||||
react-is@16.13.1:
|
||||
resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==}
|
||||
|
||||
react-refresh@0.17.0:
|
||||
resolution: {integrity: sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
@@ -3207,10 +3157,6 @@ packages:
|
||||
resolve-pkg-maps@1.0.0:
|
||||
resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==}
|
||||
|
||||
resolve@2.0.0-next.5:
|
||||
resolution: {integrity: sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==}
|
||||
hasBin: true
|
||||
|
||||
restore-cursor@5.1.0:
|
||||
resolution: {integrity: sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==}
|
||||
engines: {node: '>=18'}
|
||||
@@ -3372,13 +3318,6 @@ packages:
|
||||
resolution: {integrity: sha512-o7+c9bW6zpAdJHTtujeePODAhkuicdAryFsfVKwA+wGw89wJ4GTY484WTucM9hLtDEOpOvI+aHnzqnC5lHp4Rg==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
string.prototype.matchall@4.0.12:
|
||||
resolution: {integrity: sha512-6CC9uyBL+/48dYizRf7H7VAYCMCNTBeM78x/VTUe9bFEaxBepPJDa1Ow99LqI/1yF7kuy7Q3cQsYMrcjGUcskA==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
string.prototype.repeat@1.0.0:
|
||||
resolution: {integrity: sha512-0u/TldDbKD8bFCQ/4f5+mNRrXwZ8hg2w7ZR8wa16e8z9XpePWl3eGEcUD0OXpEH/VJH/2G3gjUtR3ZOiBe2S/w==}
|
||||
|
||||
string.prototype.trim@1.2.10:
|
||||
resolution: {integrity: sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA==}
|
||||
engines: {node: '>= 0.4'}
|
||||
@@ -3407,10 +3346,6 @@ packages:
|
||||
resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==}
|
||||
engines: {node: '>=8'}
|
||||
|
||||
supports-preserve-symlinks-flag@1.0.0:
|
||||
resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
svg-parser@2.0.4:
|
||||
resolution: {integrity: sha512-e4hG1hRwoOdRb37cIMSgzNsxyzKfayW6VOflrwvR+/bzrkyxY/31WkbgnQpgtrNp1SdpJvpUAGTa/ZoiPNDuRQ==}
|
||||
|
||||
@@ -4837,15 +4772,6 @@ snapshots:
|
||||
is-string: 1.1.1
|
||||
math-intrinsics: 1.1.0
|
||||
|
||||
array.prototype.findlast@1.2.5:
|
||||
dependencies:
|
||||
call-bind: 1.0.8
|
||||
define-properties: 1.2.1
|
||||
es-abstract: 1.24.0
|
||||
es-errors: 1.3.0
|
||||
es-object-atoms: 1.1.1
|
||||
es-shim-unscopables: 1.1.0
|
||||
|
||||
array.prototype.flat@1.3.3:
|
||||
dependencies:
|
||||
call-bind: 1.0.8
|
||||
@@ -4860,14 +4786,6 @@ snapshots:
|
||||
es-abstract: 1.24.0
|
||||
es-shim-unscopables: 1.1.0
|
||||
|
||||
array.prototype.tosorted@1.1.4:
|
||||
dependencies:
|
||||
call-bind: 1.0.8
|
||||
define-properties: 1.2.1
|
||||
es-abstract: 1.24.0
|
||||
es-errors: 1.3.0
|
||||
es-shim-unscopables: 1.1.0
|
||||
|
||||
arraybuffer.prototype.slice@1.0.4:
|
||||
dependencies:
|
||||
array-buffer-byte-length: 1.0.2
|
||||
@@ -5120,10 +5038,6 @@ snapshots:
|
||||
|
||||
diff@8.0.2: {}
|
||||
|
||||
doctrine@2.1.0:
|
||||
dependencies:
|
||||
esutils: 2.0.3
|
||||
|
||||
dom-serializer@2.0.0:
|
||||
dependencies:
|
||||
domelementtype: 2.3.0
|
||||
@@ -5237,25 +5151,6 @@ snapshots:
|
||||
|
||||
es-errors@1.3.0: {}
|
||||
|
||||
es-iterator-helpers@1.2.1:
|
||||
dependencies:
|
||||
call-bind: 1.0.8
|
||||
call-bound: 1.0.4
|
||||
define-properties: 1.2.1
|
||||
es-abstract: 1.24.0
|
||||
es-errors: 1.3.0
|
||||
es-set-tostringtag: 2.1.0
|
||||
function-bind: 1.1.2
|
||||
get-intrinsic: 1.3.0
|
||||
globalthis: 1.0.4
|
||||
gopd: 1.2.0
|
||||
has-property-descriptors: 1.0.2
|
||||
has-proto: 1.2.0
|
||||
has-symbols: 1.1.0
|
||||
internal-slot: 1.1.0
|
||||
iterator.prototype: 1.1.5
|
||||
safe-array-concat: 1.1.3
|
||||
|
||||
es-object-atoms@1.1.1:
|
||||
dependencies:
|
||||
es-errors: 1.3.0
|
||||
@@ -5602,28 +5497,6 @@ snapshots:
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
eslint-plugin-react@7.37.5(eslint@9.36.0(jiti@2.5.1)):
|
||||
dependencies:
|
||||
array-includes: 3.1.9
|
||||
array.prototype.findlast: 1.2.5
|
||||
array.prototype.flatmap: 1.3.3
|
||||
array.prototype.tosorted: 1.1.4
|
||||
doctrine: 2.1.0
|
||||
es-iterator-helpers: 1.2.1
|
||||
eslint: 9.36.0(jiti@2.5.1)
|
||||
estraverse: 5.3.0
|
||||
hasown: 2.0.2
|
||||
jsx-ast-utils: 3.3.5
|
||||
minimatch: 3.1.2
|
||||
object.entries: 1.1.9
|
||||
object.fromentries: 2.0.8
|
||||
object.values: 1.2.1
|
||||
prop-types: 15.8.1
|
||||
resolve: 2.0.0-next.5
|
||||
semver: 6.3.1
|
||||
string.prototype.matchall: 4.0.12
|
||||
string.prototype.repeat: 1.0.0
|
||||
|
||||
eslint-plugin-regexp@2.10.0(eslint@9.36.0(jiti@2.5.1)):
|
||||
dependencies:
|
||||
'@eslint-community/eslint-utils': 4.9.0(eslint@9.36.0(jiti@2.5.1))
|
||||
@@ -6010,10 +5883,6 @@ snapshots:
|
||||
|
||||
is-callable@1.2.7: {}
|
||||
|
||||
is-core-module@2.16.1:
|
||||
dependencies:
|
||||
hasown: 2.0.2
|
||||
|
||||
is-data-view@1.0.2:
|
||||
dependencies:
|
||||
call-bound: 1.0.4
|
||||
@@ -6110,15 +5979,6 @@ snapshots:
|
||||
|
||||
isexe@2.0.0: {}
|
||||
|
||||
iterator.prototype@1.1.5:
|
||||
dependencies:
|
||||
define-data-property: 1.1.4
|
||||
es-object-atoms: 1.1.1
|
||||
get-intrinsic: 1.3.0
|
||||
get-proto: 1.0.1
|
||||
has-symbols: 1.1.0
|
||||
set-function-name: 2.0.2
|
||||
|
||||
jiti@2.5.1: {}
|
||||
|
||||
js-tokens@4.0.0: {}
|
||||
@@ -6660,8 +6520,6 @@ snapshots:
|
||||
dependencies:
|
||||
boolbase: 1.0.0
|
||||
|
||||
object-assign@4.1.1: {}
|
||||
|
||||
object-deep-merge@1.0.5:
|
||||
dependencies:
|
||||
type-fest: 4.2.0
|
||||
@@ -6679,13 +6537,6 @@ snapshots:
|
||||
has-symbols: 1.1.0
|
||||
object-keys: 1.1.1
|
||||
|
||||
object.entries@1.1.9:
|
||||
dependencies:
|
||||
call-bind: 1.0.8
|
||||
call-bound: 1.0.4
|
||||
define-properties: 1.2.1
|
||||
es-object-atoms: 1.1.1
|
||||
|
||||
object.fromentries@2.0.8:
|
||||
dependencies:
|
||||
call-bind: 1.0.8
|
||||
@@ -6756,8 +6607,6 @@ snapshots:
|
||||
|
||||
path-key@3.1.1: {}
|
||||
|
||||
path-parse@1.0.7: {}
|
||||
|
||||
path-type@4.0.0: {}
|
||||
|
||||
pathe@2.0.3: {}
|
||||
@@ -6811,12 +6660,6 @@ snapshots:
|
||||
|
||||
prettier@3.6.2: {}
|
||||
|
||||
prop-types@15.8.1:
|
||||
dependencies:
|
||||
loose-envify: 1.4.0
|
||||
object-assign: 4.1.1
|
||||
react-is: 16.13.1
|
||||
|
||||
punycode@2.3.1: {}
|
||||
|
||||
quansync@0.2.11: {}
|
||||
@@ -6829,8 +6672,6 @@ snapshots:
|
||||
react: 18.3.1
|
||||
scheduler: 0.23.2
|
||||
|
||||
react-is@16.13.1: {}
|
||||
|
||||
react-refresh@0.17.0: {}
|
||||
|
||||
react@18.3.1:
|
||||
@@ -6880,12 +6721,6 @@ snapshots:
|
||||
|
||||
resolve-pkg-maps@1.0.0: {}
|
||||
|
||||
resolve@2.0.0-next.5:
|
||||
dependencies:
|
||||
is-core-module: 2.16.1
|
||||
path-parse: 1.0.7
|
||||
supports-preserve-symlinks-flag: 1.0.0
|
||||
|
||||
restore-cursor@5.1.0:
|
||||
dependencies:
|
||||
onetime: 7.0.0
|
||||
@@ -7108,27 +6943,6 @@ snapshots:
|
||||
define-properties: 1.2.1
|
||||
es-abstract: 1.24.0
|
||||
|
||||
string.prototype.matchall@4.0.12:
|
||||
dependencies:
|
||||
call-bind: 1.0.8
|
||||
call-bound: 1.0.4
|
||||
define-properties: 1.2.1
|
||||
es-abstract: 1.24.0
|
||||
es-errors: 1.3.0
|
||||
es-object-atoms: 1.1.1
|
||||
get-intrinsic: 1.3.0
|
||||
gopd: 1.2.0
|
||||
has-symbols: 1.1.0
|
||||
internal-slot: 1.1.0
|
||||
regexp.prototype.flags: 1.5.4
|
||||
set-function-name: 2.0.2
|
||||
side-channel: 1.1.0
|
||||
|
||||
string.prototype.repeat@1.0.0:
|
||||
dependencies:
|
||||
define-properties: 1.2.1
|
||||
es-abstract: 1.24.0
|
||||
|
||||
string.prototype.trim@1.2.10:
|
||||
dependencies:
|
||||
call-bind: 1.0.8
|
||||
@@ -7164,8 +6978,6 @@ snapshots:
|
||||
dependencies:
|
||||
has-flag: 4.0.0
|
||||
|
||||
supports-preserve-symlinks-flag@1.0.0: {}
|
||||
|
||||
svg-parser@2.0.4: {}
|
||||
|
||||
svgo@3.3.2:
|
||||
|
||||
Reference in New Issue
Block a user