import { ChildrenLike, VirtualDOM } from '@youwol/rx-vdom' import { BehaviorSubject, Subject } from 'rxjs' /** * Represents a range view. * * * let range = new Views.Range() * display(range) * display(range.value$) * * * To setup min, max and step: * * display(new Views.Range({min:0, max:100, step: 1})) * * * The range can optionally **not** emit while dragging: * * range = new Views.Range({emitDrag: false}) * display(range) * * * * When emitting on drag, it may be relevant to debounce the values, *e.g.*: * * `range.value$.pipe(rxjs.debounceTime(100))`. * * */ export class Range implements VirtualDOM<'div'> { public readonly tag = 'div' /** * Classes associated to the view. */ public readonly class = 'mknb-Range' public readonly children: ChildrenLike public readonly value$: Subject /** * Default value. */ public readonly value: number = 0.5 /** * Minimum value. */ public readonly min: number = 0 /** * Maximum value. */ public readonly max: number = 1 /** * Step. */ public readonly step = 0.01 /** * If `true`, data are emitted in `value$` while dragging the slider. */ public readonly emitDrag: boolean = true /** * Style attributes. */ public readonly style = { fontSize: 'small', } constructor( params: { min?: number max?: number step?: number value?: number value$?: Subject emitDrag?: boolean } = {}, ) { Object.assign(this, params) if (!params.value) { this.value = 0.5 * (this.max - this.min) } if (!this.value$) { this.value$ = new BehaviorSubject(this.value) } const getValue = (from: string) => { const v = parseFloat(from) if (v < this.min) { return this.min } if (v > this.max) { return this.max } return v } const options = { min: `${this.min}`, max: `${this.max}`, step: `${this.step}`, } this.children = [ { tag: 'input', type: 'number', ...options, value: { source$: this.value$, vdomMap: (v: number) => `${v}`, }, onchange: (ev: MouseEvent) => { this.value$.next(getValue(ev.target['value'])) }, }, { tag: 'i', class: 'mx-1' }, { tag: 'input', type: 'range', ...options, value: { source$: this.value$, vdomMap: (v: number) => `${v}`, }, onchange: (ev: MouseEvent) => { this.value$.next(getValue(ev.target['value'])) }, oninput: (ev: MouseEvent) => { if (this.emitDrag) { this.value$.next(getValue(ev.target['value'])) } }, }, ] } }