mirror of
https://github.com/zoriya/v10.git
synced 2026-08-16 02:45:09 +00:00
docs(site): update getting started code examples to match new api (#473)
Co-authored-by: Darius Cepulis <dcepulis@mux.com>
This commit is contained in:
co-authored by
Darius Cepulis
parent
222d90f675
commit
5f27a6bc94
@@ -0,0 +1,35 @@
|
||||
import { useStore } from '@nanostores/react';
|
||||
import ClientCode from '@/components/Code/ClientCode';
|
||||
import type { Skin, UseCase } from '@/stores/installation';
|
||||
import { skin, useCase } from '@/stores/installation';
|
||||
|
||||
function getSkinImportParts(skin: Skin): { group: string; skinFile: string } {
|
||||
if (skin === 'minimal-video') return { group: 'video', skinFile: 'minimal-skin' };
|
||||
if (skin === 'minimal-audio') return { group: 'audio', skinFile: 'minimal-skin' };
|
||||
return { group: skin, skinFile: 'skin' };
|
||||
}
|
||||
|
||||
function generateCdnCode(useCase: UseCase, skin: Skin): string {
|
||||
if (useCase === 'background-video') {
|
||||
return `<script>
|
||||
import 'https://cdn.jsdelivr.net/npm/videojs/html/background/player.js';
|
||||
import 'https://cdn.jsdelivr.net/npm/videojs/html/background/skin.js';
|
||||
</script>
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/videojs/html/background/skin.css" />`;
|
||||
}
|
||||
|
||||
const { group, skinFile } = getSkinImportParts(skin);
|
||||
|
||||
return `<script>
|
||||
import 'https://cdn.jsdelivr.net/npm/videojs/html/${group}/player.js';
|
||||
import 'https://cdn.jsdelivr.net/npm/videojs/html/${group}/${skinFile}.js';
|
||||
</script>
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/videojs/html/${group}/${skinFile}.css" />`;
|
||||
}
|
||||
|
||||
export default function HTMLCdnCodeBlock() {
|
||||
const $useCase = useStore(useCase);
|
||||
const $skin = useStore(skin);
|
||||
|
||||
return <ClientCode code={generateCdnCode($useCase, $skin)} lang="html" />;
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
import { useEffect, useRef } from 'react';
|
||||
import ClientCode from '@/components/Code/ClientCode';
|
||||
import { Tab, TabsList, TabsPanel, TabsRoot } from '@/components/Tabs';
|
||||
import type { InstallMethod } from '@/stores/installation';
|
||||
import { installMethod } from '@/stores/installation';
|
||||
import HTMLCdnCodeBlock from './HTMLCdnCodeBlock';
|
||||
|
||||
export default function HTMLInstallTabs() {
|
||||
const ref = useRef<HTMLDivElement>(null);
|
||||
|
||||
// Observe tab changes and sync to installMethod store
|
||||
useEffect(() => {
|
||||
const root = ref.current?.querySelector('[data-tabs-root]');
|
||||
if (!root) return;
|
||||
|
||||
const observer = new MutationObserver(() => {
|
||||
const activeTab = root.querySelector('[role="tab"][data-tab-active="true"]');
|
||||
if (activeTab) {
|
||||
const value = activeTab.getAttribute('data-value');
|
||||
if (value) {
|
||||
installMethod.set(value as InstallMethod);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Observe all tab elements for attribute changes
|
||||
const tabs = root.querySelectorAll('[role="tab"]');
|
||||
tabs.forEach((tab) => {
|
||||
observer.observe(tab, { attributes: true, attributeFilter: ['data-tab-active'] });
|
||||
});
|
||||
|
||||
return () => observer.disconnect();
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div ref={ref}>
|
||||
<TabsRoot>
|
||||
<TabsList label="Installation">
|
||||
<Tab value="cdn" initial>
|
||||
cdn
|
||||
</Tab>
|
||||
<Tab value="npm">npm</Tab>
|
||||
<Tab value="pnpm">pnpm</Tab>
|
||||
<Tab value="yarn">yarn</Tab>
|
||||
<Tab value="bun">bun</Tab>
|
||||
</TabsList>
|
||||
<TabsPanel value="cdn" initial>
|
||||
<HTMLCdnCodeBlock />
|
||||
</TabsPanel>
|
||||
<TabsPanel value="npm">
|
||||
<ClientCode code="npm install @videojs/html" lang="bash" />
|
||||
</TabsPanel>
|
||||
<TabsPanel value="pnpm">
|
||||
<ClientCode code="pnpm add @videojs/html" lang="bash" />
|
||||
</TabsPanel>
|
||||
<TabsPanel value="yarn">
|
||||
<ClientCode code="yarn add @videojs/html" lang="bash" />
|
||||
</TabsPanel>
|
||||
<TabsPanel value="bun">
|
||||
<ClientCode code="bun add @videojs/html" lang="bash" />
|
||||
</TabsPanel>
|
||||
</TabsRoot>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -2,18 +2,21 @@ import { useStore } from '@nanostores/react';
|
||||
import ClientCode from '@/components/Code/ClientCode';
|
||||
import { Tab, TabsList, TabsPanel, TabsRoot } from '@/components/Tabs';
|
||||
import type { Renderer, Skin, UseCase } from '@/stores/installation';
|
||||
import { muxPlaybackId, renderer, skin, useCase } from '@/stores/installation';
|
||||
import { installMethod, muxPlaybackId, renderer, skin, useCase } from '@/stores/installation';
|
||||
|
||||
function getRendererTag(renderer: Renderer): string {
|
||||
const map: Record<Renderer, string> = {
|
||||
'background-video': 'background-video',
|
||||
cloudflare: 'cloudflare-video',
|
||||
dash: 'dash-video',
|
||||
hls: 'hls-video',
|
||||
'html5-audio': 'audio',
|
||||
'html5-video': 'video',
|
||||
jwplayer: 'jwplayer-video',
|
||||
mux: 'mux-video',
|
||||
shaka: 'shaka-video',
|
||||
'mux-audio': 'mux-audio',
|
||||
'mux-background-video': 'mux-background-video',
|
||||
'mux-video': 'mux-video',
|
||||
// shaka: 'shaka-video',
|
||||
spotify: 'spotify-audio',
|
||||
vimeo: 'vimeo-video',
|
||||
wistia: 'wistia-video',
|
||||
@@ -31,11 +34,16 @@ function getProviderTag(useCase: UseCase): string {
|
||||
return map[useCase];
|
||||
}
|
||||
|
||||
function getSkinTag(skin: Skin): string {
|
||||
function getSkinTag(useCase: UseCase, skin: Skin): string {
|
||||
// Background video has fixed skin
|
||||
if (useCase === 'background-video') {
|
||||
return 'background-video-skin';
|
||||
}
|
||||
const map: Record<Skin, string> = {
|
||||
'default-video': 'video-skin',
|
||||
'default-audio': 'audio-skin',
|
||||
minimal: 'minimal-video-skin',
|
||||
video: 'video-skin',
|
||||
audio: 'audio-skin',
|
||||
'minimal-video': 'minimal-video-skin',
|
||||
'minimal-audio': 'minimal-audio-skin',
|
||||
};
|
||||
return map[skin];
|
||||
}
|
||||
@@ -43,8 +51,8 @@ function getSkinTag(skin: Skin): string {
|
||||
function getRendererElement(renderer: Renderer, playbackId: string | null): string {
|
||||
const tag = getRendererTag(renderer);
|
||||
|
||||
// When renderer is 'mux' and we have a playback ID, use playback-id attribute
|
||||
if (renderer === 'mux' && playbackId) {
|
||||
// When renderer is a mux variant and we have a playback ID, use playback-id attribute
|
||||
if ((renderer === 'mux-video' || renderer === 'mux-audio' || renderer === 'mux-background-video') && playbackId) {
|
||||
return `<${tag} playback-id="${playbackId}"></${tag}>`;
|
||||
}
|
||||
|
||||
@@ -54,7 +62,7 @@ function getRendererElement(renderer: Renderer, playbackId: string | null): stri
|
||||
|
||||
function generateHTMLCode(useCase: UseCase, skin: Skin, renderer: Renderer, playbackId: string | null): string {
|
||||
const providerTag = getProviderTag(useCase);
|
||||
const skinTag = getSkinTag(skin);
|
||||
const skinTag = getSkinTag(useCase, skin);
|
||||
const rendererElement = getRendererElement(renderer, playbackId);
|
||||
|
||||
return `<!--
|
||||
@@ -79,17 +87,22 @@ function generateHTMLCode(useCase: UseCase, skin: Skin, renderer: Renderer, play
|
||||
</${providerTag}>`;
|
||||
}
|
||||
|
||||
function getSkinImportPath(skin: Skin): string {
|
||||
const map: Record<Skin, string> = {
|
||||
'default-video': '@videojs/html/video/skin',
|
||||
'default-audio': '@videojs/html/audio/skin',
|
||||
minimal: '@videojs/html/video/minimal-skin',
|
||||
};
|
||||
return map[skin];
|
||||
function getSkinImportParts(skin: Skin): { group: string; skinFile: string } {
|
||||
if (skin === 'minimal-video') return { group: 'video', skinFile: 'minimal-skin' };
|
||||
if (skin === 'minimal-audio') return { group: 'audio', skinFile: 'minimal-skin' };
|
||||
return { group: skin, skinFile: 'skin' };
|
||||
}
|
||||
|
||||
function generateJS(skin: Skin): string {
|
||||
return `import '${getSkinImportPath(skin)}';`;
|
||||
function generateJS(useCase: UseCase, skin: Skin): string {
|
||||
if (useCase === 'background-video') {
|
||||
return `import '@videojs/html/background/player';
|
||||
import '@videojs/html/background/skin';
|
||||
import '@videojs/html/background/skin.css';`;
|
||||
}
|
||||
const { group, skinFile } = getSkinImportParts(skin);
|
||||
return `import '@videojs/html/${group}/player';
|
||||
import '@videojs/html/${group}/${skinFile}';
|
||||
import '@videojs/html/${group}/${skinFile}.css';`;
|
||||
}
|
||||
|
||||
export default function HTMLUsageCodeBlock() {
|
||||
@@ -97,19 +110,22 @@ export default function HTMLUsageCodeBlock() {
|
||||
const $skin = useStore(skin);
|
||||
const $renderer = useStore(renderer);
|
||||
const $muxPlaybackId = useStore(muxPlaybackId);
|
||||
const $installMethod = useStore(installMethod);
|
||||
|
||||
return (
|
||||
<>
|
||||
<TabsRoot maxWidth={false}>
|
||||
<TabsList label="HTML implementation">
|
||||
<Tab value="javascript" initial>
|
||||
JavaScript
|
||||
</Tab>
|
||||
</TabsList>
|
||||
<TabsPanel value="javascript" initial>
|
||||
<ClientCode code={generateJS($skin)} lang="javascript" />
|
||||
</TabsPanel>
|
||||
</TabsRoot>
|
||||
{$installMethod !== 'cdn' && (
|
||||
<TabsRoot maxWidth={false}>
|
||||
<TabsList label="HTML implementation">
|
||||
<Tab value="javascript" initial>
|
||||
JavaScript
|
||||
</Tab>
|
||||
</TabsList>
|
||||
<TabsPanel value="javascript" initial>
|
||||
<ClientCode code={generateJS($useCase, $skin)} lang="javascript" />
|
||||
</TabsPanel>
|
||||
</TabsRoot>
|
||||
)}
|
||||
<TabsRoot maxWidth={false}>
|
||||
<TabsList label="HTML implementation">
|
||||
<Tab value="html" initial>
|
||||
|
||||
@@ -27,7 +27,7 @@ import UploaderOverlay from './UploaderOverlay';
|
||||
* 3. If 401: show login overlay, wait for auth, retry
|
||||
* 4. Upload begins with returned URL
|
||||
* 5. On success: poll for playback ID
|
||||
* 6. When ready: update renderer to 'mux', store playback ID in nanostore
|
||||
* 6. When ready: update renderer to 'mux-video', store playback ID in nanostore
|
||||
*/
|
||||
export default function MuxUploaderPanel() {
|
||||
// Local state for upload flow (not shared across islands)
|
||||
@@ -158,7 +158,7 @@ export default function MuxUploaderPanel() {
|
||||
// Success! Update local state and nanostores (for cross-island use)
|
||||
setPlaybackId(result.playbackId);
|
||||
setState('ready');
|
||||
renderer.set('mux');
|
||||
renderer.set('mux-video');
|
||||
muxPlaybackId.set(result.playbackId);
|
||||
}, [uploadId]);
|
||||
|
||||
|
||||
@@ -6,14 +6,17 @@ import { muxPlaybackId, renderer, skin, useCase } from '@/stores/installation';
|
||||
|
||||
function getRendererComponent(renderer: Renderer): string {
|
||||
const map: Record<Renderer, string> = {
|
||||
'background-video': 'BackgroundVideo',
|
||||
cloudflare: 'CloudflareVideo',
|
||||
dash: 'DashVideo',
|
||||
hls: 'HlsVideo',
|
||||
'html5-audio': 'Audio',
|
||||
'html5-video': 'Video',
|
||||
jwplayer: 'JwplayerVideo',
|
||||
mux: 'MuxVideo',
|
||||
shaka: 'ShakaVideo',
|
||||
'mux-audio': 'MuxAudio',
|
||||
'mux-background-video': 'MuxBackgroundVideo',
|
||||
'mux-video': 'MuxVideo',
|
||||
// shaka: 'ShakaVideo', this one probably adds more confusion than help at this level
|
||||
spotify: 'SpotifyAudio',
|
||||
vimeo: 'VimeoVideo',
|
||||
wistia: 'WistiaVideo',
|
||||
@@ -24,13 +27,20 @@ function getRendererComponent(renderer: Renderer): string {
|
||||
|
||||
function getSkinComponent(skin: Skin): string {
|
||||
const map: Record<Skin, string> = {
|
||||
'default-video': 'VideoSkin',
|
||||
'default-audio': 'AudioSkin',
|
||||
minimal: 'MinimalVideoSkin',
|
||||
video: 'VideoSkin',
|
||||
audio: 'AudioSkin',
|
||||
'minimal-video': 'MinimalVideoSkin',
|
||||
'minimal-audio': 'MinimalAudioSkin',
|
||||
};
|
||||
return map[skin];
|
||||
}
|
||||
|
||||
function getSkinImportParts(skin: Skin): { group: string; skinFile: string } {
|
||||
if (skin === 'minimal-video') return { group: 'video', skinFile: 'minimal-skin' };
|
||||
if (skin === 'minimal-audio') return { group: 'audio', skinFile: 'minimal-skin' };
|
||||
return { group: skin, skinFile: 'skin' };
|
||||
}
|
||||
|
||||
function getPresetAccess(useCase: UseCase): string {
|
||||
if (useCase === 'default-video') {
|
||||
return 'presets.website';
|
||||
@@ -38,13 +48,32 @@ function getPresetAccess(useCase: UseCase): string {
|
||||
return `presets['${useCase}']`;
|
||||
}
|
||||
|
||||
function getProviderComponent(useCase: UseCase): string {
|
||||
const map: Record<UseCase, string> = {
|
||||
'default-video': 'VideoProvider',
|
||||
'default-audio': 'AudioProvider',
|
||||
'background-video': 'BackgroundVideoProvider',
|
||||
};
|
||||
return map[useCase];
|
||||
}
|
||||
|
||||
function generateReactCode(useCase: UseCase, skin: Skin, renderer: Renderer, playbackId: string | null): string {
|
||||
const skinComponent = getSkinComponent(skin);
|
||||
const providerComponent = getProviderComponent(useCase);
|
||||
const rendererComponent = getRendererComponent(renderer);
|
||||
const presetAccess = getPresetAccess(useCase);
|
||||
|
||||
// Background video has fixed skin and subpath imports, others use skin picker value
|
||||
const isBackgroundVideo = useCase === 'background-video';
|
||||
const skinComponent = isBackgroundVideo ? 'BackgroundVideoSkin' : getSkinComponent(skin);
|
||||
const { group, skinFile } = getSkinImportParts(skin);
|
||||
const skinCssImport = isBackgroundVideo
|
||||
? '@videojs/react/background/skin.css'
|
||||
: `@videojs/react/${group}/${skinFile}.css`;
|
||||
const componentImportPath = isBackgroundVideo ? '@videojs/react/background' : '@videojs/react';
|
||||
|
||||
// Determine props based on renderer + playbackId
|
||||
const isMuxWithPlaybackId = renderer === 'mux' && playbackId;
|
||||
const isMuxWithPlaybackId =
|
||||
(renderer === 'mux-video' || renderer === 'mux-audio' || renderer === 'mux-background-video') && playbackId;
|
||||
const propsInterface = isMuxWithPlaybackId
|
||||
? 'interface MyPlayerProps {\n playbackId: string;\n}'
|
||||
: 'interface MyPlayerProps {\n src: string;\n}';
|
||||
@@ -53,21 +82,17 @@ function generateReactCode(useCase: UseCase, skin: Skin, renderer: Renderer, pla
|
||||
? `<${rendererComponent} playbackId={playbackId} />`
|
||||
: `<${rendererComponent} src={src} />`;
|
||||
|
||||
return `import { createPlayer, presets, ${rendererComponent} } from '@videojs/react';
|
||||
import { ${skinComponent} } from '@videojs/react/presets/${useCase}';
|
||||
import '@videojs/react/presets/${useCase}/skins/${skin}.css';
|
||||
return `import '${skinCssImport}';
|
||||
import { ${providerComponent}, ${skinComponent}, ${rendererComponent} } from '${componentImportPath}';
|
||||
|
||||
${propsInterface}
|
||||
|
||||
// Set up the player state features
|
||||
const { PlayerProvider } = createPlayer(${presetAccess});
|
||||
|
||||
export const MyPlayer = ({ ${destructuredProp} }: MyPlayerProps) => {
|
||||
return (
|
||||
{/* The Provider passes state between the UI components
|
||||
and the Media, and makes fully custom UIs possible.
|
||||
Does not render its own HTML element. */}
|
||||
<PlayerProvider>
|
||||
<${providerComponent}>
|
||||
{/* Skins contain the entire player UI and are easily swappable.
|
||||
They can each be "ejected" for full control and customization
|
||||
of UI components. */}
|
||||
@@ -77,7 +102,7 @@ export const MyPlayer = ({ ${destructuredProp} }: MyPlayerProps) => {
|
||||
They are easily swappable to handle different sources. */}
|
||||
${rendererJsx}
|
||||
</${skinComponent}>
|
||||
</PlayerProvider>
|
||||
</${providerComponent}>
|
||||
);
|
||||
};`;
|
||||
}
|
||||
|
||||
@@ -5,7 +5,8 @@ import type { Renderer } from '@/stores/installation';
|
||||
import { muxPlaybackId, renderer } from '@/stores/installation';
|
||||
|
||||
function generateUsageCode(renderer: Renderer, playbackId: string | null): string {
|
||||
const isMuxWithPlaybackId = renderer === 'mux' && playbackId;
|
||||
const isMuxWithPlaybackId =
|
||||
(renderer === 'mux-video' || renderer === 'mux-audio' || renderer === 'mux-background-video') && playbackId;
|
||||
const playerProp = isMuxWithPlaybackId ? `playbackId="${playbackId}"` : 'src="https://example.com/video.mp4"';
|
||||
|
||||
return `import { MyPlayer } from '../components/player';
|
||||
|
||||
@@ -1,41 +1,64 @@
|
||||
import { useStore } from '@nanostores/react';
|
||||
import { Box } from 'lucide-react';
|
||||
import { useEffect } from 'react';
|
||||
import type { ImageRadioOption } from '@/components/ImageRadioGroup';
|
||||
import ImageRadioGroup from '@/components/ImageRadioGroup';
|
||||
import type { Renderer } from '@/stores/installation';
|
||||
import { renderer } from '@/stores/installation';
|
||||
import { renderer, useCase } from '@/stores/installation';
|
||||
|
||||
const PRIORITY_RENDERER_OPTIONS: ImageRadioOption<Renderer>[] = [
|
||||
const VIDEO_RENDERERS: ImageRadioOption<Renderer>[] = [
|
||||
{ value: 'html5-video', label: 'HTML5 Video', image: <Box size={16} /> },
|
||||
{ value: 'html5-audio', label: 'HTML5 Audio', image: <Box size={16} /> },
|
||||
...(
|
||||
[
|
||||
{ value: 'cloudflare', label: 'Cloudflare', image: <Box size={16} /> },
|
||||
{ value: 'dash', label: 'DASH', image: <Box size={16} /> },
|
||||
{ value: 'hls', label: 'HLS', image: <Box size={16} /> },
|
||||
{ value: 'jwplayer', label: 'JW Player', image: <Box size={16} /> },
|
||||
{ value: 'mux-video', label: 'Mux', image: <Box size={16} /> },
|
||||
// { value: 'shaka', label: 'Shaka', image: <Box size={16} /> },
|
||||
{ value: 'vimeo', label: 'Vimeo', image: <Box size={16} /> },
|
||||
{ value: 'wistia', label: 'Wistia', image: <Box size={16} /> },
|
||||
{ value: 'youtube', label: 'YouTube', image: <Box size={16} /> },
|
||||
] satisfies ImageRadioOption<Renderer>[]
|
||||
).sort((a, b) => a.label.localeCompare(b.label)),
|
||||
];
|
||||
|
||||
const RENDERER_OPTIONS: ImageRadioOption<Renderer>[] = (
|
||||
[
|
||||
{ value: 'cloudflare', label: 'Cloudflare', image: <Box size={16} /> },
|
||||
{ value: 'dash', label: 'DASH', image: <Box size={16} /> },
|
||||
{ value: 'hls', label: 'HLS', image: <Box size={16} /> },
|
||||
{ value: 'jwplayer', label: 'JW Player', image: <Box size={16} /> },
|
||||
{ value: 'mux', label: 'Mux', image: <Box size={16} /> },
|
||||
{ value: 'shaka', label: 'Shaka', image: <Box size={16} /> },
|
||||
{ value: 'spotify', label: 'Spotify', image: <Box size={16} /> },
|
||||
{ value: 'vimeo', label: 'Vimeo', image: <Box size={16} /> },
|
||||
{ value: 'wistia', label: 'Wistia', image: <Box size={16} /> },
|
||||
{ value: 'youtube', label: 'YouTube', image: <Box size={16} /> },
|
||||
] satisfies ImageRadioOption<Renderer>[]
|
||||
).sort((a, b) => a.label.localeCompare(b.label));
|
||||
const AUDIO_RENDERERS: ImageRadioOption<Renderer>[] = [
|
||||
{ value: 'html5-audio', label: 'HTML5 Audio', image: <Box size={16} /> },
|
||||
{ value: 'mux-audio', label: 'Mux', image: <Box size={16} /> },
|
||||
{ value: 'spotify', label: 'Spotify', image: <Box size={16} /> },
|
||||
];
|
||||
|
||||
const ALL_RENDERER_OPTIONS = [...PRIORITY_RENDERER_OPTIONS, ...RENDERER_OPTIONS];
|
||||
const BACKGROUND_VIDEO_RENDERERS: ImageRadioOption<Renderer>[] = [
|
||||
{ value: 'background-video', label: 'Background Video', image: <Box size={16} /> },
|
||||
{ value: 'mux-background-video', label: 'Mux Background Video', image: <Box size={16} /> },
|
||||
];
|
||||
|
||||
/** URL input and renderer dropdown for manual selection */
|
||||
export default function RendererSelect() {
|
||||
const $renderer = useStore(renderer);
|
||||
const $useCase = useStore(useCase);
|
||||
|
||||
const options =
|
||||
$useCase === 'default-audio'
|
||||
? AUDIO_RENDERERS
|
||||
: $useCase === 'background-video'
|
||||
? BACKGROUND_VIDEO_RENDERERS
|
||||
: VIDEO_RENDERERS;
|
||||
|
||||
// Auto-switch renderer when use case changes and current renderer is invalid
|
||||
useEffect(() => {
|
||||
const validValues = options.map((o) => o.value);
|
||||
if (!validValues.includes($renderer)) {
|
||||
renderer.set(options[0].value);
|
||||
}
|
||||
}, [$useCase]);
|
||||
|
||||
return (
|
||||
<ImageRadioGroup
|
||||
value={$renderer}
|
||||
onChange={(value) => renderer.set(value)}
|
||||
options={ALL_RENDERER_OPTIONS}
|
||||
options={options}
|
||||
aria-label="Select renderer"
|
||||
size="sm"
|
||||
labelPosition="inline"
|
||||
|
||||
@@ -1,22 +1,36 @@
|
||||
import { useStore } from '@nanostores/react';
|
||||
import { Minus, Sparkles } from 'lucide-react';
|
||||
import { useEffect } from 'react';
|
||||
import type { ImageRadioOption } from '@/components/ImageRadioGroup';
|
||||
import ImageRadioGroup from '@/components/ImageRadioGroup';
|
||||
import type { Skin } from '@/stores/installation';
|
||||
import { skin } from '@/stores/installation';
|
||||
import { skin, useCase } from '@/stores/installation';
|
||||
|
||||
const VIDEO_SKINS: ImageRadioOption<Skin>[] = [
|
||||
{ value: 'video', label: 'Default Video', image: <Sparkles size={32} /> },
|
||||
{ value: 'minimal-video', label: 'Minimal', image: <Minus size={32} /> },
|
||||
];
|
||||
|
||||
const AUDIO_SKINS: ImageRadioOption<Skin>[] = [
|
||||
{ value: 'audio', label: 'Default Audio', image: <Sparkles size={32} /> },
|
||||
{ value: 'minimal-audio', label: 'Minimal', image: <Minus size={32} /> },
|
||||
];
|
||||
|
||||
export default function SkinPicker() {
|
||||
const $skin = useStore(skin);
|
||||
const $useCase = useStore(useCase);
|
||||
|
||||
const options = $useCase === 'default-audio' ? AUDIO_SKINS : VIDEO_SKINS;
|
||||
|
||||
// Auto-switch skin when use case changes and current skin is invalid
|
||||
useEffect(() => {
|
||||
const validValues = options.map((o) => o.value);
|
||||
if (!validValues.includes($skin)) {
|
||||
skin.set(options[0].value);
|
||||
}
|
||||
}, [$useCase]);
|
||||
|
||||
return (
|
||||
<ImageRadioGroup
|
||||
value={$skin}
|
||||
onChange={(value) => skin.set(value as Skin)}
|
||||
options={[
|
||||
{ value: 'default-video' satisfies Skin, label: 'Default Video', image: <Sparkles size={32} /> },
|
||||
{ value: 'default-audio' satisfies Skin, label: 'Default Audio', image: <Sparkles size={32} /> },
|
||||
{ value: 'minimal' satisfies Skin, label: 'Minimal', image: <Minus size={32} /> },
|
||||
]}
|
||||
aria-label="Select skin"
|
||||
/>
|
||||
<ImageRadioGroup value={$skin} onChange={(value) => skin.set(value)} options={options} aria-label="Select skin" />
|
||||
);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
---
|
||||
import H2Markdown from '../typography/H2Markdown.astro';
|
||||
import P from '../typography/P.astro';
|
||||
import SkinPickerSectionClient from './SkinPickerSectionClient.tsx';
|
||||
---
|
||||
|
||||
<SkinPickerSectionClient client:idle>
|
||||
<H2Markdown>
|
||||
Choose skin
|
||||
</H2Markdown>
|
||||
<P>Choose how your player looks.</P>
|
||||
</SkinPickerSectionClient>
|
||||
@@ -0,0 +1,19 @@
|
||||
import { useStore } from '@nanostores/react';
|
||||
import { useCase } from '@/stores/installation';
|
||||
import SkinPicker from './SkinPicker';
|
||||
|
||||
export default function SkinPickerSection({ children }: { children: React.ReactNode }) {
|
||||
const $useCase = useStore(useCase);
|
||||
|
||||
// Hide for background-video use case
|
||||
if ($useCase === 'background-video') {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
{children}
|
||||
<SkinPicker />
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -5,8 +5,9 @@ description: Get started quickly with Video.js by building your first embed code
|
||||
|
||||
import JSPicker from '@/components/installation/JSPicker.astro';
|
||||
import RendererPicker from '@/components/installation/RendererPicker';
|
||||
import SkinPicker from '@/components/installation/SkinPicker';
|
||||
import SkinPickerSection from '@/components/installation/SkinPickerSection.astro';
|
||||
import UseCasePicker from '@/components/installation/UseCasePicker';
|
||||
import HTMLInstallTabs from '@/components/installation/HTMLInstallTabs';
|
||||
import HTMLUsageCodeBlock from '@/components/installation/HTMLUsageCodeBlock';
|
||||
import ReactCreateCodeBlock from '@/components/installation/ReactCreateCodeBlock';
|
||||
import ReactUsageCodeBlock from '@/components/installation/ReactUsageCodeBlock';
|
||||
@@ -25,7 +26,7 @@ Answer the questions below to get started quickly with your first embed code.
|
||||
|
||||
## Choose your JS framework
|
||||
|
||||
Video.js aims to provide idiomatic development experiences in your favorite JS and CSS frameworks. More to come ([Vote on what's next](https://github.com/videojs/v10/discussions/categories/ideas))\[todo\].
|
||||
Video.js aims to provide idiomatic development experiences in your favorite JS and CSS frameworks. More to come. ([vote](https://github.com/videojs/v10/discussions/categories/ideas))\[todo\].
|
||||
|
||||
<ContentWidth margins="lg">
|
||||
<JSPicker />
|
||||
@@ -34,12 +35,15 @@ Video.js aims to provide idiomatic development experiences in your favorite JS a
|
||||
|
||||
## Choose your use case
|
||||
|
||||
The defaults work well for general website playback. More use case player presets to come.
|
||||
The defaults work well for general website playback. More prebuilt players to come. ([vote](https://github.com/videojs/v10/discussions/categories/ideas))\[todo\]
|
||||
|
||||
<ContentWidth margins="lg">
|
||||
<UseCasePicker client:idle />
|
||||
</ContentWidth>
|
||||
|
||||
<ContentWidth margins="lg">
|
||||
<SkinPickerSection />
|
||||
</ContentWidth>
|
||||
|
||||
## Choose your media source type
|
||||
|
||||
@@ -52,46 +56,7 @@ Video.js supports a wide range of file types and hosting services. It's easy to
|
||||
## Install Video.js
|
||||
|
||||
<FrameworkCase frameworks={["html"]}>
|
||||
<TabsRoot client:idle>
|
||||
<TabsList client:idle label="Installation">
|
||||
<Tab client:idle value="cdn" initial>cdn</Tab>
|
||||
<Tab client:idle value="npm">npm</Tab>
|
||||
<Tab client:idle value="pnpm">pnpm</Tab>
|
||||
<Tab client:idle value="yarn">yarn</Tab>
|
||||
<Tab client:idle value="bun">bun</Tab>
|
||||
</TabsList>
|
||||
<TabsPanel client:idle value="cdn">
|
||||
```html
|
||||
<script>
|
||||
// don't need this, will be included in frosted
|
||||
// import 'https://cdn.jsdelivr.net/npm/videojs/html/presets/website.js';
|
||||
import 'https://cdn.jsdelivr.net/npm/videojs/html/presets/website/skins/frosted.js';
|
||||
</script>
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/videojs/html/presets/website/skins/frosted.css" />
|
||||
```
|
||||
</TabsPanel>
|
||||
<TabsPanel client:idle value="npm" initial>
|
||||
```bash
|
||||
npm install @videojs/html
|
||||
```
|
||||
</TabsPanel>
|
||||
<TabsPanel client:idle value="pnpm">
|
||||
```bash
|
||||
pnpm add @videojs/html
|
||||
```
|
||||
</TabsPanel>
|
||||
<TabsPanel client:idle value="yarn">
|
||||
```bash
|
||||
yarn add @videojs/html
|
||||
```
|
||||
</TabsPanel>
|
||||
<TabsPanel client:idle value="bun">
|
||||
```bash
|
||||
bun add @videojs/html
|
||||
```
|
||||
</TabsPanel>
|
||||
</TabsRoot>
|
||||
|
||||
<HTMLInstallTabs client:idle />
|
||||
</FrameworkCase>
|
||||
<FrameworkCase frameworks={["react"]}>
|
||||
<TabsRoot client:idle>
|
||||
|
||||
@@ -1,26 +1,33 @@
|
||||
import { atom } from 'nanostores';
|
||||
|
||||
export type Renderer =
|
||||
| 'background-video'
|
||||
| 'cloudflare'
|
||||
| 'dash'
|
||||
| 'hls'
|
||||
| 'html5-audio'
|
||||
| 'html5-video'
|
||||
| 'jwplayer'
|
||||
| 'mux'
|
||||
| 'shaka'
|
||||
| 'mux-audio'
|
||||
| 'mux-background-video'
|
||||
| 'mux-video'
|
||||
// | 'shaka'
|
||||
| 'spotify'
|
||||
| 'vimeo'
|
||||
| 'wistia'
|
||||
| 'youtube';
|
||||
|
||||
export type Skin = 'default-video' | 'default-audio' | 'minimal';
|
||||
export type Skin = 'video' | 'audio' | 'minimal-video' | 'minimal-audio';
|
||||
|
||||
export type UseCase = 'default-video' | 'default-audio' | 'background-video';
|
||||
|
||||
export const renderer = atom<Renderer>('html5-video');
|
||||
export const skin = atom<Skin>('default-video');
|
||||
export const skin = atom<Skin>('video');
|
||||
export const useCase = atom<UseCase>('default-video');
|
||||
|
||||
export type InstallMethod = 'cdn' | 'npm' | 'pnpm' | 'yarn' | 'bun';
|
||||
|
||||
export const installMethod = atom<InstallMethod>('cdn');
|
||||
|
||||
/** Mux playback ID from successful upload (used by code generation) */
|
||||
export const muxPlaybackId = atom<string | null>(null);
|
||||
|
||||
Reference in New Issue
Block a user