@statistikzh/leu 0.24.0 → 0.24.2
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 +2 -2
- package/.release-please-manifest.json +1 -1
- package/CHANGELOG.md +15 -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-BfXSO7MN.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 +24 -31
- 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/theme.css +1 -0
- package/dist/utils-DBGsNSJW.js +33 -0
- package/dist/vscode.html-custom-data.json +38 -34
- package/dist/vue/index.d.ts +51 -44
- package/dist/web-types.json +86 -80
- package/package.json +2 -3
- 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/components/scroll-top/scroll-top.css +20 -4
- package/src/lib/utils.ts +13 -3
- package/src/styles/theme.css +1 -0
package/dist/web-types.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://raw.githubusercontent.com/JetBrains/web-types/master/schema/web-types.json",
|
|
3
3
|
"name": "@statistikzh/leu",
|
|
4
|
-
"version": "0.24.
|
|
4
|
+
"version": "0.24.2",
|
|
5
5
|
"description-markup": "markdown",
|
|
6
6
|
"contributions": {
|
|
7
7
|
"html": {
|
|
@@ -238,6 +238,49 @@
|
|
|
238
238
|
]
|
|
239
239
|
}
|
|
240
240
|
},
|
|
241
|
+
{
|
|
242
|
+
"name": "leu-chart-wrapper",
|
|
243
|
+
"description": "A wrapper element for charts.\n---\n\n\n### **Slots:**\n - **title** - The title of the chart. Use a heading tag (h2-4) depending on your context.\n- **description** - A description of the chart. Content is wrapped in a `<p>` tag by the component.\n- **chart** - The actual chart\n- **caption** - A caption for the chart, e.g. a source or explanation of the data.\n- **download** - A download button or dropdown to export the chart in different formats.",
|
|
244
|
+
"doc-url": "",
|
|
245
|
+
"attributes": [
|
|
246
|
+
{
|
|
247
|
+
"name": "pending",
|
|
248
|
+
"description": "Whether the chart is currently loading or not.\nWhen set to `true`, a spinner will be shown in the chart container.",
|
|
249
|
+
"value": { "type": "boolean", "default": "false" }
|
|
250
|
+
}
|
|
251
|
+
],
|
|
252
|
+
"slots": [
|
|
253
|
+
{
|
|
254
|
+
"name": "title",
|
|
255
|
+
"description": "The title of the chart. Use a heading tag (h2-4) depending on your context."
|
|
256
|
+
},
|
|
257
|
+
{
|
|
258
|
+
"name": "description",
|
|
259
|
+
"description": "A description of the chart. Content is wrapped in a `<p>` tag by the component."
|
|
260
|
+
},
|
|
261
|
+
{ "name": "chart", "description": "The actual chart" },
|
|
262
|
+
{
|
|
263
|
+
"name": "caption",
|
|
264
|
+
"description": "A caption for the chart, e.g. a source or explanation of the data."
|
|
265
|
+
},
|
|
266
|
+
{
|
|
267
|
+
"name": "download",
|
|
268
|
+
"description": "A download button or dropdown to export the chart in different formats."
|
|
269
|
+
}
|
|
270
|
+
],
|
|
271
|
+
"events": [],
|
|
272
|
+
"js": {
|
|
273
|
+
"properties": [
|
|
274
|
+
{
|
|
275
|
+
"name": "pending",
|
|
276
|
+
"description": "Whether the chart is currently loading or not.\nWhen set to `true`, a spinner will be shown in the chart container.",
|
|
277
|
+
"type": "boolean"
|
|
278
|
+
},
|
|
279
|
+
{ "name": "hasSlotController" }
|
|
280
|
+
],
|
|
281
|
+
"events": []
|
|
282
|
+
}
|
|
283
|
+
},
|
|
241
284
|
{
|
|
242
285
|
"name": "leu-checkbox",
|
|
243
286
|
"description": "\n---\n",
|
|
@@ -311,49 +354,6 @@
|
|
|
311
354
|
"events": []
|
|
312
355
|
}
|
|
313
356
|
},
|
|
314
|
-
{
|
|
315
|
-
"name": "leu-chart-wrapper",
|
|
316
|
-
"description": "A wrapper element for charts.\n---\n\n\n### **Slots:**\n - **title** - The title of the chart. Use a heading tag (h2-4) depending on your context.\n- **description** - A description of the chart. Content is wrapped in a `<p>` tag by the component.\n- **chart** - The actual chart\n- **caption** - A caption for the chart, e.g. a source or explanation of the data.\n- **download** - A download button or dropdown to export the chart in different formats.",
|
|
317
|
-
"doc-url": "",
|
|
318
|
-
"attributes": [
|
|
319
|
-
{
|
|
320
|
-
"name": "pending",
|
|
321
|
-
"description": "Whether the chart is currently loading or not.\nWhen set to `true`, a spinner will be shown in the chart container.",
|
|
322
|
-
"value": { "type": "boolean", "default": "false" }
|
|
323
|
-
}
|
|
324
|
-
],
|
|
325
|
-
"slots": [
|
|
326
|
-
{
|
|
327
|
-
"name": "title",
|
|
328
|
-
"description": "The title of the chart. Use a heading tag (h2-4) depending on your context."
|
|
329
|
-
},
|
|
330
|
-
{
|
|
331
|
-
"name": "description",
|
|
332
|
-
"description": "A description of the chart. Content is wrapped in a `<p>` tag by the component."
|
|
333
|
-
},
|
|
334
|
-
{ "name": "chart", "description": "The actual chart" },
|
|
335
|
-
{
|
|
336
|
-
"name": "caption",
|
|
337
|
-
"description": "A caption for the chart, e.g. a source or explanation of the data."
|
|
338
|
-
},
|
|
339
|
-
{
|
|
340
|
-
"name": "download",
|
|
341
|
-
"description": "A download button or dropdown to export the chart in different formats."
|
|
342
|
-
}
|
|
343
|
-
],
|
|
344
|
-
"events": [],
|
|
345
|
-
"js": {
|
|
346
|
-
"properties": [
|
|
347
|
-
{
|
|
348
|
-
"name": "pending",
|
|
349
|
-
"description": "Whether the chart is currently loading or not.\nWhen set to `true`, a spinner will be shown in the chart container.",
|
|
350
|
-
"type": "boolean"
|
|
351
|
-
},
|
|
352
|
-
{ "name": "hasSlotController" }
|
|
353
|
-
],
|
|
354
|
-
"events": []
|
|
355
|
-
}
|
|
356
|
-
},
|
|
357
357
|
{
|
|
358
358
|
"name": "leu-chip-group",
|
|
359
359
|
"description": "\n---\n\n\n### **Slots:**\n - _default_ - Place leu-chip-* elements inside this slot\n\n### **CSS Properties:**\n - **--leu-chip-group-gap** - The gap between the chips _(default: undefined)_",
|
|
@@ -549,6 +549,34 @@
|
|
|
549
549
|
"events": [{ "name": "input", "type": "CustomEvent" }]
|
|
550
550
|
}
|
|
551
551
|
},
|
|
552
|
+
{
|
|
553
|
+
"name": "leu-dialog",
|
|
554
|
+
"description": "\n---\n",
|
|
555
|
+
"doc-url": "",
|
|
556
|
+
"attributes": [
|
|
557
|
+
{
|
|
558
|
+
"name": "label",
|
|
559
|
+
"value": { "type": "string", "default": "\"\"" }
|
|
560
|
+
},
|
|
561
|
+
{
|
|
562
|
+
"name": "sublabel",
|
|
563
|
+
"value": { "type": "string", "default": "\"\"" }
|
|
564
|
+
},
|
|
565
|
+
{
|
|
566
|
+
"name": "open",
|
|
567
|
+
"value": { "type": "boolean", "default": "false" }
|
|
568
|
+
}
|
|
569
|
+
],
|
|
570
|
+
"events": [],
|
|
571
|
+
"js": {
|
|
572
|
+
"properties": [
|
|
573
|
+
{ "name": "label", "type": "string" },
|
|
574
|
+
{ "name": "sublabel", "type": "string" },
|
|
575
|
+
{ "name": "open", "type": "boolean" }
|
|
576
|
+
],
|
|
577
|
+
"events": []
|
|
578
|
+
}
|
|
579
|
+
},
|
|
552
580
|
{
|
|
553
581
|
"name": "leu-dropdown",
|
|
554
582
|
"description": "\n---\n",
|
|
@@ -921,34 +949,6 @@
|
|
|
921
949
|
]
|
|
922
950
|
}
|
|
923
951
|
},
|
|
924
|
-
{
|
|
925
|
-
"name": "leu-dialog",
|
|
926
|
-
"description": "\n---\n",
|
|
927
|
-
"doc-url": "",
|
|
928
|
-
"attributes": [
|
|
929
|
-
{
|
|
930
|
-
"name": "label",
|
|
931
|
-
"value": { "type": "string", "default": "\"\"" }
|
|
932
|
-
},
|
|
933
|
-
{
|
|
934
|
-
"name": "sublabel",
|
|
935
|
-
"value": { "type": "string", "default": "\"\"" }
|
|
936
|
-
},
|
|
937
|
-
{
|
|
938
|
-
"name": "open",
|
|
939
|
-
"value": { "type": "boolean", "default": "false" }
|
|
940
|
-
}
|
|
941
|
-
],
|
|
942
|
-
"events": [],
|
|
943
|
-
"js": {
|
|
944
|
-
"properties": [
|
|
945
|
-
{ "name": "label", "type": "string" },
|
|
946
|
-
{ "name": "sublabel", "type": "string" },
|
|
947
|
-
{ "name": "open", "type": "boolean" }
|
|
948
|
-
],
|
|
949
|
-
"events": []
|
|
950
|
-
}
|
|
951
|
-
},
|
|
952
952
|
{
|
|
953
953
|
"name": "leu-menu",
|
|
954
954
|
"description": "\n---\n",
|
|
@@ -1428,11 +1428,12 @@
|
|
|
1428
1428
|
},
|
|
1429
1429
|
{
|
|
1430
1430
|
"name": "leu-range",
|
|
1431
|
-
"description": "\n---\n\n\n### **Methods:**\n ",
|
|
1431
|
+
"description": "\n---\n\n\n### **Events:**\n - **input**\n\n### **Methods:**\n ",
|
|
1432
1432
|
"doc-url": "",
|
|
1433
1433
|
"attributes": [
|
|
1434
1434
|
{
|
|
1435
1435
|
"name": "value",
|
|
1436
|
+
"description": "The default value of the range slider.\nString input is parsed as a comma-separated list of numbers.",
|
|
1436
1437
|
"value": { "type": "array", "default": "[50]" }
|
|
1437
1438
|
},
|
|
1438
1439
|
{
|
|
@@ -1495,10 +1496,14 @@
|
|
|
1495
1496
|
"value": { "type": "string", "default": "\"\"" }
|
|
1496
1497
|
}
|
|
1497
1498
|
],
|
|
1498
|
-
"events": [],
|
|
1499
|
+
"events": [{ "name": "input", "type": "CustomEvent" }],
|
|
1499
1500
|
"js": {
|
|
1500
1501
|
"properties": [
|
|
1501
|
-
{
|
|
1502
|
+
{
|
|
1503
|
+
"name": "defaultValue",
|
|
1504
|
+
"description": "The default value of the range slider.\nString input is parsed as a comma-separated list of numbers.",
|
|
1505
|
+
"type": "array"
|
|
1506
|
+
},
|
|
1502
1507
|
{
|
|
1503
1508
|
"name": "min",
|
|
1504
1509
|
"description": "The minimum value of the range slider.",
|
|
@@ -1562,13 +1567,14 @@
|
|
|
1562
1567
|
},
|
|
1563
1568
|
{
|
|
1564
1569
|
"name": "value",
|
|
1565
|
-
"description": "
|
|
1570
|
+
"description": "The value of the range slider.\nString input is parsed as a comma-separated list of numbers.\nIn multiple mode, if only a single value is provided, the second handle will be set to the minimum value.\nIn single mode, only the first value will be used.",
|
|
1571
|
+
"type": "string"
|
|
1566
1572
|
},
|
|
1567
|
-
{ "name": "valueAsArray" },
|
|
1568
|
-
{ "name": "valueLow" },
|
|
1569
|
-
{ "name": "valueHigh" }
|
|
1573
|
+
{ "name": "valueAsArray", "type": "InternalRangeValue" },
|
|
1574
|
+
{ "name": "valueLow", "type": "number" },
|
|
1575
|
+
{ "name": "valueHigh", "type": "number" }
|
|
1570
1576
|
],
|
|
1571
|
-
"events": []
|
|
1577
|
+
"events": [{ "name": "input", "type": "CustomEvent" }]
|
|
1572
1578
|
}
|
|
1573
1579
|
},
|
|
1574
1580
|
{
|
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"description": "UI component library of the canton of zurich",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"author": "statistikzh",
|
|
6
|
-
"version": "0.24.
|
|
6
|
+
"version": "0.24.2",
|
|
7
7
|
"type": "module",
|
|
8
8
|
"main": "dist/index.js",
|
|
9
9
|
"module": "dist/index.js",
|
|
@@ -129,7 +129,6 @@
|
|
|
129
129
|
"bundle": false
|
|
130
130
|
},
|
|
131
131
|
"publishConfig": {
|
|
132
|
-
"access": "public"
|
|
133
|
-
"provenance": false
|
|
132
|
+
"access": "public"
|
|
134
133
|
}
|
|
135
134
|
}
|
|
@@ -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
|
}
|