@inglorious/ui 8.6.0 → 9.1.0
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/package.json +2 -2
- package/src/controls/button/button.stories.js +7 -1
- package/src/controls/button/style.css +11 -0
- package/src/controls/button/template.js +4 -4
- package/src/controls/checkbox/checkbox.stories.js +12 -0
- package/src/controls/checkbox/style.css +11 -0
- package/src/controls/checkbox/template.js +9 -1
- package/src/controls/combobox/style.css +14 -7
- package/src/controls/combobox/template.js +12 -2
- package/src/controls/radio-group/radio-group.stories.js +21 -1
- package/src/controls/radio-group/style.css +11 -0
- package/src/controls/radio-group/template.js +11 -1
- package/src/controls/rating/rating.stories.js +8 -0
- package/src/controls/rating/style.css +26 -4
- package/src/controls/rating/template.js +29 -12
- package/src/controls/slider/slider.stories.js +28 -1
- package/src/controls/slider/style.css +102 -0
- package/src/controls/slider/template.js +114 -15
- package/src/controls/switch/style.css +11 -0
- package/src/controls/switch/switch.stories.js +10 -0
- package/src/controls/switch/template.js +14 -2
- package/src/data-display/chip/chip.stories.js +17 -0
- package/src/data-display/chip/style.css +5 -0
- package/src/data-display/chip/template.js +3 -1
- package/src/data-display/divider/divider.stories.js +9 -0
- package/src/data-display/divider/template.js +12 -4
- package/src/data-display/tooltip/style.css +22 -14
- package/src/examples/dashboard/primitive-content.js +6 -6
- package/src/examples/dashboard/style.css +11 -5
- package/src/feedback/alert/alert.stories.js +9 -1
- package/src/feedback/alert/style.css +5 -0
- package/src/feedback/alert/template.js +2 -1
- package/src/feedback/dialog/dialog.stories.js +31 -0
- package/src/feedback/dialog/style.css +27 -13
- package/src/feedback/dialog/template.js +68 -18
- package/src/feedback/progress/progress.stories.js +5 -0
- package/src/feedback/progress/style.css +47 -23
- package/src/feedback/progress/template.js +18 -11
- package/src/feedback/snackbar/style.css +13 -0
- package/src/feedback/snackbar/template.js +7 -0
- package/src/layout/flex/flex.stories.js +1 -1
- package/src/layout/flex/style.css +8 -0
- package/src/navigation/drawer/drawer.stories.js +3 -7
- package/src/navigation/drawer/style.css +59 -18
- package/src/navigation/drawer/template.js +137 -80
- package/src/navigation/menu/menu.stories.js +15 -0
- package/src/navigation/menu/style.css +19 -0
- package/src/navigation/menu/template.js +59 -6
- package/src/navigation/speed-dial/style.css +32 -5
- package/src/navigation/speed-dial/template.js +41 -12
- package/src/navigation/tabs/template.js +52 -4
- package/src/shared/anchor.js +11 -0
- package/src/shared/top-layer.js +83 -0
- package/src/surfaces/accordion/accordion.stories.js +9 -0
- package/src/surfaces/accordion/style.css +14 -7
- package/src/surfaces/accordion/template.js +31 -20
- package/src/surfaces/card/card.stories.js +24 -0
- package/src/surfaces/card/style.css +15 -0
- package/src/surfaces/card/template.js +2 -0
- package/types/controls/button.d.ts +1 -1
- package/types/controls/checkbox.d.ts +2 -0
- package/types/controls/radio-group.d.ts +2 -0
- package/types/controls/rating.d.ts +4 -0
- package/types/controls/slider.d.ts +6 -2
- package/types/controls/switch.d.ts +2 -0
- package/types/data-display/chip.d.ts +2 -0
- package/types/data-display/divider.d.ts +2 -0
- package/types/feedback/alert.d.ts +1 -1
- package/types/feedback/dialog.d.ts +5 -1
- package/types/feedback/progress.d.ts +2 -0
- package/types/layout/flex.d.ts +1 -1
- package/types/navigation/drawer.d.ts +8 -3
- package/types/navigation/menu.d.ts +17 -0
- package/types/navigation/speed-dial.d.ts +5 -0
- package/types/surfaces/accordion.d.ts +2 -0
- package/types/surfaces/card.d.ts +5 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@inglorious/ui",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "9.1.0",
|
|
4
4
|
"description": "A design system for Inglorious Web with render-function components and customizable themes",
|
|
5
5
|
"author": "Matteo Antony Mistretta <antony.mistretta@gmail.com> (https://ingloriouscoderz.it)",
|
|
6
6
|
"license": "MIT",
|
|
@@ -315,7 +315,7 @@
|
|
|
315
315
|
"@iconify-icons/fa7-regular": "^2.0.0",
|
|
316
316
|
"@iconify-icons/fa7-solid": "^2.0.0",
|
|
317
317
|
"@iconify-icons/material-symbols": "^2.0.6",
|
|
318
|
-
"@inglorious/charts": "
|
|
318
|
+
"@inglorious/charts": "12.0.0",
|
|
319
319
|
"@inglorious/eslint-config": "1.2.0",
|
|
320
320
|
"@inglorious/store": "12.0.0",
|
|
321
321
|
"@inglorious/vite-plugin-jsx": "2.0.5",
|
|
@@ -32,7 +32,7 @@ export default {
|
|
|
32
32
|
},
|
|
33
33
|
size: {
|
|
34
34
|
control: "select",
|
|
35
|
-
options: ["sm", "md", "lg"],
|
|
35
|
+
options: ["xs", "sm", "md", "lg"],
|
|
36
36
|
description: "Size scale for paddings and font-size.",
|
|
37
37
|
},
|
|
38
38
|
shape: {
|
|
@@ -106,6 +106,12 @@ Ghost.args = {
|
|
|
106
106
|
variant: "ghost",
|
|
107
107
|
}
|
|
108
108
|
|
|
109
|
+
export const ExtraSmall = {}
|
|
110
|
+
ExtraSmall.args = {
|
|
111
|
+
...Default.args,
|
|
112
|
+
size: "xs",
|
|
113
|
+
}
|
|
114
|
+
|
|
109
115
|
export const Small = {}
|
|
110
116
|
Small.args = {
|
|
111
117
|
...Default.args,
|
|
@@ -153,6 +153,11 @@
|
|
|
153
153
|
cursor: not-allowed;
|
|
154
154
|
}
|
|
155
155
|
|
|
156
|
+
&.iw-button-xs {
|
|
157
|
+
padding: var(--iw-space-0) var(--iw-space-2);
|
|
158
|
+
font-size: var(--iw-font-size-xs);
|
|
159
|
+
}
|
|
160
|
+
|
|
156
161
|
&.iw-button-sm {
|
|
157
162
|
padding: var(--iw-space-1) var(--iw-space-3);
|
|
158
163
|
font-size: var(--iw-font-size-sm);
|
|
@@ -179,6 +184,12 @@
|
|
|
179
184
|
border-radius: var(--iw-radius-sm);
|
|
180
185
|
}
|
|
181
186
|
|
|
187
|
+
&.iw-button-xs.iw-button-shape-round,
|
|
188
|
+
&.iw-button-xs.iw-button-shape-square {
|
|
189
|
+
width: 1.5rem;
|
|
190
|
+
height: 1.5rem;
|
|
191
|
+
}
|
|
192
|
+
|
|
182
193
|
&.iw-button-sm.iw-button-shape-round,
|
|
183
194
|
&.iw-button-sm.iw-button-shape-square {
|
|
184
195
|
width: 2rem;
|
|
@@ -35,8 +35,8 @@ export function render(props) {
|
|
|
35
35
|
isPressed = false,
|
|
36
36
|
isFullWidth = false,
|
|
37
37
|
buttonType = "button",
|
|
38
|
-
ariaLabel
|
|
39
|
-
isAriaPressed
|
|
38
|
+
ariaLabel,
|
|
39
|
+
isAriaPressed,
|
|
40
40
|
className = "",
|
|
41
41
|
accept,
|
|
42
42
|
isMultiple = false,
|
|
@@ -74,8 +74,8 @@ export function render(props) {
|
|
|
74
74
|
return html`
|
|
75
75
|
<button
|
|
76
76
|
type=${buttonType}
|
|
77
|
-
aria-label=${ariaLabel}
|
|
78
|
-
aria-pressed=${isAriaPressed}
|
|
77
|
+
aria-label=${ariaLabel || nothing}
|
|
78
|
+
aria-pressed=${isAriaPressed ?? nothing}
|
|
79
79
|
class=${classMap(classes)}
|
|
80
80
|
?disabled=${isDisabled}
|
|
81
81
|
@click=${handleClick}
|
|
@@ -7,6 +7,10 @@ export default {
|
|
|
7
7
|
render: createRender(Checkbox),
|
|
8
8
|
argTypes: {
|
|
9
9
|
label: { control: "text", description: "Label text." },
|
|
10
|
+
description: {
|
|
11
|
+
control: "text",
|
|
12
|
+
description: "A line of detail under the label.",
|
|
13
|
+
},
|
|
10
14
|
name: { control: "text", description: "Native HTML name attribute." },
|
|
11
15
|
isChecked: { control: "boolean", description: "Current checked state." },
|
|
12
16
|
isDisabled: { control: "boolean", description: "Disables interaction." },
|
|
@@ -48,3 +52,11 @@ Checked.args = {
|
|
|
48
52
|
...Default.args,
|
|
49
53
|
isChecked: true,
|
|
50
54
|
}
|
|
55
|
+
|
|
56
|
+
export const WithDescription = {}
|
|
57
|
+
WithDescription.args = {
|
|
58
|
+
...Default.args,
|
|
59
|
+
label: "Weekly summary",
|
|
60
|
+
name: "summary",
|
|
61
|
+
description: "An email every Monday with last week's totals.",
|
|
62
|
+
}
|
|
@@ -14,6 +14,17 @@
|
|
|
14
14
|
accent-color: var(--iw-checkbox-accent);
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
+
/* With a description, the label and it stack beside the box. */
|
|
18
|
+
.iw-checkbox-text {
|
|
19
|
+
display: flex;
|
|
20
|
+
flex-direction: column;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
.iw-checkbox-description {
|
|
24
|
+
font-size: var(--iw-font-size-sm);
|
|
25
|
+
color: var(--iw-color-text-secondary);
|
|
26
|
+
}
|
|
27
|
+
|
|
17
28
|
.iw-checkbox-label {
|
|
18
29
|
font-size: var(--iw-font-size-base);
|
|
19
30
|
line-height: var(--iw-line-height-normal);
|
|
@@ -21,6 +21,7 @@ export function render(props) {
|
|
|
21
21
|
type, // eslint-disable-line no-unused-vars
|
|
22
22
|
name = "",
|
|
23
23
|
label = "",
|
|
24
|
+
description,
|
|
24
25
|
isChecked = false,
|
|
25
26
|
isDisabled = false,
|
|
26
27
|
isRequired = false,
|
|
@@ -54,7 +55,14 @@ export function render(props) {
|
|
|
54
55
|
@change=${(event) => onChange?.(event.target.checked)}
|
|
55
56
|
${ref((element) => applyElementProps(element, rest))}
|
|
56
57
|
/>
|
|
57
|
-
|
|
58
|
+
${
|
|
59
|
+
description
|
|
60
|
+
? html`<span class="iw-checkbox-text">
|
|
61
|
+
<span class="iw-checkbox-label">${label}</span>
|
|
62
|
+
<span class="iw-checkbox-description">${description}</span>
|
|
63
|
+
</span>`
|
|
64
|
+
: html`<span class="iw-checkbox-label">${label}</span>`
|
|
65
|
+
}
|
|
58
66
|
</label>
|
|
59
67
|
`
|
|
60
68
|
}
|
|
@@ -5,7 +5,8 @@
|
|
|
5
5
|
flex-direction: column;
|
|
6
6
|
gap: var(--iw-space-1);
|
|
7
7
|
font-family: var(--iw-font-family-sans);
|
|
8
|
-
|
|
8
|
+
/* Each combobox anchors its own dropdown to its own frame. */
|
|
9
|
+
anchor-scope: --iw-combobox;
|
|
9
10
|
|
|
10
11
|
&.iw-combobox-full-width {
|
|
11
12
|
width: 100%;
|
|
@@ -38,6 +39,7 @@
|
|
|
38
39
|
}
|
|
39
40
|
|
|
40
41
|
.iw-combobox-frame {
|
|
42
|
+
anchor-name: --iw-combobox;
|
|
41
43
|
display: flex;
|
|
42
44
|
align-items: center;
|
|
43
45
|
/* The gap spaces the text from the arrow, as it does for Input's icons. */
|
|
@@ -182,12 +184,17 @@
|
|
|
182
184
|
}
|
|
183
185
|
}
|
|
184
186
|
|
|
187
|
+
/* A popover in the top layer, placed under the frame and as wide as it,
|
|
188
|
+
flipping above when there is no room below. The browser's popover box
|
|
189
|
+
is centred in the viewport, so its insets, margin and colours are reset. */
|
|
185
190
|
.iw-combobox-dropdown {
|
|
186
|
-
position:
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
+
position-anchor: --iw-combobox;
|
|
192
|
+
position-area: block-end span-inline-end;
|
|
193
|
+
position-try-fallbacks: flip-block;
|
|
194
|
+
inset: auto;
|
|
195
|
+
width: anchor-size(width);
|
|
196
|
+
padding: 0;
|
|
197
|
+
color: var(--iw-color-text);
|
|
191
198
|
display: flex;
|
|
192
199
|
flex-direction: column;
|
|
193
200
|
max-height: 16rem;
|
|
@@ -198,7 +205,7 @@
|
|
|
198
205
|
border-start-end-radius: 0;
|
|
199
206
|
background: var(--iw-color-bg);
|
|
200
207
|
box-shadow: var(--iw-shadow-lg);
|
|
201
|
-
margin
|
|
208
|
+
margin: -1px 0 0;
|
|
202
209
|
|
|
203
210
|
.iw-combobox-dropdown-search {
|
|
204
211
|
width: 100%;
|
|
@@ -12,6 +12,7 @@ import { when } from "@inglorious/web/directives/when"
|
|
|
12
12
|
|
|
13
13
|
import { Chip } from "../../data-display/chip/index.js"
|
|
14
14
|
import { fallbackLabel } from "../../shared/fallback-label.js"
|
|
15
|
+
import { syncPopover } from "../../shared/top-layer.js"
|
|
15
16
|
import {
|
|
16
17
|
filterOptions,
|
|
17
18
|
formatOption,
|
|
@@ -263,7 +264,12 @@ export const Combobox = {
|
|
|
263
264
|
|
|
264
265
|
/**
|
|
265
266
|
* Renders the dropdown panel that contains search input, options, and empty/loading states.
|
|
266
|
-
* Shown only when the combobox is open
|
|
267
|
+
* Shown only when the combobox is open, as a popover in the top layer so no
|
|
268
|
+
* container can clip it, anchored under the control by CSS.
|
|
269
|
+
*
|
|
270
|
+
* A manual popover: the control that opens it is not a button, so it cannot
|
|
271
|
+
* be the popover's invoker, and a light dismiss would close the dropdown on
|
|
272
|
+
* the very press that toggles it. Outside presses and Esc are handled here.
|
|
267
273
|
* @param {ComboboxProps} props
|
|
268
274
|
* @returns {TemplateResult}
|
|
269
275
|
*/
|
|
@@ -276,7 +282,11 @@ export const Combobox = {
|
|
|
276
282
|
id=${id ? `${id}-control-listbox` : nothing}
|
|
277
283
|
class="iw-combobox-dropdown"
|
|
278
284
|
role="listbox"
|
|
279
|
-
|
|
285
|
+
popover="manual"
|
|
286
|
+
${ref((el) => {
|
|
287
|
+
syncPopover(el, { isOpen: true })
|
|
288
|
+
trackOutsideClick(props, el)
|
|
289
|
+
})}
|
|
280
290
|
>
|
|
281
291
|
${when(isSearchable, () => this.renderSearchInput?.(props))}
|
|
282
292
|
${when(isLoading, () => this.renderLoading?.(props))}
|
|
@@ -20,7 +20,11 @@ export default {
|
|
|
20
20
|
description: "Semantic accent color.",
|
|
21
21
|
},
|
|
22
22
|
isDisabled: { control: "boolean", description: "Disables all options." },
|
|
23
|
-
options: {
|
|
23
|
+
options: {
|
|
24
|
+
control: "object",
|
|
25
|
+
description:
|
|
26
|
+
"List of radio options: `label`, `value`, and an optional `description` under the label.",
|
|
27
|
+
},
|
|
24
28
|
onChange: { action: "onChange" },
|
|
25
29
|
},
|
|
26
30
|
parameters: {
|
|
@@ -52,3 +56,19 @@ Row.args = {
|
|
|
52
56
|
...Default.args,
|
|
53
57
|
direction: "row",
|
|
54
58
|
}
|
|
59
|
+
|
|
60
|
+
export const WithDescriptions = {}
|
|
61
|
+
WithDescriptions.args = {
|
|
62
|
+
...Default.args,
|
|
63
|
+
label: "Shipping",
|
|
64
|
+
name: "shipping",
|
|
65
|
+
value: "standard",
|
|
66
|
+
options: [
|
|
67
|
+
{
|
|
68
|
+
label: "Standard",
|
|
69
|
+
value: "standard",
|
|
70
|
+
description: "3 to 5 working days, free",
|
|
71
|
+
},
|
|
72
|
+
{ label: "Express", value: "express", description: "Next day, €9" },
|
|
73
|
+
],
|
|
74
|
+
}
|
|
@@ -19,6 +19,17 @@
|
|
|
19
19
|
cursor: pointer;
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
+
/* With a description, the label and it stack beside the circle. */
|
|
23
|
+
.iw-radio-text {
|
|
24
|
+
display: flex;
|
|
25
|
+
flex-direction: column;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
.iw-radio-description {
|
|
29
|
+
font-size: var(--iw-font-size-sm);
|
|
30
|
+
color: var(--iw-color-text-secondary);
|
|
31
|
+
}
|
|
32
|
+
|
|
22
33
|
.iw-radio {
|
|
23
34
|
margin: 0;
|
|
24
35
|
accent-color: var(--iw-radio-accent);
|
|
@@ -55,6 +55,7 @@ export function render(props) {
|
|
|
55
55
|
${options.map((option, index) => {
|
|
56
56
|
const {
|
|
57
57
|
label: optionLabel,
|
|
58
|
+
description: optionDescription,
|
|
58
59
|
value: optionValue,
|
|
59
60
|
isDisabled: optionDisabled = false,
|
|
60
61
|
...optionRest
|
|
@@ -73,7 +74,16 @@ export function render(props) {
|
|
|
73
74
|
@change=${(event) => onChange?.(event.target.value)}
|
|
74
75
|
${ref((element) => applyElementProps(element, optionRest))}
|
|
75
76
|
/>
|
|
76
|
-
|
|
77
|
+
${
|
|
78
|
+
optionDescription
|
|
79
|
+
? html`<span class="iw-radio-text">
|
|
80
|
+
<span>${optionLabel}</span>
|
|
81
|
+
<span class="iw-radio-description"
|
|
82
|
+
>${optionDescription}</span
|
|
83
|
+
>
|
|
84
|
+
</span>`
|
|
85
|
+
: html`<span>${optionLabel}</span>`
|
|
86
|
+
}
|
|
77
87
|
</label>
|
|
78
88
|
`
|
|
79
89
|
})}
|
|
@@ -8,6 +8,14 @@ export default {
|
|
|
8
8
|
argTypes: {
|
|
9
9
|
value: { control: "number", description: "Current rating value." },
|
|
10
10
|
max: { control: "number", description: "Maximum rating value." },
|
|
11
|
+
name: {
|
|
12
|
+
control: "text",
|
|
13
|
+
description: "Groups the radios and names the value in a form.",
|
|
14
|
+
},
|
|
15
|
+
ariaLabel: {
|
|
16
|
+
control: "text",
|
|
17
|
+
description: 'Names the group for assistive tech. Defaults to "Rating".',
|
|
18
|
+
},
|
|
11
19
|
isDisabled: { control: "boolean", description: "Disables interaction." },
|
|
12
20
|
isReadOnly: {
|
|
13
21
|
control: "boolean",
|
|
@@ -1,17 +1,39 @@
|
|
|
1
|
+
/* A fieldset of native radios: the inputs stay in the page for the keyboard
|
|
2
|
+
and assistive tech but out of sight, and each star is the label that
|
|
3
|
+
checks its radio. */
|
|
1
4
|
.iw-rating {
|
|
2
5
|
display: inline-flex;
|
|
3
6
|
align-items: center;
|
|
4
7
|
gap: var(--iw-space-1);
|
|
8
|
+
margin: 0;
|
|
9
|
+
padding: 0;
|
|
10
|
+
border: 0;
|
|
11
|
+
min-inline-size: 0;
|
|
5
12
|
|
|
6
13
|
.iw-rating-item {
|
|
7
|
-
|
|
8
|
-
background: transparent;
|
|
9
|
-
padding: 0;
|
|
10
|
-
margin: 0;
|
|
14
|
+
position: relative;
|
|
11
15
|
cursor: pointer;
|
|
12
16
|
color: var(--iw-color-warning);
|
|
13
17
|
font-size: 1.25rem;
|
|
14
18
|
line-height: 1;
|
|
19
|
+
border-radius: var(--iw-radius-sm);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
.iw-rating-input {
|
|
23
|
+
position: absolute;
|
|
24
|
+
inset: 0;
|
|
25
|
+
margin: 0;
|
|
26
|
+
opacity: 0;
|
|
27
|
+
cursor: inherit;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
.iw-rating-item:has(.iw-rating-input:focus-visible) {
|
|
31
|
+
outline: 2px solid var(--iw-color-primary);
|
|
32
|
+
outline-offset: 2px;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
&:disabled .iw-rating-item {
|
|
36
|
+
cursor: default;
|
|
15
37
|
}
|
|
16
38
|
|
|
17
39
|
&.iw-rating-sm .iw-rating-item {
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* @typedef {import('@inglorious/web').TemplateResult} TemplateResult
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import { html } from "@inglorious/web"
|
|
6
|
+
import { html, nothing } from "@inglorious/web"
|
|
7
7
|
import { classMap } from "@inglorious/web/directives/class-map"
|
|
8
8
|
|
|
9
9
|
const DEFAULT_VALUE = 0
|
|
@@ -12,12 +12,16 @@ const INDEX_TO_RATING = 1
|
|
|
12
12
|
|
|
13
13
|
/**
|
|
14
14
|
* Renders a star (or custom symbol) rating control.
|
|
15
|
+
* Each star is a native radio, so the arrow keys move between them, the group
|
|
16
|
+
* is announced as one choice, and the value submits with a form under `name`.
|
|
15
17
|
* Supports max value, read-only/disabled states, and emits selection changes.
|
|
16
18
|
* @param {RatingProps} props
|
|
17
19
|
* @returns {TemplateResult}
|
|
18
20
|
*/
|
|
19
21
|
export function render(props) {
|
|
20
22
|
const {
|
|
23
|
+
id,
|
|
24
|
+
name,
|
|
21
25
|
value = DEFAULT_VALUE,
|
|
22
26
|
max = DEFAULT_MAX,
|
|
23
27
|
isDisabled = false,
|
|
@@ -25,9 +29,14 @@ export function render(props) {
|
|
|
25
29
|
symbol = "★",
|
|
26
30
|
emptySymbol = "☆",
|
|
27
31
|
size = "md",
|
|
32
|
+
ariaLabel = "Rating",
|
|
28
33
|
onChange,
|
|
29
34
|
} = props
|
|
30
35
|
|
|
36
|
+
// Radios are a group by sharing a name: without one, the arrow keys do not
|
|
37
|
+
// move between the stars.
|
|
38
|
+
const groupName = name || id
|
|
39
|
+
|
|
31
40
|
const classes = {
|
|
32
41
|
"iw-rating": true,
|
|
33
42
|
[`iw-rating-${size}`]: size !== "md",
|
|
@@ -36,22 +45,30 @@ export function render(props) {
|
|
|
36
45
|
}
|
|
37
46
|
|
|
38
47
|
return html`
|
|
39
|
-
<
|
|
48
|
+
<fieldset
|
|
49
|
+
id=${id || nothing}
|
|
50
|
+
class=${classMap(classes)}
|
|
51
|
+
aria-label=${ariaLabel}
|
|
52
|
+
?disabled=${isDisabled || isReadOnly}
|
|
53
|
+
>
|
|
40
54
|
${Array.from({ length: max }, (_, index) => {
|
|
41
55
|
const ratingValue = index + INDEX_TO_RATING
|
|
42
56
|
const isActive = ratingValue <= value
|
|
43
57
|
return html`
|
|
44
|
-
<
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
58
|
+
<label class="iw-rating-item">
|
|
59
|
+
<input
|
|
60
|
+
class="iw-rating-input"
|
|
61
|
+
type="radio"
|
|
62
|
+
name=${groupName || nothing}
|
|
63
|
+
value=${ratingValue}
|
|
64
|
+
aria-label=${`${ratingValue} of ${max}`}
|
|
65
|
+
.checked=${ratingValue === value}
|
|
66
|
+
@change=${() => onChange?.(ratingValue)}
|
|
67
|
+
/>
|
|
68
|
+
<span aria-hidden="true">${isActive ? symbol : emptySymbol}</span>
|
|
69
|
+
</label>
|
|
53
70
|
`
|
|
54
71
|
})}
|
|
55
|
-
</
|
|
72
|
+
</fieldset>
|
|
56
73
|
`
|
|
57
74
|
}
|
|
@@ -8,7 +8,11 @@ export default {
|
|
|
8
8
|
argTypes: {
|
|
9
9
|
label: { control: "text", description: "Field label." },
|
|
10
10
|
name: { control: "text", description: "Native input name." },
|
|
11
|
-
value: {
|
|
11
|
+
value: {
|
|
12
|
+
control: "object",
|
|
13
|
+
description:
|
|
14
|
+
"One number, or `[low, high]` for a range with a thumb at each end.",
|
|
15
|
+
},
|
|
12
16
|
min: { control: "number", description: "Minimum value." },
|
|
13
17
|
max: { control: "number", description: "Maximum value." },
|
|
14
18
|
step: { control: "number", description: "Step value." },
|
|
@@ -20,6 +24,10 @@ export default {
|
|
|
20
24
|
isDisabled: { control: "boolean", description: "Disables interaction." },
|
|
21
25
|
isValueVisible: { control: "boolean", description: "Shows current value." },
|
|
22
26
|
isFullWidth: { control: "boolean", description: "Expands width to 100%." },
|
|
27
|
+
hasMarks: {
|
|
28
|
+
control: "boolean",
|
|
29
|
+
description: "Draws a tick at every step.",
|
|
30
|
+
},
|
|
23
31
|
onChange: { action: "onChange" },
|
|
24
32
|
},
|
|
25
33
|
parameters: {
|
|
@@ -44,3 +52,22 @@ Default.args = {
|
|
|
44
52
|
isValueVisible: true,
|
|
45
53
|
isFullWidth: false,
|
|
46
54
|
}
|
|
55
|
+
|
|
56
|
+
export const Range = {}
|
|
57
|
+
Range.args = {
|
|
58
|
+
...Default.args,
|
|
59
|
+
label: "Price",
|
|
60
|
+
name: "price",
|
|
61
|
+
value: [20, 80],
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export const Marks = {}
|
|
65
|
+
Marks.args = {
|
|
66
|
+
...Default.args,
|
|
67
|
+
label: "Rating",
|
|
68
|
+
name: "rating",
|
|
69
|
+
value: 3,
|
|
70
|
+
min: 1,
|
|
71
|
+
max: 5,
|
|
72
|
+
hasMarks: true,
|
|
73
|
+
}
|
|
@@ -23,6 +23,108 @@
|
|
|
23
23
|
accent-color: var(--iw-slider-accent);
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
+
/* A tick per step, under the track: their ends sit under the thumb's ends
|
|
27
|
+
of travel, half a thumb in from the track's. */
|
|
28
|
+
.iw-slider-marks {
|
|
29
|
+
display: flex;
|
|
30
|
+
justify-content: space-between;
|
|
31
|
+
padding-inline: calc(var(--iw-slider-thumb, 1rem) / 2);
|
|
32
|
+
|
|
33
|
+
span {
|
|
34
|
+
inline-size: 1px;
|
|
35
|
+
block-size: var(--iw-space-2);
|
|
36
|
+
background: var(--iw-color-border);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/* Two thumbs on one track. The inputs lie on top of each other with their
|
|
41
|
+
own tracks hidden, and only their thumbs take presses, so either can be
|
|
42
|
+
dragged; the track below is drawn here, filled between the two. */
|
|
43
|
+
&.iw-slider-range {
|
|
44
|
+
--iw-slider-thumb: 1rem;
|
|
45
|
+
|
|
46
|
+
.iw-slider-track {
|
|
47
|
+
position: relative;
|
|
48
|
+
block-size: var(--iw-slider-thumb);
|
|
49
|
+
|
|
50
|
+
&::before,
|
|
51
|
+
&::after {
|
|
52
|
+
content: "";
|
|
53
|
+
position: absolute;
|
|
54
|
+
inset-block-start: 50%;
|
|
55
|
+
block-size: 0.25rem;
|
|
56
|
+
translate: 0 -50%;
|
|
57
|
+
border-radius: var(--iw-radius-full, 999px);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
&::before {
|
|
61
|
+
inset-inline: 0;
|
|
62
|
+
background: color-mix(
|
|
63
|
+
in srgb,
|
|
64
|
+
var(--iw-slider-accent) 25%,
|
|
65
|
+
transparent
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
&::after {
|
|
70
|
+
inset-inline-start: var(--iw-slider-low);
|
|
71
|
+
inset-inline-end: calc(100% - var(--iw-slider-high));
|
|
72
|
+
background: var(--iw-slider-accent);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
.iw-slider-input {
|
|
77
|
+
position: absolute;
|
|
78
|
+
inset: 0;
|
|
79
|
+
z-index: 1;
|
|
80
|
+
margin: 0;
|
|
81
|
+
appearance: none;
|
|
82
|
+
background: transparent;
|
|
83
|
+
pointer-events: none;
|
|
84
|
+
|
|
85
|
+
&::-webkit-slider-runnable-track {
|
|
86
|
+
background: transparent;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
&::-moz-range-track {
|
|
90
|
+
background: transparent;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
&::-webkit-slider-thumb {
|
|
94
|
+
appearance: none;
|
|
95
|
+
inline-size: var(--iw-slider-thumb);
|
|
96
|
+
block-size: var(--iw-slider-thumb);
|
|
97
|
+
border: 2px solid var(--iw-color-surface);
|
|
98
|
+
border-radius: 50%;
|
|
99
|
+
background: var(--iw-slider-accent);
|
|
100
|
+
box-shadow: var(--iw-shadow-sm);
|
|
101
|
+
cursor: pointer;
|
|
102
|
+
pointer-events: auto;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
&::-moz-range-thumb {
|
|
106
|
+
inline-size: var(--iw-slider-thumb);
|
|
107
|
+
block-size: var(--iw-slider-thumb);
|
|
108
|
+
border: 2px solid var(--iw-color-surface);
|
|
109
|
+
border-radius: 50%;
|
|
110
|
+
background: var(--iw-slider-accent);
|
|
111
|
+
box-shadow: var(--iw-shadow-sm);
|
|
112
|
+
cursor: pointer;
|
|
113
|
+
pointer-events: auto;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
&:focus-visible::-webkit-slider-thumb {
|
|
117
|
+
outline: 2px solid var(--iw-slider-accent);
|
|
118
|
+
outline-offset: 2px;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
&:focus-visible::-moz-range-thumb {
|
|
122
|
+
outline: 2px solid var(--iw-slider-accent);
|
|
123
|
+
outline-offset: 2px;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
26
128
|
&.iw-slider-full-width {
|
|
27
129
|
width: 100%;
|
|
28
130
|
}
|