mirror of
https://github.com/zoriya/v10.git
synced 2026-08-16 02:45:09 +00:00
feat(site): tailwind demo variants for util and controller references
Adds react/tailwind and html/tailwind demo variants for create-player, html-create-player, player-controller, render-element, use-button, use-player, and use-store, gated on the style preference via StyleCase. Refs #840 https://claude.ai/code/session_01LUcwUd8E4Ekakp7NTFb6c5
This commit is contained in:
@@ -0,0 +1,40 @@
|
||||
import { createPlayer } from '@videojs/react';
|
||||
import { Video, videoFeatures } from '@videojs/react/video';
|
||||
|
||||
const Player = createPlayer({
|
||||
features: videoFeatures,
|
||||
});
|
||||
|
||||
function Controls() {
|
||||
const store = Player.usePlayer();
|
||||
const paused = Player.usePlayer((s) => s.paused);
|
||||
|
||||
return (
|
||||
<div className="absolute bottom-2.5 left-2.5">
|
||||
<button
|
||||
type="button"
|
||||
className="cursor-pointer rounded-full border border-white/30 bg-white/70 px-5 py-2 text-black backdrop-blur-[10px]"
|
||||
onClick={() => (paused ? store.play() : store.pause())}
|
||||
>
|
||||
{paused ? 'Play' : 'Pause'}
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default function BasicUsage() {
|
||||
return (
|
||||
<Player.Provider>
|
||||
<Player.Container className="relative">
|
||||
<Video
|
||||
className="w-full"
|
||||
src="https://stream.mux.com/BV3YZtogl89mg9VcNBhhnHm02Y34zI1nlMuMQfAbl3dM/highest.mp4"
|
||||
autoPlay
|
||||
muted
|
||||
playsInline
|
||||
/>
|
||||
<Controls />
|
||||
</Player.Container>
|
||||
</Player.Provider>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
---
|
||||
import HtmlDemo from '@/components/docs/demos/HtmlDemo.astro';
|
||||
import html from './BasicUsage.html?raw';
|
||||
---
|
||||
|
||||
<HtmlDemo html={html} />
|
||||
<script>
|
||||
import "./BasicUsage.ts";
|
||||
</script>
|
||||
@@ -0,0 +1,17 @@
|
||||
<demo-video-player class="relative">
|
||||
<media-container>
|
||||
<video
|
||||
class="w-full"
|
||||
src="https://stream.mux.com/BV3YZtogl89mg9VcNBhhnHm02Y34zI1nlMuMQfAbl3dM/highest.mp4"
|
||||
autoplay
|
||||
muted
|
||||
playsinline
|
||||
></video>
|
||||
<demo-play-toggle
|
||||
class="absolute bottom-2.5 left-2.5 cursor-pointer rounded-full border border-white/30 bg-white/70 px-5 py-2 text-black backdrop-blur-[10px]"
|
||||
>
|
||||
<span class="hidden in-data-paused:inline">Play</span>
|
||||
<span class="hidden not-in-data-paused:inline">Pause</span>
|
||||
</demo-play-toggle>
|
||||
</media-container>
|
||||
</demo-video-player>
|
||||
@@ -0,0 +1,59 @@
|
||||
import {
|
||||
applyElementProps,
|
||||
applyStateDataAttrs,
|
||||
createButton,
|
||||
createPlayer,
|
||||
MediaElement,
|
||||
PlayerController,
|
||||
selectPlayback,
|
||||
} from '@videojs/html';
|
||||
import { videoFeatures } from '@videojs/html/video';
|
||||
import '@videojs/html/media/container';
|
||||
|
||||
const { ProviderMixin, context } = createPlayer({
|
||||
features: videoFeatures,
|
||||
});
|
||||
|
||||
class VideoPlayer extends ProviderMixin(MediaElement) {
|
||||
static readonly tagName = 'demo-video-player';
|
||||
}
|
||||
|
||||
class PlayToggle extends MediaElement {
|
||||
static readonly tagName = 'demo-play-toggle';
|
||||
|
||||
readonly #player = new PlayerController(this, context, selectPlayback);
|
||||
|
||||
#disconnect: AbortController | null = null;
|
||||
|
||||
override connectedCallback(): void {
|
||||
super.connectedCallback();
|
||||
this.#disconnect = new AbortController();
|
||||
|
||||
const buttonProps = createButton({
|
||||
onActivate: () => {
|
||||
const state = this.#player.value;
|
||||
if (!state) return;
|
||||
state.paused ? state.play() : state.pause();
|
||||
},
|
||||
isDisabled: () => !this.#player.value,
|
||||
});
|
||||
|
||||
applyElementProps(this, buttonProps, { signal: this.#disconnect.signal });
|
||||
}
|
||||
|
||||
override disconnectedCallback(): void {
|
||||
super.disconnectedCallback();
|
||||
this.#disconnect?.abort();
|
||||
this.#disconnect = null;
|
||||
}
|
||||
|
||||
protected override update(changed: Map<string, unknown>): void {
|
||||
super.update(changed);
|
||||
const state = this.#player.value;
|
||||
if (!state) return;
|
||||
applyStateDataAttrs(this, state, { paused: 'data-paused', ended: 'data-ended' });
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define(VideoPlayer.tagName, VideoPlayer);
|
||||
customElements.define(PlayToggle.tagName, PlayToggle);
|
||||
@@ -0,0 +1,9 @@
|
||||
---
|
||||
import HtmlDemo from '@/components/docs/demos/HtmlDemo.astro';
|
||||
import html from './BasicUsage.html?raw';
|
||||
---
|
||||
|
||||
<HtmlDemo html={html} />
|
||||
<script>
|
||||
import "./BasicUsage.ts";
|
||||
</script>
|
||||
@@ -0,0 +1,36 @@
|
||||
<demo-ctrl-player class="relative">
|
||||
<media-container>
|
||||
<video
|
||||
class="w-full"
|
||||
src="https://stream.mux.com/BV3YZtogl89mg9VcNBhhnHm02Y34zI1nlMuMQfAbl3dM/highest.mp4"
|
||||
autoplay
|
||||
muted
|
||||
playsinline
|
||||
></video>
|
||||
<div class="flex items-center gap-4 border-t border-black/10 bg-black/5 p-3">
|
||||
<demo-ctrl-actions class="flex gap-1.5">
|
||||
<button
|
||||
type="button"
|
||||
class="play cursor-pointer rounded-md border border-gray-300 bg-white px-3 py-1 text-[13px]"
|
||||
>
|
||||
Play
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="pause cursor-pointer rounded-md border border-gray-300 bg-white px-3 py-1 text-[13px]"
|
||||
>
|
||||
Pause
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="volume cursor-pointer rounded-md border border-gray-300 bg-white px-3 py-1 text-[13px]"
|
||||
>
|
||||
50% Volume
|
||||
</button>
|
||||
</demo-ctrl-actions>
|
||||
<demo-ctrl-state class="text-[13px] text-gray-700 tabular-nums">
|
||||
<span class="text">Paused: Yes | Time: 0.0s | Volume: 100%</span>
|
||||
</demo-ctrl-state>
|
||||
</div>
|
||||
</media-container>
|
||||
</demo-ctrl-player>
|
||||
@@ -0,0 +1,78 @@
|
||||
import {
|
||||
applyElementProps,
|
||||
createButton,
|
||||
createPlayer,
|
||||
MediaElement,
|
||||
PlayerController,
|
||||
selectPlayback,
|
||||
selectTime,
|
||||
selectVolume,
|
||||
} from '@videojs/html';
|
||||
import { videoFeatures } from '@videojs/html/video';
|
||||
import '@videojs/html/media/container';
|
||||
|
||||
const { ProviderMixin, context } = createPlayer({
|
||||
features: videoFeatures,
|
||||
});
|
||||
|
||||
class DemoPlayer extends ProviderMixin(MediaElement) {
|
||||
static readonly tagName = 'demo-ctrl-player';
|
||||
}
|
||||
|
||||
class PlayerActions extends MediaElement {
|
||||
static readonly tagName = 'demo-ctrl-actions';
|
||||
|
||||
readonly #player = new PlayerController(this, context);
|
||||
|
||||
#disconnect: AbortController | null = null;
|
||||
|
||||
override connectedCallback(): void {
|
||||
super.connectedCallback();
|
||||
this.#disconnect = new AbortController();
|
||||
const signal = this.#disconnect.signal;
|
||||
|
||||
const playBtn = this.querySelector<HTMLButtonElement>('.play')!;
|
||||
const pauseBtn = this.querySelector<HTMLButtonElement>('.pause')!;
|
||||
const volumeBtn = this.querySelector<HTMLButtonElement>('.volume')!;
|
||||
|
||||
const bind = (el: HTMLElement, action: () => void) => {
|
||||
const props = createButton({ onActivate: action, isDisabled: () => !this.#player.value });
|
||||
applyElementProps(el, props, { signal });
|
||||
};
|
||||
|
||||
bind(playBtn, () => this.#player.value?.play());
|
||||
bind(pauseBtn, () => this.#player.value?.pause());
|
||||
bind(volumeBtn, () => this.#player.value?.setVolume(0.5));
|
||||
}
|
||||
|
||||
override disconnectedCallback(): void {
|
||||
super.disconnectedCallback();
|
||||
this.#disconnect?.abort();
|
||||
this.#disconnect = null;
|
||||
}
|
||||
}
|
||||
|
||||
class PlayerState extends MediaElement {
|
||||
static readonly tagName = 'demo-ctrl-state';
|
||||
|
||||
readonly #playback = new PlayerController(this, context, selectPlayback);
|
||||
readonly #time = new PlayerController(this, context, selectTime);
|
||||
readonly #volume = new PlayerController(this, context, selectVolume);
|
||||
|
||||
protected override update(changed: Map<string, unknown>): void {
|
||||
super.update(changed);
|
||||
const playback = this.#playback.value;
|
||||
const time = this.#time.value;
|
||||
const volume = this.#volume.value;
|
||||
if (!playback) return;
|
||||
|
||||
const el = this.querySelector('.text');
|
||||
if (el) {
|
||||
el.textContent = `Paused: ${playback.paused ? 'Yes' : 'No'} | Time: ${(time?.currentTime ?? 0).toFixed(1)}s | Volume: ${Math.round((volume?.volume ?? 0) * 100)}%`;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define(DemoPlayer.tagName, DemoPlayer);
|
||||
customElements.define(PlayerActions.tagName, PlayerActions);
|
||||
customElements.define(PlayerState.tagName, PlayerState);
|
||||
@@ -0,0 +1,68 @@
|
||||
import { renderElement } from '@videojs/react';
|
||||
import { type ReactNode, useState } from 'react';
|
||||
|
||||
interface TagState {
|
||||
active: boolean;
|
||||
}
|
||||
|
||||
function Tag({
|
||||
className,
|
||||
style,
|
||||
render,
|
||||
active,
|
||||
children,
|
||||
}: renderElement.ComponentProps<TagState> & { active: boolean; children?: ReactNode }) {
|
||||
const state: TagState = { active };
|
||||
|
||||
return renderElement(
|
||||
'span',
|
||||
{ className, style, render },
|
||||
{
|
||||
state,
|
||||
props: { children },
|
||||
stateAttrMap: { active: 'data-active' },
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
export default function BasicUsage() {
|
||||
const [active, setActive] = useState(false);
|
||||
|
||||
const className = (state: TagState) =>
|
||||
state.active
|
||||
? 'inline-flex items-center rounded-full bg-blue-500 px-3 py-1.5 text-white transition-all duration-200'
|
||||
: 'inline-flex items-center rounded-full bg-gray-200 px-3 py-1.5 text-gray-700 transition-all duration-200';
|
||||
|
||||
const style = (state: TagState) => ({
|
||||
fontSize: state.active ? '1.125rem' : '0.875rem',
|
||||
});
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-4 p-4">
|
||||
<button
|
||||
type="button"
|
||||
className="cursor-pointer self-start rounded-md border border-gray-300 bg-neutral-100 px-4 py-1.5"
|
||||
onClick={() => setActive((prev) => !prev)}
|
||||
>
|
||||
{active ? 'Deactivate' : 'Activate'}
|
||||
</button>
|
||||
|
||||
<div className="flex flex-wrap gap-3">
|
||||
<Tag active={active} className={className} style={style}>
|
||||
Default <span>
|
||||
</Tag>
|
||||
|
||||
<Tag active={active} className={className} style={style} render={<strong />}>
|
||||
Element <strong>
|
||||
</Tag>
|
||||
|
||||
<Tag
|
||||
active={active}
|
||||
className={className}
|
||||
style={style}
|
||||
render={(props, state) => <em {...props}>{state.active ? 'Active!' : 'Inactive'}</em>}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
import { useButton } from '@videojs/react';
|
||||
import type { Ref } from 'react';
|
||||
import { useState } from 'react';
|
||||
|
||||
export default function BasicUsage() {
|
||||
const [count, setCount] = useState(0);
|
||||
const [disabled, setDisabled] = useState(false);
|
||||
|
||||
const { getButtonProps, buttonRef } = useButton({
|
||||
displayName: 'ActivateButton',
|
||||
onActivate: () => setCount((c) => c + 1),
|
||||
isDisabled: () => disabled,
|
||||
});
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-3 p-4">
|
||||
<button
|
||||
ref={buttonRef as Ref<HTMLButtonElement>}
|
||||
{...getButtonProps()}
|
||||
className="cursor-pointer self-start rounded-md border border-gray-300 bg-neutral-100 px-5 py-2 tabular-nums transition-opacity duration-200 disabled:cursor-not-allowed disabled:opacity-50"
|
||||
disabled={disabled}
|
||||
>
|
||||
Activated {count} times
|
||||
</button>
|
||||
<label className="flex items-center gap-1.5 text-sm text-gray-500">
|
||||
<input type="checkbox" checked={disabled} onChange={(e) => setDisabled(e.target.checked)} />
|
||||
Disabled
|
||||
</label>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
import { createPlayer } from '@videojs/react';
|
||||
import { Video, videoFeatures } from '@videojs/react/video';
|
||||
|
||||
const Player = createPlayer({
|
||||
features: videoFeatures,
|
||||
});
|
||||
|
||||
function StateDisplay() {
|
||||
const state = Player.usePlayer((s) => ({
|
||||
paused: s.paused,
|
||||
currentTime: s.currentTime,
|
||||
duration: s.duration,
|
||||
}));
|
||||
|
||||
return (
|
||||
<dl className="m-0 flex gap-4 border-t border-black/10 bg-black/5 p-3 text-[13px]">
|
||||
<div className="flex gap-2">
|
||||
<dt className="text-gray-500">Paused</dt>
|
||||
<dd className="m-0 tabular-nums">{String(state.paused)}</dd>
|
||||
</div>
|
||||
<div className="flex gap-2">
|
||||
<dt className="text-gray-500">Time</dt>
|
||||
<dd className="m-0 tabular-nums">
|
||||
{state.currentTime.toFixed(1)}s / {state.duration.toFixed(1)}s
|
||||
</dd>
|
||||
</div>
|
||||
</dl>
|
||||
);
|
||||
}
|
||||
|
||||
export default function Selector() {
|
||||
return (
|
||||
<Player.Provider>
|
||||
<Player.Container className="relative">
|
||||
<Video
|
||||
className="w-full"
|
||||
src="https://stream.mux.com/BV3YZtogl89mg9VcNBhhnHm02Y34zI1nlMuMQfAbl3dM/highest.mp4"
|
||||
autoPlay
|
||||
muted
|
||||
playsInline
|
||||
loop
|
||||
/>
|
||||
<StateDisplay />
|
||||
</Player.Container>
|
||||
</Player.Provider>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
import { createPlayer } from '@videojs/react';
|
||||
import { Video, videoFeatures } from '@videojs/react/video';
|
||||
|
||||
const Player = createPlayer({
|
||||
features: videoFeatures,
|
||||
});
|
||||
|
||||
function Controls() {
|
||||
const store = Player.usePlayer();
|
||||
|
||||
return (
|
||||
<div className="flex gap-1.5 border-t border-black/10 bg-black/5 p-3">
|
||||
<button
|
||||
type="button"
|
||||
className="cursor-pointer rounded-md border border-gray-300 bg-white px-3 py-1 text-[13px]"
|
||||
onClick={() => store.play()}
|
||||
>
|
||||
Play
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className="cursor-pointer rounded-md border border-gray-300 bg-white px-3 py-1 text-[13px]"
|
||||
onClick={() => store.pause()}
|
||||
>
|
||||
Pause
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default function StoreAccess() {
|
||||
return (
|
||||
<Player.Provider>
|
||||
<Player.Container className="relative">
|
||||
<Video
|
||||
className="w-full"
|
||||
src="https://stream.mux.com/BV3YZtogl89mg9VcNBhhnHm02Y34zI1nlMuMQfAbl3dM/highest.mp4"
|
||||
autoPlay
|
||||
muted
|
||||
playsInline
|
||||
loop
|
||||
/>
|
||||
<Controls />
|
||||
</Player.Container>
|
||||
</Player.Provider>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
import { createPlayer, useStore } from '@videojs/react';
|
||||
import { Video, videoFeatures } from '@videojs/react/video';
|
||||
|
||||
const Player = createPlayer({
|
||||
features: videoFeatures,
|
||||
});
|
||||
|
||||
function DerivedState() {
|
||||
const store = Player.usePlayer();
|
||||
const derived = useStore(store, (s) => ({
|
||||
remaining: s.duration - s.currentTime,
|
||||
progress: s.duration > 0 ? (s.currentTime / s.duration) * 100 : 0,
|
||||
}));
|
||||
|
||||
return (
|
||||
<dl className="m-0 flex gap-4 border-t border-black/10 bg-black/5 p-3 text-[13px]">
|
||||
<div className="flex gap-2">
|
||||
<dt className="text-gray-500">Remaining</dt>
|
||||
<dd className="m-0 tabular-nums">{derived.remaining.toFixed(1)}s</dd>
|
||||
</div>
|
||||
<div className="flex gap-2">
|
||||
<dt className="text-gray-500">Progress</dt>
|
||||
<dd className="m-0 tabular-nums">{derived.progress.toFixed(1)}%</dd>
|
||||
</div>
|
||||
</dl>
|
||||
);
|
||||
}
|
||||
|
||||
export default function Selector() {
|
||||
return (
|
||||
<Player.Provider>
|
||||
<Player.Container className="relative">
|
||||
<Video
|
||||
className="w-full"
|
||||
src="https://stream.mux.com/BV3YZtogl89mg9VcNBhhnHm02Y34zI1nlMuMQfAbl3dM/highest.mp4"
|
||||
autoPlay
|
||||
muted
|
||||
playsInline
|
||||
loop
|
||||
/>
|
||||
<DerivedState />
|
||||
</Player.Container>
|
||||
</Player.Provider>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
import { createPlayer, useStore } from '@videojs/react';
|
||||
import { Video, videoFeatures } from '@videojs/react/video';
|
||||
|
||||
const Player = createPlayer({
|
||||
features: videoFeatures,
|
||||
});
|
||||
|
||||
function SeekControls() {
|
||||
const store = Player.usePlayer();
|
||||
const s = useStore(store);
|
||||
|
||||
return (
|
||||
<div className="flex gap-1.5 border-t border-black/10 bg-black/5 p-3">
|
||||
<button
|
||||
type="button"
|
||||
className="cursor-pointer rounded-md border border-gray-300 bg-white px-3 py-1 text-[13px]"
|
||||
onClick={() => s.seek(0)}
|
||||
>
|
||||
Go to start
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className="cursor-pointer rounded-md border border-gray-300 bg-white px-3 py-1 text-[13px]"
|
||||
onClick={() => s.seek(s.state.duration / 2)}
|
||||
>
|
||||
Go to middle
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default function StoreAccess() {
|
||||
return (
|
||||
<Player.Provider>
|
||||
<Player.Container className="relative">
|
||||
<Video
|
||||
className="w-full"
|
||||
src="https://stream.mux.com/BV3YZtogl89mg9VcNBhhnHm02Y34zI1nlMuMQfAbl3dM/highest.mp4"
|
||||
autoPlay
|
||||
muted
|
||||
playsInline
|
||||
loop
|
||||
/>
|
||||
<SeekControls />
|
||||
</Player.Container>
|
||||
</Player.Provider>
|
||||
);
|
||||
}
|
||||
@@ -10,6 +10,8 @@ import StyleCase from "@/components/docs/StyleCase.astro";
|
||||
import BasicUsageDemoReact from "@/components/docs/demos/create-player/react/css/BasicUsage";
|
||||
import basicUsageReactTsx from "@/components/docs/demos/create-player/react/css/BasicUsage.tsx?raw";
|
||||
import basicUsageReactCss from "@/components/docs/demos/create-player/react/css/BasicUsage.css?raw";
|
||||
import BasicUsageDemoReactTailwind from "@/components/docs/demos/create-player/react/tailwind/BasicUsage";
|
||||
import basicUsageReactTailwindTsx from "@/components/docs/demos/create-player/react/tailwind/BasicUsage.tsx?raw";
|
||||
|
||||
`createPlayer` is the entry point for setting up a Video.js player in React. It accepts a configuration object with a `features` array and returns hooks and components for building a player.
|
||||
|
||||
@@ -38,6 +40,15 @@ const Player = createPlayer({ features: videoFeatures });
|
||||
<BasicUsageDemoReact client:idle />
|
||||
</Demo>
|
||||
</StyleCase>
|
||||
<StyleCase styles={["tailwind"]}>
|
||||
<Demo
|
||||
files={[
|
||||
{ title: "App.tsx", code: basicUsageReactTailwindTsx, lang: "tsx" },
|
||||
]}
|
||||
>
|
||||
<BasicUsageDemoReactTailwind client:idle />
|
||||
</Demo>
|
||||
</StyleCase>
|
||||
</FrameworkCase>
|
||||
|
||||
<UtilReference util="createPlayer" />
|
||||
|
||||
@@ -11,6 +11,9 @@ import BasicUsageDemoHtml from "@/components/docs/demos/html-create-player/html/
|
||||
import basicUsageHtml from "@/components/docs/demos/html-create-player/html/css/BasicUsage.html?raw";
|
||||
import basicUsageHtmlCss from "@/components/docs/demos/html-create-player/html/css/BasicUsage.css?raw";
|
||||
import basicUsageHtmlTs from "@/components/docs/demos/html-create-player/html/css/BasicUsage.ts?raw";
|
||||
import BasicUsageDemoHtmlTailwind from "@/components/docs/demos/html-create-player/html/tailwind/BasicUsage.astro";
|
||||
import basicUsageTailwindHtml from "@/components/docs/demos/html-create-player/html/tailwind/BasicUsage.html?raw";
|
||||
import basicUsageTailwindHtmlTs from "@/components/docs/demos/html-create-player/html/tailwind/BasicUsage.ts?raw";
|
||||
|
||||
`createPlayer` is the entry point for setting up a Video.js player with HTML custom elements. It accepts a configuration object with a `features` array and returns a typed <DocsLink slug="reference/player-controller">`PlayerController`</DocsLink>, `context`, `ProviderMixin`, `ContainerMixin`, and a `create` store factory.
|
||||
|
||||
@@ -71,4 +74,13 @@ class ComposedPlayer extends ProviderMixin(ContainerMixin(MediaElement)) {}
|
||||
</Demo>
|
||||
</StyleCase>
|
||||
|
||||
<StyleCase styles={["tailwind"]}>
|
||||
<Demo files={[
|
||||
{ title: "index.html", code: basicUsageTailwindHtml, lang: "html" },
|
||||
{ title: "index.ts", code: basicUsageTailwindHtmlTs, lang: "ts" },
|
||||
]}>
|
||||
<BasicUsageDemoHtmlTailwind />
|
||||
</Demo>
|
||||
</StyleCase>
|
||||
|
||||
<UtilReference util="createPlayer" slug="html-create-player" />
|
||||
|
||||
@@ -12,6 +12,9 @@ import BasicUsageDemoHtml from "@/components/docs/demos/player-controller/html/c
|
||||
import basicUsageHtml from "@/components/docs/demos/player-controller/html/css/BasicUsage.html?raw";
|
||||
import basicUsageHtmlCss from "@/components/docs/demos/player-controller/html/css/BasicUsage.css?raw";
|
||||
import basicUsageHtmlTs from "@/components/docs/demos/player-controller/html/css/BasicUsage.ts?raw";
|
||||
import BasicUsageDemoHtmlTailwind from "@/components/docs/demos/player-controller/html/tailwind/BasicUsage.astro";
|
||||
import basicUsageTailwindHtml from "@/components/docs/demos/player-controller/html/tailwind/BasicUsage.html?raw";
|
||||
import basicUsageTailwindHtmlTs from "@/components/docs/demos/player-controller/html/tailwind/BasicUsage.ts?raw";
|
||||
|
||||
`PlayerController` is a reactive controller that consumes the player store from <DocsLink slug="reference/player-context">`playerContext`</DocsLink>. Without a selector it returns the store instance directly (no subscription — use this for actions). With a selector it returns the selected value and subscribes to changes, triggering a host update on shallow-equal change. Access the current value via `.value`, which returns `undefined` until connected to a provider.
|
||||
|
||||
@@ -31,6 +34,16 @@ import basicUsageHtmlTs from "@/components/docs/demos/player-controller/html/css
|
||||
<BasicUsageDemoHtml />
|
||||
</Demo>
|
||||
</StyleCase>
|
||||
<StyleCase styles={["tailwind"]}>
|
||||
<Demo
|
||||
files={[
|
||||
{ title: "index.html", code: basicUsageTailwindHtml, lang: "html" },
|
||||
{ title: "index.ts", code: basicUsageTailwindHtmlTs, lang: "ts" },
|
||||
]}
|
||||
>
|
||||
<BasicUsageDemoHtmlTailwind />
|
||||
</Demo>
|
||||
</StyleCase>
|
||||
</FrameworkCase>
|
||||
|
||||
<UtilReference util="PlayerController" />
|
||||
|
||||
@@ -11,6 +11,8 @@ import StyleCase from "@/components/docs/StyleCase.astro";
|
||||
import BasicUsageDemoReact from "@/components/docs/demos/render-element/react/css/BasicUsage";
|
||||
import basicUsageReactTsx from "@/components/docs/demos/render-element/react/css/BasicUsage.tsx?raw";
|
||||
import basicUsageReactCss from "@/components/docs/demos/render-element/react/css/BasicUsage.css?raw";
|
||||
import BasicUsageDemoReactTailwind from "@/components/docs/demos/render-element/react/tailwind/BasicUsage";
|
||||
import basicUsageReactTailwindTsx from "@/components/docs/demos/render-element/react/tailwind/BasicUsage.tsx?raw";
|
||||
|
||||
`renderElement` renders a UI component element, handling default tag rendering, render props (element or function), props merging, ref composition, and state-driven `className`/`style`.
|
||||
|
||||
@@ -66,6 +68,15 @@ The `render` prop lets consumers fully customize the rendered element while pres
|
||||
<BasicUsageDemoReact client:idle />
|
||||
</Demo>
|
||||
</StyleCase>
|
||||
<StyleCase styles={["tailwind"]}>
|
||||
<Demo
|
||||
files={[
|
||||
{ title: "App.tsx", code: basicUsageReactTailwindTsx, lang: "tsx" },
|
||||
]}
|
||||
>
|
||||
<BasicUsageDemoReactTailwind client:idle />
|
||||
</Demo>
|
||||
</StyleCase>
|
||||
</FrameworkCase>
|
||||
|
||||
<UtilReference util="renderElement" />
|
||||
|
||||
@@ -11,6 +11,8 @@ import StyleCase from "@/components/docs/StyleCase.astro";
|
||||
import BasicUsageDemoReact from "@/components/docs/demos/use-button/react/css/BasicUsage";
|
||||
import basicUsageReactTsx from "@/components/docs/demos/use-button/react/css/BasicUsage.tsx?raw";
|
||||
import basicUsageReactCss from "@/components/docs/demos/use-button/react/css/BasicUsage.css?raw";
|
||||
import BasicUsageDemoReactTailwind from "@/components/docs/demos/use-button/react/tailwind/BasicUsage";
|
||||
import basicUsageReactTailwindTsx from "@/components/docs/demos/use-button/react/tailwind/BasicUsage.tsx?raw";
|
||||
|
||||
`useButton` provides button behavior including keyboard activation and accessibility checks. It returns a `getButtonProps` function for spreading onto a `<button>` element and a `buttonRef` for validation.
|
||||
|
||||
@@ -31,6 +33,15 @@ import basicUsageReactCss from "@/components/docs/demos/use-button/react/css/Bas
|
||||
<BasicUsageDemoReact client:idle />
|
||||
</Demo>
|
||||
</StyleCase>
|
||||
<StyleCase styles={["tailwind"]}>
|
||||
<Demo
|
||||
files={[
|
||||
{ title: "App.tsx", code: basicUsageReactTailwindTsx, lang: "tsx" },
|
||||
]}
|
||||
>
|
||||
<BasicUsageDemoReactTailwind client:idle />
|
||||
</Demo>
|
||||
</StyleCase>
|
||||
</FrameworkCase>
|
||||
|
||||
<UtilReference util="useButton" />
|
||||
|
||||
@@ -11,9 +11,13 @@ import StyleCase from "@/components/docs/StyleCase.astro";
|
||||
import StoreAccessDemoReact from "@/components/docs/demos/use-player/react/css/StoreAccess";
|
||||
import storeAccessReactTsx from "@/components/docs/demos/use-player/react/css/StoreAccess.tsx?raw";
|
||||
import storeAccessReactCss from "@/components/docs/demos/use-player/react/css/StoreAccess.css?raw";
|
||||
import StoreAccessDemoReactTailwind from "@/components/docs/demos/use-player/react/tailwind/StoreAccess";
|
||||
import storeAccessReactTailwindTsx from "@/components/docs/demos/use-player/react/tailwind/StoreAccess.tsx?raw";
|
||||
import SelectorDemoReact from "@/components/docs/demos/use-player/react/css/Selector";
|
||||
import selectorReactTsx from "@/components/docs/demos/use-player/react/css/Selector.tsx?raw";
|
||||
import selectorReactCss from "@/components/docs/demos/use-player/react/css/Selector.css?raw";
|
||||
import SelectorDemoReactTailwind from "@/components/docs/demos/use-player/react/tailwind/Selector";
|
||||
import selectorReactTailwindTsx from "@/components/docs/demos/use-player/react/tailwind/Selector.tsx?raw";
|
||||
|
||||
`Player.usePlayer` gives you access to the player store from any component within a `Player.Provider`. It has two overloads — without arguments for direct store access, or with a selector for reactive state subscriptions. `Player.usePlayer` is typed to the features you passed to <DocsLink slug="reference/create-player">`createPlayer`</DocsLink>. It's also available as a standalone import (`import { usePlayer } from '@videojs/react'`), but the standalone version returns an untyped store.
|
||||
|
||||
@@ -34,6 +38,15 @@ Call `Player.usePlayer()` without arguments to get the store instance. Use this
|
||||
<StoreAccessDemoReact client:idle />
|
||||
</Demo>
|
||||
</StyleCase>
|
||||
<StyleCase styles={["tailwind"]}>
|
||||
<Demo
|
||||
files={[
|
||||
{ title: "App.tsx", code: storeAccessReactTailwindTsx, lang: "tsx" },
|
||||
]}
|
||||
>
|
||||
<StoreAccessDemoReactTailwind client:idle />
|
||||
</Demo>
|
||||
</StyleCase>
|
||||
</FrameworkCase>
|
||||
|
||||
### Selector Subscription
|
||||
@@ -51,6 +64,15 @@ Pass a selector function to subscribe to specific state. The component re-render
|
||||
<SelectorDemoReact client:idle />
|
||||
</Demo>
|
||||
</StyleCase>
|
||||
<StyleCase styles={["tailwind"]}>
|
||||
<Demo
|
||||
files={[
|
||||
{ title: "App.tsx", code: selectorReactTailwindTsx, lang: "tsx" },
|
||||
]}
|
||||
>
|
||||
<SelectorDemoReactTailwind client:idle />
|
||||
</Demo>
|
||||
</StyleCase>
|
||||
</FrameworkCase>
|
||||
|
||||
<UtilReference util="usePlayer" />
|
||||
|
||||
@@ -11,9 +11,13 @@ import StyleCase from "@/components/docs/StyleCase.astro";
|
||||
import StoreAccessDemoReact from "@/components/docs/demos/use-store/react/css/StoreAccess";
|
||||
import storeAccessReactTsx from "@/components/docs/demos/use-store/react/css/StoreAccess.tsx?raw";
|
||||
import storeAccessReactCss from "@/components/docs/demos/use-store/react/css/StoreAccess.css?raw";
|
||||
import StoreAccessDemoReactTailwind from "@/components/docs/demos/use-store/react/tailwind/StoreAccess";
|
||||
import storeAccessReactTailwindTsx from "@/components/docs/demos/use-store/react/tailwind/StoreAccess.tsx?raw";
|
||||
import SelectorDemoReact from "@/components/docs/demos/use-store/react/css/Selector";
|
||||
import selectorReactTsx from "@/components/docs/demos/use-store/react/css/Selector.tsx?raw";
|
||||
import selectorReactCss from "@/components/docs/demos/use-store/react/css/Selector.css?raw";
|
||||
import SelectorDemoReactTailwind from "@/components/docs/demos/use-store/react/tailwind/Selector";
|
||||
import selectorReactTailwindTsx from "@/components/docs/demos/use-store/react/tailwind/Selector.tsx?raw";
|
||||
|
||||
`useStore` subscribes to a store instance directly. It has the same two overloads as <DocsLink slug="reference/use-player">`usePlayer`</DocsLink> — without a selector for store access, or with a selector for reactive state. Within a player provider, `usePlayer` is usually simpler since it reads the store from context. Reach for `useStore` when you have a store instance directly or need to derive computed values from the store.
|
||||
|
||||
@@ -34,6 +38,15 @@ Call `useStore(store)` without a selector to get the store instance back for imp
|
||||
<StoreAccessDemoReact client:idle />
|
||||
</Demo>
|
||||
</StyleCase>
|
||||
<StyleCase styles={["tailwind"]}>
|
||||
<Demo
|
||||
files={[
|
||||
{ title: "App.tsx", code: storeAccessReactTailwindTsx, lang: "tsx" },
|
||||
]}
|
||||
>
|
||||
<StoreAccessDemoReactTailwind client:idle />
|
||||
</Demo>
|
||||
</StyleCase>
|
||||
</FrameworkCase>
|
||||
|
||||
### Selector Subscription
|
||||
@@ -51,6 +64,15 @@ Pass a selector to derive and subscribe to computed values from the store. The c
|
||||
<SelectorDemoReact client:idle />
|
||||
</Demo>
|
||||
</StyleCase>
|
||||
<StyleCase styles={["tailwind"]}>
|
||||
<Demo
|
||||
files={[
|
||||
{ title: "App.tsx", code: selectorReactTailwindTsx, lang: "tsx" },
|
||||
]}
|
||||
>
|
||||
<SelectorDemoReactTailwind client:idle />
|
||||
</Demo>
|
||||
</StyleCase>
|
||||
</FrameworkCase>
|
||||
|
||||
<UtilReference util="useStore" />
|
||||
|
||||
Reference in New Issue
Block a user