@aidenlx/player
Version:
Headless web components that make integrating media on the a web a breeze.
176 lines (147 loc) • 4.67 kB
text/typescript
import { derived, DisposalBin, formatTime, ReadableStore, round } from '@vidstack/foundation';
import { css, type CSSResultGroup, html, LitElement, type PropertyValues } from 'lit';
import { property, state } from 'lit/decorators.js';
import { sliderStoreContext } from './store';
/**
* Outputs the current slider value as text.
*
* @tagname vds-slider-value-text
* @example
* ```html
* <vds-slider-value-text
* type="current"
* ></vds-slider-value-text>
* ```
* @example
* ```html
* <vds-slider-value-text
* format="time"
* show-hours
* pad-hours
* ></vds-slider-value-text>
* ```
* @example
* ```html
* <vds-slider-value-text
* format="percent"
* decimal-places="2"
* ></vds-slider-value-text>
* ```
*/
export class SliderValueTextElement extends LitElement {
static override get styles(): CSSResultGroup {
return [
css`
:host {
display: inline-block;
contain: content;
}
:host([hidden]) {
display: none;
}
`,
];
}
protected _disposal = new DisposalBin();
protected _sliderStoreConsumer = sliderStoreContext.consume(this);
protected get _sliderStore() {
return this._sliderStoreConsumer.value;
}
protected __value = 0;
// -------------------------------------------------------------------------------------------
// Properties
// -------------------------------------------------------------------------------------------
/**
* Whether to use the slider's current value, or pointer value.
*/
type: 'current' | 'pointer' = 'current';
/**
* Determines how the value is formatted.
*
* @default undefined
*/
format?: 'percent' | 'time';
/**
* Whether the time should always show the hours unit, even if the time is less than
* 1 hour. Only available if the `format` attribute is set to `time`.
*
* @default false
* @example `20:30` -> `0:20:35`
*/
showHours = false;
/**
* Whether the hours unit should be padded with zeroes to a length of 2. Only available if
* the `format` attribute is set to `time`.
*
* @default false
* @example `1:20:03` -> `01:20:03`
*/
padHours = false;
/**
* Round the value when formatted as a percentage to the given number of decimal places. Only
* available if `format` attribute is `percent`.
*
* @default 2
*/
decimalPlaces = 2;
// -------------------------------------------------------------------------------------------
// Lifecycle
// -------------------------------------------------------------------------------------------
override connectedCallback(): void {
super.connectedCallback();
this._handleTypeChange();
}
protected override update(changedProperties: PropertyValues): void {
if (changedProperties.has('type') || changedProperties.has('format')) {
this._handleTypeChange();
}
super.update(changedProperties);
}
override disconnectedCallback(): void {
this._disposal.empty();
super.disconnectedCallback();
}
protected override render() {
return html`${this._getValueText()}`;
}
// -------------------------------------------------------------------------------------------
// Methods
// -------------------------------------------------------------------------------------------
protected _handleTypeChange() {
this._disposal.empty();
const valueStore = this._sliderStore[this.type === 'current' ? 'value' : 'pointerValue'];
const store = this.format === 'percent' ? this._createPercentStore(valueStore) : valueStore;
const unsub = store.subscribe(($value) => {
this.__value = $value;
});
this._disposal.add(unsub);
}
protected _createPercentStore(valueStore: ReadableStore<number>) {
return derived(
[valueStore, this._sliderStore.min, this._sliderStore.max],
([$value, $min, $max]) => {
const range = $max - $min;
return ($value / range) * 100;
},
);
}
protected _getValueText(): string {
switch (this.format) {
case 'percent':
return this._getPercentFormat();
case 'time':
return this._getTimeFormat();
default:
return `${this.__value}`;
}
}
protected _getPercentFormat(): string {
return `${round(this.__value, this.decimalPlaces)}%`;
}
protected _getTimeFormat(): string {
return formatTime(this.__value, this.padHours, this.showHours);
}
}