Skip to content

Commit 295f617

Browse files
committed
refactor(packages)!: fix audit issues and remove createMediaButton curry
1 parent 0f84115 commit 295f617

16 files changed

Lines changed: 73 additions & 73 deletions

File tree

packages/core/src/core/ui/slider/slider-core.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ export interface SliderProps {
2525
max?: number | undefined;
2626
}
2727

28-
/** Current pointer/drag interaction state, typically provided by a DOM controller. */
28+
/** Current pointer/drag input state, typically provided by a DOM controller. */
2929
export interface SliderInput {
3030
/** Pointer position as a percentage of the track (0–100). */
3131
pointerPercent: number;

packages/core/src/dom/ui/popover/popover.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -299,7 +299,7 @@ export function createPopover(options: PopoverOptions): PopoverApi {
299299
popupEl = el;
300300

301301
if (el) {
302-
// If the interaction is already open (e.g., React mount after state
302+
// If the popover is already open (e.g., React mount after state
303303
// change), show the popover now. In `applyOpen` the element may not
304304
// have been in the DOM yet, so the earlier `tryShowPopover` was a no-op.
305305
if (state.current.active) {

packages/core/src/dom/ui/popover/tests/popover.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ describe('createPopover', () => {
99
});
1010

1111
describe('open/close', () => {
12-
it('updates interaction state and calls onOpenChange when opening', () => {
12+
it('updates input state and calls onOpenChange when opening', () => {
1313
const { popover, onOpenChange } = createTestPopover();
1414

1515
popover.open();

packages/html/src/ui/slider/slider-thumb-element.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import { SliderDataAttrs } from '@videojs/core';
21
import { applyElementProps, applyStateDataAttrs } from '@videojs/core/dom';
32
import type { PropertyValues } from '@videojs/element';
43
import { ContextConsumer } from '@videojs/element/context';
@@ -46,6 +45,6 @@ export class SliderThumbElement extends MediaElement {
4645
applyElementProps(this, ctx.thumbAttrs);
4746

4847
// Apply state data attributes.
49-
applyStateDataAttrs(this, ctx.state, SliderDataAttrs);
48+
applyStateDataAttrs(this, ctx.state, ctx.stateAttrMap);
5049
}
5150
}

packages/html/src/ui/slider/slider-value-element.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import { SliderDataAttrs } from '@videojs/core';
21
import { applyStateDataAttrs } from '@videojs/core/dom';
32
import type { PropertyDeclarationMap, PropertyValues } from '@videojs/element';
43
import { ContextConsumer } from '@videojs/element/context';
@@ -35,6 +34,6 @@ export class SliderValueElement extends MediaElement {
3534

3635
this.textContent = ctx.formatValue ? ctx.formatValue(value, this.type) : String(Math.round(value));
3736

38-
applyStateDataAttrs(this, ctx.state, SliderDataAttrs);
37+
applyStateDataAttrs(this, ctx.state, ctx.stateAttrMap);
3938
}
4039
}

packages/html/src/ui/thumbnail/thumbnail-element.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ export class ThumbnailElement extends MediaElement {
119119
fetchpriority: this.fetchPriority,
120120
});
121121

122-
// Track src changes via the handle.
122+
// Track src changes via the thumbnail API.
123123
this.#api?.updateSrc(thumbnail?.url);
124124

125125
if (!thumbnail) {
Lines changed: 53 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -1,82 +1,84 @@
11
'use client';
22

3-
import type { InferMediaState, MediaUIComponent, StateAttrMap } from '@videojs/core';
3+
import type { InferComponentState, InferMediaState, MediaUIComponent, StateAttrMap } from '@videojs/core';
44
import { logMissingFeature } from '@videojs/core/dom';
55
import type { Selector } from '@videojs/store';
66
import type { ForwardedRef, ForwardRefExoticComponent, RefAttributes } from 'react';
77
import { forwardRef, useState } from 'react';
88

99
import { usePlayer } from '../player/context';
10+
import type { renderElement as renderElementFn } from '../utils/use-render';
1011
import { renderElement } from '../utils/use-render';
1112
import { useButton } from './hooks/use-button';
1213

13-
interface MediaButtonConfig<Core extends MediaUIComponent> {
14+
interface MediaButtonConfig<Core extends Required<MediaUIComponent>> {
1415
displayName: string;
1516
core: { new (): Core; defaultProps: Record<string, unknown> };
16-
stateAttrMap: StateAttrMap<object>;
17+
stateAttrMap: StateAttrMap<InferComponentState<Core>>;
1718
selector: Selector<object, InferMediaState<Core> | undefined>;
1819
action: (core: Core, state: InferMediaState<Core>) => void;
1920
}
2021

21-
/**
22-
* Creates a media button React component. Curried so `ComponentProps` is explicit
23-
* while `Core` is inferred from the config.
24-
*/
25-
export function createMediaButton<ComponentProps extends object>() {
26-
return <Core extends Required<MediaUIComponent>>(
27-
config: MediaButtonConfig<Core>
28-
): ForwardRefExoticComponent<ComponentProps & RefAttributes<HTMLButtonElement>> => {
29-
const { displayName, core: CoreClass, stateAttrMap, selector, action } = config;
30-
31-
// Props that exist in the core's defaultProps are routed to setProps; the rest go to the DOM element.
32-
const corePropKeys = new Set(Object.keys(CoreClass.defaultProps));
33-
34-
const Component = forwardRef(function MediaButton(
35-
componentProps: Record<string, unknown>,
36-
forwardedRef: ForwardedRef<HTMLButtonElement>
37-
) {
38-
const { render, className, style, ...rest } = componentProps;
39-
40-
const coreProps: Record<string, unknown> = {};
41-
const elementProps: Record<string, unknown> = {};
42-
43-
for (const key of Object.keys(rest)) {
44-
if (corePropKeys.has(key)) {
45-
coreProps[key] = rest[key];
46-
} else {
47-
elementProps[key] = rest[key];
48-
}
22+
/** Creates a media button React component from a core class and config. */
23+
export function createMediaButton<Core extends Required<MediaUIComponent>, Props extends object>(
24+
config: MediaButtonConfig<Core>
25+
): ForwardRefExoticComponent<Props & RefAttributes<HTMLButtonElement>> {
26+
const { displayName, core: CoreClass, stateAttrMap, selector, action } = config;
27+
28+
// Props that exist in the core's defaultProps are routed to setProps; the rest go to the DOM element.
29+
const corePropKeys = new Set(Object.keys(CoreClass.defaultProps));
30+
31+
const Component = forwardRef(function MediaButton(
32+
componentProps: Record<string, unknown>,
33+
forwardedRef: ForwardedRef<HTMLButtonElement>
34+
) {
35+
const { render, className, style, ...rest } = componentProps;
36+
37+
const coreProps: Record<string, unknown> = {};
38+
const elementProps: Record<string, unknown> = {};
39+
40+
for (const key of Object.keys(rest)) {
41+
if (corePropKeys.has(key)) {
42+
coreProps[key] = rest[key];
43+
} else {
44+
elementProps[key] = rest[key];
4945
}
46+
}
5047

51-
const feature = usePlayer(selector);
48+
const feature = usePlayer(selector);
5249

53-
const [core] = useState(() => new CoreClass());
54-
core.setProps(coreProps);
50+
const [core] = useState(() => new CoreClass());
51+
core.setProps(coreProps);
5552

56-
const { getButtonProps, buttonRef } = useButton({
57-
displayName,
58-
onActivate: () => action(core, feature!),
59-
isDisabled: () => !!coreProps.disabled || !feature,
60-
});
53+
const { getButtonProps, buttonRef } = useButton({
54+
displayName,
55+
onActivate: () => action(core, feature!),
56+
isDisabled: () => !!coreProps.disabled || !feature,
57+
});
6158

62-
if (!feature) {
63-
if (__DEV__) logMissingFeature(displayName, selector.displayName ?? displayName);
64-
return null;
65-
}
59+
if (!feature) {
60+
if (__DEV__) logMissingFeature(displayName, selector.displayName ?? displayName);
61+
return null;
62+
}
63+
64+
type State = InferComponentState<Core>;
6665

67-
core.setMedia(feature);
68-
const state = core.getState() as object & Record<string, unknown>;
66+
core.setMedia(feature);
67+
const state = core.getState() as State;
6968

70-
return renderElement('button', { render, className, style } as Parameters<typeof renderElement>[1], {
69+
return renderElement(
70+
'button',
71+
{ render, className, style } as renderElementFn.ComponentProps<InferComponentState<Core>>,
72+
{
7173
state,
7274
stateAttrMap,
7375
ref: [forwardedRef, buttonRef],
7476
props: [core.getAttrs(state), elementProps, getButtonProps()],
75-
});
76-
});
77+
}
78+
);
79+
});
7780

78-
Component.displayName = displayName;
81+
Component.displayName = displayName;
7982

80-
return Component as unknown as ForwardRefExoticComponent<ComponentProps & RefAttributes<HTMLButtonElement>>;
81-
};
83+
return Component as unknown as ForwardRefExoticComponent<Props & RefAttributes<HTMLButtonElement>>;
8284
}

packages/react/src/ui/fullscreen-button/fullscreen-button.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ export interface FullscreenButtonProps
1111
FullscreenButtonCore.Props {}
1212

1313
/** A button that toggles fullscreen. */
14-
export const FullscreenButton = createMediaButton<FullscreenButtonProps>()({
14+
export const FullscreenButton = createMediaButton<FullscreenButtonCore, FullscreenButtonProps>({
1515
displayName: 'FullscreenButton',
1616
core: FullscreenButtonCore,
1717
stateAttrMap: FullscreenButtonDataAttrs,

packages/react/src/ui/hooks/use-slider.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,10 +45,10 @@ export interface UseSliderReturnValue<State extends SliderState = SliderState> {
4545
}
4646

4747
/**
48-
* Manages slider interaction lifecycle for React.
48+
* Manages slider input lifecycle for React.
4949
*
5050
* Wraps `createSlider()` from `@videojs/core/dom` and subscribes to its
51-
* interaction state via `useSnapshot`. Returns split props for the root
51+
* input state via `useSnapshot`. Returns split props for the root
5252
* (pointer events) and thumb (keyboard/focus) elements.
5353
*/
5454
export function useSlider<State extends SliderState = SliderState>(

packages/react/src/ui/mute-button/mute-button.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import { createMediaButton } from '../create-media-button';
99
export interface MuteButtonProps extends UIComponentProps<'button', MuteButtonCore.State>, MuteButtonCore.Props {}
1010

1111
/** A button that toggles mute state. */
12-
export const MuteButton = createMediaButton<MuteButtonProps>()({
12+
export const MuteButton = createMediaButton<MuteButtonCore, MuteButtonProps>({
1313
displayName: 'MuteButton',
1414
core: MuteButtonCore,
1515
stateAttrMap: MuteButtonDataAttrs,

0 commit comments

Comments
 (0)