mirror of
https://github.com/zoriya/v10.git
synced 2026-08-16 02:45:09 +00:00
chore(root): prepare workspace for alpha (#276)
This commit is contained in:
@@ -1,148 +0,0 @@
|
||||
import type { MediaStore } from '@videojs/core/store';
|
||||
import { ConsumerMixin } from '@open-wc/context-protocol';
|
||||
import { shallowEqual, toCamelCase } from '@videojs/utils';
|
||||
import { setAttributes } from '@videojs/utils/dom';
|
||||
|
||||
/**
|
||||
* Generic types for HTML component hooks pattern
|
||||
* Mirrors the React hooks architecture for consistency
|
||||
*/
|
||||
export type StateHook<E extends HTMLElement, T = any> = (element: E, mediaStore: MediaStore) => T;
|
||||
|
||||
export type PropsHook<E extends HTMLElement, T = any, P = any> = (element: E, state: T) => P;
|
||||
|
||||
export interface ConnectedComponentConstructor<E extends HTMLElement, State> {
|
||||
new (state: State): E;
|
||||
}
|
||||
|
||||
let currentCoreInstances: any[] = [];
|
||||
// There might be multiple getCoreState calls in a single state hook
|
||||
// which should create a different core instance.
|
||||
let currentCoreIndex: number = 0;
|
||||
|
||||
/**
|
||||
* 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
|
||||
* @param displayName - Display name for debugging
|
||||
* @returns Connected custom element class with media store integration
|
||||
*/
|
||||
export function toConnectedHTMLComponent<E extends HTMLElement, State = any>(
|
||||
BaseClass: CustomElementConstructor,
|
||||
stateHook: StateHook<E, State> | undefined,
|
||||
propsHook: PropsHook<E, State>,
|
||||
displayName?: string,
|
||||
): ConnectedComponentConstructor<E, State> {
|
||||
const ConnectedComponent = class extends ConsumerMixin(BaseClass) {
|
||||
static get observedAttributes(): string[] {
|
||||
return [
|
||||
// @ts-expect-error ts(2339)
|
||||
...(super.observedAttributes ?? []),
|
||||
];
|
||||
}
|
||||
|
||||
_state: State | undefined;
|
||||
#mediaStore: MediaStore | undefined;
|
||||
#coreInstances: { core: any; listening: boolean }[] = [];
|
||||
|
||||
contexts = {
|
||||
mediaStore: (mediaStore: any) => {
|
||||
this.#mediaStore = mediaStore;
|
||||
|
||||
// Subscribe to media store state changes
|
||||
mediaStore.subscribe(() => {
|
||||
this.#render();
|
||||
|
||||
for (const instance of this.#coreInstances) {
|
||||
if (!instance.listening) {
|
||||
instance.listening = true;
|
||||
instance.core.subscribe(this.#render);
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
#render = (): void => {
|
||||
if (!this.#mediaStore) return;
|
||||
|
||||
currentCoreIndex = 0;
|
||||
currentCoreInstances = this.#coreInstances;
|
||||
|
||||
// Split into two phases: state transformation, then props update
|
||||
const state = stateHook?.(this as unknown as E, this.#mediaStore);
|
||||
const props = propsHook(this as unknown as E, state ?? {} as State);
|
||||
this._update(props, state, this.#mediaStore);
|
||||
};
|
||||
|
||||
_update(props: any, state: State | undefined, _mediaStore: MediaStore): void {
|
||||
this._state = state;
|
||||
// @ts-expect-error any
|
||||
super._update?.(props, state, _mediaStore);
|
||||
setAttributes(this, props);
|
||||
}
|
||||
|
||||
attributeChangedCallback(name: string, oldValue: string, newValue: string): void {
|
||||
super.attributeChangedCallback?.(name, oldValue, newValue);
|
||||
this.#render();
|
||||
}
|
||||
|
||||
connectedCallback(): void {
|
||||
super.connectedCallback?.();
|
||||
}
|
||||
|
||||
disconnectedCallback(): void {
|
||||
super.disconnectedCallback?.();
|
||||
}
|
||||
|
||||
handleEvent(event: CustomEvent): void {
|
||||
// @ts-expect-error any
|
||||
super.handleEvent?.(event);
|
||||
}
|
||||
};
|
||||
|
||||
// Set display name for debugging and dev tools
|
||||
if (displayName) {
|
||||
Object.defineProperty(ConnectedComponent, 'name', { value: displayName });
|
||||
}
|
||||
|
||||
return ConnectedComponent as unknown as ConnectedComponentConstructor<E, State>;
|
||||
}
|
||||
|
||||
export function getCoreState<T extends {
|
||||
subscribe: (callback: (state: any) => void) => () => void;
|
||||
getState: () => any;
|
||||
setState: (state: any) => void;
|
||||
}>(CoreClass: new () => T, state: any): any {
|
||||
let core = currentCoreInstances[currentCoreIndex]?.core as T;
|
||||
if (!core) {
|
||||
core = new CoreClass();
|
||||
currentCoreInstances[currentCoreIndex] = { core, listening: false };
|
||||
}
|
||||
|
||||
currentCoreIndex++;
|
||||
|
||||
const coreState = core.getState();
|
||||
const oldState: Record<string, any> = {};
|
||||
for (const key in state) {
|
||||
oldState[key] = coreState[key];
|
||||
}
|
||||
// Only set the state if it has changed
|
||||
if (!shallowEqual(oldState, state)) {
|
||||
core.setState(state);
|
||||
}
|
||||
|
||||
return core.getState();
|
||||
}
|
||||
|
||||
export function getPropsFromAttrs(element: HTMLElement): Record<string, any> {
|
||||
const props: Record<string, any> = {};
|
||||
for (const attr of element.attributes) {
|
||||
const camelName = toCamelCase(attr.name);
|
||||
props[camelName] = element[camelName as keyof typeof element];
|
||||
}
|
||||
return props;
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
export function defineCustomElement(tagName: string, ctor: CustomElementConstructor): void {
|
||||
if (typeof window === 'undefined') return;
|
||||
|
||||
if (window.customElements.get(tagName)) return;
|
||||
|
||||
window.customElements.define(tagName, ctor);
|
||||
}
|
||||
Reference in New Issue
Block a user