Merge pull request #15 from muxinc/feat/default-skin
feat(ui): port over default skin
@@ -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
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
"@vjs-10/html": "*"
|
||||
},
|
||||
"devDependencies": {
|
||||
"typescript": "^5.3.0",
|
||||
"vite": "^5.0.0"
|
||||
"typescript": "^5.9.2",
|
||||
"vite": "^7.1.6"
|
||||
}
|
||||
}
|
||||
@@ -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>
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
@@ -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>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
"extends": "../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"baseUrl": ".",
|
||||
"jsx": "react-jsx",
|
||||
"paths": {
|
||||
"@vjs/react": ["../../packages/react/src"],
|
||||
"@vjs/html": ["../../packages/html/src"],
|
||||
|
||||
@@ -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
|
||||
},
|
||||
});
|
||||
@@ -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'], // Don’t prebundle, keeps rebuilds fast.
|
||||
},
|
||||
} satisfies UserConfig;
|
||||
@@ -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 |
@@ -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 |
@@ -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 |
@@ -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 |
@@ -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 |
@@ -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 |
@@ -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 |
@@ -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 |
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
"@vjs-10/playback-engine": "*"
|
||||
},
|
||||
"devDependencies": {
|
||||
"typescript": "^5.3.0"
|
||||
"typescript": "^5.9.2"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
|
||||
@@ -33,7 +33,7 @@
|
||||
"hls.js": "^1.6.7"
|
||||
},
|
||||
"devDependencies": {
|
||||
"typescript": "^5.3.0"
|
||||
"typescript": "^5.9.2"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -33,7 +33,7 @@
|
||||
"@vjs-10/html-media-elements": "*"
|
||||
},
|
||||
"devDependencies": {
|
||||
"typescript": "^5.3.0"
|
||||
"typescript": "^5.9.2"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
|
||||
@@ -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"
|
||||
},
|
||||
|
||||
@@ -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
|
||||
@@ -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": {
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
|
||||
@@ -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, 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.
|
||||
'@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;
|
||||