feat: implement current time display components

Adds current time display functionality across HTML and React platforms:
- Core state definition for current time with duration support
- HTML component with shadow DOM and span rendering
- React component following hook-style architecture
- Integration into both HTML and React default skins
- Proper exports and component registration

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Christian Pillsbury
2025-09-12 15:59:15 -07:00
committed by Christian Pillsbury
co-authored by Claude
parent 6db4c3c4b8
commit 5bd0a154db
8 changed files with 249 additions and 1 deletions
@@ -0,0 +1,114 @@
import {
toConnectedHTMLComponent,
StateHook,
PropsHook,
} from '../utils/component-factory';
import { currentTimeDisplayStateDefinition, formatDisplayTime } from '@vjs-10/media-store';
import { namedNodeMapToObject } from '../utils/element-utils.js';
export function getTemplateHTML(
this: typeof CurrentTimeDisplayBase,
_attrs: Record<string, string>,
_props: Record<string, any> = {},
) {
return /* html */ `
<span></span>
`;
}
export class CurrentTimeDisplayBase extends HTMLElement {
static shadowRootOptions = {
mode: 'open' as ShadowRootMode,
};
static getTemplateHTML = getTemplateHTML;
_state:
| {
currentTime: number | undefined;
duration: number | undefined;
}
| undefined;
constructor() {
super();
if (!this.shadowRoot) {
this.attachShadow(
(this.constructor as typeof CurrentTimeDisplayBase).shadowRootOptions,
);
const attrs = namedNodeMapToObject(this.attributes);
const html = (
this.constructor as typeof CurrentTimeDisplayBase
).getTemplateHTML(attrs);
const shadowRoot = this.shadowRoot as unknown as ShadowRoot;
shadowRoot.setHTMLUnsafe
? shadowRoot.setHTMLUnsafe(html)
: (shadowRoot.innerHTML = html);
}
}
get currentTime() {
return this._state?.currentTime;
}
get duration() {
return this._state?.duration;
}
_update(_props: any, state: any) {
this._state = state;
// Update the span content with formatted current time
const spanElement = this.shadowRoot?.querySelector('span') as HTMLElement;
if (spanElement) {
spanElement.textContent = formatDisplayTime(state.currentTime);
}
}
}
/**
* CurrentTimeDisplay state hook - equivalent to React's useCurrentTimeDisplayState
* Handles media store state subscription and transformation
*/
export const useCurrentTimeDisplayState: StateHook<{
currentTime: number | undefined;
duration: number | undefined;
}> = {
keys: [...currentTimeDisplayStateDefinition.keys],
transform: (rawState, _mediaStore) => ({
...currentTimeDisplayStateDefinition.stateTransform(rawState),
// Current time display is read-only, so no request methods needed
}),
};
/**
* CurrentTimeDisplay props hook - equivalent to React's useCurrentTimeDisplayProps
* Handles element attributes and properties based on state
*/
export const useCurrentTimeDisplayProps: PropsHook<{
currentTime: number | undefined;
duration: number | undefined;
}> = (_state, _element) => {
const baseProps: Record<string, any> = {};
return baseProps;
};
/**
* Connected CurrentTimeDisplay component using hook-style architecture
* Equivalent to React's CurrentTimeDisplay = toConnectedComponent(...)
*/
export const CurrentTimeDisplay = toConnectedHTMLComponent(
CurrentTimeDisplayBase,
useCurrentTimeDisplayState,
useCurrentTimeDisplayProps,
'CurrentTimeDisplay',
);
// Register the custom element
if (!globalThis.customElements.get('media-current-time-display')) {
// @ts-ignore - Custom element constructor compatibility
globalThis.customElements.define('media-current-time-display', CurrentTimeDisplay);
}
export default CurrentTimeDisplay;
+1
View File
@@ -7,6 +7,7 @@ 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 { CurrentTimeDisplay } from './components/media-current-time-display.js';
export function defineVjsPlayer() {
/** @TODO - Reimplement me (at least as a POC) (CJP) */
@@ -7,6 +7,7 @@ import '../components/media-volume-range';
import '../components/media-time-range';
import '../components/media-fullscreen-button';
import '../components/media-duration-display';
import '../components/media-current-time-display';
import '@vjs-10/html-icons';
export function getTemplateHTML() {
@@ -99,6 +100,7 @@ export function getTemplateHTML() {
<media-play-icon class="icon play-icon"></media-play-icon>
<media-pause-icon class="icon pause-icon"></media-pause-icon>
</media-play-button>
<media-current-time-display></media-current-time-display>
<media-time-range></media-time-range>
<media-duration-display></media-duration-display>
<media-mute-button class="button">