mirror of
https://github.com/zoriya/v10.git
synced 2026-08-16 02:45:09 +00:00
refactor(packages): dry up core, html, and react UI architecture (#699)
This commit is contained in:
+5
-3
@@ -16,12 +16,14 @@ export interface SliderContextValue {
|
||||
formatValue?: ((value: number, type: 'current' | 'pointer') => string) | undefined;
|
||||
}
|
||||
|
||||
const SliderContext = createContext<SliderContextValue | undefined>(undefined);
|
||||
const SliderContext = createContext<SliderContextValue | null>(null);
|
||||
|
||||
export function SliderProvider({ value, children }: { value: SliderContextValue; children: React.ReactNode }) {
|
||||
return <SliderContext.Provider value={value}>{children}</SliderContext.Provider>;
|
||||
}
|
||||
|
||||
export function useSliderContext(): SliderContextValue | undefined {
|
||||
return useContext(SliderContext);
|
||||
export function useSliderContext(): SliderContextValue {
|
||||
const ctx = useContext(SliderContext);
|
||||
if (!ctx) throw new Error('Slider compound components must be used within a Slider.Root');
|
||||
return ctx;
|
||||
}
|
||||
@@ -1,35 +1,18 @@
|
||||
'use client';
|
||||
|
||||
import type { SliderState } from '@videojs/core';
|
||||
import type { ForwardedRef } from 'react';
|
||||
import { forwardRef } from 'react';
|
||||
|
||||
import type { UIComponentProps } from '../../utils/types';
|
||||
import { renderElement } from '../../utils/use-render';
|
||||
import { useSliderContext } from './slider-context';
|
||||
import { createContextPart } from '../create-context-part';
|
||||
import { useSliderContext } from './context';
|
||||
|
||||
export interface SliderBufferProps extends UIComponentProps<'div', SliderState> {}
|
||||
|
||||
/** Displays the buffered range on the slider track. */
|
||||
export const SliderBuffer = forwardRef(function SliderBuffer(
|
||||
componentProps: SliderBufferProps,
|
||||
forwardedRef: ForwardedRef<HTMLDivElement>
|
||||
) {
|
||||
const { render, className, style, ...elementProps } = componentProps;
|
||||
|
||||
const context = useSliderContext();
|
||||
if (!context) return null;
|
||||
|
||||
return renderElement(
|
||||
'div',
|
||||
{ render, className, style },
|
||||
{
|
||||
state: context.state,
|
||||
stateAttrMap: context.stateAttrMap,
|
||||
ref: forwardedRef,
|
||||
props: [elementProps],
|
||||
}
|
||||
);
|
||||
export const SliderBuffer = createContextPart<SliderBufferProps, SliderState>({
|
||||
displayName: 'SliderBuffer',
|
||||
tag: 'div',
|
||||
useContext: useSliderContext,
|
||||
});
|
||||
|
||||
export namespace SliderBuffer {
|
||||
|
||||
@@ -1,35 +1,18 @@
|
||||
'use client';
|
||||
|
||||
import type { SliderState } from '@videojs/core';
|
||||
import type { ForwardedRef } from 'react';
|
||||
import { forwardRef } from 'react';
|
||||
|
||||
import type { UIComponentProps } from '../../utils/types';
|
||||
import { renderElement } from '../../utils/use-render';
|
||||
import { useSliderContext } from './slider-context';
|
||||
import { createContextPart } from '../create-context-part';
|
||||
import { useSliderContext } from './context';
|
||||
|
||||
export interface SliderFillProps extends UIComponentProps<'div', SliderState> {}
|
||||
|
||||
/** Displays the filled portion from start to the current value. */
|
||||
export const SliderFill = forwardRef(function SliderFill(
|
||||
componentProps: SliderFillProps,
|
||||
forwardedRef: ForwardedRef<HTMLDivElement>
|
||||
) {
|
||||
const { render, className, style, ...elementProps } = componentProps;
|
||||
|
||||
const context = useSliderContext();
|
||||
if (!context) return null;
|
||||
|
||||
return renderElement(
|
||||
'div',
|
||||
{ render, className, style },
|
||||
{
|
||||
state: context.state,
|
||||
stateAttrMap: context.stateAttrMap,
|
||||
ref: forwardedRef,
|
||||
props: [elementProps],
|
||||
}
|
||||
);
|
||||
export const SliderFill = createContextPart<SliderFillProps, SliderState>({
|
||||
displayName: 'SliderFill',
|
||||
tag: 'div',
|
||||
useContext: useSliderContext,
|
||||
});
|
||||
|
||||
export namespace SliderFill {
|
||||
|
||||
@@ -8,7 +8,7 @@ import { forwardRef, useState } from 'react';
|
||||
import type { UIComponentProps } from '../../utils/types';
|
||||
import { renderElement } from '../../utils/use-render';
|
||||
import { useSlider } from '../hooks/use-slider';
|
||||
import { SliderProvider } from './slider-context';
|
||||
import { SliderProvider } from './context';
|
||||
|
||||
export interface SliderRootProps extends UIComponentProps<'div', SliderCore.State>, SliderCore.Props {
|
||||
onValueChange?: ((value: number) => void) | undefined;
|
||||
@@ -44,9 +44,6 @@ export const SliderRoot = forwardRef(function SliderRoot(
|
||||
const [core] = useState(() => new SliderCore());
|
||||
core.setProps({ label, min, max, step, largeStep, orientation, disabled, thumbAlignment });
|
||||
|
||||
const { min: resolvedMin, max: resolvedMax, step: resolvedStep, largeStep: resolvedLargeStep } = core.props;
|
||||
const range = resolvedMax - resolvedMin || 1;
|
||||
|
||||
const {
|
||||
state,
|
||||
cssVars,
|
||||
@@ -55,10 +52,13 @@ export const SliderRoot = forwardRef(function SliderRoot(
|
||||
rootProps,
|
||||
thumbProps,
|
||||
} = useSlider({
|
||||
computeState: (interaction) => core.getState(interaction, value),
|
||||
computeState: (input) => {
|
||||
core.setInput(input);
|
||||
return core.getSliderState(value);
|
||||
},
|
||||
getPercent: () => core.percentFromValue(value),
|
||||
getStepPercent: () => (resolvedStep / range) * 100,
|
||||
getLargeStepPercent: () => (resolvedLargeStep / range) * 100,
|
||||
getStepPercent: () => core.getStepPercent(),
|
||||
getLargeStepPercent: () => core.getLargeStepPercent(),
|
||||
orientation,
|
||||
disabled,
|
||||
adjustPercent: (rawPercent, thumbSize, trackSize) =>
|
||||
|
||||
@@ -6,7 +6,7 @@ import { forwardRef } from 'react';
|
||||
|
||||
import type { UIComponentProps } from '../../utils/types';
|
||||
import { renderElement } from '../../utils/use-render';
|
||||
import { useSliderContext } from './slider-context';
|
||||
import { useSliderContext } from './context';
|
||||
|
||||
export interface SliderThumbProps extends UIComponentProps<'div', SliderState> {}
|
||||
|
||||
@@ -18,8 +18,6 @@ export const SliderThumb = forwardRef(function SliderThumb(
|
||||
const { render, className, style, ...elementProps } = componentProps;
|
||||
|
||||
const context = useSliderContext();
|
||||
if (!context) return null;
|
||||
|
||||
const { state, thumbRef, thumbProps, getAttrs } = context;
|
||||
const attrs = getAttrs(state);
|
||||
|
||||
|
||||
@@ -1,35 +1,18 @@
|
||||
'use client';
|
||||
|
||||
import type { SliderState } from '@videojs/core';
|
||||
import type { ForwardedRef } from 'react';
|
||||
import { forwardRef } from 'react';
|
||||
|
||||
import type { UIComponentProps } from '../../utils/types';
|
||||
import { renderElement } from '../../utils/use-render';
|
||||
import { useSliderContext } from './slider-context';
|
||||
import { createContextPart } from '../create-context-part';
|
||||
import { useSliderContext } from './context';
|
||||
|
||||
export interface SliderTrackProps extends UIComponentProps<'div', SliderState> {}
|
||||
|
||||
/** Contains the slider's visual track and interactive hit zone. */
|
||||
export const SliderTrack = forwardRef(function SliderTrack(
|
||||
componentProps: SliderTrackProps,
|
||||
forwardedRef: ForwardedRef<HTMLDivElement>
|
||||
) {
|
||||
const { render, className, style, ...elementProps } = componentProps;
|
||||
|
||||
const context = useSliderContext();
|
||||
if (!context) return null;
|
||||
|
||||
return renderElement(
|
||||
'div',
|
||||
{ render, className, style },
|
||||
{
|
||||
state: context.state,
|
||||
stateAttrMap: context.stateAttrMap,
|
||||
ref: forwardedRef,
|
||||
props: [elementProps],
|
||||
}
|
||||
);
|
||||
export const SliderTrack = createContextPart<SliderTrackProps, SliderState>({
|
||||
displayName: 'SliderTrack',
|
||||
tag: 'div',
|
||||
useContext: useSliderContext,
|
||||
});
|
||||
|
||||
export namespace SliderTrack {
|
||||
|
||||
@@ -6,7 +6,7 @@ import { forwardRef } from 'react';
|
||||
|
||||
import type { UIComponentProps } from '../../utils/types';
|
||||
import { renderElement } from '../../utils/use-render';
|
||||
import { useSliderContext } from './slider-context';
|
||||
import { useSliderContext } from './context';
|
||||
|
||||
export interface SliderValueProps extends UIComponentProps<'output', SliderState> {
|
||||
/** Which slider value to display: the current position or the pointer position. */
|
||||
@@ -23,8 +23,6 @@ export const SliderValue = forwardRef(function SliderValue(
|
||||
const { render, className, style, type = 'current', format, ...elementProps } = componentProps;
|
||||
|
||||
const context = useSliderContext();
|
||||
if (!context) return null;
|
||||
|
||||
const { state, pointerValue, formatValue } = context;
|
||||
|
||||
const rawValue = type === 'pointer' ? pointerValue : state.value;
|
||||
|
||||
@@ -9,9 +9,9 @@ import { SliderThumb } from '../slider-thumb';
|
||||
import { SliderTrack } from '../slider-track';
|
||||
import { SliderValue } from '../slider-value';
|
||||
|
||||
const { mockSliderHandle } = vi.hoisted(() => ({
|
||||
mockSliderHandle: () => ({
|
||||
interaction: {
|
||||
const { mockSliderApi } = vi.hoisted(() => ({
|
||||
mockSliderApi: () => ({
|
||||
input: {
|
||||
current: {
|
||||
pointerPercent: 0,
|
||||
dragPercent: 0,
|
||||
@@ -37,7 +37,7 @@ const { mockSliderHandle } = vi.hoisted(() => ({
|
||||
|
||||
vi.mock('@videojs/core/dom', async (importOriginal) => {
|
||||
const orig: Record<string, unknown> = await importOriginal();
|
||||
return { ...orig, createSlider: vi.fn(mockSliderHandle) };
|
||||
return { ...orig, createSlider: vi.fn(mockSliderApi) };
|
||||
});
|
||||
|
||||
vi.mock('@videojs/store/react', () => ({
|
||||
@@ -97,10 +97,8 @@ describe('SliderTrack', () => {
|
||||
expect(container.querySelector('[data-testid="track"]')).toBeTruthy();
|
||||
});
|
||||
|
||||
it('returns null outside of SliderRoot', () => {
|
||||
const { container } = render(<SliderTrack />);
|
||||
|
||||
expect(container.firstElementChild).toBeNull();
|
||||
it('throws outside of SliderRoot', () => {
|
||||
expect(() => render(<SliderTrack />)).toThrow('Slider compound components must be used within a Slider.Root');
|
||||
});
|
||||
|
||||
it('forwards ref', () => {
|
||||
@@ -126,10 +124,8 @@ describe('SliderFill', () => {
|
||||
expect(container.querySelector('[data-testid="fill"]')).toBeTruthy();
|
||||
});
|
||||
|
||||
it('returns null outside of SliderRoot', () => {
|
||||
const { container } = render(<SliderFill />);
|
||||
|
||||
expect(container.firstElementChild).toBeNull();
|
||||
it('throws outside of SliderRoot', () => {
|
||||
expect(() => render(<SliderFill />)).toThrow('Slider compound components must be used within a Slider.Root');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -144,10 +140,8 @@ describe('SliderBuffer', () => {
|
||||
expect(container.querySelector('[data-testid="buffer"]')).toBeTruthy();
|
||||
});
|
||||
|
||||
it('returns null outside of SliderRoot', () => {
|
||||
const { container } = render(<SliderBuffer />);
|
||||
|
||||
expect(container.firstElementChild).toBeNull();
|
||||
it('throws outside of SliderRoot', () => {
|
||||
expect(() => render(<SliderBuffer />)).toThrow('Slider compound components must be used within a Slider.Root');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -162,10 +156,8 @@ describe('SliderThumb', () => {
|
||||
expect(container.querySelector('[data-testid="thumb"]')).toBeTruthy();
|
||||
});
|
||||
|
||||
it('returns null outside of SliderRoot', () => {
|
||||
const { container } = render(<SliderThumb />);
|
||||
|
||||
expect(container.firstElementChild).toBeNull();
|
||||
it('throws outside of SliderRoot', () => {
|
||||
expect(() => render(<SliderThumb />)).toThrow('Slider compound components must be used within a Slider.Root');
|
||||
});
|
||||
|
||||
it('forwards ref', () => {
|
||||
@@ -203,10 +195,8 @@ describe('SliderValue', () => {
|
||||
expect(el?.tagName).toBe('OUTPUT');
|
||||
});
|
||||
|
||||
it('returns null outside of SliderRoot', () => {
|
||||
const { container } = render(<SliderValue />);
|
||||
|
||||
expect(container.firstElementChild).toBeNull();
|
||||
it('throws outside of SliderRoot', () => {
|
||||
expect(() => render(<SliderValue />)).toThrow('Slider compound components must be used within a Slider.Root');
|
||||
});
|
||||
|
||||
it('displays rounded value by default', () => {
|
||||
|
||||
Reference in New Issue
Block a user