@statistikzh/leu 0.24.0 → 0.24.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/publish.yml +7 -0
- package/.release-please-manifest.json +1 -1
- package/CHANGELOG.md +8 -0
- package/dist/Accordion.js +1 -1
- package/dist/Button.js +1 -1
- package/dist/ButtonGroup.js +1 -1
- package/dist/ChartWrapper.js +1 -1
- package/dist/Checkbox.js +1 -1
- package/dist/CheckboxGroup.js +1 -1
- package/dist/Chip.js +1 -1
- package/dist/ChipGroup.js +1 -1
- package/dist/ChipLink.js +1 -1
- package/dist/ChipRemovable.js +1 -1
- package/dist/ChipSelectable.js +1 -1
- package/dist/Dialog.js +1 -1
- package/dist/Dropdown.js +1 -1
- package/dist/FileInput.js +1 -1
- package/dist/Icon.js +1 -1
- package/dist/Input.js +1 -1
- package/dist/{LeuElement-BfbOWTGZ.js → LeuElement-jrR2M5pZ.js} +1 -1
- package/dist/Menu.js +1 -1
- package/dist/MenuItem.js +1 -1
- package/dist/Message.js +1 -1
- package/dist/Pagination.js +1 -1
- package/dist/Placeholder.js +1 -1
- package/dist/Popup.js +1 -1
- package/dist/ProgressBar.js +1 -1
- package/dist/Radio.js +1 -1
- package/dist/RadioGroup.js +1 -1
- package/dist/Range.d.ts +32 -20
- package/dist/Range.js +137 -72
- package/dist/ScrollTop.js +2 -25
- package/dist/Select.js +1 -1
- package/dist/Spinner.js +1 -1
- package/dist/Table.js +1 -1
- package/dist/Tag.js +1 -1
- package/dist/VisuallyHidden.js +1 -1
- package/dist/components/range/Range.d.ts +33 -20
- package/dist/components/range/Range.d.ts.map +1 -1
- package/dist/components/range/stories/range.stories.d.ts +1 -0
- package/dist/components/range/stories/range.stories.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/leu-accordion.js +1 -1
- package/dist/leu-button-group.js +1 -1
- package/dist/leu-button.js +1 -1
- package/dist/leu-chart-wrapper.js +1 -1
- package/dist/leu-checkbox-group.js +1 -1
- package/dist/leu-checkbox.js +1 -1
- package/dist/leu-chip-group.js +1 -1
- package/dist/leu-chip-link.js +1 -1
- package/dist/leu-chip-removable.js +1 -1
- package/dist/leu-chip-selectable.js +1 -1
- package/dist/leu-dialog.js +1 -1
- package/dist/leu-dropdown.js +1 -1
- package/dist/leu-file-input.js +1 -1
- package/dist/leu-icon.js +1 -1
- package/dist/leu-input.js +1 -1
- package/dist/leu-menu-item.js +1 -1
- package/dist/leu-menu.js +1 -1
- package/dist/leu-message.js +1 -1
- package/dist/leu-pagination.js +1 -1
- package/dist/leu-placeholder.js +1 -1
- package/dist/leu-popup.js +1 -1
- package/dist/leu-progress-bar.js +1 -1
- package/dist/leu-radio-group.js +1 -1
- package/dist/leu-radio.js +1 -1
- package/dist/leu-range.js +3 -1
- package/dist/leu-scroll-top.js +2 -1
- package/dist/leu-select.js +1 -1
- package/dist/leu-spinner.js +1 -1
- package/dist/leu-table.js +1 -1
- package/dist/leu-tag.js +1 -1
- package/dist/leu-visually-hidden.js +1 -1
- package/dist/lib/utils.d.ts +10 -3
- package/dist/lib/utils.d.ts.map +1 -1
- package/dist/utils-DBGsNSJW.js +33 -0
- package/dist/vscode.html-custom-data.json +66 -62
- package/dist/vue/index.d.ts +80 -73
- package/dist/web-types.json +143 -137
- package/package.json +1 -1
- package/src/components/range/Range.ts +160 -87
- package/src/components/range/stories/range.stories.ts +3 -0
- package/src/components/range/test/range.test.ts +59 -0
- package/src/lib/utils.ts +13 -3
|
@@ -1,14 +1,18 @@
|
|
|
1
|
-
import { html, nothing } from "lit"
|
|
1
|
+
import { html, nothing, PropertyValues } from "lit"
|
|
2
|
+
import { property, query } from "lit/decorators.js"
|
|
3
|
+
import { ifDefined } from "lit/directives/if-defined.js"
|
|
2
4
|
|
|
3
|
-
import { property } from "lit/decorators.js"
|
|
4
5
|
import styles from "./range.css"
|
|
5
6
|
import { LeuElement } from "../../lib/LeuElement.js"
|
|
7
|
+
import { clamp, isNumber } from "../../lib/utils.js"
|
|
8
|
+
|
|
9
|
+
type InternalRangeValue = [number, number] | [number]
|
|
6
10
|
|
|
7
11
|
const defaultValueConverter = {
|
|
8
|
-
fromAttribute(value) {
|
|
12
|
+
fromAttribute(value: string) {
|
|
9
13
|
return value.split(",").map((v) => Number(v.trim()))
|
|
10
14
|
},
|
|
11
|
-
toAttribute(value) {
|
|
15
|
+
toAttribute(value: number[]) {
|
|
12
16
|
return value.join(",")
|
|
13
17
|
},
|
|
14
18
|
}
|
|
@@ -26,7 +30,15 @@ export class LeuRange extends LeuElement {
|
|
|
26
30
|
delegatesFocus: true,
|
|
27
31
|
}
|
|
28
32
|
|
|
29
|
-
|
|
33
|
+
/**
|
|
34
|
+
* The default value of the range slider.
|
|
35
|
+
* String input is parsed as a comma-separated list of numbers.
|
|
36
|
+
*/
|
|
37
|
+
@property({
|
|
38
|
+
converter: defaultValueConverter,
|
|
39
|
+
attribute: "value",
|
|
40
|
+
reflect: true,
|
|
41
|
+
})
|
|
30
42
|
defaultValue = [50]
|
|
31
43
|
|
|
32
44
|
/**
|
|
@@ -110,116 +122,175 @@ export class LeuRange extends LeuElement {
|
|
|
110
122
|
@property({ attribute: false })
|
|
111
123
|
valueFormatter?: (value: number) => string
|
|
112
124
|
|
|
113
|
-
|
|
114
|
-
this.
|
|
115
|
-
|
|
125
|
+
protected _value: InternalRangeValue = this.defaultValue.map((v) =>
|
|
126
|
+
this.clampAndRoundValue(v),
|
|
127
|
+
) as InternalRangeValue
|
|
116
128
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
129
|
+
/**
|
|
130
|
+
* The value of the range slider.
|
|
131
|
+
* String input is parsed as a comma-separated list of numbers.
|
|
132
|
+
* In multiple mode, if only a single value is provided, the second handle will be set to the minimum value.
|
|
133
|
+
* In single mode, only the first value will be used.
|
|
134
|
+
*/
|
|
135
|
+
@property({ attribute: false })
|
|
136
|
+
set value(value: string | number | Array<string | number>) {
|
|
137
|
+
let nextValue: Array<number> = []
|
|
138
|
+
|
|
139
|
+
if (typeof value === "string") {
|
|
140
|
+
nextValue = value
|
|
141
|
+
.split(",")
|
|
142
|
+
.map((v) => Number(v.trim()))
|
|
143
|
+
.filter(isNumber)
|
|
144
|
+
} else if (isNumber(value)) {
|
|
145
|
+
nextValue = [value]
|
|
146
|
+
} else if (Array.isArray(value)) {
|
|
147
|
+
nextValue = value.map((v: unknown) => Number(v)).filter(isNumber)
|
|
148
|
+
}
|
|
122
149
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
this.style.setProperty("--high", normalizedRange[1].toString())
|
|
150
|
+
if (nextValue.length === 0) {
|
|
151
|
+
return
|
|
152
|
+
}
|
|
127
153
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
154
|
+
// In multiple mode, we need to ensure that we always have two values.
|
|
155
|
+
// `min` is a fallback for the second value.
|
|
156
|
+
if (this.multiple && nextValue.length === 1) {
|
|
157
|
+
nextValue.unshift(this.min)
|
|
158
|
+
}
|
|
131
159
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
)
|
|
136
|
-
const normalizedValue = this._getNormalizedValue(input.valueAsNumber)
|
|
137
|
-
output.style.setProperty("--value", normalizedValue.toString())
|
|
138
|
-
output.value = this.formatValue(input.valueAsNumber)
|
|
139
|
-
})
|
|
160
|
+
this._value = nextValue
|
|
161
|
+
.slice(0, this.multiple ? 2 : 1)
|
|
162
|
+
.map((v) => this.clampAndRoundValue(v)) as InternalRangeValue
|
|
140
163
|
}
|
|
141
164
|
|
|
142
|
-
get value() {
|
|
143
|
-
return this.
|
|
165
|
+
get value(): string {
|
|
166
|
+
return this._value.join(",")
|
|
144
167
|
}
|
|
145
168
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
* The value has to be an array if "multiple" range is used.
|
|
149
|
-
* Otherwise it has to be a string.
|
|
150
|
-
*/
|
|
151
|
-
set value(value: string | Array<string>) {
|
|
152
|
-
if (this.multiple && Array.isArray(value)) {
|
|
153
|
-
const inputs = this._inputs
|
|
154
|
-
|
|
155
|
-
value.forEach((v, i) => {
|
|
156
|
-
inputs[i].value = v
|
|
157
|
-
})
|
|
158
|
-
this._updateStyles()
|
|
159
|
-
} else if (!this.multiple) {
|
|
160
|
-
this._getBaseInput().value = value
|
|
161
|
-
this._updateStyles()
|
|
162
|
-
}
|
|
169
|
+
get valueAsArray(): InternalRangeValue {
|
|
170
|
+
return this._value.slice() as InternalRangeValue
|
|
163
171
|
}
|
|
164
172
|
|
|
165
|
-
get
|
|
166
|
-
return
|
|
173
|
+
get valueLow(): number {
|
|
174
|
+
return Math.min(...this._value)
|
|
167
175
|
}
|
|
168
176
|
|
|
169
|
-
get
|
|
170
|
-
|
|
177
|
+
get valueHigh(): number {
|
|
178
|
+
return Math.max(...this._value)
|
|
179
|
+
}
|
|
171
180
|
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
}
|
|
181
|
+
@query("#container")
|
|
182
|
+
protected container: HTMLDivElement
|
|
175
183
|
|
|
176
|
-
|
|
184
|
+
@query("#input-base")
|
|
185
|
+
protected inputBase: HTMLInputElement
|
|
186
|
+
|
|
187
|
+
@query("#input-ghost")
|
|
188
|
+
protected inputGhost: HTMLInputElement | null
|
|
189
|
+
|
|
190
|
+
@query("output[for=input-base]")
|
|
191
|
+
protected outputBase: HTMLOutputElement
|
|
192
|
+
|
|
193
|
+
@query("output[for=input-ghost]")
|
|
194
|
+
protected outputGhost: HTMLOutputElement | null
|
|
195
|
+
|
|
196
|
+
updated() {
|
|
197
|
+
this.updateStyles()
|
|
177
198
|
}
|
|
178
199
|
|
|
179
|
-
|
|
180
|
-
|
|
200
|
+
protected willUpdate(changedProperties: PropertyValues<this>): void {
|
|
201
|
+
// Reflect defaultValue changes to the value property
|
|
202
|
+
// to ensure backwards compatibility with previous versions
|
|
203
|
+
if (changedProperties.has("defaultValue")) {
|
|
204
|
+
this.value = this.defaultValue.map((v) =>
|
|
205
|
+
this.clampAndRoundValue(v),
|
|
206
|
+
) as InternalRangeValue
|
|
207
|
+
}
|
|
181
208
|
|
|
182
|
-
if (
|
|
183
|
-
|
|
209
|
+
if (
|
|
210
|
+
changedProperties.has("min") ||
|
|
211
|
+
changedProperties.has("max") ||
|
|
212
|
+
changedProperties.has("step")
|
|
213
|
+
) {
|
|
214
|
+
this._value = this._value.map((v) =>
|
|
215
|
+
this.clampAndRoundValue(v),
|
|
216
|
+
) as InternalRangeValue
|
|
184
217
|
}
|
|
185
218
|
|
|
186
|
-
|
|
219
|
+
if (changedProperties.has("multiple") && this.multiple) {
|
|
220
|
+
// When switching to multiple mode, ensure that we have two values
|
|
221
|
+
if (this._value.length === 1) {
|
|
222
|
+
this._value = [this.min, this._value[0]]
|
|
223
|
+
}
|
|
224
|
+
} else if (changedProperties.has("multiple") && !this.multiple) {
|
|
225
|
+
// When switching to single mode, keep only the lower value
|
|
226
|
+
this._value = [this.valueLow]
|
|
227
|
+
}
|
|
187
228
|
}
|
|
188
229
|
|
|
189
|
-
protected
|
|
190
|
-
|
|
230
|
+
protected updateStyles() {
|
|
231
|
+
const normalizedRange = this.getNormalizedRange()
|
|
232
|
+
this.container?.style.setProperty("--low", normalizedRange[0].toString())
|
|
233
|
+
this.container?.style.setProperty("--high", normalizedRange[1].toString())
|
|
234
|
+
|
|
235
|
+
const inputs = this.multiple
|
|
236
|
+
? [this.inputBase, this.inputGhost]
|
|
237
|
+
: [this.inputBase]
|
|
238
|
+
|
|
239
|
+
inputs.forEach((input) => {
|
|
240
|
+
const output =
|
|
241
|
+
input.id === "input-base" ? this.outputBase : this.outputGhost
|
|
242
|
+
const normalizedValue = this.getNormalizedValue(input.valueAsNumber)
|
|
243
|
+
output.style.setProperty("--value", normalizedValue.toString())
|
|
244
|
+
output.value = this.formatValue(input.valueAsNumber)
|
|
245
|
+
})
|
|
191
246
|
}
|
|
192
247
|
|
|
193
|
-
protected
|
|
194
|
-
|
|
248
|
+
protected clampAndRoundValue(value: number) {
|
|
249
|
+
const clampedValue = clamp(value, this.min, this.max)
|
|
250
|
+
const roundedValue =
|
|
251
|
+
Math.round((clampedValue - this.min) / this.step) * this.step + this.min
|
|
252
|
+
|
|
253
|
+
return roundedValue
|
|
195
254
|
}
|
|
196
255
|
|
|
197
|
-
protected
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
256
|
+
protected handleInput(e: Event & { target: HTMLInputElement }) {
|
|
257
|
+
e.stopPropagation()
|
|
258
|
+
|
|
259
|
+
if (this.multiple) {
|
|
260
|
+
this.value = [this.inputBase.valueAsNumber, this.inputGhost.valueAsNumber]
|
|
261
|
+
} else {
|
|
262
|
+
this.value = [this.inputBase.valueAsNumber]
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
this.dispatchEvent(
|
|
266
|
+
new CustomEvent("input", {
|
|
267
|
+
composed: true,
|
|
268
|
+
bubbles: true,
|
|
269
|
+
detail: { value: this.value, valueAsArray: this.valueAsArray },
|
|
270
|
+
}),
|
|
271
|
+
)
|
|
202
272
|
}
|
|
203
273
|
|
|
204
|
-
protected
|
|
274
|
+
protected getNormalizedValue(value: number) {
|
|
205
275
|
return (value - this.min) / (this.max - this.min)
|
|
206
276
|
}
|
|
207
277
|
|
|
208
|
-
protected
|
|
278
|
+
protected getNormalizedRange() {
|
|
209
279
|
if (this.multiple) {
|
|
210
280
|
return this.valueAsArray
|
|
211
|
-
.map((value) => this.
|
|
281
|
+
.map((value) => this.getNormalizedValue(value))
|
|
212
282
|
.sort((a, b) => a - b)
|
|
213
283
|
}
|
|
214
284
|
|
|
215
|
-
return [0, this.
|
|
285
|
+
return [0, this.getNormalizedValue(this.valueAsArray[0])]
|
|
216
286
|
}
|
|
217
287
|
|
|
218
288
|
/**
|
|
219
|
-
*
|
|
220
|
-
*
|
|
289
|
+
* This event handler is only applied to the "base" input element and only when in "multiple" mode.
|
|
290
|
+
* It handles pointer events on the *track* and the thumb.
|
|
291
|
+
* This method determines if the interaction was closer to the base or the ghost input.
|
|
221
292
|
*/
|
|
222
|
-
protected
|
|
293
|
+
protected handlePointerDown(e: PointerEvent & { target: HTMLInputElement }) {
|
|
223
294
|
const clickValue =
|
|
224
295
|
this.min + ((this.max - this.min) * e.offsetX) / this.offsetWidth
|
|
225
296
|
const middleValue = (this.valueAsArray[0] + this.valueAsArray[1]) / 2
|
|
@@ -232,8 +303,7 @@ export class LeuRange extends LeuElement {
|
|
|
232
303
|
* As the pointerdown event is fired before the input event, we first overwrite the value
|
|
233
304
|
* of the input element that was not clicked on. The active input element will update itself.
|
|
234
305
|
*/
|
|
235
|
-
|
|
236
|
-
this._getGhostInput().value = e.target.value
|
|
306
|
+
this.inputGhost.value = e.target.value
|
|
237
307
|
}
|
|
238
308
|
}
|
|
239
309
|
|
|
@@ -258,7 +328,7 @@ export class LeuRange extends LeuElement {
|
|
|
258
328
|
(tick) =>
|
|
259
329
|
html`<span
|
|
260
330
|
class="tick"
|
|
261
|
-
style="left: ${this.
|
|
331
|
+
style="left: ${this.getNormalizedValue(tick) * 100}%"
|
|
262
332
|
></span>`,
|
|
263
333
|
)}
|
|
264
334
|
</div>`
|
|
@@ -267,13 +337,14 @@ export class LeuRange extends LeuElement {
|
|
|
267
337
|
render() {
|
|
268
338
|
const inputs = this.multiple ? ["base", "ghost"] : ["base"]
|
|
269
339
|
|
|
270
|
-
const { multiple, disabled, label,
|
|
340
|
+
const { multiple, disabled, label, valueAsArray } = this
|
|
271
341
|
|
|
272
342
|
return html`
|
|
273
343
|
<div
|
|
344
|
+
id="container"
|
|
274
345
|
class="container"
|
|
275
|
-
role=${multiple ? "group" : undefined}
|
|
276
|
-
aria-labelledby=${multiple ? "group-label" : undefined}
|
|
346
|
+
role=${ifDefined(multiple ? "group" : undefined)}
|
|
347
|
+
aria-labelledby=${ifDefined(multiple ? "group-label" : undefined)}
|
|
277
348
|
>
|
|
278
349
|
${multiple
|
|
279
350
|
? html`<span id="group-label" class="label">${label}</span>`
|
|
@@ -284,7 +355,7 @@ export class LeuRange extends LeuElement {
|
|
|
284
355
|
html`<output
|
|
285
356
|
class="output"
|
|
286
357
|
for="input-${type}"
|
|
287
|
-
value=${this.formatValue(
|
|
358
|
+
value=${this.formatValue(valueAsArray[index])}
|
|
288
359
|
></output>`,
|
|
289
360
|
)}
|
|
290
361
|
</div>
|
|
@@ -292,9 +363,9 @@ export class LeuRange extends LeuElement {
|
|
|
292
363
|
${inputs.map(
|
|
293
364
|
(type, index) => html`
|
|
294
365
|
<input
|
|
295
|
-
@input=${
|
|
366
|
+
@input=${this.handleInput}
|
|
296
367
|
@pointerdown=${multiple && !disabled && index === 0
|
|
297
|
-
? this.
|
|
368
|
+
? this.handlePointerDown
|
|
298
369
|
: undefined}
|
|
299
370
|
type="range"
|
|
300
371
|
class="range range--${type}"
|
|
@@ -303,9 +374,11 @@ export class LeuRange extends LeuElement {
|
|
|
303
374
|
min=${this.min}
|
|
304
375
|
max=${this.max}
|
|
305
376
|
step=${this.step}
|
|
306
|
-
aria-label=${
|
|
377
|
+
aria-label=${ifDefined(
|
|
378
|
+
multiple ? RANGE_LABELS[index] : undefined,
|
|
379
|
+
)}
|
|
307
380
|
?disabled=${disabled}
|
|
308
|
-
.value=${
|
|
381
|
+
.value=${valueAsArray[index].toString()}
|
|
309
382
|
/>
|
|
310
383
|
`,
|
|
311
384
|
)}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Meta, StoryObj } from "@storybook/web-components"
|
|
2
|
+
import { action } from "@storybook/addon-actions"
|
|
2
3
|
import { html } from "lit"
|
|
3
4
|
import { ifDefined } from "lit/directives/if-defined.js"
|
|
4
5
|
|
|
@@ -20,6 +21,7 @@ export default {
|
|
|
20
21
|
},
|
|
21
22
|
args: {
|
|
22
23
|
label: "Bereich",
|
|
24
|
+
oninput: action("input"),
|
|
23
25
|
},
|
|
24
26
|
} satisfies Meta<StoryArgs>
|
|
25
27
|
|
|
@@ -39,6 +41,7 @@ const Template: Story = {
|
|
|
39
41
|
?hide-label=${args["hide-label"]}
|
|
40
42
|
?show-ticks=${args["show-ticks"]}
|
|
41
43
|
?show-range-labels=${args["show-range-labels"]}
|
|
44
|
+
@input=${args.oninput}
|
|
42
45
|
>
|
|
43
46
|
</leu-range>`,
|
|
44
47
|
}
|
|
@@ -166,4 +166,63 @@ describe("LeuRange", () => {
|
|
|
166
166
|
const input = el.shadowRoot?.querySelector("input")
|
|
167
167
|
expect(input).to.have.attribute("disabled")
|
|
168
168
|
})
|
|
169
|
+
|
|
170
|
+
it("clamps and rounds when value is set", async () => {
|
|
171
|
+
const el = await defaultFixture({ min: 0, max: 10, step: 3 })
|
|
172
|
+
|
|
173
|
+
el.value = "8"
|
|
174
|
+
await el.updateComplete
|
|
175
|
+
|
|
176
|
+
expect(el.value).to.equal("9")
|
|
177
|
+
})
|
|
178
|
+
|
|
179
|
+
it("re-normalizes when min/max/step changes", async () => {
|
|
180
|
+
const el = await defaultFixture({ min: 0, max: 10, step: 2 })
|
|
181
|
+
|
|
182
|
+
el.value = "9"
|
|
183
|
+
await el.updateComplete
|
|
184
|
+
|
|
185
|
+
expect(el.value).to.equal("10")
|
|
186
|
+
|
|
187
|
+
el.max = 6
|
|
188
|
+
await el.updateComplete
|
|
189
|
+
|
|
190
|
+
expect(el.value).to.equal("6")
|
|
191
|
+
})
|
|
192
|
+
|
|
193
|
+
it("sets the second handle to min when multiple and a single value is provided", async () => {
|
|
194
|
+
const el = await defaultFixture({
|
|
195
|
+
multiple: true,
|
|
196
|
+
min: 10,
|
|
197
|
+
max: 100,
|
|
198
|
+
value: 20,
|
|
199
|
+
})
|
|
200
|
+
|
|
201
|
+
expect(el.value).to.equal("10,20")
|
|
202
|
+
|
|
203
|
+
el.value = "30"
|
|
204
|
+
await el.updateComplete
|
|
205
|
+
expect(el.value).to.equal("10,30")
|
|
206
|
+
|
|
207
|
+
el.value = "30, 40"
|
|
208
|
+
await el.updateComplete
|
|
209
|
+
expect(el.value).to.equal("30,40")
|
|
210
|
+
})
|
|
211
|
+
|
|
212
|
+
it("re-normalizes both values when multiple and min/max/step changes", async () => {
|
|
213
|
+
const el = await defaultFixture({
|
|
214
|
+
multiple: true,
|
|
215
|
+
min: 0,
|
|
216
|
+
max: 10,
|
|
217
|
+
step: 2,
|
|
218
|
+
value: "3,7",
|
|
219
|
+
})
|
|
220
|
+
|
|
221
|
+
expect(el.value).to.equal("4,8")
|
|
222
|
+
|
|
223
|
+
el.max = 6
|
|
224
|
+
await el.updateComplete
|
|
225
|
+
|
|
226
|
+
expect(el.value).to.equal("4,6")
|
|
227
|
+
})
|
|
169
228
|
})
|
package/src/lib/utils.ts
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* @param {Number} timeout - Default is 500 ms
|
|
5
5
|
* @returns {Function} - Your function wrapped in a timeout function
|
|
6
6
|
*/
|
|
7
|
-
const debounce = function debounce(func, timeout = 500) {
|
|
7
|
+
export const debounce = function debounce(func, timeout = 500) {
|
|
8
8
|
let timer = null
|
|
9
9
|
return (...args) => {
|
|
10
10
|
clearTimeout(timer)
|
|
@@ -20,7 +20,7 @@ const debounce = function debounce(func, timeout = 500) {
|
|
|
20
20
|
* @param {Number} timeout - Default is 500 ms
|
|
21
21
|
* @returns {Function} - Your function wrapped in a timeout function
|
|
22
22
|
*/
|
|
23
|
-
const throttle = function throttle(func, timeout = 500) {
|
|
23
|
+
export const throttle = function throttle(func, timeout = 500) {
|
|
24
24
|
let timer = null
|
|
25
25
|
return (...args) => {
|
|
26
26
|
if (timer === null) {
|
|
@@ -32,4 +32,14 @@ const throttle = function throttle(func, timeout = 500) {
|
|
|
32
32
|
}
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
-
|
|
35
|
+
/**
|
|
36
|
+
* Clamp a number between a minimum and maximum value.
|
|
37
|
+
*/
|
|
38
|
+
export const clamp = (value: number, min: number, max: number) =>
|
|
39
|
+
Math.min(Math.max(value, min), max)
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Check if a value is a finite number.
|
|
43
|
+
*/
|
|
44
|
+
export const isNumber = (value: unknown): value is number =>
|
|
45
|
+
typeof value === "number" && Number.isFinite(value)
|