From 5c422452e4b547dc00f13082b755ea12d1860f21 Mon Sep 17 00:00:00 2001 From: Wesley Luyten Date: Wed, 4 Mar 2026 16:05:20 -0600 Subject: [PATCH] feat: small state and naming fixes (#719) --- .claude/plans/slider.md | 6 ++--- .claude/plans/store/feature-api-redesign.md | 4 ++-- .claude/plans/store/store-v2.md | 4 ++-- .claude/skills/docs/patterns/code-examples.md | 2 +- internal/design/ui/slider/architecture.md | 2 +- internal/design/ui/slider/parts.md | 2 +- packages/core/src/core/media/state.ts | 8 +++---- .../core/ui/mute-button/mute-button-core.ts | 2 +- .../tests/mute-button-core.test.ts | 10 ++++---- .../src/core/ui/pip-button/pip-button-core.ts | 4 ++-- .../pip-button/tests/pip-button-core.test.ts | 18 +++++++-------- .../tests/volume-slider-core.test.ts | 4 ++-- .../core/src/dom/presentation/fullscreen.ts | 8 +++---- packages/core/src/dom/presentation/pip.ts | 10 ++++---- .../core/src/dom/store/features/fullscreen.ts | 18 +++++++-------- packages/core/src/dom/store/features/pip.ts | 23 +++++++++++-------- .../core/src/dom/store/features/playback.ts | 1 + .../src/dom/store/features/tests/pip.test.ts | 16 ++++++------- .../dom/store/features/tests/volume.test.ts | 14 +++++------ .../core/src/dom/store/features/volume.ts | 4 ++-- .../ui/volume-slider/volume-slider-element.ts | 2 +- .../tests/volume-slider.test.tsx | 4 ++-- .../ui/volume-slider/volume-slider-root.tsx | 8 +++---- packages/store/README.md | 6 ++--- .../player-controller/html/css/BasicUsage.ts | 2 +- 25 files changed, 94 insertions(+), 88 deletions(-) diff --git a/.claude/plans/slider.md b/.claude/plans/slider.md index 1bd10ec3..29df41d5 100644 --- a/.claude/plans/slider.md +++ b/.claude/plans/slider.md @@ -770,7 +770,7 @@ All generic parts consume `SliderContext` for data attributes. Each is a thin `f - **Feature guard:** `if (!volume) { if (__DEV__) logMissingFeature('VolumeSlider', 'volume'); return null; }` - Lazy-inits `VolumeSliderCore`, calls `setProps`. - Calls `useSlider` with `computeState = (interaction) => core.getVolumeState(interaction, mediaState)`. -- `onValueChange`: calls `mediaState.changeVolume(core.valueFromPercent(percent) / 100)` immediately. Volume changes are cheap and instant — no throttle. +- `onValueChange`: calls `mediaState.setVolume(core.valueFromPercent(percent) / 100)` immediately. Volume changes are cheap and instant — no throttle. - `onValueCommit`: same as `onValueChange` (no separate commit behavior for volume). - Provides `SliderContext` with percentage formatting: `formatValue = (value) => Math.round(value) + '%'`. - Uses `SliderDataAttrs` for state-to-data-attr mapping. @@ -887,7 +887,7 @@ Update `packages/sandbox/src/react/main.tsx` (or create a new route) with a work - Renders with mocked player store. - Fill is 0 when muted. -- `changeVolume` called on value change. +- `setVolume` called on value change. - `aria-valuetext` includes muted state. ### 3.9 Verify @@ -1250,7 +1250,7 @@ export class VolumeSliderElement extends MediaElement { super.connectedCallback(); // 1. Create AbortController // 2. Create slider via createSlider({ getElement: () => this, ... }) - // 3. onValueChange: volumeState.changeVolume(percent / 100) + // 3. onValueChange: volumeState.setVolume(percent / 100) // 4. Apply rootProps via applyElementProps(this, rootProps, signal) // 5. Subscribe: slider.interaction.subscribe(() => this.requestUpdate(), { signal }) // 6. Provide sliderContext via ContextProvider diff --git a/.claude/plans/store/feature-api-redesign.md b/.claude/plans/store/feature-api-redesign.md index b09bd46a..bde0a016 100644 --- a/.claude/plans/store/feature-api-redesign.md +++ b/.claude/plans/store/feature-api-redesign.md @@ -165,7 +165,7 @@ export const volumeFeature = defineFeature()({ state: ({ task }) => ({ volume: 1, muted: false, - changeVolume(volume: number) { + setVolume(volume: number) { return task({ key: 'volume', handler: ({ target }) => { target.volume = volume; } }); }, }), @@ -180,7 +180,7 @@ export const volumeFeature = defineFeature()({ state: ({ task, target }) => ({ volume: 1, muted: false, - changeVolume(volume: number) { + setVolume(volume: number) { return task({ key: 'volume', handler: () => { target().volume = volume; } }); }, }), diff --git a/.claude/plans/store/store-v2.md b/.claude/plans/store/store-v2.md index 5ed20986..aaf053b5 100644 --- a/.claude/plans/store/store-v2.md +++ b/.claude/plans/store/store-v2.md @@ -978,7 +978,7 @@ const audioFeature = defineFeature()( }, // Action - sync - toggleMute() { + toggleMuted() { const t = target(); t.muted = !t.muted; }, @@ -1019,7 +1019,7 @@ store.pause() // Promise // Actions - sync (no Promise) store.seek(30) // void store.setVolume(0.5) // void -store.toggleMute() // void +store.toggleMuted() // void // Actions - state only store.getEffectiveVolume() // number diff --git a/.claude/skills/docs/patterns/code-examples.md b/.claude/skills/docs/patterns/code-examples.md index 2b3ab74b..850eba31 100644 --- a/.claude/skills/docs/patterns/code-examples.md +++ b/.claude/skills/docs/patterns/code-examples.md @@ -60,7 +60,7 @@ const store: Store = createStore; -// { volume: number; muted: boolean; setVolume: ...; toggleMute: ... } +// { volume: number; muted: boolean; setVolume: ...; toggleMuted: ... } ``` ### Show Type Imports diff --git a/internal/design/ui/slider/architecture.md b/internal/design/ui/slider/architecture.md index b3f43eb8..12b71d9e 100644 --- a/internal/design/ui/slider/architecture.md +++ b/internal/design/ui/slider/architecture.md @@ -536,7 +536,7 @@ The `SliderCSSVars` constant stays in `@videojs/core` (alongside `SliderDataAttr | --- | --- | --- | | `dragging`, `pointing`, `focused`, `pointerPercent`, `dragPercent` | `interaction: State` | Managed internally. UI subscribes and reads `.current`. | | `onValueChange(percent)` | React: `props.onValueChange(value)` / HTML: `CustomEvent('value-change')` | Generic root exposes. Domain roots handle internally. | -| `onValueCommit(percent)` | React: `props.onValueCommit(value)` / HTML: `CustomEvent('value-commit')` | Generic root exposes. Domain roots handle internally (`time.seek()`, `volume.changeVolume()`). | +| `onValueCommit(percent)` | React: `props.onValueCommit(value)` / HTML: `CustomEvent('value-commit')` | Generic root exposes. Domain roots handle internally (`time.seek()`, `volume.setVolume()`). | | Drag start/end | React: `props.onDragStart()` / HTML: `CustomEvent('drag-start')` | All roots expose. Controls feature uses to pause auto-hide. | ### User-Facing Surface by Component diff --git a/internal/design/ui/slider/parts.md b/internal/design/ui/slider/parts.md index 98b12710..0f0297ca 100644 --- a/internal/design/ui/slider/parts.md +++ b/internal/design/ui/slider/parts.md @@ -608,7 +608,7 @@ These attributes are provided by Root to the Thumb element via context: #### Behavior -- `VolumeSlider.Root` calls `volume.changeVolume(value / 100)` from `onValueChange` — every pointermove and keyboard step triggers an immediate volume update. `onValueCommit` is not used; volume changes are cheap and instant, so there's no need for a separate commit step or throttling. +- `VolumeSlider.Root` calls `volume.setVolume(value / 100)` from `onValueChange` — every pointermove and keyboard step triggers an immediate volume update. `onValueCommit` is not used; volume changes are cheap and instant, so there's no need for a separate commit step or throttling. - When muted, fill shows 0% but `aria-valuenow` shows the actual volume. `aria-valuetext` communicates both: `"75 percent, muted"`. #### Events (HTML) diff --git a/packages/core/src/core/media/state.ts b/packages/core/src/core/media/state.ts index 6c7ae778..d3d32074 100644 --- a/packages/core/src/core/media/state.ts +++ b/packages/core/src/core/media/state.ts @@ -62,13 +62,13 @@ export interface MediaVolumeState { * * @see https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/volume */ - changeVolume(volume: number): number; + setVolume(volume: number): number; /** * Toggle mute state. Returns the new muted value. * * @see https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/muted */ - toggleMute(): boolean; + toggleMuted(): boolean; } export interface MediaTimeState { @@ -224,11 +224,11 @@ export interface MediaPictureInPictureState { * * @see https://developer.mozilla.org/en-US/docs/Web/API/HTMLVideoElement/requestPictureInPicture */ - requestPiP(): Promise; + requestPictureInPicture(): Promise; /** * Exit picture-in-picture mode. * * @see https://developer.mozilla.org/en-US/docs/Web/API/Document/exitPictureInPicture */ - exitPiP(): Promise; + exitPictureInPicture(): Promise; } diff --git a/packages/core/src/core/ui/mute-button/mute-button-core.ts b/packages/core/src/core/ui/mute-button/mute-button-core.ts index 28c22d95..1cdc702f 100644 --- a/packages/core/src/core/ui/mute-button/mute-button-core.ts +++ b/packages/core/src/core/ui/mute-button/mute-button-core.ts @@ -75,7 +75,7 @@ export class MuteButtonCore { toggle(media: MediaVolumeState): void { if (this.#props.disabled) return; - media.toggleMute(); + media.toggleMuted(); } } diff --git a/packages/core/src/core/ui/mute-button/tests/mute-button-core.test.ts b/packages/core/src/core/ui/mute-button/tests/mute-button-core.test.ts index 9b620ad2..23d25ef4 100644 --- a/packages/core/src/core/ui/mute-button/tests/mute-button-core.test.ts +++ b/packages/core/src/core/ui/mute-button/tests/mute-button-core.test.ts @@ -9,8 +9,8 @@ function createMediaState(overrides: Partial = {}): MediaVolum volume: 1, muted: false, volumeAvailability: 'available', - changeVolume: vi.fn((v: number) => v), - toggleMute: vi.fn(() => false), + setVolume: vi.fn((v: number) => v), + toggleMuted: vi.fn(() => false), ...overrides, }; } @@ -108,18 +108,18 @@ describe('MuteButtonCore', () => { }); describe('toggle', () => { - it('calls toggleMute', () => { + it('calls toggleMuted', () => { const core = new MuteButtonCore(); const media = createMediaState(); core.toggle(media); - expect(media.toggleMute).toHaveBeenCalled(); + expect(media.toggleMuted).toHaveBeenCalled(); }); it('does nothing when disabled', () => { const core = new MuteButtonCore({ disabled: true }); const media = createMediaState(); core.toggle(media); - expect(media.toggleMute).not.toHaveBeenCalled(); + expect(media.toggleMuted).not.toHaveBeenCalled(); }); }); }); diff --git a/packages/core/src/core/ui/pip-button/pip-button-core.ts b/packages/core/src/core/ui/pip-button/pip-button-core.ts index a448182f..a3c9381e 100644 --- a/packages/core/src/core/ui/pip-button/pip-button-core.ts +++ b/packages/core/src/core/ui/pip-button/pip-button-core.ts @@ -71,9 +71,9 @@ export class PiPButtonCore { try { if (media.pip) { - await media.exitPiP(); + await media.exitPictureInPicture(); } else { - await media.requestPiP(); + await media.requestPictureInPicture(); } } catch { // PiP requests can fail (user gesture required, permissions, etc.) diff --git a/packages/core/src/core/ui/pip-button/tests/pip-button-core.test.ts b/packages/core/src/core/ui/pip-button/tests/pip-button-core.test.ts index 1982498e..070e317d 100644 --- a/packages/core/src/core/ui/pip-button/tests/pip-button-core.test.ts +++ b/packages/core/src/core/ui/pip-button/tests/pip-button-core.test.ts @@ -8,8 +8,8 @@ function createMediaState(overrides: Partial = {}): return { pip: false, pipAvailability: 'available', - requestPiP: vi.fn(async () => {}), - exitPiP: vi.fn(async () => {}), + requestPictureInPicture: vi.fn(async () => {}), + exitPictureInPicture: vi.fn(async () => {}), ...overrides, }; } @@ -82,38 +82,38 @@ describe('PiPButtonCore', () => { }); describe('toggle', () => { - it('calls requestPiP when not in PiP', async () => { + it('calls requestPictureInPicture when not in PiP', async () => { const core = new PiPButtonCore(); const media = createMediaState({ pip: false }); await core.toggle(media); - expect(media.requestPiP).toHaveBeenCalled(); + expect(media.requestPictureInPicture).toHaveBeenCalled(); }); - it('calls exitPiP when in PiP', async () => { + it('calls exitPictureInPicture when in PiP', async () => { const core = new PiPButtonCore(); const media = createMediaState({ pip: true }); await core.toggle(media); - expect(media.exitPiP).toHaveBeenCalled(); + expect(media.exitPictureInPicture).toHaveBeenCalled(); }); it('does nothing when disabled', async () => { const core = new PiPButtonCore({ disabled: true }); const media = createMediaState(); await core.toggle(media); - expect(media.requestPiP).not.toHaveBeenCalled(); + expect(media.requestPictureInPicture).not.toHaveBeenCalled(); }); it('does nothing when unsupported', async () => { const core = new PiPButtonCore(); const media = createMediaState({ pipAvailability: 'unsupported' }); await core.toggle(media); - expect(media.requestPiP).not.toHaveBeenCalled(); + expect(media.requestPictureInPicture).not.toHaveBeenCalled(); }); it('catches PiP errors silently', async () => { const core = new PiPButtonCore(); const media = createMediaState({ - requestPiP: vi.fn(async () => { + requestPictureInPicture: vi.fn(async () => { throw new Error('permission denied'); }), }); diff --git a/packages/core/src/core/ui/volume-slider/tests/volume-slider-core.test.ts b/packages/core/src/core/ui/volume-slider/tests/volume-slider-core.test.ts index c97ecfa7..7c06adeb 100644 --- a/packages/core/src/core/ui/volume-slider/tests/volume-slider-core.test.ts +++ b/packages/core/src/core/ui/volume-slider/tests/volume-slider-core.test.ts @@ -20,8 +20,8 @@ function createMediaState(overrides: Partial = {}): MediaVolum volume: 1, muted: false, volumeAvailability: 'available', - changeVolume: vi.fn((v: number) => v), - toggleMute: vi.fn(() => false), + setVolume: vi.fn((v: number) => v), + toggleMuted: vi.fn(() => false), ...overrides, }; } diff --git a/packages/core/src/dom/presentation/fullscreen.ts b/packages/core/src/dom/presentation/fullscreen.ts index e583cb98..8049d740 100644 --- a/packages/core/src/dom/presentation/fullscreen.ts +++ b/packages/core/src/dom/presentation/fullscreen.ts @@ -3,7 +3,7 @@ import { isFunction } from '@videojs/utils/predicate'; import type { WebKitDocument, WebKitFullscreenElement, WebKitVideoElement } from './types'; /** Check if the Fullscreen API is supported on this platform. */ -export function isFullscreenSupported(): boolean { +export function isFullscreenEnabled(): boolean { const doc = document as WebKitDocument; // Standard API or WebKit prefix @@ -27,7 +27,7 @@ export function getFullscreenElement(): Element | null { * * Uses `:fullscreen` pseudo-class which works across Shadow DOM boundaries. */ -export function isElementFullscreen(container: HTMLElement | null, media: HTMLMediaElement): boolean { +export function isFullscreenElement(container: HTMLElement | null, media: HTMLMediaElement): boolean { const video = media as WebKitVideoElement; // iOS Safari video-only fullscreen @@ -49,12 +49,12 @@ export function isElementFullscreen(container: HTMLElement | null, media: HTMLMe } /** - * Enter fullscreen mode. + * Request fullscreen mode. * * Tries container first (to show custom UI), falls back to media element * for platforms that only support video fullscreen (iOS Safari). */ -export async function enterFullscreen(container: HTMLElement | null, media: HTMLMediaElement): Promise { +export async function requestFullscreen(container: HTMLElement | null, media: HTMLMediaElement): Promise { const video = media as WebKitVideoElement; // Try container first (standard and WebKit APIs) diff --git a/packages/core/src/dom/presentation/pip.ts b/packages/core/src/dom/presentation/pip.ts index b39205b6..ba0a502c 100644 --- a/packages/core/src/dom/presentation/pip.ts +++ b/packages/core/src/dom/presentation/pip.ts @@ -7,7 +7,7 @@ import type { WebKitVideoElement } from './types'; * * Note: Safari PWAs don't support PiP even though the API exists. */ -export function isPiPSupported(): boolean { +export function isPictureInPictureEnabled(): boolean { // Check standard PiP API if (document.pictureInPictureEnabled) { // Safari PWAs have the API but it doesn't work @@ -24,7 +24,7 @@ export function isPiPSupported(): boolean { /** * Check if Picture-in-Picture is currently active for a media element. */ -export function isPiPActive(media: HTMLMediaElement): boolean { +export function isPictureInPictureElement(media: HTMLMediaElement): boolean { // Standard PiP API if (document.pictureInPictureElement === media) { return true; @@ -36,12 +36,12 @@ export function isPiPActive(media: HTMLMediaElement): boolean { } /** - * Enter Picture-in-Picture mode. + * Request Picture-in-Picture mode. * * Uses standard API where available, falls back to iOS Safari's * WebKit presentation mode. */ -export async function enterPiP(media: HTMLMediaElement): Promise { +export async function requestPictureInPicture(media: HTMLMediaElement): Promise { const video = media as HTMLVideoElement & WebKitVideoElement; // Standard PiP API (only available on HTMLVideoElement) @@ -65,7 +65,7 @@ export async function enterPiP(media: HTMLMediaElement): Promise { * Uses standard API where available, falls back to iOS Safari's * WebKit presentation mode. */ -export async function exitPiP(media?: HTMLMediaElement): Promise { +export async function exitPictureInPicture(media?: HTMLMediaElement): Promise { // Standard PiP API if (document.pictureInPictureElement && isFunction(document.exitPictureInPicture)) { await document.exitPictureInPicture(); diff --git a/packages/core/src/dom/store/features/fullscreen.ts b/packages/core/src/dom/store/features/fullscreen.ts index a07dfcdc..1227752a 100644 --- a/packages/core/src/dom/store/features/fullscreen.ts +++ b/packages/core/src/dom/store/features/fullscreen.ts @@ -3,12 +3,12 @@ import { listen } from '@videojs/utils/dom'; import type { MediaFullscreenState } from '../../../core/media/state'; import { definePlayerFeature } from '../../feature'; import { - enterFullscreen, exitFullscreen, - isElementFullscreen, - isFullscreenSupported, + isFullscreenElement, + isFullscreenEnabled, + requestFullscreen, } from '../../presentation/fullscreen'; -import { exitPiP, isPiPActive } from '../../presentation/pip'; +import { exitPictureInPicture, isPictureInPictureElement } from '../../presentation/pip'; import type { WebKitVideoElement } from '../../presentation/types'; export const fullscreenFeature = definePlayerFeature({ @@ -21,11 +21,11 @@ export const fullscreenFeature = definePlayerFeature({ const { media, container } = target(); // Exit PiP first if active (browser behavior is inconsistent) - if (isPiPActive(media)) { - await exitPiP(media); + if (isPictureInPictureElement(media)) { + await exitPictureInPicture(media); } - return enterFullscreen(container, media); + return requestFullscreen(container, media); }, async exitFullscreen() { @@ -37,12 +37,12 @@ export const fullscreenFeature = definePlayerFeature({ const { media, container } = target; set({ - fullscreenAvailability: isFullscreenSupported() ? 'available' : 'unsupported', + fullscreenAvailability: isFullscreenEnabled() ? 'available' : 'unsupported', }); const sync = () => set({ - fullscreen: isElementFullscreen(container, media), + fullscreen: isFullscreenElement(container, media), }); sync(); diff --git a/packages/core/src/dom/store/features/pip.ts b/packages/core/src/dom/store/features/pip.ts index 6236f8f0..14782a44 100644 --- a/packages/core/src/dom/store/features/pip.ts +++ b/packages/core/src/dom/store/features/pip.ts @@ -2,8 +2,13 @@ import { listen } from '@videojs/utils/dom'; import type { MediaPictureInPictureState } from '../../../core/media/state'; import { definePlayerFeature } from '../../feature'; -import { exitFullscreen, isElementFullscreen } from '../../presentation/fullscreen'; -import { enterPiP, exitPiP, isPiPActive, isPiPSupported } from '../../presentation/pip'; +import { exitFullscreen, isFullscreenElement } from '../../presentation/fullscreen'; +import { + exitPictureInPicture, + isPictureInPictureElement, + isPictureInPictureEnabled, + requestPictureInPicture, +} from '../../presentation/pip'; import type { WebKitVideoElement } from '../../presentation/types'; export const pipFeature = definePlayerFeature({ @@ -12,20 +17,20 @@ export const pipFeature = definePlayerFeature({ pip: false, pipAvailability: 'unavailable', - async requestPiP() { + async requestPictureInPicture() { const { media, container } = target(); // Exit fullscreen first if active - if (isElementFullscreen(container, media)) { + if (isFullscreenElement(container, media)) { await exitFullscreen(); } - return enterPiP(media); + return requestPictureInPicture(media); }, - async exitPiP() { + async exitPictureInPicture() { const { media } = target(); - return exitPiP(media); + return exitPictureInPicture(media); }, }), @@ -33,12 +38,12 @@ export const pipFeature = definePlayerFeature({ const { media } = target; set({ - pipAvailability: isPiPSupported() ? 'available' : 'unsupported', + pipAvailability: isPictureInPictureEnabled() ? 'available' : 'unsupported', }); const sync = () => set({ - pip: isPiPActive(media), + pip: isPictureInPictureElement(media), }); sync(); diff --git a/packages/core/src/dom/store/features/playback.ts b/packages/core/src/dom/store/features/playback.ts index deb40d78..7fe14bdb 100644 --- a/packages/core/src/dom/store/features/playback.ts +++ b/packages/core/src/dom/store/features/playback.ts @@ -31,6 +31,7 @@ export const playbackFeature = definePlayerFeature({ sync(); + listen(media, 'emptied', sync, { signal }); listen(media, 'play', sync, { signal }); listen(media, 'pause', sync, { signal }); listen(media, 'ended', sync, { signal }); diff --git a/packages/core/src/dom/store/features/tests/pip.test.ts b/packages/core/src/dom/store/features/tests/pip.test.ts index 38a1758f..0f617c14 100644 --- a/packages/core/src/dom/store/features/tests/pip.test.ts +++ b/packages/core/src/dom/store/features/tests/pip.test.ts @@ -85,19 +85,19 @@ describe('pipFeature', () => { }); describe('actions', () => { - it('requestPiP() calls requestPictureInPicture on video', async () => { + it('requestPictureInPicture() calls requestPictureInPicture on video', async () => { const video = createMockVideo(); video.requestPictureInPicture = vi.fn().mockResolvedValue({}); const store = createStore()(pipFeature); store.attach({ media: video, container: null }); - await store.requestPiP(); + await store.requestPictureInPicture(); expect(video.requestPictureInPicture).toHaveBeenCalled(); }); - it('exitPiP() calls document.exitPictureInPicture', async () => { + it('exitPictureInPicture() calls document.exitPictureInPicture', async () => { const originalExit = document.exitPictureInPicture; document.exitPictureInPicture = vi.fn().mockResolvedValue(undefined); @@ -113,7 +113,7 @@ describe('pipFeature', () => { const store = createStore()(pipFeature); store.attach({ media: video, container: null }); - await store.exitPiP(); + await store.exitPictureInPicture(); expect(document.exitPictureInPicture).toHaveBeenCalled(); @@ -122,7 +122,7 @@ describe('pipFeature', () => { }); describe('transitions', () => { - it('requestPiP() exits fullscreen first if active', async () => { + it('requestPictureInPicture() exits fullscreen first if active', async () => { const originalExit = document.exitFullscreen; document.exitFullscreen = vi.fn().mockResolvedValue(undefined); @@ -140,7 +140,7 @@ describe('pipFeature', () => { const store = createStore()(pipFeature); store.attach({ media: video, container }); - await store.requestPiP(); + await store.requestPictureInPicture(); expect(document.exitFullscreen).toHaveBeenCalled(); expect(video.requestPictureInPicture).toHaveBeenCalled(); @@ -148,7 +148,7 @@ describe('pipFeature', () => { document.exitFullscreen = originalExit; }); - it('requestPiP() does not exit fullscreen if not active', async () => { + it('requestPictureInPicture() does not exit fullscreen if not active', async () => { const originalExit = document.exitFullscreen; document.exitFullscreen = vi.fn().mockResolvedValue(undefined); @@ -158,7 +158,7 @@ describe('pipFeature', () => { const store = createStore()(pipFeature); store.attach({ media: video, container: null }); - await store.requestPiP(); + await store.requestPictureInPicture(); expect(document.exitFullscreen).not.toHaveBeenCalled(); expect(video.requestPictureInPicture).toHaveBeenCalled(); diff --git a/packages/core/src/dom/store/features/tests/volume.test.ts b/packages/core/src/dom/store/features/tests/volume.test.ts index 601a883b..ad5ee4af 100644 --- a/packages/core/src/dom/store/features/tests/volume.test.ts +++ b/packages/core/src/dom/store/features/tests/volume.test.ts @@ -47,13 +47,13 @@ describe('volumeFeature', () => { }); describe('actions', () => { - describe('changeVolume', () => { + describe('setVolume', () => { it('sets volume on target', async () => { const video = createMockVideo({}); const store = createStore()(volumeFeature); store.attach({ media: video, container: null }); - const result = await store.changeVolume(0.7); + const result = await store.setVolume(0.7); expect(video.volume).toBe(0.7); expect(result).toBe(0.7); @@ -64,7 +64,7 @@ describe('volumeFeature', () => { const store = createStore()(volumeFeature); store.attach({ media: video, container: null }); - await store.changeVolume(-0.5); + await store.setVolume(-0.5); expect(video.volume).toBe(0); }); @@ -74,19 +74,19 @@ describe('volumeFeature', () => { const store = createStore()(volumeFeature); store.attach({ media: video, container: null }); - await store.changeVolume(1.5); + await store.setVolume(1.5); expect(video.volume).toBe(1); }); }); - describe('toggleMute', () => { + describe('toggleMuted', () => { it('toggles mute from false to true', async () => { const video = createMockVideo({ muted: false }); const store = createStore()(volumeFeature); store.attach({ media: video, container: null }); - const result = await store.toggleMute(); + const result = await store.toggleMuted(); expect(video.muted).toBe(true); expect(result).toBe(true); @@ -97,7 +97,7 @@ describe('volumeFeature', () => { const store = createStore()(volumeFeature); store.attach({ media: video, container: null }); - const result = await store.toggleMute(); + const result = await store.toggleMuted(); expect(video.muted).toBe(false); expect(result).toBe(false); diff --git a/packages/core/src/dom/store/features/volume.ts b/packages/core/src/dom/store/features/volume.ts index 5eb45c3d..553ee59d 100644 --- a/packages/core/src/dom/store/features/volume.ts +++ b/packages/core/src/dom/store/features/volume.ts @@ -10,13 +10,13 @@ export const volumeFeature = definePlayerFeature({ muted: false, volumeAvailability: 'unavailable', - changeVolume(volume: number) { + setVolume(volume: number) { const { media } = target(); media.volume = Math.max(0, Math.min(1, volume)); return media.volume; }, - toggleMute() { + toggleMuted() { const { media } = target(); media.muted = !media.muted; return media.muted; diff --git a/packages/html/src/ui/volume-slider/volume-slider-element.ts b/packages/html/src/ui/volume-slider/volume-slider-element.ts index c5aae4f6..599d6ef7 100644 --- a/packages/html/src/ui/volume-slider/volume-slider-element.ts +++ b/packages/html/src/ui/volume-slider/volume-slider-element.ts @@ -129,6 +129,6 @@ export class VolumeSliderElement extends MediaElement { #setVolume(percent: number): void { const media = this.#volumeState.value; - media?.changeVolume(this.#core.valueFromPercent(percent) / 100); + media?.setVolume(this.#core.valueFromPercent(percent) / 100); } } diff --git a/packages/react/src/ui/volume-slider/tests/volume-slider.test.tsx b/packages/react/src/ui/volume-slider/tests/volume-slider.test.tsx index 3d43ee7c..cbeb1048 100644 --- a/packages/react/src/ui/volume-slider/tests/volume-slider.test.tsx +++ b/packages/react/src/ui/volume-slider/tests/volume-slider.test.tsx @@ -39,8 +39,8 @@ const { mockSliderApi, mockVolumeState } = vi.hoisted(() => ({ volume: 0.8, muted: false, volumeAvailability: 'available' as const, - changeVolume: vi.fn(), - toggleMute: vi.fn(), + setVolume: vi.fn(), + toggleMuted: vi.fn(), }, })); diff --git a/packages/react/src/ui/volume-slider/volume-slider-root.tsx b/packages/react/src/ui/volume-slider/volume-slider-root.tsx index 2622ea13..02e529fb 100644 --- a/packages/react/src/ui/volume-slider/volume-slider-root.tsx +++ b/packages/react/src/ui/volume-slider/volume-slider-root.tsx @@ -15,8 +15,8 @@ const noopVolume = { volume: 0, muted: false, volumeAvailability: 'unsupported' as const, - changeVolume: () => 0, - toggleMute: () => false, + setVolume: () => 0, + toggleMuted: () => false, }; export interface VolumeSliderRootProps extends UIComponentProps<'div', VolumeSliderCore.State>, VolumeSliderCore.Props { @@ -64,10 +64,10 @@ export const VolumeSliderRoot = forwardRef { - volumeRef.current?.changeVolume(percent / 100); + volumeRef.current?.setVolume(percent / 100); }, onValueCommit: (percent) => { - volumeRef.current?.changeVolume(percent / 100); + volumeRef.current?.setVolume(percent / 100); }, onDragStart, onDragEnd, diff --git a/packages/store/README.md b/packages/store/README.md index ec81b53a..f0036334 100644 --- a/packages/store/README.md +++ b/packages/store/README.md @@ -68,7 +68,7 @@ const volumeSlice = defineSlice()({ }, // Action - directly updates target - toggleMute() { + toggleMuted() { const media = target(); media.muted = !media.muted; return media.muted; @@ -99,7 +99,7 @@ const volumeSlice = defineSlice()({ // Infer types from the slice type VolumeState = InferSliceState; -// { volume: number; muted: boolean; changeVolume: ...; toggleMute: ... } +// { volume: number; muted: boolean; setVolume: ...; toggleMuted: ... } ``` ### Combining Slices @@ -138,7 +138,7 @@ state: ({ target }) => ({ volume: 1, // Action - changeVolume(volume: number) { + setVolume(volume: number) { const media = target(); media.volume = volume; return media.volume; diff --git a/site/src/components/docs/demos/player-controller/html/css/BasicUsage.ts b/site/src/components/docs/demos/player-controller/html/css/BasicUsage.ts index 90ad8b41..58c974da 100644 --- a/site/src/components/docs/demos/player-controller/html/css/BasicUsage.ts +++ b/site/src/components/docs/demos/player-controller/html/css/BasicUsage.ts @@ -33,7 +33,7 @@ class PlayerActions extends MediaElement { bind(playBtn, () => this.#player.value?.play()); bind(pauseBtn, () => this.#player.value?.pause()); - bind(volumeBtn, () => this.#player.value?.changeVolume(0.5)); + bind(volumeBtn, () => this.#player.value?.setVolume(0.5)); } override disconnectedCallback(): void {