-
Notifications
You must be signed in to change notification settings - Fork 31
/
Copy pathslider.ts
228 lines (206 loc) · 6.46 KB
/
slider.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
import { customElement, html, property, query, TemplateResult } from "lit-element";
import { nothing } from "lit-html";
import { ifDefined } from "lit-html/directives/if-defined";
import { FormElement } from "../behavior/form-element/form-element-behavior";
import { IInputBehaviorProperties, InputBehavior } from "../behavior/input/input-behavior";
import { AriaRole } from "../util/aria";
import { cssResult } from "../util/css";
import styles from "./slider.scss";
/**
* Properties of the slider.
*/
export interface ISliderProperties extends IInputBehaviorProperties {
thumbLabel: boolean;
min: number;
max: number;
step?: number;
bufferMin: number;
bufferMax: number;
bufferValue?: number;
}
/**
* Make selections from a range of values.
* @slot thumb-label - Optional slot for the thumb label.
* @cssprop --slider-height - Height
* @cssprop --slider-bg - Background
* @cssprop --slider-bg-buffer - Background of the buffer part
* @cssprop --slider-bg-active - Background of the active part
* @cssprop --slider-bg-disabled - Background when disabled
* @cssprop --slider-bg-buffer-disabled - Background of the buffer part when disabled
* @cssprop --slider-bg-active-disabled - Background of the active part when disabled
* @cssprop --slider-thumb-focus-ring-bg - Background of the thumb focus ring
* @cssprop --slider-thumb-bg - Background of the thumb
* @cssprop --slider-thumb-bg-disabled - Background of the thumb when disabled
* @cssprop --slider-thumb-focus-ring-size - Size of the thumb focus ring when focused
* @cssprop --slider-thumb-size - Size of the thumb
* @cssprop --slider-thumb-space - Space between slider track and thumb label
* @cssprop --slider-thumb-transition - Transition of the thumb
* @cssprop --slider-thumb-transform-focus - Transform of the thumb when focused
* @cssprop --slider-thumb-border-radius - Border radius of the thumb
* @cssprop --slider-thumb-label-size - Size of the thumb label
* @cssprop --slider-thumb-label-border-radius - Border radius of the thumb label
* @cssprop --slider-thumb-label-bg - Background of the thumb label
* @cssprop --slider-thumb-label-transition - Transition of the thumb label
* @cssprop --slider-thumb-label-font-size - Font size of the thumb label
* @cssprop --slider-thumb-label-color - Color of the thumb label
*/
@customElement("wl-slider")
export class Slider extends InputBehavior implements ISliderProperties {
static styles = [...InputBehavior.styles, cssResult(styles)];
/**
* Role of the slider.
* @attr
*/
@property({ type: String, reflect: true }) role: AriaRole = "slider";
/**
* Label above the thumb that shows the value.
* @attr
*/
@property({ type: Boolean }) thumbLabel: boolean = false;
/**
* The minimum value allowed.
* @attr
*/
@property({ type: Number }) min: number = 0;
/**
* The maximum value allowed.
* @attr
*/
@property({ type: Number }) max: number = 100;
/**
* The legal number intervals
* @attr
*/
@property({ type: Number }) step?: number;
/**
* The minimum buffer value allowed.
* @attr
*/
@property({ type: Number }) bufferMin: number = 0;
/**
* The maximum buffer value allowed.
* @attr
*/
@property({ type: Number }) bufferMax: number = 100;
/**
* The buffer value.
* @attr
*/
@property({ type: Number }) bufferValue?: number;
/**
* Slider element.
*/
@query("#slider") protected $slider!: HTMLInputElement;
/**
* The element that the user interacts with.
*/
protected get $interactiveElement(): FormElement {
return this.$slider;
}
/**
* Value in percentage.
*/
get perc() {
return (this.$slider.valueAsNumber - this.min) / (this.max - this.min);
}
/**
* Buffer value in percentage.
*/
get bufferPerc() {
return ((this.bufferValue || 0) - this.bufferMin) / (this.bufferMax - this.bufferMin);
}
/**
* When the properties changes we need to upgrade the background.
* @param props
*/
protected updated(props: Map<keyof ISliderProperties, unknown>) {
super.updated(props as Map<keyof IInputBehaviorProperties, unknown>);
this.updateBackground();
}
/**
* Sets the value of the form element and updates the background.
* @param value
*/
protected setValue(value: string) {
super.setValue(value);
// Update the slider that the user interacts with
if (this.$slider != null) {
this.$slider.value = value;
this.updateBackground();
this.requestUpdate().then();
}
}
/**
* Updates the background properties.
*/
protected updateBackground() {
requestAnimationFrame(() => {
this.style.setProperty("--_perc", this.perc.toString());
this.style.setProperty("--_buffer-perc", this.bufferPerc.toString());
});
}
/**
* Update the value of the form element when the slider value changes.
*/
protected sliderValueChanged() {
this.value = this.$slider.value;
this.requestUpdate().then();
}
/**
* Renders the form element.
* The reason we need to create two different range sliders is because the pseudo selectors of
* a range input cannot be styled using the ::slotted(..) selector.
*/
protected renderFormElement(id?: string, style?: string, onInput?: (e: Event) => void, tabIndex?: string): TemplateResult {
return html`
<input
type="range"
style="${ifDefined(style)}"
id="${ifDefined(id)}"
.value="${this.value}"
?required="${this.required}"
?disabled="${this.disabled}"
?readonly="${this.readonly}"
aria-label="${ifDefined(this.label)}"
name="${ifDefined(this.name)}"
autocomplete="${ifDefined(this.autocomplete)}"
min="${ifDefined(this.min)}"
max="${ifDefined(this.max)}"
step="${ifDefined(this.step)}"
@input="${onInput || (() => {})}"
tabindex="${tabIndex || this.disabled ? -1 : 0}"
/>
`;
}
/**
* Returns the template for the element.
*/
protected render(): TemplateResult {
return html`
<div id="container">
<slot id="before" name="before"></slot>
<div id="wrapper">
<div id="label">${this.label}</div>
<div id="slot-wrapper">
${this.renderFormElement("slider", undefined, this.sliderValueChanged)}
${this.thumbLabel
? html`
<div id="thumb-container">
<div id="thumb-label"><slot name="thumb-label">${this.value}</slot></div>
</div>
`
: nothing}
<slot id="slot"></slot>
</div>
${this.renderFormElement(this.formElementId, `display: none`, undefined, "-1")}
</div>
<slot id="after" name="after"></slot>
</div>
`;
}
}
declare global {
interface HTMLElementTagNameMap {
"wl-slider": Slider;
}
}