mirror of
https://github.com/zoriya/v10.git
synced 2026-08-14 18:04:49 +00:00
feat: rename range to slider (#46)
This commit is contained in:
+6
-6
@@ -2,7 +2,7 @@ import { map } from 'nanostores';
|
||||
|
||||
import { shallowEqual } from '../utils/state';
|
||||
|
||||
export interface RangeState {
|
||||
export interface SliderState {
|
||||
_trackElement: HTMLElement | null;
|
||||
_pointerRatio: number;
|
||||
_hovering: boolean;
|
||||
@@ -11,10 +11,10 @@ export interface RangeState {
|
||||
_pointerWidth: number;
|
||||
}
|
||||
|
||||
export class Range {
|
||||
export class Slider {
|
||||
#element: HTMLElement | null = null;
|
||||
#abortController: AbortController | null = null;
|
||||
#state = map<RangeState>({
|
||||
#state = map<SliderState>({
|
||||
_trackElement: null,
|
||||
_pointerRatio: 0,
|
||||
_hovering: false,
|
||||
@@ -42,16 +42,16 @@ export class Range {
|
||||
this.#abortController = null;
|
||||
}
|
||||
|
||||
subscribe(callback: (state: RangeState) => void): () => void {
|
||||
subscribe(callback: (state: SliderState) => void): () => void {
|
||||
return this.#state.subscribe(callback);
|
||||
}
|
||||
|
||||
setState(state: Partial<RangeState>): void {
|
||||
setState(state: Partial<SliderState>): void {
|
||||
if (shallowEqual(state, this.#state.get())) return;
|
||||
this.#state.set({ ...this.#state.get(), ...state });
|
||||
}
|
||||
|
||||
getState(): RangeState {
|
||||
getState(): SliderState {
|
||||
const state = this.#state.get();
|
||||
|
||||
let _pointerWidth = 0;
|
||||
+9
-9
@@ -1,8 +1,8 @@
|
||||
import type { RangeState } from './range';
|
||||
import type { SliderState } from './slider';
|
||||
|
||||
import { Range } from './range';
|
||||
import { Slider } from './slider';
|
||||
|
||||
export interface TimeRangeState extends RangeState {
|
||||
export interface TimeSliderState extends SliderState {
|
||||
currentTime: number;
|
||||
duration: number;
|
||||
requestSeek: (time: number) => void;
|
||||
@@ -10,12 +10,12 @@ export interface TimeRangeState extends RangeState {
|
||||
_durationText: string;
|
||||
}
|
||||
|
||||
export class TimeRange extends Range {
|
||||
export class TimeSlider extends Slider {
|
||||
#seekingTime: number | null = null;
|
||||
#oldCurrentTime: number | null = null;
|
||||
|
||||
getState(): TimeRangeState {
|
||||
const state = super.getState() as TimeRangeState;
|
||||
getState(): TimeSliderState {
|
||||
const state = super.getState() as TimeSliderState;
|
||||
|
||||
// When dragging, use pointer position for immediate feedback;
|
||||
// While seeking, use seeking time so it doesn't jump back to the current time;
|
||||
@@ -61,7 +61,7 @@ export class TimeRange extends Range {
|
||||
#handlePointerDown(event: PointerEvent) {
|
||||
super.handleEvent(event);
|
||||
|
||||
const { _pointerRatio, duration, requestSeek } = super.getState() as TimeRangeState;
|
||||
const { _pointerRatio, duration, requestSeek } = super.getState() as TimeSliderState;
|
||||
|
||||
this.#seekingTime = _pointerRatio * duration;
|
||||
requestSeek(this.#seekingTime);
|
||||
@@ -70,7 +70,7 @@ export class TimeRange extends Range {
|
||||
#handlePointerMove(event: PointerEvent) {
|
||||
super.handleEvent(event);
|
||||
|
||||
const { _dragging, _pointerRatio, duration, requestSeek } = super.getState() as TimeRangeState;
|
||||
const { _dragging, _pointerRatio, duration, requestSeek } = super.getState() as TimeSliderState;
|
||||
|
||||
if (_dragging) {
|
||||
this.#seekingTime = _pointerRatio * duration;
|
||||
@@ -79,7 +79,7 @@ export class TimeRange extends Range {
|
||||
}
|
||||
|
||||
#handlePointerUp(event: PointerEvent) {
|
||||
const { _dragging, _pointerRatio, duration, requestSeek } = super.getState() as TimeRangeState;
|
||||
const { _dragging, _pointerRatio, duration, requestSeek } = super.getState() as TimeSliderState;
|
||||
|
||||
if (_dragging) {
|
||||
this.#seekingTime = _pointerRatio * duration;
|
||||
+9
-9
@@ -1,8 +1,8 @@
|
||||
import type { RangeState } from './range';
|
||||
import type { SliderState } from './slider';
|
||||
|
||||
import { Range } from './range';
|
||||
import { Slider } from './slider';
|
||||
|
||||
export interface VolumeRangeState extends RangeState {
|
||||
export interface VolumeSliderState extends SliderState {
|
||||
volume: number;
|
||||
muted: boolean;
|
||||
volumeLevel: string;
|
||||
@@ -10,9 +10,9 @@ export interface VolumeRangeState extends RangeState {
|
||||
_volumeText: string;
|
||||
}
|
||||
|
||||
export class VolumeRange extends Range {
|
||||
getState(): VolumeRangeState {
|
||||
const state = super.getState() as VolumeRangeState;
|
||||
export class VolumeSlider extends Slider {
|
||||
getState(): VolumeSliderState {
|
||||
const state = super.getState() as VolumeSliderState;
|
||||
|
||||
// When dragging, use pointer position for immediate feedback;
|
||||
// Otherwise, use current volume;
|
||||
@@ -49,14 +49,14 @@ export class VolumeRange extends Range {
|
||||
#handlePointerDown(event: PointerEvent) {
|
||||
super.handleEvent(event);
|
||||
|
||||
const { _pointerRatio, requestVolumeChange } = super.getState() as VolumeRangeState;
|
||||
const { _pointerRatio, requestVolumeChange } = super.getState() as VolumeSliderState;
|
||||
requestVolumeChange(_pointerRatio);
|
||||
}
|
||||
|
||||
#handlePointerMove(event: PointerEvent) {
|
||||
super.handleEvent(event);
|
||||
|
||||
const { _dragging, _pointerRatio, requestVolumeChange } = super.getState() as VolumeRangeState;
|
||||
const { _dragging, _pointerRatio, requestVolumeChange } = super.getState() as VolumeSliderState;
|
||||
|
||||
if (_dragging) {
|
||||
requestVolumeChange(_pointerRatio);
|
||||
@@ -64,7 +64,7 @@ export class VolumeRange extends Range {
|
||||
}
|
||||
|
||||
#handlePointerUp(event: PointerEvent) {
|
||||
const { _dragging, _pointerRatio, requestVolumeChange } = super.getState() as VolumeRangeState;
|
||||
const { _dragging, _pointerRatio, requestVolumeChange } = super.getState() as VolumeSliderState;
|
||||
|
||||
if (_dragging) {
|
||||
requestVolumeChange(_pointerRatio);
|
||||
@@ -1,2 +1,2 @@
|
||||
export { TimeRange, type TimeRangeState } from './components/time-range.js';
|
||||
export { VolumeRange, type VolumeRangeState } from './components/volume-range.js';
|
||||
export { TimeSlider, type TimeSliderState } from './components/time-slider.js';
|
||||
export { VolumeSlider, type VolumeSliderState } from './components/volume-slider.js';
|
||||
|
||||
+7
-7
@@ -1,23 +1,23 @@
|
||||
export interface TimeRangeState {
|
||||
export interface TimeSliderState {
|
||||
currentTime: number;
|
||||
duration: number;
|
||||
}
|
||||
|
||||
export interface TimeRangeMethods {
|
||||
export interface TimeSliderMethods {
|
||||
requestSeek: (time: number) => void;
|
||||
}
|
||||
|
||||
export interface TimeRangeStateDefinition {
|
||||
export interface TimeSliderStateDefinition {
|
||||
keys: string[];
|
||||
stateTransform: (rawState: any) => TimeRangeState;
|
||||
createRequestMethods: (dispatch: (action: { type: string; detail?: any }) => void) => TimeRangeMethods;
|
||||
stateTransform: (rawState: any) => TimeSliderState;
|
||||
createRequestMethods: (dispatch: (action: { type: string; detail?: any }) => void) => TimeSliderMethods;
|
||||
}
|
||||
|
||||
/**
|
||||
* TimeRange state definition
|
||||
* TimeSlider state definition
|
||||
* Defines the core state logic that can be shared between implementations
|
||||
*/
|
||||
export const timeRangeStateDefinition: TimeRangeStateDefinition = {
|
||||
export const timeSliderStateDefinition: TimeSliderStateDefinition = {
|
||||
keys: ['currentTime', 'duration'],
|
||||
stateTransform: (rawState: any) => ({
|
||||
currentTime: rawState.currentTime ?? 0,
|
||||
+7
-7
@@ -1,24 +1,24 @@
|
||||
export interface VolumeRangeState {
|
||||
export interface VolumeSliderState {
|
||||
volume: number;
|
||||
muted: boolean;
|
||||
volumeLevel: 'high' | 'medium' | 'low' | 'off';
|
||||
}
|
||||
|
||||
export interface VolumeRangeMethods {
|
||||
export interface VolumeSliderMethods {
|
||||
requestVolumeChange: (volume: number) => void;
|
||||
}
|
||||
|
||||
export interface VolumeRangeStateDefinition {
|
||||
export interface VolumeSliderStateDefinition {
|
||||
keys: string[];
|
||||
stateTransform: (rawState: any) => VolumeRangeState;
|
||||
createRequestMethods: (dispatch: (action: { type: string; detail?: any }) => void) => VolumeRangeMethods;
|
||||
stateTransform: (rawState: any) => VolumeSliderState;
|
||||
createRequestMethods: (dispatch: (action: { type: string; detail?: any }) => void) => VolumeSliderMethods;
|
||||
}
|
||||
|
||||
/**
|
||||
* VolumeRange state definition
|
||||
* VolumeSlider state definition
|
||||
* Defines the core state logic that can be shared between implementations
|
||||
*/
|
||||
export const volumeRangeStateDefinition: VolumeRangeStateDefinition = {
|
||||
export const volumeSliderStateDefinition: VolumeSliderStateDefinition = {
|
||||
keys: ['volume', 'muted', 'volumeLevel'],
|
||||
stateTransform: (rawState: any) => ({
|
||||
volume: rawState.volume ?? 1,
|
||||
@@ -3,8 +3,8 @@ export * from './component-state-definitions/duration-display';
|
||||
export * from './component-state-definitions/fullscreen-button';
|
||||
export * from './component-state-definitions/mute-button';
|
||||
export * from './component-state-definitions/play-button';
|
||||
export * from './component-state-definitions/time-range';
|
||||
export * from './component-state-definitions/volume-range';
|
||||
export * from './component-state-definitions/time-slider';
|
||||
export * from './component-state-definitions/volume-slider';
|
||||
export * from './factory';
|
||||
export { createMediaStore } from './media-store';
|
||||
export * from './state-mediators/audible';
|
||||
|
||||
+127
-128
@@ -1,22 +1,55 @@
|
||||
import type { ConnectedComponentConstructor, PropsHook, StateHook } from '../utils/component-factory';
|
||||
|
||||
import { TimeRange as CoreTimeRange } from '@vjs-10/core';
|
||||
import { timeRangeStateDefinition } from '@vjs-10/media-store';
|
||||
import { TimeSlider as CoreTimeSlider } from '@vjs-10/core';
|
||||
import { timeSliderStateDefinition } from '@vjs-10/media-store';
|
||||
|
||||
import { toConnectedHTMLComponent } from '../utils/component-factory';
|
||||
|
||||
interface TimeRangeRootState {
|
||||
interface TimeSliderRootState {
|
||||
currentTime: number;
|
||||
duration: number;
|
||||
requestSeek: (time: number) => void;
|
||||
core: CoreTimeRange | null;
|
||||
core: CoreTimeSlider | null;
|
||||
}
|
||||
|
||||
export class TimeRangeRootBase extends HTMLElement {
|
||||
/**
|
||||
* TimeSlider Root props hook - equivalent to React's useTimeSliderRootProps
|
||||
* Handles element attributes and properties based on state
|
||||
*/
|
||||
export const getTimeSliderRootProps: PropsHook<{
|
||||
currentTime: number;
|
||||
duration: number;
|
||||
requestSeek: (time: number) => void;
|
||||
core: CoreTimeSlider | null;
|
||||
}> = (state, element) => {
|
||||
const formatTime = (time: number) => {
|
||||
const minutes = Math.floor(time / 60);
|
||||
const seconds = Math.floor(time % 60);
|
||||
return `${minutes}:${seconds.toString().padStart(2, '0')}`;
|
||||
};
|
||||
|
||||
const currentTimeText = formatTime(state.currentTime);
|
||||
const durationText = formatTime(state.duration);
|
||||
|
||||
const baseProps: Record<string, any> = {
|
||||
/** data attributes/props */
|
||||
'data-current-time': state.currentTime.toString(),
|
||||
'data-duration': state.duration.toString(),
|
||||
'data-orientation': (element as any).orientation || 'horizontal',
|
||||
/** aria attributes/props */
|
||||
'aria-label': 'Seek',
|
||||
'aria-valuetext': `${currentTimeText} of ${durationText}`,
|
||||
'aria-orientation': (element as any).orientation || 'horizontal',
|
||||
};
|
||||
|
||||
return baseProps;
|
||||
};
|
||||
|
||||
export class TimeSliderRootBase extends HTMLElement {
|
||||
static readonly observedAttributes: readonly string[] = ['orientation'];
|
||||
|
||||
_state: TimeRangeRootState | undefined;
|
||||
_core: CoreTimeRange | null = null;
|
||||
_state: TimeSliderRootState | undefined;
|
||||
_core: CoreTimeSlider | null = null;
|
||||
|
||||
get currentTime(): number {
|
||||
return this._state?.currentTime ?? 0;
|
||||
@@ -32,7 +65,7 @@ export class TimeRangeRootBase extends HTMLElement {
|
||||
|
||||
attributeChangedCallback(name: string, _oldValue: string | null, _newValue: string | null): void {
|
||||
if (name === 'orientation' && this._state) {
|
||||
this._render(useTimeRangeRootProps(this._state, this), this._state);
|
||||
this._render(getTimeSliderRootProps(this._state, this), this._state);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,8 +73,8 @@ export class TimeRangeRootBase extends HTMLElement {
|
||||
this._state = state;
|
||||
|
||||
if (state && !this._core) {
|
||||
this._core = new CoreTimeRange();
|
||||
this._core.subscribe(() => this._render(useTimeRangeRootProps(state, this), state));
|
||||
this._core = new CoreTimeSlider();
|
||||
this._core.subscribe(() => this._render(getTimeSliderRootProps(state, this), state));
|
||||
this._core.attach(this);
|
||||
state.core = this._core;
|
||||
}
|
||||
@@ -70,14 +103,14 @@ export class TimeRangeRootBase extends HTMLElement {
|
||||
}
|
||||
}
|
||||
|
||||
export class TimeRangeTrackBase extends HTMLElement {
|
||||
export class TimeSliderTrackBase extends HTMLElement {
|
||||
constructor() {
|
||||
super();
|
||||
}
|
||||
|
||||
connectedCallback(): void {
|
||||
// Set this element as the track element in the core TimeRange
|
||||
const rootElement = this.closest('media-time-range-root') as any;
|
||||
// Set this element as the track element in the core TimeSlider
|
||||
const rootElement = this.closest('media-time-slider-root') as any;
|
||||
if (rootElement?._state?.core) {
|
||||
rootElement._state.core.setState({ _trackElement: this });
|
||||
}
|
||||
@@ -98,7 +131,7 @@ export class TimeRangeTrackBase extends HTMLElement {
|
||||
}
|
||||
}
|
||||
|
||||
export class TimeRangeProgressBase extends HTMLElement {
|
||||
export class TimeSliderProgressBase extends HTMLElement {
|
||||
constructor() {
|
||||
super();
|
||||
this.style.position = 'absolute';
|
||||
@@ -125,7 +158,7 @@ export class TimeRangeProgressBase extends HTMLElement {
|
||||
}
|
||||
}
|
||||
|
||||
export class TimeRangePointerBase extends HTMLElement {
|
||||
export class TimeSliderPointerBase extends HTMLElement {
|
||||
constructor() {
|
||||
super();
|
||||
this.style.position = 'absolute';
|
||||
@@ -152,7 +185,7 @@ export class TimeRangePointerBase extends HTMLElement {
|
||||
}
|
||||
}
|
||||
|
||||
export class TimeRangeThumbBase extends HTMLElement {
|
||||
export class TimeSliderThumbBase extends HTMLElement {
|
||||
constructor() {
|
||||
super();
|
||||
this.style.position = 'absolute';
|
||||
@@ -175,157 +208,123 @@ export class TimeRangeThumbBase extends HTMLElement {
|
||||
}
|
||||
}
|
||||
|
||||
export const useTimeRangeRootState: StateHook<{
|
||||
export const useTimeSliderRootState: StateHook<{
|
||||
currentTime: number;
|
||||
duration: number;
|
||||
requestSeek: (time: number) => void;
|
||||
core: CoreTimeRange | null;
|
||||
core: CoreTimeSlider | null;
|
||||
}> = {
|
||||
keys: timeRangeStateDefinition.keys,
|
||||
keys: timeSliderStateDefinition.keys,
|
||||
transform: (rawState, mediaStore) => ({
|
||||
...timeRangeStateDefinition.stateTransform(rawState),
|
||||
...timeRangeStateDefinition.createRequestMethods(mediaStore.dispatch),
|
||||
...timeSliderStateDefinition.stateTransform(rawState),
|
||||
...timeSliderStateDefinition.createRequestMethods(mediaStore.dispatch),
|
||||
core: null,
|
||||
}),
|
||||
};
|
||||
|
||||
export const useTimeRangeRootProps: PropsHook<{
|
||||
export const getTimeSliderTrackProps: PropsHook<Record<string, never>> = (_state, element) => {
|
||||
// Get orientation from parent root element if not provided in state
|
||||
const rootElement = element.closest('media-time-slider-root') as any;
|
||||
return {
|
||||
'data-orientation': rootElement?.orientation || 'horizontal',
|
||||
};
|
||||
};
|
||||
|
||||
export const getTimeSliderProgressProps: PropsHook<Record<string, never>> = (_state, element) => {
|
||||
// Get orientation from parent root element if not provided in state
|
||||
const rootElement = element.closest('media-time-slider-root') as any;
|
||||
return {
|
||||
'data-orientation': rootElement?.orientation || 'horizontal',
|
||||
};
|
||||
};
|
||||
|
||||
export const getTimeSliderPointerProps: PropsHook<Record<string, never>> = (_state, element) => {
|
||||
// Get orientation from parent root element if not provided in state
|
||||
const rootElement = element.closest('media-time-slider-root') as any;
|
||||
return {
|
||||
'data-orientation': rootElement?.orientation || 'horizontal',
|
||||
};
|
||||
};
|
||||
|
||||
export const getTimeSliderThumbProps: PropsHook<Record<string, never>> = (_state, element) => {
|
||||
// Get orientation from parent root element if not provided in state
|
||||
const rootElement = element.closest('media-time-slider-root') as any;
|
||||
return {
|
||||
'data-orientation': rootElement?.orientation || 'horizontal',
|
||||
};
|
||||
};
|
||||
|
||||
export const TimeSliderRoot: ConnectedComponentConstructor<{
|
||||
currentTime: number;
|
||||
duration: number;
|
||||
requestSeek: (time: number) => void;
|
||||
core: CoreTimeRange | null;
|
||||
}> = (state, element) => {
|
||||
const formatTime = (time: number) => {
|
||||
const minutes = Math.floor(time / 60);
|
||||
const seconds = Math.floor(time % 60);
|
||||
return `${minutes}:${seconds.toString().padStart(2, '0')}`;
|
||||
};
|
||||
core: CoreTimeSlider | null;
|
||||
}> = toConnectedHTMLComponent(TimeSliderRootBase, useTimeSliderRootState, getTimeSliderRootProps, 'TimeSliderRoot');
|
||||
|
||||
const currentTimeText = formatTime(state.currentTime);
|
||||
const durationText = formatTime(state.duration);
|
||||
|
||||
const baseProps: Record<string, any> = {
|
||||
/** data attributes/props */
|
||||
'data-current-time': state.currentTime.toString(),
|
||||
'data-duration': state.duration.toString(),
|
||||
'data-orientation': (element as any).orientation || 'horizontal',
|
||||
/** aria attributes/props */
|
||||
'aria-label': 'Seek',
|
||||
'aria-valuetext': `${currentTimeText} of ${durationText}`,
|
||||
'aria-orientation': (element as any).orientation || 'horizontal',
|
||||
};
|
||||
|
||||
return baseProps;
|
||||
};
|
||||
|
||||
export const useTimeRangeTrackProps: PropsHook<{}> = (_state, element) => {
|
||||
// Get orientation from parent root element if not provided in state
|
||||
const rootElement = element.closest('media-time-range-root') as any;
|
||||
return {
|
||||
'data-orientation': rootElement?.orientation || 'horizontal',
|
||||
};
|
||||
};
|
||||
|
||||
export const useTimeRangeProgressProps: PropsHook<{}> = (_state, element) => {
|
||||
// Get orientation from parent root element if not provided in state
|
||||
const rootElement = element.closest('media-time-range-root') as any;
|
||||
return {
|
||||
'data-orientation': rootElement?.orientation || 'horizontal',
|
||||
};
|
||||
};
|
||||
|
||||
export const useTimeRangePointerProps: PropsHook<{}> = (_state, element) => {
|
||||
// Get orientation from parent root element if not provided in state
|
||||
const rootElement = element.closest('media-time-range-root') as any;
|
||||
return {
|
||||
'data-orientation': rootElement?.orientation || 'horizontal',
|
||||
};
|
||||
};
|
||||
|
||||
export const useTimeRangeThumbProps: PropsHook<{}> = (_state, element) => {
|
||||
// Get orientation from parent root element if not provided in state
|
||||
const rootElement = element.closest('media-time-range-root') as any;
|
||||
return {
|
||||
'data-orientation': rootElement?.orientation || 'horizontal',
|
||||
};
|
||||
};
|
||||
|
||||
export const TimeRangeRoot: ConnectedComponentConstructor<{
|
||||
currentTime: number;
|
||||
duration: number;
|
||||
requestSeek: (time: number) => void;
|
||||
core: CoreTimeRange | null;
|
||||
}> = toConnectedHTMLComponent(TimeRangeRootBase, useTimeRangeRootState, useTimeRangeRootProps, 'TimeRangeRoot');
|
||||
|
||||
export const TimeRangeTrack: ConnectedComponentConstructor<any> = toConnectedHTMLComponent(
|
||||
TimeRangeTrackBase,
|
||||
export const TimeSliderTrack: ConnectedComponentConstructor<any> = toConnectedHTMLComponent(
|
||||
TimeSliderTrackBase,
|
||||
{ keys: [], transform: () => ({}) },
|
||||
useTimeRangeTrackProps,
|
||||
'TimeRangeTrack',
|
||||
getTimeSliderTrackProps,
|
||||
'TimeSliderTrack',
|
||||
);
|
||||
|
||||
export const TimeRangeProgress: ConnectedComponentConstructor<any> = toConnectedHTMLComponent(
|
||||
TimeRangeProgressBase,
|
||||
export const TimeSliderProgress: ConnectedComponentConstructor<any> = toConnectedHTMLComponent(
|
||||
TimeSliderProgressBase,
|
||||
{ keys: [], transform: () => ({}) },
|
||||
useTimeRangeProgressProps,
|
||||
'TimeRangeProgress',
|
||||
getTimeSliderProgressProps,
|
||||
'TimeSliderProgress',
|
||||
);
|
||||
|
||||
export const TimeRangePointer: ConnectedComponentConstructor<any> = toConnectedHTMLComponent(
|
||||
TimeRangePointerBase,
|
||||
export const TimeSliderPointer: ConnectedComponentConstructor<any> = toConnectedHTMLComponent(
|
||||
TimeSliderPointerBase,
|
||||
{ keys: [], transform: () => ({}) },
|
||||
useTimeRangePointerProps,
|
||||
'TimeRangePointer',
|
||||
getTimeSliderPointerProps,
|
||||
'TimeSliderPointer',
|
||||
);
|
||||
|
||||
export const TimeRangeThumb: ConnectedComponentConstructor<any> = toConnectedHTMLComponent(
|
||||
TimeRangeThumbBase,
|
||||
export const TimeSliderThumb: ConnectedComponentConstructor<any> = toConnectedHTMLComponent(
|
||||
TimeSliderThumbBase,
|
||||
{ keys: [], transform: () => ({}) },
|
||||
useTimeRangeThumbProps,
|
||||
'TimeRangeThumb',
|
||||
getTimeSliderThumbProps,
|
||||
'TimeSliderThumb',
|
||||
);
|
||||
|
||||
export const TimeRange = Object.assign(
|
||||
export const TimeSlider = Object.assign(
|
||||
{},
|
||||
{
|
||||
Root: TimeRangeRoot,
|
||||
Track: TimeRangeTrack,
|
||||
Progress: TimeRangeProgress,
|
||||
Pointer: TimeRangePointer,
|
||||
Thumb: TimeRangeThumb,
|
||||
Root: TimeSliderRoot,
|
||||
Track: TimeSliderTrack,
|
||||
Progress: TimeSliderProgress,
|
||||
Pointer: TimeSliderPointer,
|
||||
Thumb: TimeSliderThumb,
|
||||
},
|
||||
) as {
|
||||
Root: typeof TimeRangeRoot;
|
||||
Track: typeof TimeRangeTrack;
|
||||
Progress: typeof TimeRangeProgress;
|
||||
Pointer: typeof TimeRangePointer;
|
||||
Thumb: typeof TimeRangeThumb;
|
||||
Root: typeof TimeSliderRoot;
|
||||
Track: typeof TimeSliderTrack;
|
||||
Progress: typeof TimeSliderProgress;
|
||||
Pointer: typeof TimeSliderPointer;
|
||||
Thumb: typeof TimeSliderThumb;
|
||||
};
|
||||
|
||||
// Register custom elements
|
||||
if (!globalThis.customElements.get('media-time-range-root')) {
|
||||
// @ts-ignore - Custom element constructor compatibility
|
||||
globalThis.customElements.define('media-time-range-root', TimeRangeRoot);
|
||||
if (!globalThis.customElements.get('media-time-slider-root')) {
|
||||
globalThis.customElements.define('media-time-slider-root', TimeSliderRoot);
|
||||
}
|
||||
|
||||
if (!globalThis.customElements.get('media-time-range-track')) {
|
||||
// @ts-ignore - Custom element constructor compatibility
|
||||
globalThis.customElements.define('media-time-range-track', TimeRangeTrack);
|
||||
if (!globalThis.customElements.get('media-time-slider-track')) {
|
||||
globalThis.customElements.define('media-time-slider-track', TimeSliderTrack);
|
||||
}
|
||||
|
||||
if (!globalThis.customElements.get('media-time-range-progress')) {
|
||||
// @ts-ignore - Custom element constructor compatibility
|
||||
globalThis.customElements.define('media-time-range-progress', TimeRangeProgress);
|
||||
if (!globalThis.customElements.get('media-time-slider-progress')) {
|
||||
globalThis.customElements.define('media-time-slider-progress', TimeSliderProgress);
|
||||
}
|
||||
|
||||
if (!globalThis.customElements.get('media-time-range-pointer')) {
|
||||
// @ts-ignore - Custom element constructor compatibility
|
||||
globalThis.customElements.define('media-time-range-pointer', TimeRangePointer);
|
||||
if (!globalThis.customElements.get('media-time-slider-pointer')) {
|
||||
globalThis.customElements.define('media-time-slider-pointer', TimeSliderPointer);
|
||||
}
|
||||
|
||||
if (!globalThis.customElements.get('media-time-range-thumb')) {
|
||||
// @ts-ignore - Custom element constructor compatibility
|
||||
globalThis.customElements.define('media-time-range-thumb', TimeRangeThumb);
|
||||
if (!globalThis.customElements.get('media-time-slider-thumb')) {
|
||||
globalThis.customElements.define('media-time-slider-thumb', TimeSliderThumb);
|
||||
}
|
||||
|
||||
export default TimeRange;
|
||||
export default TimeSlider;
|
||||
+123
-127
@@ -1,26 +1,53 @@
|
||||
import type { ConnectedComponentConstructor, PropsHook, StateHook } from '../utils/component-factory';
|
||||
|
||||
import { VolumeRange as CoreVolumeRange } from '@vjs-10/core';
|
||||
import { volumeRangeStateDefinition } from '@vjs-10/media-store';
|
||||
import { VolumeSlider as CoreVolumeSlider } from '@vjs-10/core';
|
||||
import { volumeSliderStateDefinition } from '@vjs-10/media-store';
|
||||
|
||||
import { toConnectedHTMLComponent } from '../utils/component-factory';
|
||||
|
||||
/**
|
||||
* VolumeRange Root component - Main container with pointer event handling
|
||||
* VolumeSlider Root props hook - equivalent to React's useVolumeSliderRootProps
|
||||
* Handles element attributes and properties based on state
|
||||
*/
|
||||
interface VolumeRangeRootState {
|
||||
export const getVolumeSliderRootProps: PropsHook<{
|
||||
volume: number;
|
||||
muted: boolean;
|
||||
volumeLevel: string;
|
||||
requestVolumeChange: (volume: number) => void;
|
||||
core: CoreVolumeRange | null;
|
||||
core: CoreVolumeSlider | null;
|
||||
}> = (state, element) => {
|
||||
const volumeText = `${Math.round(state.muted ? 0 : state.volume * 100)}%`;
|
||||
|
||||
const baseProps: Record<string, any> = {
|
||||
/** data attributes/props */
|
||||
'data-muted': state.muted.toString(),
|
||||
'data-volume-level': state.volumeLevel,
|
||||
'data-orientation': (element as any).orientation || 'horizontal',
|
||||
/** aria attributes/props */
|
||||
'aria-label': 'Volume',
|
||||
'aria-valuetext': volumeText,
|
||||
'aria-orientation': (element as any).orientation || 'horizontal',
|
||||
};
|
||||
|
||||
return baseProps;
|
||||
};
|
||||
|
||||
/**
|
||||
* VolumeSlider Root component - Main container with pointer event handling
|
||||
*/
|
||||
interface VolumeSliderRootState {
|
||||
volume: number;
|
||||
muted: boolean;
|
||||
volumeLevel: string;
|
||||
requestVolumeChange: (volume: number) => void;
|
||||
core: CoreVolumeSlider | null;
|
||||
}
|
||||
|
||||
export class VolumeRangeRootBase extends HTMLElement {
|
||||
export class VolumeSliderRootBase extends HTMLElement {
|
||||
static readonly observedAttributes: readonly string[] = ['orientation'];
|
||||
|
||||
_state: VolumeRangeRootState | undefined;
|
||||
_core: CoreVolumeRange | null = null;
|
||||
_state: VolumeSliderRootState | undefined;
|
||||
_core: CoreVolumeSlider | null = null;
|
||||
|
||||
get volume(): number | undefined {
|
||||
return this._state?.volume;
|
||||
@@ -40,7 +67,7 @@ export class VolumeRangeRootBase extends HTMLElement {
|
||||
|
||||
attributeChangedCallback(name: string, _oldValue: string | null, _newValue: string | null): void {
|
||||
if (name === 'orientation' && this._state) {
|
||||
this._render(useVolumeRangeRootProps(this._state, this), this._state);
|
||||
this._render(getVolumeSliderRootProps(this._state, this), this._state);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,8 +75,8 @@ export class VolumeRangeRootBase extends HTMLElement {
|
||||
this._state = state;
|
||||
|
||||
if (state && !this._core) {
|
||||
this._core = new CoreVolumeRange();
|
||||
this._core.subscribe(() => this._render(useVolumeRangeRootProps(state, this), state));
|
||||
this._core = new CoreVolumeSlider();
|
||||
this._core.subscribe(() => this._render(getVolumeSliderRootProps(state, this), state));
|
||||
this._core.attach(this);
|
||||
state.core = this._core;
|
||||
}
|
||||
@@ -79,16 +106,16 @@ export class VolumeRangeRootBase extends HTMLElement {
|
||||
}
|
||||
|
||||
/**
|
||||
* VolumeRange Track component - Track element that captures pointer events
|
||||
* VolumeSlider Track component - Track element that captures pointer events
|
||||
*/
|
||||
export class VolumeRangeTrackBase extends HTMLElement {
|
||||
export class VolumeSliderTrackBase extends HTMLElement {
|
||||
constructor() {
|
||||
super();
|
||||
}
|
||||
|
||||
connectedCallback(): void {
|
||||
// Set this element as the track element in the core VolumeRange
|
||||
const rootElement = this.closest('media-volume-range-root') as any;
|
||||
// Set this element as the track element in the core VolumeSlider
|
||||
const rootElement = this.closest('media-volume-slider-root') as any;
|
||||
if (rootElement?._state?.core) {
|
||||
rootElement._state.core.setState({ _trackElement: this });
|
||||
}
|
||||
@@ -110,9 +137,9 @@ export class VolumeRangeTrackBase extends HTMLElement {
|
||||
}
|
||||
|
||||
/**
|
||||
* VolumeRange Progress component - Shows current progress
|
||||
* VolumeSlider Progress component - Shows current progress
|
||||
*/
|
||||
export class VolumeRangeProgressBase extends HTMLElement {
|
||||
export class VolumeSliderProgressBase extends HTMLElement {
|
||||
constructor() {
|
||||
super();
|
||||
this.style.position = 'absolute';
|
||||
@@ -140,9 +167,9 @@ export class VolumeRangeProgressBase extends HTMLElement {
|
||||
}
|
||||
|
||||
/**
|
||||
* VolumeRange Thumb component - Draggable thumb element
|
||||
* VolumeSlider Thumb component - Draggable thumb element
|
||||
*/
|
||||
export class VolumeRangeThumbBase extends HTMLElement {
|
||||
export class VolumeSliderThumbBase extends HTMLElement {
|
||||
constructor() {
|
||||
super();
|
||||
this.style.position = 'absolute';
|
||||
@@ -166,162 +193,131 @@ export class VolumeRangeThumbBase extends HTMLElement {
|
||||
}
|
||||
|
||||
/**
|
||||
* VolumeRange Root state hook - equivalent to React's useVolumeRangeRootState
|
||||
* VolumeSlider Root state hook - equivalent to React's useVolumeSliderRootState
|
||||
* Handles media store state subscription and transformation
|
||||
*/
|
||||
export const useVolumeRangeRootState: StateHook<{
|
||||
export const useVolumeSliderRootState: StateHook<{
|
||||
volume: number;
|
||||
muted: boolean;
|
||||
volumeLevel: string;
|
||||
requestVolumeChange: (volume: number) => void;
|
||||
core: CoreVolumeRange | null;
|
||||
core: CoreVolumeSlider | null;
|
||||
}> = {
|
||||
keys: volumeRangeStateDefinition.keys,
|
||||
keys: volumeSliderStateDefinition.keys,
|
||||
transform: (rawState, mediaStore) => ({
|
||||
...volumeRangeStateDefinition.stateTransform(rawState),
|
||||
...volumeRangeStateDefinition.createRequestMethods(mediaStore.dispatch),
|
||||
...volumeSliderStateDefinition.stateTransform(rawState),
|
||||
...volumeSliderStateDefinition.createRequestMethods(mediaStore.dispatch),
|
||||
core: null,
|
||||
}),
|
||||
};
|
||||
|
||||
/**
|
||||
* VolumeRange Root props hook - equivalent to React's useVolumeRangeRootProps
|
||||
* Handles element attributes and properties based on state
|
||||
* VolumeSlider Track props hook
|
||||
*/
|
||||
export const useVolumeRangeRootProps: PropsHook<{
|
||||
export const getVolumeSliderTrackProps: PropsHook<Record<string, never>> = (_state, element) => {
|
||||
// Get orientation from parent root element if not provided in state
|
||||
const rootElement = element.closest('media-volume-slider-root') as any;
|
||||
return {
|
||||
'data-orientation': rootElement?.orientation || 'horizontal',
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* VolumeSlider Progress props hook
|
||||
*/
|
||||
export const getVolumeSliderProgressProps: PropsHook<Record<string, never>> = (_state, element) => {
|
||||
// Get orientation from parent root element if not provided in state
|
||||
const rootElement = element.closest('media-volume-slider-root') as any;
|
||||
return {
|
||||
'data-orientation': rootElement?.orientation || 'horizontal',
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* VolumeSlider Thumb props hook
|
||||
*/
|
||||
export const getVolumeSliderThumbProps: PropsHook<Record<string, never>> = (_state, element) => {
|
||||
// Get orientation from parent root element if not provided in state
|
||||
const rootElement = element.closest('media-volume-slider-root') as any;
|
||||
return {
|
||||
'data-orientation': rootElement?.orientation || 'horizontal',
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Connected VolumeSlider Root component using hook-style architecture
|
||||
*/
|
||||
export const VolumeSliderRoot: ConnectedComponentConstructor<{
|
||||
volume: number;
|
||||
muted: boolean;
|
||||
volumeLevel: string;
|
||||
requestVolumeChange: (volume: number) => void;
|
||||
core: CoreVolumeRange | null;
|
||||
}> = (state, element) => {
|
||||
const volumeText = `${Math.round(state.muted ? 0 : state.volume * 100)}%`;
|
||||
|
||||
const baseProps: Record<string, any> = {
|
||||
/** data attributes/props */
|
||||
'data-muted': state.muted.toString(),
|
||||
'data-volume-level': state.volumeLevel,
|
||||
'data-orientation': (element as any).orientation || 'horizontal',
|
||||
/** aria attributes/props */
|
||||
'aria-label': 'Volume',
|
||||
'aria-valuetext': volumeText,
|
||||
'aria-orientation': (element as any).orientation || 'horizontal',
|
||||
};
|
||||
|
||||
return baseProps;
|
||||
};
|
||||
core: CoreVolumeSlider | null;
|
||||
}> = toConnectedHTMLComponent(VolumeSliderRootBase, useVolumeSliderRootState, getVolumeSliderRootProps, 'VolumeSliderRoot');
|
||||
|
||||
/**
|
||||
* VolumeRange Track props hook
|
||||
* Connected VolumeSlider Track component
|
||||
*/
|
||||
export const useVolumeRangeTrackProps: PropsHook<{}> = (_state, element) => {
|
||||
// Get orientation from parent root element if not provided in state
|
||||
const rootElement = element.closest('media-volume-range-root') as any;
|
||||
return {
|
||||
'data-orientation': rootElement?.orientation || 'horizontal',
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* VolumeRange Progress props hook
|
||||
*/
|
||||
export const useVolumeRangeProgressProps: PropsHook<{}> = (_state, element) => {
|
||||
// Get orientation from parent root element if not provided in state
|
||||
const rootElement = element.closest('media-volume-range-root') as any;
|
||||
return {
|
||||
'data-orientation': rootElement?.orientation || 'horizontal',
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* VolumeRange Thumb props hook
|
||||
*/
|
||||
export const useVolumeRangeThumbProps: PropsHook<{}> = (_state, element) => {
|
||||
// Get orientation from parent root element if not provided in state
|
||||
const rootElement = element.closest('media-volume-range-root') as any;
|
||||
return {
|
||||
'data-orientation': rootElement?.orientation || 'horizontal',
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Connected VolumeRange Root component using hook-style architecture
|
||||
*/
|
||||
export const VolumeRangeRoot: ConnectedComponentConstructor<{
|
||||
volume: number;
|
||||
muted: boolean;
|
||||
volumeLevel: string;
|
||||
requestVolumeChange: (volume: number) => void;
|
||||
core: CoreVolumeRange | null;
|
||||
}> = toConnectedHTMLComponent(VolumeRangeRootBase, useVolumeRangeRootState, useVolumeRangeRootProps, 'VolumeRangeRoot');
|
||||
|
||||
/**
|
||||
* Connected VolumeRange Track component
|
||||
*/
|
||||
export const VolumeRangeTrack: ConnectedComponentConstructor<any> = toConnectedHTMLComponent(
|
||||
VolumeRangeTrackBase,
|
||||
export const VolumeSliderTrack: ConnectedComponentConstructor<any> = toConnectedHTMLComponent(
|
||||
VolumeSliderTrackBase,
|
||||
{ keys: [], transform: () => ({}) },
|
||||
useVolumeRangeTrackProps,
|
||||
'VolumeRangeTrack',
|
||||
getVolumeSliderTrackProps,
|
||||
'VolumeSliderTrack',
|
||||
);
|
||||
|
||||
/**
|
||||
* Connected VolumeRange Progress component
|
||||
* Connected VolumeSlider Progress component
|
||||
*/
|
||||
export const VolumeRangeProgress: ConnectedComponentConstructor<any> = toConnectedHTMLComponent(
|
||||
VolumeRangeProgressBase,
|
||||
export const VolumeSliderProgress: ConnectedComponentConstructor<any> = toConnectedHTMLComponent(
|
||||
VolumeSliderProgressBase,
|
||||
{ keys: [], transform: () => ({}) },
|
||||
useVolumeRangeProgressProps,
|
||||
'VolumeRangeProgress',
|
||||
getVolumeSliderProgressProps,
|
||||
'VolumeSliderProgress',
|
||||
);
|
||||
|
||||
/**
|
||||
* Connected VolumeRange Thumb component
|
||||
* Connected VolumeSlider Thumb component
|
||||
*/
|
||||
export const VolumeRangeThumb: ConnectedComponentConstructor<any> = toConnectedHTMLComponent(
|
||||
VolumeRangeThumbBase,
|
||||
export const VolumeSliderThumb: ConnectedComponentConstructor<any> = toConnectedHTMLComponent(
|
||||
VolumeSliderThumbBase,
|
||||
{ keys: [], transform: () => ({}) },
|
||||
useVolumeRangeThumbProps,
|
||||
'VolumeRangeThumb',
|
||||
getVolumeSliderThumbProps,
|
||||
'VolumeSliderThumb',
|
||||
);
|
||||
|
||||
/**
|
||||
* Compound VolumeRange component object
|
||||
* Compound VolumeSlider component object
|
||||
*/
|
||||
export const VolumeRange = Object.assign(
|
||||
export const VolumeSlider = Object.assign(
|
||||
{},
|
||||
{
|
||||
Root: VolumeRangeRoot,
|
||||
Track: VolumeRangeTrack,
|
||||
Progress: VolumeRangeProgress,
|
||||
Thumb: VolumeRangeThumb,
|
||||
Root: VolumeSliderRoot,
|
||||
Track: VolumeSliderTrack,
|
||||
Progress: VolumeSliderProgress,
|
||||
Thumb: VolumeSliderThumb,
|
||||
},
|
||||
) as {
|
||||
Root: typeof VolumeRangeRoot;
|
||||
Track: typeof VolumeRangeTrack;
|
||||
Progress: typeof VolumeRangeProgress;
|
||||
Thumb: typeof VolumeRangeThumb;
|
||||
Root: typeof VolumeSliderRoot;
|
||||
Track: typeof VolumeSliderTrack;
|
||||
Progress: typeof VolumeSliderProgress;
|
||||
Thumb: typeof VolumeSliderThumb;
|
||||
};
|
||||
|
||||
// Register custom elements
|
||||
if (!globalThis.customElements.get('media-volume-range-root')) {
|
||||
// @ts-ignore - Custom element constructor compatibility
|
||||
globalThis.customElements.define('media-volume-range-root', VolumeRangeRoot);
|
||||
if (!globalThis.customElements.get('media-volume-slider-root')) {
|
||||
globalThis.customElements.define('media-volume-slider-root', VolumeSliderRoot);
|
||||
}
|
||||
|
||||
if (!globalThis.customElements.get('media-volume-range-track')) {
|
||||
// @ts-ignore - Custom element constructor compatibility
|
||||
globalThis.customElements.define('media-volume-range-track', VolumeRangeTrack);
|
||||
if (!globalThis.customElements.get('media-volume-slider-track')) {
|
||||
globalThis.customElements.define('media-volume-slider-track', VolumeSliderTrack);
|
||||
}
|
||||
|
||||
if (!globalThis.customElements.get('media-volume-range-progress')) {
|
||||
// @ts-ignore - Custom element constructor compatibility
|
||||
globalThis.customElements.define('media-volume-range-progress', VolumeRangeProgress);
|
||||
if (!globalThis.customElements.get('media-volume-slider-progress')) {
|
||||
globalThis.customElements.define('media-volume-slider-progress', VolumeSliderProgress);
|
||||
}
|
||||
|
||||
if (!globalThis.customElements.get('media-volume-range-thumb')) {
|
||||
// @ts-ignore - Custom element constructor compatibility
|
||||
globalThis.customElements.define('media-volume-range-thumb', VolumeRangeThumb);
|
||||
if (!globalThis.customElements.get('media-volume-slider-thumb')) {
|
||||
globalThis.customElements.define('media-volume-slider-thumb', VolumeSliderThumb);
|
||||
}
|
||||
|
||||
export default VolumeRange;
|
||||
export default VolumeSlider;
|
||||
@@ -5,7 +5,7 @@ export { FullscreenButton } from './components/media-fullscreen-button.js';
|
||||
export { MuteButton } from './components/media-mute-button.js';
|
||||
// New hook-style components
|
||||
export { PlayButton } from './components/media-play-button.js';
|
||||
export { VolumeRange } from './components/media-volume-range.js';
|
||||
export { VolumeSlider } from './components/media-volume-slider.js';
|
||||
export * as MediaProvider from './media-provider.js';
|
||||
export * as MediaThemeDefault from './skins/media-skin-default.js';
|
||||
|
||||
|
||||
@@ -4,8 +4,8 @@ import { uniqueId } from '../utils/element-utils';
|
||||
import '../media-container';
|
||||
import '../components/media-play-button';
|
||||
import '../components/media-mute-button';
|
||||
import '../components/media-volume-range';
|
||||
import '../components/media-time-range';
|
||||
import '../components/media-volume-slider';
|
||||
import '../components/media-time-slider';
|
||||
import '../components/media-fullscreen-button';
|
||||
import '../components/media-duration-display';
|
||||
import '../components/media-current-time-display';
|
||||
@@ -114,8 +114,8 @@ export function getTemplateHTML() {
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
/* TimeRange Component Styles */
|
||||
media-time-range-root {
|
||||
/* TimeSlider Component Styles */
|
||||
media-time-slider-root {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
@@ -126,21 +126,21 @@ export function getTemplateHTML() {
|
||||
}
|
||||
|
||||
/* Horizontal orientation styles */
|
||||
media-time-range-root[data-orientation="horizontal"] {
|
||||
media-time-slider-root[data-orientation="horizontal"] {
|
||||
min-width: 100px;
|
||||
width: 100%;
|
||||
height: 20px;
|
||||
}
|
||||
|
||||
/* Vertical orientation styles */
|
||||
media-time-range-root[data-orientation="vertical"] {
|
||||
media-time-slider-root[data-orientation="vertical"] {
|
||||
min-width: 20px;
|
||||
width: 20px;
|
||||
height: 100px;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
media-time-range-track {
|
||||
media-time-slider-track {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: .375rem;
|
||||
@@ -151,18 +151,18 @@ export function getTemplateHTML() {
|
||||
}
|
||||
|
||||
/* Horizontal track styles */
|
||||
media-time-range-track[data-orientation="horizontal"] {
|
||||
media-time-slider-track[data-orientation="horizontal"] {
|
||||
width: 100%;
|
||||
height: .375rem;
|
||||
}
|
||||
|
||||
/* Vertical track styles */
|
||||
media-time-range-track[data-orientation="vertical"] {
|
||||
media-time-slider-track[data-orientation="vertical"] {
|
||||
width: .375rem;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
media-time-range-thumb {
|
||||
media-time-slider-thumb {
|
||||
width: .75rem;
|
||||
height: .75rem;
|
||||
background-color: #fff;
|
||||
@@ -170,12 +170,12 @@ export function getTemplateHTML() {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
media-time-range-pointer {
|
||||
media-time-slider-pointer {
|
||||
background-color: rgba(255, 255, 255, .5);
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
media-time-range-progress {
|
||||
media-time-slider-progress {
|
||||
background-color: #007bff;
|
||||
border-radius: inherit;
|
||||
}
|
||||
@@ -185,8 +185,8 @@ export function getTemplateHTML() {
|
||||
padding: 14px 0;
|
||||
}
|
||||
|
||||
/* VolumeRange Component Styles */
|
||||
media-volume-range-root {
|
||||
/* VolumeSlider Component Styles */
|
||||
media-volume-slider-root {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
@@ -197,21 +197,21 @@ export function getTemplateHTML() {
|
||||
}
|
||||
|
||||
/* Horizontal orientation styles */
|
||||
media-volume-range-root[data-orientation="horizontal"] {
|
||||
media-volume-slider-root[data-orientation="horizontal"] {
|
||||
min-width: 80px;
|
||||
width: 80px;
|
||||
height: 20px;
|
||||
}
|
||||
|
||||
/* Vertical orientation styles */
|
||||
media-volume-range-root[data-orientation="vertical"] {
|
||||
media-volume-slider-root[data-orientation="vertical"] {
|
||||
min-width: 20px;
|
||||
width: 20px;
|
||||
height: 80px;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
media-volume-range-track {
|
||||
media-volume-slider-track {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: .375rem;
|
||||
@@ -222,18 +222,18 @@ export function getTemplateHTML() {
|
||||
}
|
||||
|
||||
/* Horizontal track styles */
|
||||
media-volume-range-track[data-orientation="horizontal"] {
|
||||
media-volume-slider-track[data-orientation="horizontal"] {
|
||||
width: 100%;
|
||||
height: .375rem;
|
||||
}
|
||||
|
||||
/* Vertical track styles */
|
||||
media-volume-range-track[data-orientation="vertical"] {
|
||||
media-volume-slider-track[data-orientation="vertical"] {
|
||||
width: .375rem;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
media-volume-range-thumb {
|
||||
media-volume-slider-thumb {
|
||||
width: .75rem;
|
||||
height: .75rem;
|
||||
background-color: #fff;
|
||||
@@ -241,7 +241,7 @@ export function getTemplateHTML() {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
media-volume-range-progress {
|
||||
media-volume-slider-progress {
|
||||
background-color: #007bff;
|
||||
border-radius: inherit;
|
||||
}
|
||||
@@ -302,13 +302,13 @@ export function getTemplateHTML() {
|
||||
</media-tooltip-root>
|
||||
<!-- Use the show-remaining attribute to show count down/remaining time -->
|
||||
<media-current-time-display show-remaining></media-current-time-display>
|
||||
<media-time-range-root>
|
||||
<media-time-range-track>
|
||||
<media-time-range-progress></media-time-range-progress>
|
||||
<media-time-range-pointer></media-time-range-pointer>
|
||||
</media-time-range-track>
|
||||
<media-time-range-thumb></media-time-range-thumb>
|
||||
</media-time-range-root>
|
||||
<media-time-slider-root>
|
||||
<media-time-slider-track>
|
||||
<media-time-slider-progress></media-time-slider-progress>
|
||||
<media-time-slider-pointer></media-time-slider-pointer>
|
||||
</media-time-slider-track>
|
||||
<media-time-slider-thumb></media-time-slider-thumb>
|
||||
</media-time-slider-root>
|
||||
<media-duration-display></media-duration-display>
|
||||
<media-popover-root open-on-hover delay="200" close-delay="100">
|
||||
<media-popover-trigger>
|
||||
@@ -321,12 +321,12 @@ export function getTemplateHTML() {
|
||||
<media-popover-portal root-id="${portalId}">
|
||||
<media-popover-positioner side="top" side-offset="0">
|
||||
<media-popover-popup>
|
||||
<media-volume-range-root orientation="vertical">
|
||||
<media-volume-range-track>
|
||||
<media-volume-range-progress></media-volume-range-progress>
|
||||
</media-volume-range-track>
|
||||
<media-volume-range-thumb></media-volume-range-thumb>
|
||||
</media-volume-range-root>
|
||||
<media-volume-slider-root orientation="vertical">
|
||||
<media-volume-slider-track>
|
||||
<media-volume-slider-progress></media-volume-slider-progress>
|
||||
</media-volume-slider-track>
|
||||
<media-volume-slider-thumb></media-volume-slider-thumb>
|
||||
</media-volume-slider-root>
|
||||
</media-popover-popup>
|
||||
</media-popover-positioner>
|
||||
</media-popover-portal>
|
||||
|
||||
+58
-71
@@ -1,28 +1,26 @@
|
||||
import type { ConnectedComponent } from '../utils/component-factory';
|
||||
|
||||
import { TimeRange as CoreTimeRange } from '@vjs-10/core';
|
||||
import { TimeSlider as CoreTimeSlider } from '@vjs-10/core';
|
||||
|
||||
import { timeRangeStateDefinition } from '@vjs-10/media-store';
|
||||
import { timeSliderStateDefinition } from '@vjs-10/media-store';
|
||||
import { shallowEqual, useMediaSelector, useMediaStore } from '@vjs-10/react-media-store';
|
||||
import { useCallback, useMemo } from 'react';
|
||||
|
||||
import { toConnectedComponent, toContextComponent, useCore } from '../utils/component-factory';
|
||||
|
||||
export namespace TimeRange {
|
||||
export interface State {
|
||||
currentTime: number;
|
||||
duration: number;
|
||||
requestSeek: (time: number) => void;
|
||||
core: CoreTimeRange;
|
||||
orientation: 'horizontal' | 'vertical';
|
||||
}
|
||||
|
||||
export interface Props extends React.ComponentProps<'div'> {
|
||||
orientation?: 'horizontal' | 'vertical';
|
||||
}
|
||||
export interface TimeSliderState {
|
||||
currentTime: number;
|
||||
duration: number;
|
||||
requestSeek: (time: number) => void;
|
||||
core: CoreTimeSlider;
|
||||
orientation: 'horizontal' | 'vertical';
|
||||
}
|
||||
|
||||
interface TimeRangeRenderProps extends React.ComponentProps<'div'> {
|
||||
export interface TimeSliderProps extends React.ComponentProps<'div'> {
|
||||
orientation?: 'horizontal' | 'vertical';
|
||||
}
|
||||
|
||||
interface TimeSliderRenderProps extends React.ComponentProps<'div'> {
|
||||
'data-orientation'?: 'horizontal' | 'vertical';
|
||||
'data-current-time'?: number;
|
||||
'data-duration'?: number;
|
||||
@@ -32,12 +30,12 @@ interface TimeRangeRenderProps extends React.ComponentProps<'div'> {
|
||||
// ROOT COMPONENT
|
||||
// ============================================================================
|
||||
|
||||
export function useTimeRangeRootState(props: TimeRange.Props): TimeRange.State {
|
||||
export function useTimeSliderRootState(props: TimeSliderProps): TimeSliderState {
|
||||
const { orientation = 'horizontal' } = props;
|
||||
const mediaStore = useMediaStore();
|
||||
const mediaState = useMediaSelector(timeRangeStateDefinition.stateTransform, shallowEqual);
|
||||
const mediaMethods = useMemo(() => timeRangeStateDefinition.createRequestMethods(mediaStore.dispatch), [mediaStore]);
|
||||
const core = useCore(CoreTimeRange, { ...mediaState, ...mediaMethods });
|
||||
const mediaState = useMediaSelector(timeSliderStateDefinition.stateTransform, shallowEqual);
|
||||
const mediaMethods = useMemo(() => timeSliderStateDefinition.createRequestMethods(mediaStore.dispatch), [mediaStore]);
|
||||
const core = useCore(CoreTimeSlider, { ...mediaState, ...mediaMethods });
|
||||
|
||||
return {
|
||||
...mediaState,
|
||||
@@ -47,7 +45,7 @@ export function useTimeRangeRootState(props: TimeRange.Props): TimeRange.State {
|
||||
};
|
||||
}
|
||||
|
||||
export function useTimeRangeRootProps(props: TimeRange.Props, state: TimeRange.State): TimeRangeRenderProps {
|
||||
export function useTimeSliderRootProps(props: TimeSliderProps, state: TimeSliderState): TimeSliderRenderProps {
|
||||
const { _fillWidth, _pointerWidth, _currentTimeText, _durationText } = state.core.getState();
|
||||
|
||||
const { children, className, id, style, orientation = 'horizontal' } = props;
|
||||
@@ -78,25 +76,22 @@ export function useTimeRangeRootProps(props: TimeRange.Props, state: TimeRange.S
|
||||
};
|
||||
}
|
||||
|
||||
type useTimeRangeRootState = typeof useTimeRangeRootState;
|
||||
type useTimeRangeRootProps = typeof useTimeRangeRootProps;
|
||||
|
||||
export function renderTimeRangeRoot(props: TimeRangeRenderProps): JSX.Element {
|
||||
export function renderTimeSliderRoot(props: TimeSliderRenderProps): JSX.Element {
|
||||
return <div {...props} />;
|
||||
}
|
||||
|
||||
const TimeRangeRoot: ConnectedComponent<TimeRange.Props, typeof renderTimeRangeRoot> = toConnectedComponent(
|
||||
useTimeRangeRootState,
|
||||
useTimeRangeRootProps,
|
||||
renderTimeRangeRoot,
|
||||
'TimeRange.Root',
|
||||
const TimeSliderRoot: ConnectedComponent<TimeSliderProps, typeof renderTimeSliderRoot> = toConnectedComponent(
|
||||
useTimeSliderRootState,
|
||||
useTimeSliderRootProps,
|
||||
renderTimeSliderRoot,
|
||||
'TimeSlider.Root',
|
||||
);
|
||||
|
||||
// ============================================================================
|
||||
// TRACK COMPONENT
|
||||
// ============================================================================
|
||||
|
||||
export function useTimeRangeTrackProps(props: React.ComponentProps<'div'>, context: TimeRange.State): TimeRangeRenderProps {
|
||||
export function useTimeSliderTrackProps(props: React.ComponentProps<'div'>, context: TimeSliderState): TimeSliderRenderProps {
|
||||
return {
|
||||
ref: useCallback((el: HTMLDivElement) => {
|
||||
context.core?.setState({ _trackElement: el });
|
||||
@@ -110,23 +105,21 @@ export function useTimeRangeTrackProps(props: React.ComponentProps<'div'>, conte
|
||||
};
|
||||
}
|
||||
|
||||
type useTimeRangeTrackProps = typeof useTimeRangeTrackProps;
|
||||
|
||||
export function renderTimeRangeTrack(props: TimeRangeRenderProps): JSX.Element {
|
||||
export function renderTimeSliderTrack(props: TimeSliderRenderProps): JSX.Element {
|
||||
return <div {...props} />;
|
||||
}
|
||||
|
||||
const TimeRangeTrack: ConnectedComponent<React.ComponentProps<'div'>, typeof renderTimeRangeTrack> = toContextComponent(
|
||||
useTimeRangeTrackProps,
|
||||
renderTimeRangeTrack,
|
||||
'TimeRange.Track',
|
||||
const TimeSliderTrack: ConnectedComponent<React.ComponentProps<'div'>, typeof renderTimeSliderTrack> = toContextComponent(
|
||||
useTimeSliderTrackProps,
|
||||
renderTimeSliderTrack,
|
||||
'TimeSlider.Track',
|
||||
);
|
||||
|
||||
// ============================================================================
|
||||
// THUMB COMPONENT
|
||||
// ============================================================================
|
||||
|
||||
export function useTimeRangeThumbProps(props: React.ComponentProps<'div'>, context: TimeRange.State): TimeRangeRenderProps {
|
||||
export function getTimeSliderThumbProps(props: React.ComponentProps<'div'>, context: TimeSliderState): TimeSliderRenderProps {
|
||||
return {
|
||||
'data-orientation': context.orientation,
|
||||
...props,
|
||||
@@ -140,23 +133,21 @@ export function useTimeRangeThumbProps(props: React.ComponentProps<'div'>, conte
|
||||
};
|
||||
}
|
||||
|
||||
type useTimeRangeThumbProps = typeof useTimeRangeThumbProps;
|
||||
|
||||
export function renderTimeRangeThumb(props: TimeRangeRenderProps): JSX.Element {
|
||||
export function renderTimeSliderThumb(props: TimeSliderRenderProps): JSX.Element {
|
||||
return <div {...props} />;
|
||||
}
|
||||
|
||||
const TimeRangeThumb: ConnectedComponent<React.ComponentProps<'div'>, typeof renderTimeRangeThumb> = toContextComponent(
|
||||
useTimeRangeThumbProps,
|
||||
renderTimeRangeThumb,
|
||||
'TimeRange.Thumb',
|
||||
const TimeSliderThumb: ConnectedComponent<React.ComponentProps<'div'>, typeof renderTimeSliderThumb> = toContextComponent(
|
||||
getTimeSliderThumbProps,
|
||||
renderTimeSliderThumb,
|
||||
'TimeSlider.Thumb',
|
||||
);
|
||||
|
||||
// ============================================================================
|
||||
// POINTER COMPONENT
|
||||
// ============================================================================
|
||||
|
||||
export function useTimeRangePointerProps(props: React.ComponentProps<'div'>, context: TimeRange.State): TimeRangeRenderProps {
|
||||
export function getTimeSliderPointerProps(props: React.ComponentProps<'div'>, context: TimeSliderState): TimeSliderRenderProps {
|
||||
return {
|
||||
'data-orientation': context.orientation,
|
||||
...props,
|
||||
@@ -169,22 +160,20 @@ export function useTimeRangePointerProps(props: React.ComponentProps<'div'>, con
|
||||
};
|
||||
}
|
||||
|
||||
type useTimeRangePointerProps = typeof useTimeRangePointerProps;
|
||||
|
||||
export function renderTimeRangePointer(props: TimeRangeRenderProps): JSX.Element {
|
||||
export function renderTimeSliderPointer(props: TimeSliderRenderProps): JSX.Element {
|
||||
return <div {...props} />;
|
||||
}
|
||||
|
||||
const TimeRangePointer: ConnectedComponent<
|
||||
const TimeSliderPointer: ConnectedComponent<
|
||||
React.ComponentProps<'div'>,
|
||||
typeof renderTimeRangePointer
|
||||
> = toContextComponent(useTimeRangePointerProps, renderTimeRangePointer, 'TimeRange.Pointer');
|
||||
typeof renderTimeSliderPointer
|
||||
> = toContextComponent(getTimeSliderPointerProps, renderTimeSliderPointer, 'TimeSlider.Pointer');
|
||||
|
||||
// ============================================================================
|
||||
// PROGRESS COMPONENT
|
||||
// ============================================================================
|
||||
|
||||
export function useTimeRangeProgressProps(props: React.ComponentProps<'div'>, context: TimeRange.State): TimeRangeRenderProps {
|
||||
export function getTimeSliderProgressProps(props: React.ComponentProps<'div'>, context: TimeSliderState): TimeSliderRenderProps {
|
||||
return {
|
||||
'data-orientation': context.orientation,
|
||||
...props,
|
||||
@@ -198,36 +187,34 @@ export function useTimeRangeProgressProps(props: React.ComponentProps<'div'>, co
|
||||
};
|
||||
}
|
||||
|
||||
type useTimeRangeProgressProps = typeof useTimeRangeProgressProps;
|
||||
|
||||
export function renderTimeRangeProgress(props: TimeRangeRenderProps): JSX.Element {
|
||||
export function renderTimeSliderProgress(props: TimeSliderRenderProps): JSX.Element {
|
||||
return <div {...props} />;
|
||||
}
|
||||
|
||||
const TimeRangeProgress: ConnectedComponent<
|
||||
const TimeSliderProgress: ConnectedComponent<
|
||||
React.ComponentProps<'div'>,
|
||||
typeof renderTimeRangeProgress
|
||||
> = toContextComponent(useTimeRangeProgressProps, renderTimeRangeProgress, 'TimeRange.Progress');
|
||||
typeof renderTimeSliderProgress
|
||||
> = toContextComponent(getTimeSliderProgressProps, renderTimeSliderProgress, 'TimeSlider.Progress');
|
||||
|
||||
// ============================================================================
|
||||
// EXPORTS
|
||||
// ============================================================================
|
||||
|
||||
export const TimeRange = Object.assign(
|
||||
export const TimeSlider = Object.assign(
|
||||
{},
|
||||
{
|
||||
Root: TimeRangeRoot,
|
||||
Track: TimeRangeTrack,
|
||||
Thumb: TimeRangeThumb,
|
||||
Pointer: TimeRangePointer,
|
||||
Progress: TimeRangeProgress,
|
||||
Root: TimeSliderRoot,
|
||||
Track: TimeSliderTrack,
|
||||
Thumb: TimeSliderThumb,
|
||||
Pointer: TimeSliderPointer,
|
||||
Progress: TimeSliderProgress,
|
||||
},
|
||||
) as {
|
||||
Root: typeof TimeRangeRoot;
|
||||
Track: typeof TimeRangeTrack;
|
||||
Thumb: typeof TimeRangeThumb;
|
||||
Pointer: typeof TimeRangePointer;
|
||||
Progress: typeof TimeRangeProgress;
|
||||
Root: typeof TimeSliderRoot;
|
||||
Track: typeof TimeSliderTrack;
|
||||
Thumb: typeof TimeSliderThumb;
|
||||
Pointer: typeof TimeSliderPointer;
|
||||
Progress: typeof TimeSliderProgress;
|
||||
};
|
||||
|
||||
export default TimeRange;
|
||||
export default TimeSlider;
|
||||
+50
-61
@@ -1,29 +1,27 @@
|
||||
import type { ConnectedComponent } from '../utils/component-factory';
|
||||
|
||||
import { VolumeRange as CoreVolumeRange } from '@vjs-10/core';
|
||||
import { VolumeSlider as CoreVolumeSlider } from '@vjs-10/core';
|
||||
|
||||
import { volumeRangeStateDefinition } from '@vjs-10/media-store';
|
||||
import { volumeSliderStateDefinition } from '@vjs-10/media-store';
|
||||
import { shallowEqual, useMediaSelector, useMediaStore } from '@vjs-10/react-media-store';
|
||||
import { useCallback, useMemo } from 'react';
|
||||
|
||||
import { toConnectedComponent, toContextComponent, useCore } from '../utils/component-factory';
|
||||
|
||||
export namespace VolumeRange {
|
||||
export interface State {
|
||||
volume: number;
|
||||
muted: boolean;
|
||||
volumeLevel: string;
|
||||
requestVolumeChange: (volume: number) => void;
|
||||
core: CoreVolumeRange;
|
||||
orientation: 'horizontal' | 'vertical';
|
||||
}
|
||||
|
||||
export interface Props extends React.ComponentProps<'div'> {
|
||||
orientation?: 'horizontal' | 'vertical';
|
||||
}
|
||||
export interface VolumeSliderState {
|
||||
volume: number;
|
||||
muted: boolean;
|
||||
volumeLevel: string;
|
||||
requestVolumeChange: (volume: number) => void;
|
||||
core: CoreVolumeSlider;
|
||||
orientation: 'horizontal' | 'vertical';
|
||||
}
|
||||
|
||||
interface VolumeRangeRenderProps extends React.ComponentProps<'div'> {
|
||||
export interface VolumeSliderProps extends React.ComponentProps<'div'> {
|
||||
orientation?: 'horizontal' | 'vertical';
|
||||
}
|
||||
|
||||
interface VolumeSliderRenderProps extends React.ComponentProps<'div'> {
|
||||
'data-orientation'?: 'horizontal' | 'vertical';
|
||||
'data-muted'?: boolean;
|
||||
'data-volume-level'?: string;
|
||||
@@ -33,15 +31,15 @@ interface VolumeRangeRenderProps extends React.ComponentProps<'div'> {
|
||||
// ROOT COMPONENT
|
||||
// ============================================================================
|
||||
|
||||
export function useVolumeRangeRootState(props: VolumeRange.Props): VolumeRange.State {
|
||||
export function useVolumeSliderRootState(props: VolumeSliderProps): VolumeSliderState {
|
||||
const { orientation = 'horizontal' } = props;
|
||||
const mediaStore = useMediaStore();
|
||||
const mediaState = useMediaSelector(volumeRangeStateDefinition.stateTransform, shallowEqual);
|
||||
const mediaState = useMediaSelector(volumeSliderStateDefinition.stateTransform, shallowEqual);
|
||||
const mediaMethods = useMemo(
|
||||
() => volumeRangeStateDefinition.createRequestMethods(mediaStore.dispatch),
|
||||
() => volumeSliderStateDefinition.createRequestMethods(mediaStore.dispatch),
|
||||
[mediaStore],
|
||||
);
|
||||
const core = useCore(CoreVolumeRange, { ...mediaState, ...mediaMethods });
|
||||
const core = useCore(CoreVolumeSlider, { ...mediaState, ...mediaMethods });
|
||||
|
||||
return {
|
||||
...mediaState,
|
||||
@@ -51,7 +49,7 @@ export function useVolumeRangeRootState(props: VolumeRange.Props): VolumeRange.S
|
||||
};
|
||||
}
|
||||
|
||||
export function useVolumeRangeRootProps(props: VolumeRange.Props, state: VolumeRange.State): VolumeRangeRenderProps {
|
||||
export function useVolumeSliderRootProps(props: VolumeSliderProps, state: VolumeSliderState): VolumeSliderRenderProps {
|
||||
const { _fillWidth, _pointerWidth, _volumeText } = state.core.getState();
|
||||
|
||||
const { children, className, id, style, orientation = 'horizontal' } = props;
|
||||
@@ -82,25 +80,22 @@ export function useVolumeRangeRootProps(props: VolumeRange.Props, state: VolumeR
|
||||
};
|
||||
}
|
||||
|
||||
type useVolumeRangeRootState = typeof useVolumeRangeRootState;
|
||||
type useVolumeRangeRootProps = typeof useVolumeRangeRootProps;
|
||||
|
||||
export function renderVolumeRangeRoot(props: VolumeRangeRenderProps): JSX.Element {
|
||||
export function renderVolumeSliderRoot(props: VolumeSliderRenderProps): JSX.Element {
|
||||
return <div {...props} />;
|
||||
}
|
||||
|
||||
const VolumeRangeRoot: ConnectedComponent<VolumeRange.Props, typeof renderVolumeRangeRoot> = toConnectedComponent(
|
||||
useVolumeRangeRootState,
|
||||
useVolumeRangeRootProps,
|
||||
renderVolumeRangeRoot,
|
||||
'VolumeRange.Root',
|
||||
const VolumeSliderRoot: ConnectedComponent<VolumeSliderProps, typeof renderVolumeSliderRoot> = toConnectedComponent(
|
||||
useVolumeSliderRootState,
|
||||
useVolumeSliderRootProps,
|
||||
renderVolumeSliderRoot,
|
||||
'VolumeSlider.Root',
|
||||
);
|
||||
|
||||
// ============================================================================
|
||||
// TRACK COMPONENT
|
||||
// ============================================================================
|
||||
|
||||
export function useVolumeRangeTrackProps(props: React.ComponentProps<'div'>, context: VolumeRange.State): VolumeRangeRenderProps {
|
||||
export function useVolumeSliderTrackProps(props: React.ComponentProps<'div'>, context: VolumeSliderState): VolumeSliderRenderProps {
|
||||
return {
|
||||
ref: useCallback((el: HTMLDivElement) => {
|
||||
context.core?.setState({ _trackElement: el });
|
||||
@@ -114,22 +109,20 @@ export function useVolumeRangeTrackProps(props: React.ComponentProps<'div'>, con
|
||||
};
|
||||
}
|
||||
|
||||
type useVolumeRangeTrackProps = typeof useVolumeRangeTrackProps;
|
||||
|
||||
export function renderVolumeRangeTrack(props: VolumeRangeRenderProps): JSX.Element {
|
||||
export function renderVolumeSliderTrack(props: VolumeSliderRenderProps): JSX.Element {
|
||||
return <div {...props} />;
|
||||
}
|
||||
|
||||
const VolumeRangeTrack: ConnectedComponent<
|
||||
const VolumeSliderTrack: ConnectedComponent<
|
||||
React.ComponentProps<'div'>,
|
||||
typeof renderVolumeRangeTrack
|
||||
> = toContextComponent(useVolumeRangeTrackProps, renderVolumeRangeTrack, 'VolumeRange.Track');
|
||||
typeof renderVolumeSliderTrack
|
||||
> = toContextComponent(useVolumeSliderTrackProps, renderVolumeSliderTrack, 'VolumeSlider.Track');
|
||||
|
||||
// ============================================================================
|
||||
// THUMB COMPONENT
|
||||
// ============================================================================
|
||||
|
||||
export function useVolumeRangeThumbProps(props: React.ComponentProps<'div'>, context: VolumeRange.State): VolumeRangeRenderProps {
|
||||
export function getVolumeSliderThumbProps(props: React.ComponentProps<'div'>, context: VolumeSliderState): VolumeSliderRenderProps {
|
||||
return {
|
||||
'data-orientation': context.orientation,
|
||||
...props,
|
||||
@@ -143,22 +136,20 @@ export function useVolumeRangeThumbProps(props: React.ComponentProps<'div'>, con
|
||||
};
|
||||
}
|
||||
|
||||
type useVolumeRangeThumbProps = typeof useVolumeRangeThumbProps;
|
||||
|
||||
export function renderVolumeRangeThumb(props: VolumeRangeRenderProps): JSX.Element {
|
||||
export function renderVolumeSliderThumb(props: VolumeSliderRenderProps): JSX.Element {
|
||||
return <div {...props} />;
|
||||
}
|
||||
|
||||
const VolumeRangeThumb: ConnectedComponent<
|
||||
const VolumeSliderThumb: ConnectedComponent<
|
||||
React.ComponentProps<'div'>,
|
||||
typeof renderVolumeRangeThumb
|
||||
> = toContextComponent(useVolumeRangeThumbProps, renderVolumeRangeThumb, 'VolumeRange.Thumb');
|
||||
typeof renderVolumeSliderThumb
|
||||
> = toContextComponent(getVolumeSliderThumbProps, renderVolumeSliderThumb, 'VolumeSlider.Thumb');
|
||||
|
||||
// ============================================================================
|
||||
// PROGRESS COMPONENT
|
||||
// ============================================================================
|
||||
|
||||
export function useVolumeRangeProgressProps(props: React.ComponentProps<'div'>, context: VolumeRange.State): VolumeRangeRenderProps {
|
||||
export function getVolumeSliderProgressProps(props: React.ComponentProps<'div'>, context: VolumeSliderState): VolumeSliderRenderProps {
|
||||
return {
|
||||
'data-orientation': context.orientation,
|
||||
...props,
|
||||
@@ -172,34 +163,32 @@ export function useVolumeRangeProgressProps(props: React.ComponentProps<'div'>,
|
||||
};
|
||||
}
|
||||
|
||||
type useVolumeRangeProgressProps = typeof useVolumeRangeProgressProps;
|
||||
|
||||
export function renderVolumeRangeProgress(props: VolumeRangeRenderProps): JSX.Element {
|
||||
export function renderVolumeSliderProgress(props: VolumeSliderRenderProps): JSX.Element {
|
||||
return <div {...props} />;
|
||||
}
|
||||
|
||||
const VolumeRangeProgress: ConnectedComponent<
|
||||
const VolumeSliderProgress: ConnectedComponent<
|
||||
React.ComponentProps<'div'>,
|
||||
typeof renderVolumeRangeProgress
|
||||
> = toContextComponent(useVolumeRangeProgressProps, renderVolumeRangeProgress, 'VolumeRange.Progress');
|
||||
typeof renderVolumeSliderProgress
|
||||
> = toContextComponent(getVolumeSliderProgressProps, renderVolumeSliderProgress, 'VolumeSlider.Progress');
|
||||
|
||||
// ============================================================================
|
||||
// EXPORTS
|
||||
// ============================================================================
|
||||
|
||||
export const VolumeRange = Object.assign(
|
||||
export const VolumeSlider = Object.assign(
|
||||
{},
|
||||
{
|
||||
Root: VolumeRangeRoot,
|
||||
Track: VolumeRangeTrack,
|
||||
Thumb: VolumeRangeThumb,
|
||||
Progress: VolumeRangeProgress,
|
||||
Root: VolumeSliderRoot,
|
||||
Track: VolumeSliderTrack,
|
||||
Thumb: VolumeSliderThumb,
|
||||
Progress: VolumeSliderProgress,
|
||||
},
|
||||
) as {
|
||||
Root: typeof VolumeRangeRoot;
|
||||
Track: typeof VolumeRangeTrack;
|
||||
Thumb: typeof VolumeRangeThumb;
|
||||
Progress: typeof VolumeRangeProgress;
|
||||
Root: typeof VolumeSliderRoot;
|
||||
Track: typeof VolumeSliderTrack;
|
||||
Thumb: typeof VolumeSliderThumb;
|
||||
Progress: typeof VolumeSliderProgress;
|
||||
};
|
||||
|
||||
export default VolumeRange;
|
||||
export default VolumeSlider;
|
||||
@@ -7,6 +7,6 @@ export { MuteButton } from './components/MuteButton';
|
||||
export { PlayButton } from './components/PlayButton';
|
||||
export { Popover } from './components/Popover';
|
||||
export { MediaElementVideo, Video } from './components/Video';
|
||||
export { VolumeRange } from './components/VolumeRange';
|
||||
export { VolumeSlider } from './components/VolumeSlider';
|
||||
export * from './skins';
|
||||
export * from '@vjs-10/react-media-store';
|
||||
|
||||
@@ -19,9 +19,9 @@ import { MediaContainer } from '../../components/MediaContainer';
|
||||
import MuteButton from '../../components/MuteButton';
|
||||
import PlayButton from '../../components/PlayButton';
|
||||
import { Popover } from '../../components/Popover';
|
||||
import { TimeRange } from '../../components/TimeRange';
|
||||
import { TimeSlider } from '../../components/TimeSlider';
|
||||
import { Tooltip } from '../../components/Tooltip';
|
||||
import { VolumeRange } from '../../components/VolumeRange';
|
||||
import { VolumeSlider } from '../../components/VolumeSlider';
|
||||
import styles from './styles';
|
||||
|
||||
type SkinProps = PropsWithChildren<{
|
||||
@@ -62,13 +62,13 @@ export default function MediaSkinDefault({ children, className = '' }: SkinProps
|
||||
className={styles.TimeDisplay}
|
||||
/>
|
||||
|
||||
<TimeRange.Root className={styles.SliderRoot}>
|
||||
<TimeRange.Track className={styles.SliderTrack}>
|
||||
<TimeRange.Progress className={styles.SliderProgress} />
|
||||
<TimeRange.Pointer className={styles.SliderPointer} />
|
||||
</TimeRange.Track>
|
||||
<TimeRange.Thumb className={styles.SliderThumb} />
|
||||
</TimeRange.Root>
|
||||
<TimeSlider.Root className={styles.SliderRoot}>
|
||||
<TimeSlider.Track className={styles.SliderTrack}>
|
||||
<TimeSlider.Progress className={styles.SliderProgress} />
|
||||
<TimeSlider.Pointer className={styles.SliderPointer} />
|
||||
</TimeSlider.Track>
|
||||
<TimeSlider.Thumb className={styles.SliderThumb} />
|
||||
</TimeSlider.Root>
|
||||
|
||||
<DurationDisplay className={styles.TimeDisplay} />
|
||||
</div>
|
||||
@@ -84,12 +84,12 @@ export default function MediaSkinDefault({ children, className = '' }: SkinProps
|
||||
<Popover.Portal rootId={portalId}>
|
||||
<Popover.Positioner side="top" sideOffset={12}>
|
||||
<Popover.Popup className={styles.PopoverPopup}>
|
||||
<VolumeRange.Root className={styles.SliderRoot} orientation="vertical">
|
||||
<VolumeRange.Track className={styles.SliderTrack}>
|
||||
<VolumeRange.Progress className={styles.SliderProgress} />
|
||||
</VolumeRange.Track>
|
||||
<VolumeRange.Thumb className={styles.SliderThumb} />
|
||||
</VolumeRange.Root>
|
||||
<VolumeSlider.Root className={styles.SliderRoot} orientation="vertical">
|
||||
<VolumeSlider.Track className={styles.SliderTrack}>
|
||||
<VolumeSlider.Progress className={styles.SliderProgress} />
|
||||
</VolumeSlider.Track>
|
||||
<VolumeSlider.Thumb className={styles.SliderThumb} />
|
||||
</VolumeSlider.Root>
|
||||
</Popover.Popup>
|
||||
</Popover.Positioner>
|
||||
</Popover.Portal>
|
||||
|
||||
@@ -108,8 +108,8 @@
|
||||
}
|
||||
}
|
||||
|
||||
/* TimeRange Component Styles */
|
||||
.TimeRangeRoot {
|
||||
/* TimeSlider Component Styles */
|
||||
.TimeSliderRoot {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
position: relative;
|
||||
@@ -119,7 +119,7 @@
|
||||
margin: 0 0.5rem;
|
||||
}
|
||||
|
||||
.TimeRangeTrack {
|
||||
.TimeSliderTrack {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 0.375rem;
|
||||
@@ -128,7 +128,7 @@
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.TimeRangeThumb {
|
||||
.TimeSliderThumb {
|
||||
width: 0.75rem;
|
||||
height: 0.75rem;
|
||||
background-color: #fff;
|
||||
@@ -136,18 +136,18 @@
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.TimeRangePointer {
|
||||
.TimeSliderPointer {
|
||||
background-color: rgba(255, 255, 255, 0.5);
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.TimeRangeProgress {
|
||||
.TimeSliderProgress {
|
||||
background-color: #007bff;
|
||||
border-radius: inherit;
|
||||
}
|
||||
|
||||
/* VolumeRange Component Styles */
|
||||
.VolumeRangeRoot {
|
||||
/* VolumeSlider Component Styles */
|
||||
.VolumeSliderRoot {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
position: relative;
|
||||
@@ -157,7 +157,7 @@
|
||||
margin: 0 0.5rem;
|
||||
}
|
||||
|
||||
.VolumeRangeTrack {
|
||||
.VolumeSliderTrack {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 0.375rem;
|
||||
@@ -166,7 +166,7 @@
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.VolumeRangeThumb {
|
||||
.VolumeSliderThumb {
|
||||
width: 0.75rem;
|
||||
height: 0.75rem;
|
||||
background-color: #fff;
|
||||
@@ -174,7 +174,7 @@
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.VolumeRangeProgress {
|
||||
.VolumeSliderProgress {
|
||||
background-color: #007bff;
|
||||
border-radius: inherit;
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ import { FullscreenButton } from '../../components/FullscreenButton';
|
||||
import { MediaContainer } from '../../components/MediaContainer';
|
||||
import MuteButton from '../../components/MuteButton';
|
||||
import PlayButton from '../../components/PlayButton';
|
||||
import { TimeRange } from '../../components/TimeRange';
|
||||
import { TimeSlider } from '../../components/TimeSlider';
|
||||
import styles from './styles';
|
||||
|
||||
type SkinProps = PropsWithChildren<{
|
||||
@@ -46,13 +46,13 @@ export default function MediaSkinDefault({ children, className = '' }: SkinProps
|
||||
<DurationDisplay className={`${styles.TimeDisplay} opacity-50`} />
|
||||
</div>
|
||||
|
||||
<TimeRange.Root className={`${styles.SliderRoot} ${styles.TimeSliderRoot}`}>
|
||||
<TimeRange.Track className={styles.SliderTrack}>
|
||||
<TimeRange.Progress className={styles.SliderProgress} />
|
||||
<TimeRange.Pointer className={styles.SliderPointer} />
|
||||
</TimeRange.Track>
|
||||
<TimeRange.Thumb className={`${styles.SliderThumb} ${styles.TimeSliderThumb}`} />
|
||||
</TimeRange.Root>
|
||||
<TimeSlider.Root className={`${styles.SliderRoot} ${styles.TimeSliderRoot}`}>
|
||||
<TimeSlider.Track className={styles.SliderTrack}>
|
||||
<TimeSlider.Progress className={styles.SliderProgress} />
|
||||
<TimeSlider.Pointer className={styles.SliderPointer} />
|
||||
</TimeSlider.Track>
|
||||
<TimeSlider.Thumb className={`${styles.SliderThumb} ${styles.TimeSliderThumb}`} />
|
||||
</TimeSlider.Root>
|
||||
|
||||
<div className={styles.ButtonGroup}>
|
||||
<MuteButton className={`${styles.Button} ${styles.IconButton} ${styles.MuteButton}`}>
|
||||
|
||||
Reference in New Issue
Block a user