mirror of
https://github.com/zoriya/v10.git
synced 2026-08-05 13:48:14 +00:00
feat: migrate examples from prototype and add CSS modules support
- Migrate HTML and React demo applications from prototype to examples/ directory - Add examples to workspace configuration with selective build support - Update workspace scripts for library-only builds (build:libs) and example dev servers - Fix React MediaProvider context conflicts by consolidating exports - Add esbuild-css-modules-plugin for CSS modules support in React package - Configure tsup for flattened output structure with CSS processing - Update package exports and imports for new @vjs-10/* scoped packages 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude
parent
7403cc7281
commit
19c85fc2f8
@@ -0,0 +1,11 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>VJS HTML Demo</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<script type="module" src="/src/main.ts"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"name": "html-demo",
|
||||
"version": "1.0.0",
|
||||
"description": "HTML example for Video.js 10",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "vite build",
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"dependencies": {
|
||||
"@vjs-10/html": "*"
|
||||
},
|
||||
"devDependencies": {
|
||||
"typescript": "^5.3.0",
|
||||
"vite": "^5.0.0"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
import { defineVjsPlayer } from '@vjs-10/html';
|
||||
|
||||
defineVjsPlayer();
|
||||
|
||||
document.body.innerHTML = `
|
||||
<media-provider>
|
||||
<media-skin-default>
|
||||
<video slot="media" src="https://www.w3schools.com/html/mov_bbb.mp4" muted></video>
|
||||
</media-skin-default>
|
||||
</media-provider>
|
||||
`;
|
||||
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"extends": "../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"@vjs/html": ["../../packages/html/src"],
|
||||
"@vjs/react": ["../../packages/react/src"],
|
||||
"@vjs/core": ["../../packages/core/src"]
|
||||
}
|
||||
},
|
||||
"include": ["src"]
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
import { defineConfig } from 'vite';
|
||||
import { fileURLToPath } from 'url';
|
||||
|
||||
const __dirname = fileURLToPath(new URL('.', import.meta.url));
|
||||
|
||||
export default defineConfig({
|
||||
root: __dirname,
|
||||
});
|
||||
@@ -0,0 +1,11 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>VJS React Demo</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<script type="module" src="/src/main.tsx"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,24 @@
|
||||
{
|
||||
"name": "react-demo",
|
||||
"version": "1.0.0",
|
||||
"description": "React example for Video.js 10",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "vite build",
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"dependencies": {
|
||||
"@vjs-10/react": "*",
|
||||
"react": "^19.1.0",
|
||||
"react-dom": "^19.1.0"
|
||||
},
|
||||
"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"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom/client';
|
||||
import { MediaProvider, Video, MediaSkinDefault } from '@vjs-10/react';
|
||||
|
||||
const DemoPlayer = () => {
|
||||
return (
|
||||
<MediaProvider>
|
||||
<MediaSkinDefault>
|
||||
{/* @ts-ignore */}
|
||||
{/* <Video muted src="https://www.w3schools.com/html/mov_bbb.mp4" /> */}
|
||||
<Video muted src="https://stream.mux.com/a4nOgmxGWg6gULfcBbAa00gXyfcwPnAFldF8RdsNyk8M.m3u8" />
|
||||
</MediaSkinDefault>
|
||||
</MediaProvider>
|
||||
);
|
||||
};
|
||||
|
||||
ReactDOM.createRoot(document.getElementById('root')!).render(
|
||||
<DemoPlayer />
|
||||
);
|
||||
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"extends": "../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"@vjs/react": ["../../packages/react/src"],
|
||||
"@vjs/html": ["../../packages/html/src"],
|
||||
"@vjs/core": ["../../packages/core/src"]
|
||||
}
|
||||
},
|
||||
"include": ["src"]
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
import { defineConfig } from 'vite';
|
||||
import react from '@vitejs/plugin-react';
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [react()],
|
||||
});
|
||||
Generated
+1967
-1
File diff suppressed because it is too large
Load Diff
+8
-4
@@ -7,21 +7,25 @@
|
||||
"packages/core/*",
|
||||
"packages/html/*",
|
||||
"packages/react/*",
|
||||
"packages/react-native/*"
|
||||
"packages/react-native/*",
|
||||
"examples/*"
|
||||
],
|
||||
"scripts": {
|
||||
"build": "npm run build --workspaces --if-present",
|
||||
"build:libs": "npm run build --workspace=packages/core/* --workspace=packages/html/* --workspace=packages/react/* --workspace=packages/react-native/*",
|
||||
"test": "npm run test --workspaces --if-present",
|
||||
"lint": "npm run lint --workspaces --if-present",
|
||||
"typecheck": "npm run typecheck --workspaces --if-present",
|
||||
"clean": "npm run clean --workspaces --if-present"
|
||||
"clean": "npm run clean --workspaces --if-present",
|
||||
"dev:html": "npm run dev --workspace=examples/html-demo",
|
||||
"dev:react": "npm run dev --workspace=examples/react-demo"
|
||||
},
|
||||
"devDependencies": {
|
||||
"typescript": "^5.3.0",
|
||||
"@types/node": "^20.0.0",
|
||||
"@types/react": "^18.0.0",
|
||||
"react": "^18.0.0",
|
||||
"tsup": "^8.0.0"
|
||||
"tsup": "^8.0.0",
|
||||
"typescript": "^5.3.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18.0.0",
|
||||
|
||||
@@ -18,4 +18,4 @@ export default defineConfig({
|
||||
incremental: false,
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,182 +1,14 @@
|
||||
import React, { createContext, useContext, useEffect, useState, useRef, ReactNode } from 'react';
|
||||
// Re-export everything from MediaProvider - this is the primary implementation
|
||||
export {
|
||||
MediaProvider,
|
||||
MediaContext,
|
||||
useMediaStore,
|
||||
useMediaDispatch,
|
||||
useMediaRef,
|
||||
useMediaSelector
|
||||
} from './MediaProvider.js';
|
||||
|
||||
// @ts-ignore - Placeholder types until media-store exports are updated
|
||||
type MediaStore = any;
|
||||
type MediaState = any;
|
||||
type MediaStateOwner = any;
|
||||
import { MediaElementRef } from '@vjs-10/react-media-elements';
|
||||
|
||||
const MediaStoreContext = createContext<MediaStore | null>(null);
|
||||
|
||||
export interface MediaStoreProviderProps {
|
||||
children: ReactNode;
|
||||
store?: MediaStore;
|
||||
}
|
||||
|
||||
export const MediaStoreProvider: React.FC<MediaStoreProviderProps> = ({
|
||||
children,
|
||||
store = {} as MediaStore,
|
||||
}) => {
|
||||
return (
|
||||
<MediaStoreContext.Provider value={store}>
|
||||
{children}
|
||||
</MediaStoreContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
export function useMediaStore(): MediaStore {
|
||||
const store = useContext(MediaStoreContext);
|
||||
if (!store) {
|
||||
throw new Error('useMediaStore must be used within a MediaStoreProvider');
|
||||
}
|
||||
return store;
|
||||
}
|
||||
|
||||
export function useMediaState(): [MediaState, (updates: Partial<MediaState>) => void] {
|
||||
const store = useMediaStore();
|
||||
const [state, setState] = useState<MediaState>(store.getState());
|
||||
|
||||
useEffect(() => {
|
||||
const owner: MediaStateOwner = {
|
||||
getState: () => state,
|
||||
setState: (newState: Partial<MediaState>) => {
|
||||
setState((current: any) => ({ ...current, ...newState }));
|
||||
},
|
||||
};
|
||||
|
||||
store.addOwner(owner);
|
||||
|
||||
return () => {
|
||||
store.removeOwner(owner);
|
||||
};
|
||||
}, [store, state]);
|
||||
|
||||
const updateState = (updates: Partial<MediaState>) => {
|
||||
store.updateState(updates);
|
||||
};
|
||||
|
||||
return [state, updateState];
|
||||
}
|
||||
|
||||
export class ReactMediaStateOwner implements MediaStateOwner {
|
||||
private elementRef: React.RefObject<MediaElementRef>;
|
||||
private store: MediaStore;
|
||||
private intervalId?: NodeJS.Timeout;
|
||||
|
||||
constructor(elementRef: React.RefObject<MediaElementRef>, store: MediaStore) {
|
||||
this.elementRef = elementRef;
|
||||
this.store = store;
|
||||
this.startPolling();
|
||||
}
|
||||
|
||||
getState(): MediaState {
|
||||
const element = this.elementRef.current;
|
||||
if (!element) {
|
||||
return {
|
||||
currentTime: 0,
|
||||
duration: 0,
|
||||
paused: true,
|
||||
volume: 1,
|
||||
muted: false,
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
currentTime: element.currentTime,
|
||||
duration: element.duration,
|
||||
paused: element.paused,
|
||||
volume: element.volume,
|
||||
muted: element.muted,
|
||||
};
|
||||
}
|
||||
|
||||
setState(state: Partial<MediaState>): void {
|
||||
const element = this.elementRef.current;
|
||||
if (!element) return;
|
||||
|
||||
if (state.currentTime !== undefined && state.currentTime !== element.currentTime) {
|
||||
element.currentTime = state.currentTime;
|
||||
}
|
||||
if (state.volume !== undefined && state.volume !== element.volume) {
|
||||
element.volume = state.volume;
|
||||
}
|
||||
if (state.muted !== undefined && state.muted !== element.muted) {
|
||||
element.muted = state.muted;
|
||||
}
|
||||
}
|
||||
|
||||
private startPolling() {
|
||||
this.intervalId = setInterval(() => {
|
||||
const currentState = this.getState();
|
||||
this.store.updateState(currentState);
|
||||
}, 100);
|
||||
}
|
||||
|
||||
destroy() {
|
||||
if (this.intervalId) {
|
||||
clearInterval(this.intervalId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function useMediaElementStore(elementRef: React.RefObject<MediaElementRef>): MediaStore {
|
||||
const store = useMediaStore();
|
||||
const ownerRef = useRef<ReactMediaStateOwner | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (elementRef.current && !ownerRef.current) {
|
||||
ownerRef.current = new ReactMediaStateOwner(elementRef, store);
|
||||
store.addOwner(ownerRef.current);
|
||||
}
|
||||
|
||||
return () => {
|
||||
if (ownerRef.current) {
|
||||
store.removeOwner(ownerRef.current);
|
||||
ownerRef.current.destroy();
|
||||
ownerRef.current = null;
|
||||
}
|
||||
};
|
||||
}, [elementRef, store]);
|
||||
|
||||
return store;
|
||||
}
|
||||
|
||||
export function useCurrentTime(): [number, (time: number) => void] {
|
||||
const [state, updateState] = useMediaState();
|
||||
|
||||
const setCurrentTime = (time: number) => {
|
||||
updateState({ currentTime: time });
|
||||
};
|
||||
|
||||
return [state.currentTime, setCurrentTime];
|
||||
}
|
||||
|
||||
export function useVolume(): [number, boolean, (volume: number) => void, (muted: boolean) => void] {
|
||||
const [state, updateState] = useMediaState();
|
||||
|
||||
const setVolume = (volume: number) => {
|
||||
updateState({ volume });
|
||||
};
|
||||
|
||||
const setMuted = (muted: boolean) => {
|
||||
updateState({ muted });
|
||||
};
|
||||
|
||||
return [state.volume, state.muted, setVolume, setMuted];
|
||||
}
|
||||
|
||||
export function usePlaybackState(): [boolean, () => void, () => void] {
|
||||
const [state, updateState] = useMediaState();
|
||||
|
||||
const play = () => {
|
||||
updateState({ paused: false });
|
||||
};
|
||||
|
||||
const pause = () => {
|
||||
updateState({ paused: true });
|
||||
};
|
||||
|
||||
return [state.paused, play, pause];
|
||||
}
|
||||
|
||||
// Re-export placeholder types
|
||||
export type { MediaStore, MediaState, MediaStateOwner };
|
||||
export type MediaStore = any;
|
||||
export type MediaState = any;
|
||||
export type MediaStateOwner = any;
|
||||
@@ -37,6 +37,7 @@
|
||||
"react": ">=16.8.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"esbuild-css-modules-plugin": "^3.1.5",
|
||||
"typescript": "^5.3.0",
|
||||
"@types/react": "^18.0.0",
|
||||
"react": "^18.0.0"
|
||||
|
||||
@@ -3,7 +3,6 @@ import { PauseIcon, PlayIcon } from '@vjs-10/react-icons';
|
||||
import PlayButton from '../components/connected-with-defaults/PlayButton';
|
||||
import MuteButton from '../components/connected-with-defaults/MuteButton';
|
||||
import { VolumeHighIcon, VolumeLowIcon, VolumeOffIcon } from '@vjs-10/react-icons';
|
||||
/** @ts-ignore */
|
||||
import styles from './styles.module.css';
|
||||
|
||||
export const MediaSkinDefault: React.FC<{ children: React.ReactNode }> = ({
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
import { defineConfig } from 'tsup';
|
||||
import cssPlugin from 'esbuild-css-modules-plugin';
|
||||
|
||||
export default defineConfig({
|
||||
entry: ['src/index.tsx'],
|
||||
entry: {
|
||||
index: 'src/index.tsx',
|
||||
},
|
||||
format: ['cjs', 'esm'],
|
||||
clean: true,
|
||||
sourcemap: true,
|
||||
@@ -11,6 +14,11 @@ export default defineConfig({
|
||||
/^@vjs-10\//,
|
||||
/^[^.]/,
|
||||
],
|
||||
// Add esbuild plugin for CSS modules
|
||||
esbuildPlugins: [cssPlugin()],
|
||||
esbuildOptions(options, context) {
|
||||
options.outbase = './src'; // Ensure 'src' is the base for output paths
|
||||
},
|
||||
// Generate TypeScript declarations with custom config
|
||||
dts: {
|
||||
compilerOptions: {
|
||||
@@ -18,4 +26,4 @@ export default defineConfig({
|
||||
incremental: false,
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user