diff --git a/.node-version b/.node-version
new file mode 100644
index 00000000..e2228113
--- /dev/null
+++ b/.node-version
@@ -0,0 +1 @@
+22.19.0
diff --git a/cspell.json b/cspell.json
index 105988ea..a83aeef3 100644
--- a/cspell.json
+++ b/cspell.json
@@ -14,8 +14,10 @@
],
"words": [
"antfu",
+ "noto",
"nums",
- "rahim"
+ "rahim",
+ "segoe"
],
"ignoreWords": [],
"import": []
diff --git a/examples/react-demo/index.html b/examples/react-demo/index.html
index 7a6a9840..e6d535d5 100644
--- a/examples/react-demo/index.html
+++ b/examples/react-demo/index.html
@@ -6,7 +6,7 @@
-
+
diff --git a/examples/react-demo/src/App.tsx b/examples/react-demo/src/App.tsx
new file mode 100644
index 00000000..12b7542d
--- /dev/null
+++ b/examples/react-demo/src/App.tsx
@@ -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(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(getParam('skin', DEFAULT_SKIN));
+ const [mediaSourceKey, setMediaSourceKey] = useState(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 = useCallback((event) => {
+ const value = event.target.value as SkinKey;
+ setSkinKey(value);
+ setParam('skin', value);
+ }, []);
+ const onChangeMediaSource: ChangeEventHandler = 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 (
+ <>
+
+
+
+
+
+
+ {/* @ts-ignore -- types are incorrect */}
+
+
+
+
+
+ >
+ );
+}
+
diff --git a/examples/react-demo/src/globals.css b/examples/react-demo/src/globals.css
index 555d3400..ca5200df 100644
--- a/examples/react-demo/src/globals.css
+++ b/examples/react-demo/src/globals.css
@@ -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);
diff --git a/examples/react-demo/src/main.tsx b/examples/react-demo/src/main.tsx
index 606c7ec7..30c39a5e 100644
--- a/examples/react-demo/src/main.tsx
+++ b/examples/react-demo/src/main.tsx
@@ -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 (
-
-
-
-
-
- {/* @ts-ignore -- types are incorrect */}
- {/* */}
- {/* @ts-ignore -- types are incorrect */}
-
-
-
-
-
-
-
-
- {/* @ts-ignore -- types are incorrect */}
-
-
-
-
-
-
- );
-}
-
-ReactDOM.createRoot(document.getElementById('root')!).render();
+createRoot(document.getElementById('root')!).render(
+
+
+ ,
+);
diff --git a/package.json b/package.json
index 10bb8fe5..d1acafd5 100644
--- a/package.json
+++ b/package.json
@@ -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",
diff --git a/packages/react/react/src/skins/default/MediaSkinDefault.tsx b/packages/react/react/src/skins/default/MediaSkinDefault.tsx
index 4e48c0ce..1a3ca851 100644
--- a/packages/react/react/src/skins/default/MediaSkinDefault.tsx
+++ b/packages/react/react/src/skins/default/MediaSkinDefault.tsx
@@ -46,12 +46,12 @@ export default function MediaSkinDefault({ children, className = '' }: SkinProps
className={styles.TimeDisplay}
/>
-
-
-
-
+
+
+
+
-
+
@@ -66,12 +66,12 @@ export default function MediaSkinDefault({ children, className = '' }: SkinProps
-
-
-
-
+
+
+
+
-
+
diff --git a/packages/react/react/src/skins/default/styles.ts b/packages/react/react/src/skins/default/styles.ts
index 0450a52e..a94fb73a 100644
--- a/packages/react/react/src/skins/default/styles.ts
+++ b/packages/react/react/src/skins/default/styles.ts
@@ -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;
diff --git a/packages/react/react/src/skins/default/types.ts b/packages/react/react/src/skins/default/types.ts
new file mode 100644
index 00000000..a58af1cf
--- /dev/null
+++ b/packages/react/react/src/skins/default/types.ts
@@ -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;
+}
\ No newline at end of file
diff --git a/packages/react/react/src/skins/index.ts b/packages/react/react/src/skins/index.ts
index 479a9ab2..bd5af525 100644
--- a/packages/react/react/src/skins/index.ts
+++ b/packages/react/react/src/skins/index.ts
@@ -1 +1,2 @@
-export * from './default'
\ No newline at end of file
+export * from './default'
+export * from './toasted'
\ No newline at end of file
diff --git a/packages/react/react/src/skins/toasted/MediaSkinToasted.tsx b/packages/react/react/src/skins/toasted/MediaSkinToasted.tsx
new file mode 100644
index 00000000..1ee90287
--- /dev/null
+++ b/packages/react/react/src/skins/toasted/MediaSkinToasted.tsx
@@ -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 (
+
+ {children}
+
+
+ {/*
*/}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+};
diff --git a/packages/react/react/src/skins/toasted/index.ts b/packages/react/react/src/skins/toasted/index.ts
new file mode 100644
index 00000000..692577a7
--- /dev/null
+++ b/packages/react/react/src/skins/toasted/index.ts
@@ -0,0 +1 @@
+export { default as MediaSkinToasted } from './MediaSkinToasted';
\ No newline at end of file
diff --git a/packages/react/react/src/skins/toasted/styles.ts b/packages/react/react/src/skins/toasted/styles.ts
new file mode 100644
index 00000000..6e3a9a69
--- /dev/null
+++ b/packages/react/react/src/skins/toasted/styles.ts
@@ -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;
diff --git a/packages/react/react/src/skins/toasted/types.ts b/packages/react/react/src/skins/toasted/types.ts
new file mode 100644
index 00000000..f8276f7d
--- /dev/null
+++ b/packages/react/react/src/skins/toasted/types.ts
@@ -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;
+}
\ No newline at end of file
diff --git a/packages/react/react/src/utils/cn.ts b/packages/react/react/src/utils/cn.ts
new file mode 100644
index 00000000..af3edfde
--- /dev/null
+++ b/packages/react/react/src/utils/cn.ts
@@ -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(' ');
+}
\ No newline at end of file
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 061dc93b..ff4dd784 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -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: