mirror of
https://github.com/zoriya/v10.git
synced 2026-08-05 05:37:21 +00:00
refactor(html): implement hook-style component architecture for PlayButton and MuteButton (gradual migration to more shareable with React).
This commit is contained in:
committed by
Christian Pillsbury
parent
c54945fd58
commit
74dea64ddd
@@ -15,4 +15,4 @@ export const playable = {
|
||||
mediapauserequest: () => true,
|
||||
},
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
@@ -1,65 +0,0 @@
|
||||
import { toConnectedHTMLComponent, StateHook, PropsHook, EventsHook } from '../utils/component-factory';
|
||||
import { MediaMuteButton } from './ui/media-mute-button';
|
||||
|
||||
/**
|
||||
* MuteButton state hook - equivalent to React's useMuteButtonState
|
||||
* Handles media store state subscription and transformation
|
||||
*/
|
||||
export const useMuteButtonState: StateHook<{ muted: boolean; volumeLevel: string }> = {
|
||||
keys: ['mediaMuted', 'mediaVolumeLevel'],
|
||||
transform: (rawState) => ({
|
||||
muted: rawState.mediaMuted ?? false,
|
||||
volumeLevel: rawState.mediaVolumeLevel ?? 'off'
|
||||
})
|
||||
};
|
||||
|
||||
/**
|
||||
* MuteButton props hook - equivalent to React's useMuteButtonProps
|
||||
* Handles element attributes and properties based on state
|
||||
*/
|
||||
export const useMuteButtonProps: PropsHook<{ muted: boolean; volumeLevel: string }> = (state, element) => {
|
||||
// Handle boolean data attribute: present with empty string when true, absent when false
|
||||
// This matches the React component behavior exactly
|
||||
if (state.muted) {
|
||||
element.setAttribute('data-muted', '');
|
||||
} else {
|
||||
element.removeAttribute('data-muted');
|
||||
}
|
||||
|
||||
// Set volume level data attribute
|
||||
element.setAttribute('data-volume-level', state.volumeLevel);
|
||||
|
||||
// Set element properties for backwards compatibility
|
||||
// @ts-ignore - Custom element property
|
||||
element.mediaMuted = state.muted;
|
||||
// @ts-ignore - Custom element property
|
||||
element.mediaVolumeLevel = state.volumeLevel;
|
||||
};
|
||||
|
||||
/**
|
||||
* MuteButton events hook - equivalent to React's event handlers
|
||||
* Handles event dispatch to media store
|
||||
*/
|
||||
export const useMuteButtonEvents: EventsHook = {
|
||||
events: ['mediamuterequest', 'mediaunmuterequest'],
|
||||
handler: (event, mediaStore) => {
|
||||
if (['mediamuterequest', 'mediaunmuterequest'].includes(event.type)) {
|
||||
const { type, detail } = event;
|
||||
mediaStore.dispatch({ type, detail });
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Connected MuteButton component using hook-style architecture
|
||||
* Equivalent to React's MuteButton = toConnectedComponent(...)
|
||||
*/
|
||||
export const MuteButton = toConnectedHTMLComponent(
|
||||
MediaMuteButton,
|
||||
useMuteButtonState,
|
||||
useMuteButtonProps,
|
||||
useMuteButtonEvents,
|
||||
'MuteButton'
|
||||
);
|
||||
|
||||
export default MuteButton;
|
||||
@@ -1,59 +0,0 @@
|
||||
import { toConnectedHTMLComponent, StateHook, PropsHook, EventsHook } from '../utils/component-factory';
|
||||
import { MediaPlayButton } from './ui/media-play-button';
|
||||
|
||||
/**
|
||||
* PlayButton state hook - equivalent to React's usePlayButtonState
|
||||
* Handles media store state subscription and transformation
|
||||
*/
|
||||
export const usePlayButtonState: StateHook<{ paused: boolean }> = {
|
||||
keys: ['mediaPaused'],
|
||||
transform: (rawState) => ({
|
||||
paused: rawState.mediaPaused ?? true
|
||||
})
|
||||
};
|
||||
|
||||
/**
|
||||
* PlayButton props hook - equivalent to React's usePlayButtonProps
|
||||
* Handles element attributes and properties based on state
|
||||
*/
|
||||
export const usePlayButtonProps: PropsHook<{ paused: boolean }> = (state, element) => {
|
||||
// Handle boolean data attribute: present with empty string when true, absent when false
|
||||
// This matches the React component behavior exactly
|
||||
if (state.paused) {
|
||||
element.setAttribute('data-paused', '');
|
||||
} else {
|
||||
element.removeAttribute('data-paused');
|
||||
}
|
||||
|
||||
// Set element property for backwards compatibility
|
||||
// @ts-ignore - Custom element property
|
||||
element.mediaPaused = state.paused;
|
||||
};
|
||||
|
||||
/**
|
||||
* PlayButton events hook - equivalent to React's event handlers
|
||||
* Handles event dispatch to media store
|
||||
*/
|
||||
export const usePlayButtonEvents: EventsHook = {
|
||||
events: ['mediaplayrequest', 'mediapauserequest'],
|
||||
handler: (event, mediaStore) => {
|
||||
if (['mediaplayrequest', 'mediapauserequest'].includes(event.type)) {
|
||||
const { type, detail } = event;
|
||||
mediaStore.dispatch({ type, detail });
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Connected PlayButton component using hook-style architecture
|
||||
* Equivalent to React's PlayButton = toConnectedComponent(...)
|
||||
*/
|
||||
export const PlayButton = toConnectedHTMLComponent(
|
||||
MediaPlayButton,
|
||||
usePlayButtonState,
|
||||
usePlayButtonProps,
|
||||
usePlayButtonEvents,
|
||||
'PlayButton'
|
||||
);
|
||||
|
||||
export default PlayButton;
|
||||
@@ -1,10 +0,0 @@
|
||||
import { MuteButton } from '../MuteButton.js';
|
||||
|
||||
// NOTE: In this architecture it will be important to decouple component class definitions from their registration in the CustomElementsRegistry. (CJP)
|
||||
if (!globalThis.customElements.get('media-mute-button')) {
|
||||
// @ts-ignore - Custom element constructor compatibility
|
||||
globalThis.customElements.define('media-mute-button', MuteButton);
|
||||
}
|
||||
|
||||
export { MuteButton as MediaMuteButton };
|
||||
export default MuteButton;
|
||||
@@ -1,10 +0,0 @@
|
||||
import { PlayButton } from '../PlayButton.js';
|
||||
|
||||
// NOTE: In this architecture it will be important to decouple component class definitions from their registration in the CustomElementsRegistry. (CJP)
|
||||
if (!globalThis.customElements.get('media-play-button')) {
|
||||
// @ts-ignore - Custom element constructor compatibility
|
||||
globalThis.customElements.define('media-play-button', PlayButton);
|
||||
}
|
||||
|
||||
export { PlayButton as MediaPlayButton };
|
||||
export default PlayButton;
|
||||
+14
-7
@@ -1,9 +1,9 @@
|
||||
import { namedNodeMapToObject } from '../../utils/element-utils.js';
|
||||
import { namedNodeMapToObject } from '../utils/element-utils.js';
|
||||
|
||||
export function getTemplateHTML(
|
||||
this: typeof MediaChromeButton,
|
||||
_attrs: Record<string, string>,
|
||||
_props: Record<string, any> = {}
|
||||
_props: Record<string, any> = {},
|
||||
) {
|
||||
return /* html */ `
|
||||
<style>
|
||||
@@ -31,17 +31,24 @@ export class MediaChromeButton extends HTMLElement {
|
||||
|
||||
if (!this.shadowRoot) {
|
||||
// Set up the Shadow DOM if not using Declarative Shadow DOM.
|
||||
this.attachShadow((this.constructor as typeof MediaChromeButton).shadowRootOptions);
|
||||
this.attachShadow(
|
||||
(this.constructor as typeof MediaChromeButton).shadowRootOptions,
|
||||
);
|
||||
|
||||
const attrs = namedNodeMapToObject(this.attributes);
|
||||
const html = (this.constructor as typeof MediaChromeButton).getTemplateHTML(attrs);
|
||||
const html = (
|
||||
this.constructor as typeof MediaChromeButton
|
||||
).getTemplateHTML(attrs);
|
||||
// From MDN: setHTMLUnsafe should be used instead of ShadowRoot.innerHTML
|
||||
// when a string of HTML may contain declarative shadow roots.
|
||||
const shadowRoot = this.shadowRoot as unknown as ShadowRoot;
|
||||
shadowRoot.setHTMLUnsafe ? shadowRoot.setHTMLUnsafe(html) : (shadowRoot.innerHTML = html);
|
||||
shadowRoot.setHTMLUnsafe
|
||||
? shadowRoot.setHTMLUnsafe(html)
|
||||
: (shadowRoot.innerHTML = html);
|
||||
}
|
||||
|
||||
this.addEventListener('click', this.handleClick);
|
||||
this.addEventListener('click', this);
|
||||
}
|
||||
handleClick() {}
|
||||
|
||||
handleEvent(_event: Event) {}
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
import {
|
||||
toConnectedHTMLComponent,
|
||||
StateHook,
|
||||
PropsHook,
|
||||
} from '../utils/component-factory';
|
||||
import { MediaChromeButton } from './media-chrome-button';
|
||||
|
||||
export class MediaMuteButton extends MediaChromeButton {
|
||||
_state:
|
||||
| {
|
||||
muted: boolean;
|
||||
volumeLevel: string;
|
||||
requestUnmute: () => void;
|
||||
requestMute: () => void;
|
||||
}
|
||||
| undefined;
|
||||
|
||||
handleEvent(event: Event) {
|
||||
const { type } = event;
|
||||
if (type === 'click') {
|
||||
const state = this._state;
|
||||
if (state) {
|
||||
if (state.muted) {
|
||||
state.requestUnmute();
|
||||
} else {
|
||||
state.requestMute();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
get muted() {
|
||||
return this._state?.muted;
|
||||
}
|
||||
|
||||
get volumeLevel() {
|
||||
return this._state?.volumeLevel;
|
||||
}
|
||||
|
||||
_update(props: any, state: any) {
|
||||
this._state = state;
|
||||
// Make generic
|
||||
this.toggleAttribute('data-muted', props['data-muted']);
|
||||
this.setAttribute('data-volume-level', props['data-volume-level']);
|
||||
this.setAttribute('role', props['role']);
|
||||
this.setAttribute('aria-label', props['aria-label']);
|
||||
this.setAttribute('data-tooltip', props['data-tooltip']);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* MuteButton state hook - equivalent to React's useMuteButtonState
|
||||
* Handles media store state subscription and transformation
|
||||
*/
|
||||
export const useMuteButtonState: StateHook<{
|
||||
muted: boolean;
|
||||
volumeLevel: string;
|
||||
}> = {
|
||||
keys: ['mediaMuted', 'mediaVolumeLevel'],
|
||||
transform: (rawState, mediaStore) => ({
|
||||
muted: rawState.mediaMuted ?? false,
|
||||
volumeLevel: rawState.mediaVolumeLevel ?? 'off',
|
||||
requestMute() {
|
||||
const type = 'mediamuterequest';
|
||||
mediaStore.dispatch({ type });
|
||||
},
|
||||
requestUnmute() {
|
||||
const type = 'mediaunmuterequest';
|
||||
mediaStore.dispatch({ type });
|
||||
},
|
||||
}),
|
||||
};
|
||||
|
||||
/**
|
||||
* MuteButton props hook - equivalent to React's useMuteButtonProps
|
||||
* Handles element attributes and properties based on state
|
||||
*/
|
||||
export const useMuteButtonProps: PropsHook<{
|
||||
muted: boolean;
|
||||
volumeLevel: string;
|
||||
}> = (state, _element) => {
|
||||
const baseProps: Record<string, any> = {
|
||||
/** data attributes/props */
|
||||
['data-muted']: state.muted,
|
||||
['data-volume-level']: state.volumeLevel,
|
||||
/** @TODO Need another state provider in core for i18n (CJP) */
|
||||
/** aria attributes/props */
|
||||
role: 'button',
|
||||
['aria-label']: state.muted ? 'unmute' : 'mute',
|
||||
/** tooltip */
|
||||
['data-tooltip']: state.muted ? 'Unmute' : 'Mute',
|
||||
/** @TODO Figure out how we want to handle attr overrides (e.g. aria-label) (CJP) */
|
||||
/** external props spread last to allow for overriding */
|
||||
// ...props,
|
||||
};
|
||||
|
||||
return baseProps;
|
||||
};
|
||||
|
||||
/**
|
||||
* Connected MuteButton component using hook-style architecture
|
||||
* Equivalent to React's MuteButton = toConnectedComponent(...)
|
||||
*/
|
||||
export const MuteButton = toConnectedHTMLComponent(
|
||||
MediaMuteButton,
|
||||
useMuteButtonState,
|
||||
useMuteButtonProps,
|
||||
'MuteButton',
|
||||
);
|
||||
|
||||
// NOTE: In this architecture it will be important to decouple component class definitions from their registration in the CustomElementsRegistry. (CJP)
|
||||
if (!globalThis.customElements.get('media-mute-button')) {
|
||||
// @ts-ignore - Custom element constructor compatibility
|
||||
globalThis.customElements.define('media-mute-button', MuteButton);
|
||||
}
|
||||
|
||||
export default MuteButton;
|
||||
@@ -0,0 +1,103 @@
|
||||
import {
|
||||
toConnectedHTMLComponent,
|
||||
StateHook,
|
||||
PropsHook,
|
||||
} from '../utils/component-factory';
|
||||
import { MediaChromeButton } from './media-chrome-button';
|
||||
|
||||
export class PlayButtonBase extends MediaChromeButton {
|
||||
_state:
|
||||
| { paused: boolean; requestPlay: () => void; requestPause: () => void }
|
||||
| undefined;
|
||||
|
||||
handleEvent(event: Event) {
|
||||
const { type } = event;
|
||||
const state = this._state;
|
||||
if (state) {
|
||||
if (type === 'click') {
|
||||
if (state.paused) {
|
||||
state.requestPlay();
|
||||
} else {
|
||||
state.requestPause();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
get paused() {
|
||||
return this._state?.paused;
|
||||
}
|
||||
|
||||
_update(props: any, state: any) {
|
||||
this._state = state;
|
||||
/** @TODO Follow up with React vs. W.C. data-* attributes discrepancies (CJP) */
|
||||
// Make generic
|
||||
this.toggleAttribute('data-paused', props['data-paused']);
|
||||
this.setAttribute('role', props['role']);
|
||||
this.setAttribute('aria-label', props['aria-label']);
|
||||
this.setAttribute('data-tooltip', props['data-tooltip']);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* PlayButton state hook - equivalent to React's usePlayButtonState
|
||||
* Handles media store state subscription and transformation
|
||||
*/
|
||||
export const usePlayButtonState: StateHook<{ paused: boolean }> = {
|
||||
keys: ['mediaPaused'],
|
||||
transform: (rawState, mediaStore) => ({
|
||||
paused: rawState.mediaPaused ?? true,
|
||||
requestPlay() {
|
||||
const type = 'mediaplayrequest';
|
||||
mediaStore.dispatch({ type });
|
||||
},
|
||||
requestPause() {
|
||||
const type = 'mediapauserequest';
|
||||
mediaStore.dispatch({ type });
|
||||
},
|
||||
}),
|
||||
};
|
||||
|
||||
/**
|
||||
* PlayButton props hook - equivalent to React's usePlayButtonProps
|
||||
* Handles element attributes and properties based on state
|
||||
*/
|
||||
export const usePlayButtonProps: PropsHook<{ paused: boolean }> = (
|
||||
state,
|
||||
_element,
|
||||
) => {
|
||||
const baseProps: Record<string, any> = {
|
||||
/** data attributes/props */
|
||||
['data-paused']: state.paused,
|
||||
/** @TODO Need another state provider in core for i18n (CJP) */
|
||||
/** aria attributes/props */
|
||||
role: 'button',
|
||||
['aria-label']: state.paused ? 'play' : 'pause',
|
||||
/** tooltip */
|
||||
['data-tooltip']: state.paused ? 'Play' : 'Pause',
|
||||
/** @TODO Figure out how we want to handle attr overrides (e.g. aria-label) (CJP) */
|
||||
/** external props spread last to allow for overriding */
|
||||
// ...props,
|
||||
};
|
||||
|
||||
return baseProps;
|
||||
};
|
||||
|
||||
/**
|
||||
* Connected PlayButton component using hook-style architecture
|
||||
* Equivalent to React's PlayButton = toConnectedComponent(...)
|
||||
*/
|
||||
export const PlayButton = toConnectedHTMLComponent(
|
||||
PlayButtonBase,
|
||||
usePlayButtonState,
|
||||
usePlayButtonProps,
|
||||
'PlayButton',
|
||||
);
|
||||
|
||||
// NOTE: In this architecture it will be important to decouple component class definitions from their registration in the CustomElementsRegistry. (CJP)
|
||||
if (!globalThis.customElements.get('media-play-button')) {
|
||||
// @ts-ignore - Custom element constructor compatibility
|
||||
globalThis.customElements.define('media-play-button', PlayButton);
|
||||
}
|
||||
|
||||
export default PlayButton;
|
||||
@@ -1,34 +0,0 @@
|
||||
|
||||
import { MediaChromeButton } from './media-chrome-button';
|
||||
|
||||
export class MediaMuteButton extends MediaChromeButton {
|
||||
|
||||
static get observedAttributes() {
|
||||
return ['mediavolumelevel', 'mediamuted'];
|
||||
}
|
||||
|
||||
handleClick() {
|
||||
const type = this.mediaMuted ? 'mediaunmuterequest' : 'mediamuterequest';
|
||||
this.dispatchEvent(new CustomEvent(type));
|
||||
}
|
||||
|
||||
get mediaMuted() {
|
||||
return this.hasAttribute('mediamuted');
|
||||
}
|
||||
|
||||
set mediaMuted(value: boolean) {
|
||||
this.toggleAttribute('mediamuted', !!value);
|
||||
}
|
||||
|
||||
get mediaVolumeLevel() {
|
||||
return this.getAttribute('mediavolumelevel');
|
||||
}
|
||||
|
||||
set mediaVolumeLevel(value: string | null | undefined) {
|
||||
if (value == null) {
|
||||
this.removeAttribute('mediavolumelevel');
|
||||
} else {
|
||||
this.setAttribute('mediavolumelevel', value);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
|
||||
import { MediaChromeButton } from './media-chrome-button';
|
||||
|
||||
export class MediaPlayButton extends MediaChromeButton {
|
||||
|
||||
static get observedAttributes() {
|
||||
return ['mediapaused'];
|
||||
}
|
||||
|
||||
handleClick() {
|
||||
const type = this.mediaPaused ? 'mediaplayrequest' : 'mediapauserequest';
|
||||
this.dispatchEvent(new CustomEvent(type));
|
||||
}
|
||||
|
||||
get mediaPaused() {
|
||||
return this.hasAttribute('mediapaused');
|
||||
}
|
||||
|
||||
set mediaPaused(value: boolean) {
|
||||
this.toggleAttribute('mediapaused', !!value);
|
||||
}
|
||||
}
|
||||
@@ -2,12 +2,12 @@ export * as MediaProvider from './media-provider.js';
|
||||
export * as MediaThemeDefault from './skins/media-skin-default.js';
|
||||
|
||||
// New hook-style components
|
||||
export { PlayButton } from './components/PlayButton.js';
|
||||
export { MuteButton } from './components/MuteButton.js';
|
||||
export { PlayButton } from './components/media-play-button.js';
|
||||
export { MuteButton } from './components/media-mute-button.js';
|
||||
|
||||
export function defineVjsPlayer() {
|
||||
/** @TODO - Reimplement me (at least as a POC) (CJP) */
|
||||
// defineVideoProvider();
|
||||
// defineVideoDefaultSkin();
|
||||
// <video> is native, no need to define
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { MediaSkin } from '../media-skin.js';
|
||||
import { MediaSkin } from '../media-skin';
|
||||
|
||||
import '../media-container.js';
|
||||
import '../components/connected-with-defaults/media-play-button.js';
|
||||
import '../components/connected-with-defaults/media-mute-button.js';
|
||||
import '../media-container';
|
||||
import '../components/media-play-button';
|
||||
import '../components/media-mute-button';
|
||||
import '@vjs-10/html-icons';
|
||||
|
||||
export function getTemplateHTML() {
|
||||
|
||||
@@ -6,20 +6,15 @@ import { ConsumerMixin } from '@open-wc/context-protocol';
|
||||
*/
|
||||
export type StateHook<T = any> = {
|
||||
keys: string[];
|
||||
transform: (rawState: any) => T;
|
||||
transform: (rawState: any, mediaStore: any) => T;
|
||||
};
|
||||
|
||||
export type PropsHook<T = any> = (state: T, element: HTMLElement) => void;
|
||||
|
||||
export type EventsHook = {
|
||||
events: string[];
|
||||
handler: (event: CustomEvent, mediaStore: any) => void;
|
||||
};
|
||||
export type PropsHook<T = any, P = any> = (state: T, element: HTMLElement) => P;
|
||||
|
||||
/**
|
||||
* Generic factory function to create connected HTML components using hooks pattern.
|
||||
* Provides equivalent functionality to React's toConnectedComponent but for custom elements.
|
||||
*
|
||||
*
|
||||
* @param BaseClass - Base custom element class to extend
|
||||
* @param stateHook - Hook that defines state keys and transformation logic
|
||||
* @param propsHook - Hook that handles element attributes and properties based on state
|
||||
@@ -31,70 +26,48 @@ export const toConnectedHTMLComponent = <TState = any>(
|
||||
BaseClass: CustomElementConstructor,
|
||||
stateHook: StateHook<TState>,
|
||||
propsHook: PropsHook<TState>,
|
||||
eventsHook: EventsHook,
|
||||
displayName?: string
|
||||
displayName?: string,
|
||||
) => {
|
||||
const ConnectedComponent = class extends ConsumerMixin(BaseClass) {
|
||||
static get observedAttributes(): string[] {
|
||||
return [
|
||||
// @ts-ignore
|
||||
...(super.observedAttributes ?? [])
|
||||
...(super.observedAttributes ?? []),
|
||||
];
|
||||
}
|
||||
|
||||
_mediaStore: any;
|
||||
_state: TState | undefined;
|
||||
|
||||
contexts = {
|
||||
mediaStore: (mediaStore: any) => {
|
||||
this._mediaStore = mediaStore;
|
||||
|
||||
|
||||
// Subscribe to media store state changes
|
||||
// Split into two phases: state transformation, then props update
|
||||
this._mediaStore.subscribeKeys(
|
||||
stateHook.keys,
|
||||
(rawState: any) => {
|
||||
// Phase 1: Transform raw media store state (state concern)
|
||||
this._state = stateHook.transform(rawState);
|
||||
|
||||
// Phase 2: Update element attributes/properties (props concern)
|
||||
if (this._state !== undefined) {
|
||||
// @ts-ignore - Element property access
|
||||
propsHook(this._state, this);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
this._mediaStore.subscribeKeys(stateHook.keys, (rawState: any) => {
|
||||
// Phase 1: Transform raw media store state (state concern)
|
||||
const state = stateHook.transform(rawState, mediaStore);
|
||||
|
||||
// Phase 2: Update element attributes/properties (props concern)
|
||||
// @ts-ignore - Element property access
|
||||
const props = propsHook(state ?? {}, this);
|
||||
// @ts-ignore
|
||||
this._update(props, state);
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
connectedCallback(): void {
|
||||
super.connectedCallback?.();
|
||||
|
||||
// Set up event listeners
|
||||
eventsHook.events.forEach(eventType => {
|
||||
// @ts-ignore - Element property access
|
||||
this.addEventListener(eventType, this);
|
||||
});
|
||||
}
|
||||
|
||||
disconnectedCallback(): void {
|
||||
super.disconnectedCallback?.();
|
||||
|
||||
// Clean up event listeners
|
||||
eventsHook.events.forEach(eventType => {
|
||||
// @ts-ignore - Element property access
|
||||
this.removeEventListener(eventType, this);
|
||||
});
|
||||
}
|
||||
|
||||
handleEvent(event: CustomEvent): void {
|
||||
// @ts-ignore
|
||||
super.handleEvent?.(event);
|
||||
|
||||
// Delegate to event hook if media store is available
|
||||
if (this._mediaStore) {
|
||||
eventsHook.handler(event, this._mediaStore);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -104,4 +77,4 @@ export const toConnectedHTMLComponent = <TState = any>(
|
||||
}
|
||||
|
||||
return ConnectedComponent;
|
||||
};
|
||||
};
|
||||
|
||||
@@ -82,4 +82,4 @@ export const PlayButton = toConnectedComponent(
|
||||
renderPlayButton,
|
||||
'PlayButton',
|
||||
);
|
||||
export default PlayButton;
|
||||
export default PlayButton;
|
||||
|
||||
Reference in New Issue
Block a user