mirror of
https://github.com/zoriya/v10.git
synced 2026-08-14 09:59:44 +00:00
feat(html): implement duration display component
Add HTML duration display component using VJS-10 hook-style architecture: - Custom element with shadow DOM containing single span - Uses duration display state definition from media-store - Minimal implementation without styling or ARIA attributes - Follows toConnectedHTMLComponent pattern for consistency Component renders formatted duration (e.g., "4:32") in a simple span element. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
committed by
Christian Pillsbury
co-authored by
Claude
parent
d0036fc7d6
commit
e35dd44be8
@@ -0,0 +1,124 @@
|
||||
import {
|
||||
toConnectedHTMLComponent,
|
||||
StateHook,
|
||||
PropsHook,
|
||||
} from '../utils/component-factory';
|
||||
import { durationDisplayStateDefinition } from '@vjs-10/media-store';
|
||||
import { namedNodeMapToObject } from '../utils/element-utils.js';
|
||||
|
||||
export function getTemplateHTML(
|
||||
this: typeof DurationDisplayBase,
|
||||
_attrs: Record<string, string>,
|
||||
_props: Record<string, any> = {},
|
||||
) {
|
||||
return /* html */ `
|
||||
<span></span>
|
||||
`;
|
||||
}
|
||||
|
||||
export class DurationDisplayBase extends HTMLElement {
|
||||
static shadowRootOptions = {
|
||||
mode: 'open' as ShadowRootMode,
|
||||
};
|
||||
static getTemplateHTML = getTemplateHTML;
|
||||
|
||||
_state:
|
||||
| {
|
||||
duration: number | undefined;
|
||||
isValidDuration: boolean;
|
||||
formattedDuration: string;
|
||||
durationPhrase: string;
|
||||
}
|
||||
| undefined;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
if (!this.shadowRoot) {
|
||||
this.attachShadow(
|
||||
(this.constructor as typeof DurationDisplayBase).shadowRootOptions,
|
||||
);
|
||||
|
||||
const attrs = namedNodeMapToObject(this.attributes);
|
||||
const html = (
|
||||
this.constructor as typeof DurationDisplayBase
|
||||
).getTemplateHTML(attrs);
|
||||
const shadowRoot = this.shadowRoot as unknown as ShadowRoot;
|
||||
shadowRoot.setHTMLUnsafe
|
||||
? shadowRoot.setHTMLUnsafe(html)
|
||||
: (shadowRoot.innerHTML = html);
|
||||
}
|
||||
}
|
||||
|
||||
get duration() {
|
||||
return this._state?.duration;
|
||||
}
|
||||
|
||||
get formattedDuration() {
|
||||
return this._state?.formattedDuration || '--:--';
|
||||
}
|
||||
|
||||
get durationPhrase() {
|
||||
return this._state?.durationPhrase || 'Duration unknown';
|
||||
}
|
||||
|
||||
_update(_props: any, state: any) {
|
||||
this._state = state;
|
||||
|
||||
// Update the span content
|
||||
const spanElement = this.shadowRoot?.querySelector('span') as HTMLElement;
|
||||
if (spanElement) {
|
||||
spanElement.textContent = state.formattedDuration;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* DurationDisplay state hook - equivalent to React's useDurationDisplayState
|
||||
* Handles media store state subscription and transformation
|
||||
*/
|
||||
export const useDurationDisplayState: StateHook<{
|
||||
duration: number | undefined;
|
||||
isValidDuration: boolean;
|
||||
formattedDuration: string;
|
||||
durationPhrase: string;
|
||||
}> = {
|
||||
keys: [...durationDisplayStateDefinition.keys],
|
||||
transform: (rawState, _mediaStore) => ({
|
||||
...durationDisplayStateDefinition.stateTransform(rawState),
|
||||
// Duration display is read-only, so no request methods needed
|
||||
}),
|
||||
};
|
||||
|
||||
/**
|
||||
* DurationDisplay props hook - equivalent to React's useDurationDisplayProps
|
||||
* Handles element attributes and properties based on state
|
||||
*/
|
||||
export const useDurationDisplayProps: PropsHook<{
|
||||
duration: number | undefined;
|
||||
isValidDuration: boolean;
|
||||
formattedDuration: string;
|
||||
durationPhrase: string;
|
||||
}> = (_state, _element) => {
|
||||
const baseProps: Record<string, any> = {};
|
||||
return baseProps;
|
||||
};
|
||||
|
||||
/**
|
||||
* Connected DurationDisplay component using hook-style architecture
|
||||
* Equivalent to React's DurationDisplay = toConnectedComponent(...)
|
||||
*/
|
||||
export const DurationDisplay = toConnectedHTMLComponent(
|
||||
DurationDisplayBase,
|
||||
useDurationDisplayState,
|
||||
useDurationDisplayProps,
|
||||
'DurationDisplay',
|
||||
);
|
||||
|
||||
// Register the custom element
|
||||
if (!globalThis.customElements.get('media-duration-display')) {
|
||||
// @ts-ignore - Custom element constructor compatibility
|
||||
globalThis.customElements.define('media-duration-display', DurationDisplay);
|
||||
}
|
||||
|
||||
export default DurationDisplay;
|
||||
@@ -6,6 +6,7 @@ export { PlayButton } from './components/media-play-button.js';
|
||||
export { MuteButton } from './components/media-mute-button.js';
|
||||
export { VolumeRange } from './components/media-volume-range.js';
|
||||
export { FullscreenButton } from './components/media-fullscreen-button.js';
|
||||
export { DurationDisplay } from './components/media-duration-display.js';
|
||||
|
||||
export function defineVjsPlayer() {
|
||||
/** @TODO - Reimplement me (at least as a POC) (CJP) */
|
||||
|
||||
Reference in New Issue
Block a user