feat: add showRemaining functionality to current time display

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: `<media-current-time-display show-remaining></media-current-time-display>`
- React: `<CurrentTimeDisplay showRemaining />`

Both implementations include documentation comments and maintain
consistent behavior across platforms.

🤖 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 3bca85f180
commit ab07a8a9a2
4 changed files with 67 additions and 16 deletions
@@ -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;
export default CurrentTimeDisplay;
@@ -114,6 +114,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>
<!-- Use the show-remaining attribute to show count down/remaining time -->
<media-current-time-display></media-current-time-display>
<media-time-range></media-time-range>
<media-duration-display></media-duration-display>
@@ -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<useCurrentTimeDisplayState>;
export const useCurrentTimeDisplayProps = (
props: React.PropsWithChildren<{ [k: string]: any }>,
props: React.PropsWithChildren<{ showRemaining?: boolean; [k: string]: any }>,
state: ReturnType<typeof useCurrentTimeDisplayState>,
) => {
const baseProps: Record<string, any> = {
@@ -44,11 +47,19 @@ export const renderCurrentTimeDisplay = (
props: CurrentTimeDisplayProps,
state: CurrentTimeDisplayState,
) => {
return (
<span {...props}>
{formatDisplayTime(state.currentTime)}
</span>
);
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 <span {...restProps}>-{formatDisplayTime(timeToDisplay)}</span>;
};
export type renderCurrentTimeDisplay = typeof renderCurrentTimeDisplay;
@@ -59,4 +70,4 @@ export const CurrentTimeDisplay = toConnectedComponent(
renderCurrentTimeDisplay,
'CurrentTimeDisplay',
);
export default CurrentTimeDisplay;
export default CurrentTimeDisplay;
@@ -31,7 +31,11 @@ export const MediaSkinDefault: React.FC<{ children: React.ReactNode }> = ({
<PlayIcon className={styles.PlayIcon}></PlayIcon>
<PauseIcon className={styles.PauseIcon}></PauseIcon>
</PlayButton>
<CurrentTimeDisplay className={styles.TimeDisplay} />
<CurrentTimeDisplay
// Use showRemaining to show count down/remaining time
// showRemaining
className={styles.TimeDisplay}
/>
<TimeRange className={styles.TimeRange} />
<DurationDisplay className={styles.TimeDisplay} />
{/* @ts-ignore */}
@@ -48,7 +52,9 @@ export const MediaSkinDefault: React.FC<{ children: React.ReactNode }> = ({
</MuteButton>
<VolumeRange className={styles.VolumeRange} />
{/* @ts-ignore */}
<FullscreenButton className={`${styles.Button} ${styles.MediaFullscreenButton}`}>
<FullscreenButton
className={`${styles.Button} ${styles.MediaFullscreenButton}`}
>
<FullscreenEnterIcon
className={`${styles.Icon} ${styles.FullscreenEnterIcon}`}
></FullscreenEnterIcon>