From ab07a8a9a26f68066e292718f3cd17a2640c5477 Mon Sep 17 00:00:00 2001 From: Christian Pillsbury Date: Fri, 12 Sep 2025 12:14:00 -0700 Subject: [PATCH] feat: add showRemaining functionality to current time display MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds support for displaying remaining time (countdown) instead of elapsed time: **HTML Component:** - Add `show-remaining` attribute support with observedAttributes - Implement attributeChangedCallback for reactive updates - Display remaining time as `-{duration - currentTime}` when attribute present - Maintain backward compatibility with default current time display **React Component:** - Add `showRemaining` prop with proper TypeScript typing - Calculate and display remaining time in render function - Destructure prop to avoid passing to DOM element - Consistent `-{remaining}` format matching HTML component **Usage:** - HTML: `` - React: `` Both implementations include documentation comments and maintain consistent behavior across platforms. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- .../components/media-current-time-display.ts | 45 ++++++++++++++++--- .../html/html/src/skins/media-skin-default.ts | 1 + .../src/components/CurrentTimeDisplay.tsx | 27 +++++++---- .../react/src/skins/MediaSkinDefault.tsx | 10 ++++- 4 files changed, 67 insertions(+), 16 deletions(-) diff --git a/packages/html/html/src/components/media-current-time-display.ts b/packages/html/html/src/components/media-current-time-display.ts index 99269ae5..afdead26 100644 --- a/packages/html/html/src/components/media-current-time-display.ts +++ b/packages/html/html/src/components/media-current-time-display.ts @@ -3,7 +3,10 @@ import { StateHook, PropsHook, } from '../utils/component-factory'; -import { currentTimeDisplayStateDefinition, formatDisplayTime } from '@vjs-10/media-store'; +import { + currentTimeDisplayStateDefinition, + formatDisplayTime, +} from '@vjs-10/media-store'; import { namedNodeMapToObject } from '../utils/element-utils.js'; export function getTemplateHTML( @@ -21,6 +24,7 @@ export class CurrentTimeDisplayBase extends HTMLElement { mode: 'open' as ShadowRootMode, }; static getTemplateHTML = getTemplateHTML; + static observedAttributes = ['show-remaining']; _state: | { @@ -56,13 +60,39 @@ export class CurrentTimeDisplayBase extends HTMLElement { return this._state?.duration; } + get showRemaining() { + return this.hasAttribute('show-remaining'); + } + + attributeChangedCallback( + name: string, + _oldValue: string | null, + _newValue: string | null, + ) { + if (name === 'show-remaining' && this._state) { + // Re-render with current state when show-remaining attribute changes + this._update({}, this._state); + } + } + _update(_props: any, state: any) { this._state = state; - - // Update the span content with formatted current time + + // Update the span content with formatted time const spanElement = this.shadowRoot?.querySelector('span') as HTMLElement; if (spanElement) { - spanElement.textContent = formatDisplayTime(state.currentTime); + if ( + this.showRemaining && + state.duration != null && + state.currentTime != null + ) { + // Show remaining time: duration - currentTime + const remainingTime = state.duration - state.currentTime; + spanElement.textContent = `-${formatDisplayTime(remainingTime)}`; + } else { + // Show current time (default behavior) + spanElement.textContent = formatDisplayTime(state.currentTime); + } } } } @@ -108,7 +138,10 @@ export const CurrentTimeDisplay = toConnectedHTMLComponent( // 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); + globalThis.customElements.define( + 'media-current-time-display', + CurrentTimeDisplay, + ); } -export default CurrentTimeDisplay; \ No newline at end of file +export default CurrentTimeDisplay; diff --git a/packages/html/html/src/skins/media-skin-default.ts b/packages/html/html/src/skins/media-skin-default.ts index 829392f4..02198c7a 100644 --- a/packages/html/html/src/skins/media-skin-default.ts +++ b/packages/html/html/src/skins/media-skin-default.ts @@ -114,6 +114,7 @@ export function getTemplateHTML() { + diff --git a/packages/react/react/src/components/CurrentTimeDisplay.tsx b/packages/react/react/src/components/CurrentTimeDisplay.tsx index 43331527..16455a38 100644 --- a/packages/react/react/src/components/CurrentTimeDisplay.tsx +++ b/packages/react/react/src/components/CurrentTimeDisplay.tsx @@ -5,7 +5,10 @@ import { } from '@vjs-10/react-media-store'; import * as React from 'react'; import { toConnectedComponent } from '../utils/component-factory'; -import { currentTimeDisplayStateDefinition, formatDisplayTime } from '@vjs-10/media-store'; +import { + currentTimeDisplayStateDefinition, + formatDisplayTime, +} from '@vjs-10/media-store'; export const useCurrentTimeDisplayState = (_props: any) => { const mediaStore = useMediaStore(); @@ -26,7 +29,7 @@ export type useCurrentTimeDisplayState = typeof useCurrentTimeDisplayState; export type CurrentTimeDisplayState = ReturnType; export const useCurrentTimeDisplayProps = ( - props: React.PropsWithChildren<{ [k: string]: any }>, + props: React.PropsWithChildren<{ showRemaining?: boolean; [k: string]: any }>, state: ReturnType, ) => { const baseProps: Record = { @@ -44,11 +47,19 @@ export const renderCurrentTimeDisplay = ( props: CurrentTimeDisplayProps, state: CurrentTimeDisplayState, ) => { - return ( - - {formatDisplayTime(state.currentTime)} - - ); + const { showRemaining, ...restProps } = props; + + let timeToDisplay: number | undefined; + + if (showRemaining && state.duration != null && state.currentTime != null) { + // Show remaining time: duration - currentTime + timeToDisplay = state.duration - state.currentTime; + } else { + // Show current time (default behavior) + timeToDisplay = state.currentTime; + } + + return -{formatDisplayTime(timeToDisplay)}; }; export type renderCurrentTimeDisplay = typeof renderCurrentTimeDisplay; @@ -59,4 +70,4 @@ export const CurrentTimeDisplay = toConnectedComponent( renderCurrentTimeDisplay, 'CurrentTimeDisplay', ); -export default CurrentTimeDisplay; \ No newline at end of file +export default CurrentTimeDisplay; diff --git a/packages/react/react/src/skins/MediaSkinDefault.tsx b/packages/react/react/src/skins/MediaSkinDefault.tsx index 3ccf2e01..fedc45ef 100644 --- a/packages/react/react/src/skins/MediaSkinDefault.tsx +++ b/packages/react/react/src/skins/MediaSkinDefault.tsx @@ -31,7 +31,11 @@ export const MediaSkinDefault: React.FC<{ children: React.ReactNode }> = ({ - + {/* @ts-ignore */} @@ -48,7 +52,9 @@ export const MediaSkinDefault: React.FC<{ children: React.ReactNode }> = ({ {/* @ts-ignore */} - +