Merge pull request #15 from muxinc/feat/default-skin

feat(ui): port over default skin
This commit is contained in:
Sam Potts
2025-09-21 13:37:58 +10:00
committed by GitHub
41 changed files with 1200 additions and 688 deletions
+7 -2
View File
@@ -4,7 +4,7 @@ A monorepo for Video.js 10 related library packages, organized by platform and r
## Structure
```
```bash
vjs-10-monorepo/
├── packages/
│ ├── core/ # Runtime-agnostic packages
@@ -69,11 +69,13 @@ npm install
### Building
Build all packages:
```bash
npm run build
```
Build specific package:
```bash
npm run build --workspace=@vjs-10/core
```
@@ -92,6 +94,9 @@ npm run typecheck
# Run linting
npm run lint
# Run dev build
npm run dev
```
## Package Dependencies
@@ -111,4 +116,4 @@ All packages are written in TypeScript and use project references for efficient
## License
Apache-2.0
Apache-2.0
+2 -2
View File
@@ -13,7 +13,7 @@
"@vjs-10/html": "*"
},
"devDependencies": {
"typescript": "^5.3.0",
"vite": "^5.0.0"
"typescript": "^5.9.2",
"vite": "^7.1.6"
}
}
+2
View File
@@ -3,6 +3,8 @@
<head>
<meta charset="UTF-8" />
<title>VJS React Demo</title>
<link rel="preconnect" href="https://rsms.me/">
<link rel="stylesheet" href="https://rsms.me/inter/inter.css">
</head>
<body>
<div id="root"></div>
+8 -7
View File
@@ -11,14 +11,15 @@
},
"dependencies": {
"@vjs-10/react": "*",
"react": "^19.1.0",
"react-dom": "^19.1.0"
"react": "^19.1.1",
"react-dom": "^19.1.1"
},
"devDependencies": {
"@types/react": "^19.1.8",
"@types/react-dom": "^19.1.6",
"@vitejs/plugin-react": "^4.0.0",
"typescript": "^5.3.0",
"vite": "^5.0.0"
"@tailwindcss/vite": "^4.1.13",
"@types/react": "^19.1.13",
"@types/react-dom": "^19.1.9",
"@vitejs/plugin-react": "^5.0.3",
"typescript": "^5.9.2",
"vite": "^7.1.6"
}
}
+19
View File
@@ -0,0 +1,19 @@
@import 'tailwindcss';
/* TODO: Documentation for how this will work in the wild. */
@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";
}
:root {
font-feature-settings: 'liga' 1, 'calt' 1; /* fix for Chrome */
}
/* 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);
+21 -17
View File
@@ -1,24 +1,28 @@
import React from 'react';
import ReactDOM from 'react-dom/client';
import { MediaProvider, Video, MediaSkinDefault } from '@vjs-10/react';
const DemoPlayer = () => {
import './globals.css';
function DemoPlayer() {
return (
<MediaProvider>
<div style={{ width: '100%', maxWidth: '800px', margin: '0 auto' }}>
<MediaSkinDefault>
{/* @ts-ignore */}
{/* <Video
muted
src="https://stream.mux.com/A3VXy02VoUinw01pwyomEO3bHnG4P32xzV7u1j1FSzjNg/high.mp4"
/> */}
<Video
muted
src="https://stream.mux.com/a4nOgmxGWg6gULfcBbAa00gXyfcwPnAFldF8RdsNyk8M.m3u8"
/>
</MediaSkinDefault>
</div>
</MediaProvider>
<div className="min-h-screen grid place-items-center">
<MediaProvider>
<div className="w-full max-w-4xl mx-auto">
<MediaSkinDefault>
{/* @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>
</div>
);
};
+1
View File
@@ -2,6 +2,7 @@
"extends": "../../tsconfig.base.json",
"compilerOptions": {
"baseUrl": ".",
"jsx": "react-jsx",
"paths": {
"@vjs/react": ["../../packages/react/src"],
"@vjs/html": ["../../packages/html/src"],
-10
View File
@@ -1,10 +0,0 @@
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
export default defineConfig({
plugins: [react()],
server: {
port: 5173,
strictPort: false, // Allow Vite to try other ports if 5173 is taken
},
});
+19
View File
@@ -0,0 +1,19 @@
import type { UserConfig } from 'vite';
import tailwindcss from '@tailwindcss/vite';
import react from '@vitejs/plugin-react';
// https://vitejs.dev/config/
export default {
plugins: [react(), tailwindcss()],
server: {
host: '0.0.0.0', // Listen on any interface
port: 5173,
watch: {
ignored: [],
},
},
optimizeDeps: {
exclude: ['@vjs/react'], // Dont prebundle, keeps rebuilds fast.
},
} satisfies UserConfig;
+825 -561
View File
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -27,7 +27,7 @@
"react": "^18.0.0",
"tsup": "^8.0.0",
"turbo": "^2.5.5",
"typescript": "^5.3.0"
"typescript": "^5.9.2"
},
"packageManager": "npm@10.9.2",
"engines": {
@@ -1,3 +1,12 @@
<svg aria-hidden="true" viewBox="0 0 26 24" fill="currentColor">
<path d="M16 3v2.5h3.5V9H22V3h-6ZM4 9h2.5V5.5H10V3H4v6Zm15.5 9.5H16V21h6v-6h-2.5v3.5ZM6.5 15H4v6h6v-2.5H6.5V15Z" fill="currentColor"/>
</svg>
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 18 18">
<g fill="currentColor">
<path
d="M9.57,3.617c-.156-.375-.519-.617-.924-.617H4c-.552,0-1,.449-1,1v4.646c0,.406,.242,.769,.618,.924,.124,.051,.255,.076,.383,.076,.261,0,.515-.102,.706-.293l4.647-4.647c.286-.287,.371-.715,.216-1.089Z"
className="arrow-1"
/>
<path
d="M14.382,8.429c-.377-.156-.804-.068-1.089,.217l-4.647,4.647c-.286,.287-.371,.715-.216,1.089,.156,.375,.519,.617,.924,.617h4.646c.552,0,1-.449,1-1v-4.646c0-.406-.242-.769-.618-.924Z"
className="arrow-2"
/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 208 B

After

Width:  |  Height:  |  Size: 608 B

+12 -3
View File
@@ -1,3 +1,12 @@
<svg aria-hidden="true" viewBox="0 0 26 24" fill="currentColor">
<path d="M18.5 6.5V3H16v6h6V6.5h-3.5ZM16 21h2.5v-3.5H22V15h-6v6ZM4 17.5h3.5V21H10v-6H4v2.5Zm3.5-11H4V9h6V3H7.5v3.5Z" fill="currentColor"/>
</svg>
<svg xmlns="http://www.w3.org/2000/svg" height="18" width="18" viewBox="0 0 18 18">
<g fill="currentColor">
<path
d="M7.883,1.93c-.375-.157-.803-.07-1.09,.217L2.146,6.793c-.287,.287-.372,.715-.217,1.09s.518,.617,.924,.617H7.5c.551,0,1-.449,1-1V2.854c0-.406-.242-.769-.617-.924Z"
className="arrow-1"
/>
<path
d="M15.146,9.5h-4.646c-.551,0-1,.449-1,1v4.646c0,.406,.242,.769,.617,.924,.125,.052,.255,.077,.384,.077,.26,0,.514-.102,.706-.293l4.646-4.646c.287-.287,.372-.715,.217-1.09s-.518-.617-.924-.617Z"
className="arrow-2"
/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 212 B

After

Width:  |  Height:  |  Size: 582 B

+6 -3
View File
@@ -1,3 +1,6 @@
<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<path d="M6 19h4V5H6v14zm8-14v14h4V5h-4z" fill="currentColor"/>
</svg>
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 18 18">
<g fill="currentColor">
<rect x="2" y="2" width="5" height="14" rx="1.75" ry="1.75" />
<rect x="11" y="2" width="5" height="14" rx="1.75" ry="1.75" />
</g>
</svg>

Before

Width:  |  Height:  |  Size: 133 B

After

Width:  |  Height:  |  Size: 259 B

+4 -3
View File
@@ -1,3 +1,4 @@
<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<path d="M8 5v14l11-7z" fill="currentColor"/>
</svg>
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 18 18">
<path
d="M15.1,7.478L5.608,2.222c-.553-.306-1.206-.297-1.749,.023-.538,.317-.859,.877-.859,1.499V14.256c0,.622,.321,1.182,.859,1.499,.279,.164,.586,.247,.895,.247,.293,0,.586-.075,.854-.223l9.491-5.256c.556-.307,.901-.891,.901-1.522s-.345-1.215-.9-1.522Z" fill="currentColor"/>
</svg>

Before

Width:  |  Height:  |  Size: 115 B

After

Width:  |  Height:  |  Size: 375 B

+65
View File
@@ -0,0 +1,65 @@
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 18 18">
<g fill="currentColor">
<rect width="2" height="5" x="8" y=".5" rx="1" opacity="0.5">
<animate attributeName="opacity" values="1;0" dur="1s" begin="0s" repeatCount="indefinite" calcMode="linear" />
</rect>
<rect width="2" height="5" x="12.243" y="2.257" rx="1" transform="rotate(45 13.243 4.757)" opacity="0.45">
<animate
attributeName="opacity"
values="1;0"
dur="1s"
begin="0.125s"
repeatCount="indefinite"
calcMode="linear"
/>
</rect>
<rect width="5" height="2" x="12.5" y="8" rx="1" opacity="0.4">
<animate attributeName="opacity" values="1;0" dur="1s" begin="0.25s" repeatCount="indefinite" calcMode="linear" />
</rect>
<rect width="5" height="2" x="10.743" y="12.243" rx="1" transform="rotate(45 13.243 13.243)" opacity="0.35">
<animate
attributeName="opacity"
values="1;0"
dur="1s"
begin="0.375s"
repeatCount="indefinite"
calcMode="linear"
/>
</rect>
<rect width="2" height="5" x="8" y="12.5" rx="1" opacity="0.3">
<animate attributeName="opacity" values="1;0" dur="1s" begin="0.5s" repeatCount="indefinite" calcMode="linear" />
</rect>
<rect width="2" height="5" x="3.757" y="10.743" rx="1" transform="rotate(45 4.757 13.243)" opacity="0.25">
<animate
attributeName="opacity"
values="1;0"
dur="1s"
begin="0.625s"
repeatCount="indefinite"
calcMode="linear"
/>
</rect>
<rect width="5" height="2" x=".5" y="8" rx="1" opacity="0.15">
<animate attributeName="opacity" values="1;0" dur="1s" begin="0.75s" repeatCount="indefinite" calcMode="linear" />
</rect>
<rect
width="5"
height="2"
x="2.257"
y="3.757"
fill-rule="nonzero"
rx="1"
transform="rotate(45 4.757 4.757)"
opacity="0.1"
>
<animate
attributeName="opacity"
values="1;0"
dur="1s"
begin="0.875s"
repeatCount="indefinite"
calcMode="linear"
/>
</rect>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.1 KiB

+5 -2
View File
@@ -1,3 +1,6 @@
<svg aria-hidden="true" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<path d="M3 9v6h4l5 5V4L7 9H3Zm13.5 3A4.5 4.5 0 0 0 14 8v8a4.47 4.47 0 0 0 2.5-4ZM14 3.23v2.06a7 7 0 0 1 0 13.42v2.06a9 9 0 0 0 0-17.54Z" fill="currentColor"/>
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 18 18">
<g fill="currentColor">
<path d="M15.5999996,3.3 C15.1999996,2.9 14.5999996,2.9 14.1999996,3.3 C13.7999996,3.7 13.7999996,4.3 14.1999996,4.7 C15.3999996,5.9 15.9999996,7.4 15.9999996,9 C15.9999996,10.6 15.3999996,12.1 14.1999996,13.3 C13.7999996,13.7 13.7999996,14.3 14.1999996,14.7 C14.3999996,14.9 14.6999996,15 14.8999996,15 C15.1999996,15 15.3999996,14.9 15.5999996,14.7 C17.0999996,13.2 17.9999996,11.2 17.9999996,9 C17.9999996,6.8 17.0999996,4.8 15.5999996,3.3 L15.5999996,3.3 Z"/>
<path d="M11.2819745,5.28197449 C10.9060085,5.65794047 10.9060085,6.22188944 11.2819745,6.59785542 C12.0171538,7.33303477 12.2772954,8.05605449 12.2772954,9 C12.2772954,9.93588462 11.851678,10.9172014 11.2819745,11.4869049 C10.9060085,11.8628709 10.9060085,12.4268199 11.2819745,12.8027859 C11.4271642,12.9479755 11.9176724,13.0649528 12.2998149,12.9592565 C12.4124479,12.9281035 12.5156669,12.8776063 12.5978555,12.8027859 C13.773371,11.732654 14.1311161,10.1597914 14.1312524,9 C14.1312524,8.8299555 14.1286311,8.66015647 14.119665,8.4897429 C14.0674781,7.49784946 13.8010171,6.48513613 12.5978554,5.28197449 C12.2218894,4.9060085 11.6579405,4.9060085 11.2819745,5.28197449 Z M3.78571429,6.00820648 L0.714285714,6.00820648 C0.285714286,6.00820648 0,6.30901277 0,6.76022222 L0,11.2723167 C0,11.7235261 0.285714286,12.0243324 0.714285714,12.0243324 L3.78571429,12.0243324 L7.85714286,15.8819922 C8.35714286,16.1827985 9,15.8819922 9,15.2803796 L9,2.75215925 C9,2.15054666 8.35714286,1.77453879 7.85714286,2.15054666 L3.78571429,6.00820648 Z"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 248 B

After

Width:  |  Height:  |  Size: 1.6 KiB

+2 -2
View File
@@ -1,3 +1,3 @@
<svg aria-hidden="true" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<path d="M3 9v6h4l5 5V4L7 9H3Zm13.5 3A4.5 4.5 0 0 0 14 8v8a4.47 4.47 0 0 0 2.5-4Z" fill="currentColor"/>
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 18 18">
<path d="M11.2819745 5.28197449C10.9060085 5.65794047 10.9060085 6.22188944 11.2819745 6.59785542 12.0171538 7.33303477 12.2772954 8.05605449 12.2772954 9 12.2772954 9.93588462 11.851678 10.9172014 11.2819745 11.4869049 10.9060085 11.8628709 10.9060085 12.4268199 11.2819745 12.8027859 11.4271642 12.9479755 11.9176724 13.0649528 12.2998149 12.9592565 12.4124479 12.9281035 12.5156669 12.8776063 12.5978555 12.8027859 13.773371 11.732654 14.1311161 10.1597914 14.1312524 9 14.1312524 8.8299555 14.1286311 8.66015647 14.119665 8.4897429 14.0674781 7.49784946 13.8010171 6.48513613 12.5978554 5.28197449 12.2218894 4.9060085 11.6579405 4.9060085 11.2819745 5.28197449ZM3.78571429 6.00820648.714285714 6.00820648C.285714286 6.00820648 0 6.30901277 0 6.76022222L0 11.2723167C0 11.7235261.285714286 12.0243324.714285714 12.0243324L3.78571429 12.0243324 7.85714286 15.8819922C8.35714286 16.1827985 9 15.8819922 9 15.2803796L9 2.75215925C9 2.15054666 8.35714286 1.77453879 7.85714286 2.15054666L3.78571429 6.00820648Z" fill="currentColor" />
</svg>

Before

Width:  |  Height:  |  Size: 193 B

After

Width:  |  Height:  |  Size: 1.1 KiB

+2 -2
View File
@@ -1,3 +1,3 @@
<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<path d="M16.5 12A4.5 4.5 0 0 0 14 8v2.18l2.45 2.45a4.22 4.22 0 0 0 .05-.63Zm2.5 0a6.84 6.84 0 0 1-.54 2.64L20 16.15A8.8 8.8 0 0 0 21 12a9 9 0 0 0-7-8.77v2.06A7 7 0 0 1 19 12ZM4.27 3 3 4.27 7.73 9H3v6h4l5 5v-6.73l4.25 4.25A6.92 6.92 0 0 1 14 18.7v2.06A9 9 0 0 0 17.69 19l2 2.05L21 19.73l-9-9L4.27 3ZM12 4 9.91 6.09 12 8.18V4Z" fill="currentColor"/>
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 18 18">
<path d="M12.7535534 12.1464466 14.5 10.4 14.5 10.4 16.2464466 12.1464466C16.4417088 12.3417088 16.7582912 12.3417088 16.9535534 12.1464466L17.6464466 11.4535534C17.8417088 11.2582912 17.8417088 10.9417088 17.6464466 10.7464466L15.9 9 15.9 9 17.6464466 7.25355339C17.8417088 7.05829124 17.8417088 6.74170876 17.6464466 6.54644661L16.9535534 5.85355339C16.7582912 5.65829124 16.4417088 5.65829124 16.2464466 5.85355339L14.5 7.6 14.5 7.6 12.7535534 5.85355339C12.5582912 5.65829124 12.2417088 5.65829124 12.0464466 5.85355339L11.3535534 6.54644661C11.1582912 6.74170876 11.1582912 7.05829124 11.3535534 7.25355339L13.1 9 13.1 9 11.3535534 10.7464466C11.1582912 10.9417088 11.1582912 11.2582912 11.3535534 11.4535534L12.0464466 12.1464466C12.2417088 12.3417088 12.5582912 12.3417088 12.7535534 12.1464466ZM3.78571429 6.00820648.714285714 6.00820648C.285714286 6.00820648 0 6.30901277 0 6.76022222L0 11.2723167C0 11.7235261.285714286 12.0243324.714285714 12.0243324L3.78571429 12.0243324 7.85714286 15.8819922C8.35714286 16.1827985 9 15.8819922 9 15.2803796L9 2.75215925C9 2.15054666 8.35714286 1.77453879 7.85714286 2.15054666L3.78571429 6.00820648Z" fill="currentColor"/>
</svg>

Before

Width:  |  Height:  |  Size: 418 B

After

Width:  |  Height:  |  Size: 1.2 KiB

+1 -1
View File
@@ -35,7 +35,7 @@
"rollup": "^4.0.0",
"rollup-plugin-string": "^3.0.0",
"tslib": "^2.6.0",
"typescript": "^5.3.0"
"typescript": "^5.9.2"
},
"publishConfig": {
"access": "public"
+1 -1
View File
@@ -37,7 +37,7 @@
"@rollup/plugin-node-resolve": "^15.0.0",
"@rollup/plugin-commonjs": "^25.0.0",
"tslib": "^2.6.0",
"typescript": "^5.3.0"
"typescript": "^5.9.2"
},
"publishConfig": {
"access": "public"
+1 -1
View File
@@ -32,7 +32,7 @@
"@vjs-10/playback-engine": "*"
},
"devDependencies": {
"typescript": "^5.3.0"
"typescript": "^5.9.2"
},
"publishConfig": {
"access": "public"
+1 -1
View File
@@ -33,7 +33,7 @@
"hls.js": "^1.6.7"
},
"devDependencies": {
"typescript": "^5.3.0"
"typescript": "^5.9.2"
},
"publishConfig": {
"access": "public"
+1 -1
View File
@@ -38,7 +38,7 @@
"@rollup/plugin-typescript": "^11.0.0",
"rollup": "^4.0.0",
"tslib": "^2.6.0",
"typescript": "^5.3.0"
"typescript": "^5.9.2"
},
"publishConfig": {
"access": "public"
@@ -34,7 +34,7 @@
"@vjs-10/media-store": "*"
},
"devDependencies": {
"typescript": "^5.3.0"
"typescript": "^5.9.2"
},
"publishConfig": {
"access": "public"
+1 -1
View File
@@ -33,7 +33,7 @@
"@vjs-10/html-media-elements": "*"
},
"devDependencies": {
"typescript": "^5.3.0"
"typescript": "^5.9.2"
},
"publishConfig": {
"access": "public"
+1 -1
View File
@@ -40,7 +40,7 @@
"@rollup/plugin-node-resolve": "^15.0.0",
"@rollup/plugin-commonjs": "^25.0.0",
"tslib": "^2.6.0",
"typescript": "^5.3.0"
"typescript": "^5.9.2"
},
"publishConfig": {
"access": "public"
@@ -33,7 +33,7 @@
"react": ">=16.8.0"
},
"devDependencies": {
"typescript": "^5.3.0",
"typescript": "^5.9.2",
"@types/react": "^18.0.0",
"react": "^18.0.0"
},
@@ -33,7 +33,7 @@
"react": ">=16.8.0"
},
"devDependencies": {
"typescript": "^5.3.0",
"typescript": "^5.9.2",
"@types/react": "^18.0.0",
"react": "^18.0.0"
},
@@ -36,7 +36,7 @@
"react": ">=16.8.0"
},
"devDependencies": {
"typescript": "^5.3.0",
"typescript": "^5.9.2",
"@types/react": "^18.0.0",
"react": "^18.0.0"
},
+8 -4
View File
@@ -11,7 +11,8 @@ This package provides React components for media player icons that are automatic
**Important:** The React components in this package are automatically generated. Do not edit them directly.
### Generated Files Location
```
```bash
src/generated-icons/
├── index.ts # Auto-generated exports (DO NOT EDIT)
├── Play.tsx # Auto-generated component (DO NOT EDIT)
@@ -22,8 +23,10 @@ src/generated-icons/
```
### Source of Truth
All icons originate from SVG files located in:
```
```bash
packages/core/icons/assets/
├── play.svg
├── pause.svg
@@ -99,6 +102,7 @@ npm run clean
## 🏗️ Architecture
This package follows the monorepo's dependency hierarchy:
- **Depends on**: `@vjs-10/icons` (for shared SVG assets)
- **Peer dependency**: `react` (>=16.8.0)
- **Build tool**: SVGR for SVG-to-React transformation
@@ -120,8 +124,9 @@ This package follows the monorepo's dependency hierarchy:
## 🎯 Icon Naming Convention
SVG filename → React component name:
- `play.svg``PlayIcon`
- `volume-high.svg``VolumeHighIcon`
- `volume-high.svg``VolumeHighIcon`
- `my-custom-icon.svg``MyCustomIconIcon`
## 🔍 Technical Details
@@ -130,4 +135,3 @@ SVG filename → React component name:
- **Output**: TypeScript React components
- **Optimization**: Automatic SVG optimization via SVGO
- **Styling**: Uses `currentColor` for easy theming
- **Size**: Icons default to `1em` width/height for text alignment
+4 -4
View File
@@ -41,12 +41,12 @@
"@rollup/plugin-node-resolve": "^15.0.0",
"@rollup/plugin-commonjs": "^25.0.0",
"tslib": "^2.6.0",
"typescript": "^5.3.0",
"typescript": "^5.9.2",
"@types/react": "^18.0.0",
"react": "^18.0.0",
"@svgr/cli": "^8.0.0",
"@svgr/plugin-jsx": "^8.0.0",
"@svgr/plugin-prettier": "^8.0.0",
"@svgr/cli": "^8.1.0",
"@svgr/plugin-jsx": "^8.1.0",
"@svgr/plugin-prettier": "^8.1.0",
"@svgr/babel-plugin-add-jsx-attribute": "^8.0.0"
},
"publishConfig": {
+11 -11
View File
@@ -4,7 +4,7 @@ module.exports = {
plugins: ['@svgr/plugin-jsx', '@svgr/plugin-prettier'],
typescript: true,
jsxRuntime: 'automatic',
icon: true,
icon: false, // Don't set height/width attributes.
expandProps: 'end',
svgProps: {
'aria-hidden': 'true'
@@ -20,16 +20,16 @@ module.exports = {
return tpl`
/**
* @fileoverview Auto-generated React component from SVG
*
*
* This file is automatically generated by SVGR from SVG files located in:
* packages/core/icons/assets/
*
*
* DO NOT EDIT THIS FILE DIRECTLY.
*
*
* To modify this icon:
* 1. Edit the corresponding SVG file in packages/core/icons/assets/
* 2. Run \`npm run generate\` in this package to regenerate components
*
*
* @generated
*/
import * as React from 'react';
@@ -51,23 +51,23 @@ ${variables.exports};
const iconName = `${componentName}Icon`
return `export { default as ${iconName} } from './${basename}';`
})
const header = `/**
* @fileoverview Auto-generated index file for React icon components
*
*
* This file is automatically generated by SVGR from SVG files located in:
* packages/core/icons/assets/
*
*
* DO NOT EDIT THIS FILE DIRECTLY.
*
*
* To modify icons or add new ones:
* 1. Add/edit SVG files in packages/core/icons/assets/
* 2. Run \`npm run generate\` in this package to regenerate components
*
*
* @generated
*/
`
return header + exportEntries.join('\n') + '\n'
}
}
@@ -36,7 +36,7 @@
"react": ">=16.8.0"
},
"devDependencies": {
"typescript": "^5.3.0",
"typescript": "^5.9.2",
"@types/react": "^18.0.0",
"react": "^18.0.0"
},
@@ -36,7 +36,7 @@
"react": ">=16.8.0"
},
"devDependencies": {
"typescript": "^5.3.0",
"typescript": "^5.9.2",
"@types/react": "^18.0.0",
"react": "^18.0.0"
},
@@ -1,4 +1,5 @@
'use client';
/** @TODO !!! Revisit for SSR (CJP) */
import type { Context, ReactNode } from 'react';
import React, { createContext, useContext, useMemo } from 'react';
+1 -1
View File
@@ -45,7 +45,7 @@
"@rollup/plugin-commonjs": "^25.0.0",
"rollup-plugin-postcss": "^4.0.0",
"tslib": "^2.6.0",
"typescript": "^5.3.0",
"typescript": "^5.9.2",
"@types/react": "^18.0.0",
"react": "^18.0.0"
},
@@ -146,10 +146,10 @@ export const useTimeRangeRootProps = (
'data-duration': state.duration,
style: {
...props.style,
'--slider-fill': `${Math.round(sliderFill)}%`,
'--slider-fill': `${sliderFill.toFixed(3)}%`,
'--slider-pointer':
state.hovering && state.pointerPosition !== null
? `${Math.round(state.pointerPosition)}%`
? `${state.pointerPosition.toFixed(3)}%`
: '0%',
},
onPointerDown: handlePointerDown,
@@ -1,4 +1,5 @@
'use client';
import * as React from 'react';
import { useMediaRef } from '@vjs-10/react-media-store';
import { createMediaStateOwner } from '@vjs-10/media';
@@ -2,7 +2,7 @@ import * as React from 'react';
import { PauseIcon, PlayIcon } from '@vjs-10/react-icons';
import PlayButton from '../components/PlayButton';
import MuteButton from '../components/MuteButton';
import { VolumeRange } from '../components/VolumeRange';
// import { VolumeRange } from '../components/VolumeRange';
import { TimeRange } from '../components/TimeRange';
import { FullscreenButton } from '../components/FullscreenButton';
import { DurationDisplay } from '../components/DurationDisplay';
@@ -15,27 +15,32 @@ import {
FullscreenEnterIcon,
FullscreenExitIcon,
} from '@vjs-10/react-icons';
import styles from './styles.module.css';
import styles from './styles';
export const MediaSkinDefault: React.FC<{ children: React.ReactNode }> = ({
children,
}) => {
return (
<MediaContainer className={styles.Container}>
<MediaContainer className={styles.MediaContainer}>
{children}
<div className={styles.Overlay}>
<div className={styles.Spacer}></div>
<div className={styles.ControlBar}>
{/* @ts-ignore */}
<PlayButton className={styles.MediaPlayButton}>
<PlayIcon className={styles.PlayIcon}></PlayIcon>
<PauseIcon className={styles.PauseIcon}></PauseIcon>
</PlayButton>
{/* Background gradient to help with controls contrast. */}
<div className={styles.Overlay} aria-hidden="true" />
<div className={styles.Controls} data-testid="media-controls">
<PlayButton className={`${styles.Button} ${styles.IconButton} ${styles.PlayButton}`}>
<PlayIcon className={styles.PlayIcon}></PlayIcon>
<PauseIcon className={styles.PauseIcon}></PauseIcon>
</PlayButton>
<div className={styles.TimeControls}>
<CurrentTimeDisplay
// Use showRemaining to show count down/remaining time
// showRemaining
className={styles.TimeDisplay}
/>
<TimeRange.Root className={styles.TimeRangeRoot}>
<TimeRange.Track className={styles.TimeRangeTrack}>
<TimeRange.Progress className={styles.TimeRangeProgress} />
@@ -43,32 +48,23 @@ export const MediaSkinDefault: React.FC<{ children: React.ReactNode }> = ({
</TimeRange.Track>
<TimeRange.Thumb className={styles.TimeRangeThumb} />
</TimeRange.Root>
<DurationDisplay className={styles.TimeDisplay} />
{/* @ts-ignore */}
<MuteButton className={`${styles.Button} ${styles.MediaMuteButton}`}>
<VolumeHighIcon
className={`${styles.Icon} ${styles.VolumeHighIcon}`}
></VolumeHighIcon>
<VolumeLowIcon
className={`${styles.Icon} ${styles.VolumeLowIcon}`}
></VolumeLowIcon>
<VolumeOffIcon
className={`${styles.Icon} ${styles.VolumeOffIcon}`}
></VolumeOffIcon>
</MuteButton>
<VolumeRange className={styles.VolumeRange} />
{/* @ts-ignore */}
<FullscreenButton
className={`${styles.Button} ${styles.MediaFullscreenButton}`}
>
<FullscreenEnterIcon
className={`${styles.Icon} ${styles.FullscreenEnterIcon}`}
></FullscreenEnterIcon>
<FullscreenExitIcon
className={`${styles.Icon} ${styles.FullscreenExitIcon}`}
></FullscreenExitIcon>
</FullscreenButton>
</div>
<MuteButton className={`${styles.Button} ${styles.IconButton} ${styles.VolumeButton}`}>
<VolumeHighIcon className={styles.VolumeHighIcon} />
<VolumeLowIcon className={styles.VolumeLowIcon} />
<VolumeOffIcon className={styles.VolumeOffIcon} />
</MuteButton>
{/* TODO: Volume slider in a popover (requires building a popover and vertical orientation slider) or we just inline it on larger displays? */}
{/* <VolumeRange className={legacyStyles.VolumeRange} /> */}
<FullscreenButton className={`${styles.Button} ${styles.IconButton} ${styles.FullScreenButton}`}>
<FullscreenEnterIcon className={styles.FullScreenEnterIcon} />
<FullscreenExitIcon className={styles.FullScreenExitIcon} />
</FullscreenButton>
</div>
</MediaContainer>
);
+115
View File
@@ -0,0 +1,115 @@
// 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)[]) => classes.filter(Boolean).join(' ');
const styles = {
MediaContainer: cn(
'relative @container/root group/root overflow-clip rounded-4xl',
// Prevent rounded corners in fullscreen.
'[&:fullscreen]:rounded-none [&:fullscreen]:[&_video]:h-full [&:fullscreen]:[&_video]:w-full',
// Fancy borders.
'after:absolute after:inset-0 after:ring-black/10 after:ring-1 dark:after:ring-black/40 after:ring-inset after:z-10 after:pointer-events-none after:rounded-[inherit]',
'before:absolute before:inset-px before:rounded-[inherit] before:ring-white/15 before:ring-1 before:ring-inset before:z-10 before:pointer-events-none',
// Ensure the nested video inherits the radius.
'[&_video]:rounded-[inherit]',
),
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, well 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.
'@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
'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
'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',
),
Button: cn(
'group/button cursor-pointer relative shrink-0 transition select-none p-2 rounded-full',
// Background/foreground
'bg-transparent text-white/90',
// Hover and focus states
'hocus:no-underline hocus:bg-white/10 hocus:text-white',
// Focus state
'-outline-offset-2 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-blue-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-opacity [&_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'),
VolumeButton: cn(
'[&_svg]:opacity-0',
'[&[data-volume-level="high"]_.volume-high-icon]:opacity-100',
'[&[data-volume-level="medium"]_.volume-low-icon]:opacity-100',
'[&[data-volume-level="low"]_.volume-low-icon]:opacity-100',
'[&[data-volume-level="off"]_.volume-off-icon]:opacity-100',
),
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',
),
TimeControls: cn('flex-1 flex items-center gap-3 px-1.5'),
TimeDisplay: cn(
'tabular-nums text-sm @7xl/root:text-base text-shadow-2xs shadow-black/50'
),
TimeRangeRoot: cn('flex h-5 items-center flex-1 group/slider relative'),
TimeRangeTrack: cn('h-1 w-full relative select-none rounded-full bg-white/20 ring-1 ring-black/5'),
TimeRangeProgress: cn('bg-white rounded-[inherit]'),
// TODO: Work out what we want to do here.
TimeRangePointer: cn('rounded-[inherit]'),
TimeRangeThumb: 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',
)
}
export default styles;