material-inspired-component-library 1.0.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/.editorconfig +12 -0
- package/.gitattributes +9 -0
- package/.github/ISSUE_TEMPLATE/bug_report.md +35 -0
- package/.github/ISSUE_TEMPLATE/feature_request.md +20 -0
- package/LICENSE +21 -0
- package/README.md +99 -0
- package/components/README.md +12 -0
- package/components/accordion/README.md +94 -0
- package/components/bottomsheet/README.md +77 -0
- package/components/bottomsheet/bottomsheet.scss +134 -0
- package/components/bottomsheet/index.ts +152 -0
- package/components/button/README.md +92 -0
- package/components/button/button.scss +515 -0
- package/components/button/index.ts +73 -0
- package/components/card/README.md +125 -0
- package/components/card/card.scss +261 -0
- package/components/checkbox/README.md +62 -0
- package/components/checkbox/checkbox.scss +275 -0
- package/components/checkbox/index.ts +48 -0
- package/components/dialog/README.md +133 -0
- package/components/dialog/dialog.scss +262 -0
- package/components/divider/README.md +52 -0
- package/components/divider/divider.scss +74 -0
- package/components/iconbutton/README.md +86 -0
- package/components/iconbutton/iconbutton.scss +461 -0
- package/components/iconbutton/index.ts +73 -0
- package/components/list/README.md +176 -0
- package/components/list/index.ts +108 -0
- package/components/list/list.scss +295 -0
- package/components/menu/README.md +96 -0
- package/components/menu/index.ts +77 -0
- package/components/menu/menu.scss +124 -0
- package/components/radio/README.md +53 -0
- package/components/radio/radio.scss +138 -0
- package/components/select/README.md +84 -0
- package/components/select/select.scss +122 -0
- package/components/sidesheet/README.md +99 -0
- package/components/sidesheet/sidesheet.scss +162 -0
- package/components/slider/README.md +69 -0
- package/components/slider/index.ts +114 -0
- package/components/slider/slider.scss +258 -0
- package/components/switch/README.md +49 -0
- package/components/switch/switch.scss +176 -0
- package/components/textfield/README.md +75 -0
- package/components/textfield/index.ts +81 -0
- package/components/textfield/textfield.scss +387 -0
- package/components.ts +169 -0
- package/dist/bottomsheet.css +1 -0
- package/dist/bottomsheet.js +0 -0
- package/dist/button.css +1 -0
- package/dist/button.js +0 -0
- package/dist/card.css +1 -0
- package/dist/card.js +0 -0
- package/dist/checkbox.css +1 -0
- package/dist/checkbox.js +0 -0
- package/dist/dialog.css +1 -0
- package/dist/dialog.js +0 -0
- package/dist/divider.css +1 -0
- package/dist/divider.js +0 -0
- package/dist/iconbutton.css +1 -0
- package/dist/iconbutton.js +0 -0
- package/dist/list.css +1 -0
- package/dist/list.js +0 -0
- package/dist/menu.css +1 -0
- package/dist/menu.js +0 -0
- package/dist/micl.css +1 -0
- package/dist/micl.js +1 -0
- package/dist/radio.css +1 -0
- package/dist/radio.js +0 -0
- package/dist/select.css +1 -0
- package/dist/select.js +0 -0
- package/dist/sidesheet.css +1 -0
- package/dist/sidesheet.js +0 -0
- package/dist/slider.css +1 -0
- package/dist/slider.js +0 -0
- package/dist/switch.css +1 -0
- package/dist/switch.js +0 -0
- package/dist/textfield.css +1 -0
- package/dist/textfield.js +0 -0
- package/docs/accordion.html +285 -0
- package/docs/bottomsheet.html +162 -0
- package/docs/button.html +206 -0
- package/docs/card-awards.webp +0 -0
- package/docs/card-cabinet.webp +0 -0
- package/docs/card-city.webp +0 -0
- package/docs/card-fingerprint.webp +0 -0
- package/docs/card-holiday.webp +0 -0
- package/docs/card-names.webp +0 -0
- package/docs/card.html +91 -0
- package/docs/checkbox.html +99 -0
- package/docs/dialog.html +153 -0
- package/docs/divider.html +103 -0
- package/docs/docs.css +34 -0
- package/docs/docs.js +69 -0
- package/docs/iconbutton.html +197 -0
- package/docs/index.html +319 -0
- package/docs/list.html +224 -0
- package/docs/menu.html +143 -0
- package/docs/micl.css +1 -0
- package/docs/micl.js +1 -0
- package/docs/radio.html +101 -0
- package/docs/select.html +205 -0
- package/docs/sidesheet.html +115 -0
- package/docs/slider.html +72 -0
- package/docs/switch.html +151 -0
- package/docs/textfield.html +151 -0
- package/docs/themes/airblue/dark-hc.css +51 -0
- package/docs/themes/airblue/dark-mc.css +51 -0
- package/docs/themes/airblue/dark.css +51 -0
- package/docs/themes/airblue/light-hc.css +51 -0
- package/docs/themes/airblue/light-mc.css +51 -0
- package/docs/themes/airblue/light.css +51 -0
- package/docs/themes/airblue/theme.css +306 -0
- package/docs/themes/barnred/dark-hc.css +51 -0
- package/docs/themes/barnred/dark-mc.css +51 -0
- package/docs/themes/barnred/dark.css +51 -0
- package/docs/themes/barnred/light-hc.css +51 -0
- package/docs/themes/barnred/light-mc.css +51 -0
- package/docs/themes/barnred/light.css +51 -0
- package/docs/themes/barnred/theme.css +306 -0
- package/docs/themes/citrine/dark-hc.css +51 -0
- package/docs/themes/citrine/dark-mc.css +51 -0
- package/docs/themes/citrine/dark.css +51 -0
- package/docs/themes/citrine/light-hc.css +51 -0
- package/docs/themes/citrine/light-mc.css +51 -0
- package/docs/themes/citrine/light.css +51 -0
- package/docs/themes/citrine/theme.css +306 -0
- package/docs/themes/olivegreen/dark-hc.css +51 -0
- package/docs/themes/olivegreen/dark-mc.css +51 -0
- package/docs/themes/olivegreen/dark.css +51 -0
- package/docs/themes/olivegreen/light-hc.css +51 -0
- package/docs/themes/olivegreen/light-mc.css +51 -0
- package/docs/themes/olivegreen/light.css +51 -0
- package/docs/themes/olivegreen/theme.css +306 -0
- package/package.json +62 -0
- package/styles/README.md +99 -0
- package/styles/elevation.scss +36 -0
- package/styles/motion.scss +124 -0
- package/styles/ripple.scss +50 -0
- package/styles/shapes.scss +46 -0
- package/styles/statelayer.scss +42 -0
- package/styles/typography.scss +568 -0
- package/styles.scss +43 -0
- package/themes/README.md +57 -0
- package/themes/airblue/dark-hc.css +51 -0
- package/themes/airblue/dark-mc.css +51 -0
- package/themes/airblue/dark.css +51 -0
- package/themes/airblue/light-hc.css +51 -0
- package/themes/airblue/light-mc.css +51 -0
- package/themes/airblue/light.css +51 -0
- package/themes/airblue/theme.css +306 -0
- package/themes/barnred/dark-hc.css +51 -0
- package/themes/barnred/dark-mc.css +51 -0
- package/themes/barnred/dark.css +51 -0
- package/themes/barnred/light-hc.css +51 -0
- package/themes/barnred/light-mc.css +51 -0
- package/themes/barnred/light.css +51 -0
- package/themes/barnred/theme.css +306 -0
- package/themes/citrine/dark-hc.css +51 -0
- package/themes/citrine/dark-mc.css +51 -0
- package/themes/citrine/dark.css +51 -0
- package/themes/citrine/light-hc.css +51 -0
- package/themes/citrine/light-mc.css +51 -0
- package/themes/citrine/light.css +51 -0
- package/themes/citrine/theme.css +306 -0
- package/themes/olivegreen/dark-hc.css +51 -0
- package/themes/olivegreen/dark-mc.css +51 -0
- package/themes/olivegreen/dark.css +51 -0
- package/themes/olivegreen/light-hc.css +51 -0
- package/themes/olivegreen/light-mc.css +51 -0
- package/themes/olivegreen/light.css +51 -0
- package/themes/olivegreen/theme.css +306 -0
- package/tsconfig.json +110 -0
- package/webpack.config.js +49 -0
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
# Side sheet
|
|
2
|
+
This component implements the the [Material Design 3 Expressive Side sheet](https://m3.material.io/components/side-sheets/overview) design.
|
|
3
|
+
|
|
4
|
+
## Basic Usage
|
|
5
|
+
|
|
6
|
+
### HTML
|
|
7
|
+
To create a standard side sheet, use the `<dialog>` element with the `popover` attribute. The `closedby="any"` attribute allows the user to dismiss the side sheet by clicking anywhere outside of it.
|
|
8
|
+
|
|
9
|
+
```HTML
|
|
10
|
+
<dialog id="mysidesheet" class="micl-sidesheet" closedby="any" popover>
|
|
11
|
+
<div class="micl-sidesheet__headline">
|
|
12
|
+
<h2>Title</h2>
|
|
13
|
+
<button
|
|
14
|
+
type="button"
|
|
15
|
+
class="micl-iconbutton-s material-symbols-outlined"
|
|
16
|
+
popovertarget="mysidesheet"
|
|
17
|
+
aria-label="Close"
|
|
18
|
+
>close</button>
|
|
19
|
+
</div>
|
|
20
|
+
<div class="micl-sidesheet__content">
|
|
21
|
+
...your content...
|
|
22
|
+
</div>
|
|
23
|
+
</dialog>
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
### CSS
|
|
27
|
+
Import the side sheet styles into your project:
|
|
28
|
+
|
|
29
|
+
```CSS
|
|
30
|
+
@use "micl/components/sidesheet";
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
### TypeScript
|
|
34
|
+
No custom TypeScript is required for the core functionality of the side sheet component.
|
|
35
|
+
|
|
36
|
+
### Demo
|
|
37
|
+
A live example of the [Side sheet component](https://henkpb.github.io/micl/sidesheet.html) is available for you to interact with.
|
|
38
|
+
|
|
39
|
+
## Variants
|
|
40
|
+
A **modal** side sheet blocks access to the rest of the page and must be dismissed explicitly by the user. This is suitable for critical tasks or information that requires a user's full attention.
|
|
41
|
+
|
|
42
|
+
To create a modal side sheet, use the `<dialog>` element without the `popover` attribute. Use `closedby="closerequest"` to prevent the side sheet from being dismissed by clicking outside of it. You'll also need a button or other control with popovertarget to close it.
|
|
43
|
+
|
|
44
|
+
```HTML
|
|
45
|
+
<dialog id="mysidesheet" class="micl-sidesheet" closedby="closerequest">
|
|
46
|
+
<div class="micl-sidesheet__headline">
|
|
47
|
+
<button
|
|
48
|
+
type="button"
|
|
49
|
+
class="micl-iconbutton-s material-symbols-outlined"
|
|
50
|
+
aria-label="Go back"
|
|
51
|
+
>arrow_back</button>
|
|
52
|
+
<h2>Title</h2>
|
|
53
|
+
<button
|
|
54
|
+
type="button"
|
|
55
|
+
class="micl-iconbutton-s material-symbols-outlined"
|
|
56
|
+
popovertarget="mysidesheet"
|
|
57
|
+
aria-label="Close"
|
|
58
|
+
>close</button>
|
|
59
|
+
</div>
|
|
60
|
+
<div class="micl-sidesheet__content">
|
|
61
|
+
...your content...
|
|
62
|
+
</div>
|
|
63
|
+
<div class="micl-sidesheet__actions">
|
|
64
|
+
<button type="button" class="micl-button">Save</button>
|
|
65
|
+
</div>
|
|
66
|
+
</dialog>
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
To open either a standard or modal side sheet, link a button to the side sheet's ID using the `popovertarget` attribute:
|
|
70
|
+
```HTML
|
|
71
|
+
<button type="button" popovertarget="mysidesheet">Open Side Sheet</button>
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
The back-button and the actions-container are optional. To remove the vertical divider of the "standard" side sheet, assign zero to the following CSS variable:
|
|
75
|
+
```CSS
|
|
76
|
+
#mysidesheet {
|
|
77
|
+
--md-sys-sidesheet-divider-width: 0;
|
|
78
|
+
}
|
|
79
|
+
```
|
|
80
|
+
To remove the horizontal divider of the actions-container:
|
|
81
|
+
```CSS
|
|
82
|
+
#mysidesheet {
|
|
83
|
+
--md-sys-sidesheet-actions-divider-width: 0;
|
|
84
|
+
}
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### Warning
|
|
88
|
+
The **standard** side sheet component adds CSS rules to the `<body>` element to properly resize the main content area when the side sheet is open. Overriding these rules may cause the component to behave unexpectedly. The rules that are applied are:
|
|
89
|
+
|
|
90
|
+
```CSS
|
|
91
|
+
box-sizing: border-box;
|
|
92
|
+
margin-left: 0px;
|
|
93
|
+
margin-right: 0px;
|
|
94
|
+
max-width: ...varies depending on if the side sheet is opened...
|
|
95
|
+
transition: ...transition on max-width...
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
## Compatibility
|
|
99
|
+
This component uses the Popover API, which might not be supported in all browsers. Please check [Browser compatibility](https://developer.mozilla.org/en-US/docs/Web/API/Popover_API#api.htmlelement.popover) for details.
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright © 2025 Hermana AS
|
|
3
|
+
//
|
|
4
|
+
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
5
|
+
// of this software and associated documentation files (the "Software"), to deal
|
|
6
|
+
// in the Software without restriction, including without limitation the rights
|
|
7
|
+
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
8
|
+
// copies of the Software, and to permit persons to whom the Software is
|
|
9
|
+
// furnished to do so, subject to the following conditions:
|
|
10
|
+
//
|
|
11
|
+
// The above copyright notice and this permission notice shall be included in all
|
|
12
|
+
// copies or substantial portions of the Software.
|
|
13
|
+
//
|
|
14
|
+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
15
|
+
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
16
|
+
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
17
|
+
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
18
|
+
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
19
|
+
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
20
|
+
// SOFTWARE.
|
|
21
|
+
|
|
22
|
+
@use '../../styles/motion';
|
|
23
|
+
@use '../../styles/typography';
|
|
24
|
+
|
|
25
|
+
dialog.micl-sidesheet {
|
|
26
|
+
--md-sys-sidesheet-width: 256px;
|
|
27
|
+
--md-sys-sidesheet-maxwidth: 400px;
|
|
28
|
+
--md-sys-sidesheet-spring-buffer: 200px;
|
|
29
|
+
--md-sys-sidesheet-padding-standard: 24px;
|
|
30
|
+
--md-sys-sidesheet-padding-modal: 16px;
|
|
31
|
+
|
|
32
|
+
box-sizing: border-box;
|
|
33
|
+
display: none;
|
|
34
|
+
flex-direction: column;
|
|
35
|
+
justify-content: space-between;
|
|
36
|
+
min-width: calc(var(--md-sys-sidesheet-width) + var(--md-sys-sidesheet-spring-buffer));
|
|
37
|
+
max-width: calc(var(--md-sys-sidesheet-maxwidth) + var(--md-sys-sidesheet-spring-buffer));
|
|
38
|
+
height: 100%;
|
|
39
|
+
min-height: 100%;
|
|
40
|
+
margin-top: 0;
|
|
41
|
+
margin-bottom: 0;
|
|
42
|
+
margin-inline-start: auto;
|
|
43
|
+
margin-inline-end: calc(-1 * (var(--md-sys-sidesheet-maxwidth) + var(--md-sys-sidesheet-spring-buffer)));
|
|
44
|
+
padding-top: 10px;
|
|
45
|
+
padding-bottom: var(--md-sys-sidesheet-padding-standard);
|
|
46
|
+
padding-inline-start: 0;
|
|
47
|
+
padding-inline-end: var(--md-sys-sidesheet-spring-buffer);
|
|
48
|
+
background-color: var(--md-sys-color-surface);
|
|
49
|
+
border: none;
|
|
50
|
+
box-shadow: var(--md-sys-elevation-level0);
|
|
51
|
+
opacity: 0;
|
|
52
|
+
overflow: hidden;
|
|
53
|
+
transition:
|
|
54
|
+
display var(--md-sys-motion-duration-medium1) allow-discrete,
|
|
55
|
+
overlay var(--md-sys-motion-duration-medium1) allow-discrete,
|
|
56
|
+
opacity var(--md-sys-motion-duration-medium1),
|
|
57
|
+
margin-inline-end var(--md-sys-motion-duration-medium1);
|
|
58
|
+
|
|
59
|
+
.micl-sidesheet__headline {
|
|
60
|
+
display: flex;
|
|
61
|
+
align-items: center;
|
|
62
|
+
padding-inline-start: 24px;
|
|
63
|
+
padding-inline-end: 12px;
|
|
64
|
+
column-gap: 8px;
|
|
65
|
+
color: var(--md-sys-color-on-surface-variant);
|
|
66
|
+
|
|
67
|
+
h1, h2, h3, h4, h5, h6, .micl-heading {
|
|
68
|
+
@include typography.title-large;
|
|
69
|
+
|
|
70
|
+
flex: 1 2 fit-content;
|
|
71
|
+
margin: 0;
|
|
72
|
+
overflow: hidden;
|
|
73
|
+
text-overflow: ellipsis;
|
|
74
|
+
white-space: nowrap;
|
|
75
|
+
user-select: none;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
.micl-sidesheet__content {
|
|
79
|
+
box-sizing: border-box;
|
|
80
|
+
flex: 1 1 auto;
|
|
81
|
+
width: 100%;
|
|
82
|
+
max-width: 100%;
|
|
83
|
+
padding-inline-start: var(--md-sys-sidesheet-padding-standard);
|
|
84
|
+
padding-inline-end: var(--md-sys-sidesheet-padding-standard);
|
|
85
|
+
overflow: hidden auto;
|
|
86
|
+
}
|
|
87
|
+
.micl-sidesheet__actions {
|
|
88
|
+
box-sizing: border-box;
|
|
89
|
+
display: flex;
|
|
90
|
+
align-items: center;
|
|
91
|
+
justify-content: start;
|
|
92
|
+
width: 100%;
|
|
93
|
+
max-width: 100%;
|
|
94
|
+
height: calc(72px - var(--md-sys-sidesheet-padding-standard));
|
|
95
|
+
margin: auto 0 0 0;
|
|
96
|
+
padding-top: 16px;
|
|
97
|
+
overflow: hidden;
|
|
98
|
+
border-top: var(--md-sys-divider-thickness) solid var(--md-sys-divider-color);
|
|
99
|
+
|
|
100
|
+
button:active,
|
|
101
|
+
button:focus-visible,
|
|
102
|
+
button:hover {
|
|
103
|
+
color: var(--md-sys-color-primary);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
&::backdrop {
|
|
108
|
+
background-color: rgba(0, 0, 0, 0);
|
|
109
|
+
transition:
|
|
110
|
+
display var(--md-sys-motion-duration-long2) linear allow-discrete,
|
|
111
|
+
overlay var(--md-sys-motion-duration-long2) linear allow-discrete,
|
|
112
|
+
background-color var(--md-sys-motion-duration-long2);
|
|
113
|
+
}
|
|
114
|
+
&[open] {
|
|
115
|
+
background-color: var(--md-sys-color-surface-container-low);
|
|
116
|
+
border-start-start-radius: var(--md-sys-shape-corner-large);
|
|
117
|
+
border-end-start-radius: var(--md-sys-shape-corner-large);
|
|
118
|
+
box-shadow: var(--md-sys-elevation-level1);
|
|
119
|
+
|
|
120
|
+
.micl-sidesheet__headline {
|
|
121
|
+
padding-inline-start: 4px;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
&:popover-open {
|
|
125
|
+
border-inline-start: var(--md-sys-divider-thickness) solid var(--md-sys-divider-color);
|
|
126
|
+
}
|
|
127
|
+
&:popover-open,
|
|
128
|
+
&[open] {
|
|
129
|
+
display: flex;
|
|
130
|
+
margin-inline-end: calc(-1 * var(--md-sys-sidesheet-spring-buffer));
|
|
131
|
+
opacity: 1;
|
|
132
|
+
transition:
|
|
133
|
+
display var(--md-sys-motion-duration-long2) linear allow-discrete,
|
|
134
|
+
overlay var(--md-sys-motion-duration-long2) linear allow-discrete,
|
|
135
|
+
opacity var(--md-sys-motion-duration-long2) motion.$md-sys-motion-easing-emphasized-decelerate,
|
|
136
|
+
margin-inline-end var(--md-sys-motion-duration-long2) linear(motion.$md-sys-motion-spring-default-spatial);
|
|
137
|
+
|
|
138
|
+
@starting-style {
|
|
139
|
+
opacity: 0;
|
|
140
|
+
margin-inline-end: calc(-1 * (var(--md-sys-sidesheet-maxwidth) + var(--md-sys-sidesheet-spring-buffer)));
|
|
141
|
+
}
|
|
142
|
+
&::backdrop {
|
|
143
|
+
@starting-style {
|
|
144
|
+
background-color: rgba(0, 0, 0, 0);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
&[open]::backdrop {
|
|
149
|
+
background-color: rgba(0, 0, 0, 0.2);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
body:has(dialog.micl-sidesheet[popover]) {
|
|
154
|
+
box-sizing: border-box;
|
|
155
|
+
max-width: 100%;
|
|
156
|
+
margin: 0;
|
|
157
|
+
transition: max-width var(--md-sys-motion-duration-medium1);
|
|
158
|
+
}
|
|
159
|
+
body:has(dialog.micl-sidesheet:popover-open) {
|
|
160
|
+
max-width: calc(100vw - 400px);
|
|
161
|
+
transition: max-width var(--md-sys-motion-duration-long2) linear(motion.$md-sys-motion-spring-default-spatial);
|
|
162
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# Slider
|
|
2
|
+
This component implements the the [Material Design 3 Expressive Slider](https://m3.material.io/components/sliders/overview) design.
|
|
3
|
+
|
|
4
|
+
## Basic Usage
|
|
5
|
+
|
|
6
|
+
### HTML
|
|
7
|
+
To add a basic slider, use the `<input type="range">` element with one of the primary slider style classes: `micl-slider-xs`, `micl-slider-s`, `micl-slider-m`, `micl-slider-l` or `micl-slider-xl`.
|
|
8
|
+
|
|
9
|
+
```HTML
|
|
10
|
+
<input type="range" class="micl-slider-m">
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
### CSS
|
|
14
|
+
Import the slider styles into your project:
|
|
15
|
+
|
|
16
|
+
```CSS
|
|
17
|
+
@use "micl/components/slider";
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
### TypeScript
|
|
21
|
+
This component requires the **Slider** TypeScript module for functionality. You can import the specific module and handle initialization manually, or use the main MICL library for automatic initialization.
|
|
22
|
+
|
|
23
|
+
To manually initialize the component:
|
|
24
|
+
|
|
25
|
+
```TypeScript
|
|
26
|
+
import miclSlider from 'micl/components/slider';
|
|
27
|
+
|
|
28
|
+
miclSlider.initialize(document.querySelector('.micl-slider-m'));
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
### Demo
|
|
32
|
+
A live example of the [Slider component](https://henkpb.github.io/micl/slider.html) is available for you to interact with.
|
|
33
|
+
|
|
34
|
+
## Variants
|
|
35
|
+
Sliders come in **five different sizes**: extra small (`xs`), small (`s`), medium (`m`), large (`l`), and extra large (`xl`). To set a specific size, append the appropriate postfix to the `micl-slider` CSS class name:
|
|
36
|
+
|
|
37
|
+
```HTML
|
|
38
|
+
<input type="range" class="micl-slider-s" min="1" max="10" step="1" value="6">
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
Add a `<datalist>` element to suggest predefined values. The slider will show tick marks for these values.
|
|
42
|
+
```HTML
|
|
43
|
+
<input type="range" class="micl-slider-l" min="0" max="100" value="25" step="25" list="markers">
|
|
44
|
+
<datalist id="markers">
|
|
45
|
+
<option value="0"></option>
|
|
46
|
+
<option value="25"></option>
|
|
47
|
+
<option value="50"></option>
|
|
48
|
+
<option value="75"></option>
|
|
49
|
+
<option value="100"></option>
|
|
50
|
+
</datalist>
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
By default, the slider is displayed **horizontally**. To display the slider **vertically**, add the `micl-slider--vertical` CSS class.
|
|
54
|
+
|
|
55
|
+
For displaying an icon inside a medium-, large- or extra large-sized slider, wrap the icon and the slider inside a slider container:
|
|
56
|
+
|
|
57
|
+
```HTML
|
|
58
|
+
<div class="micl-slider__container">
|
|
59
|
+
<span class="micl-slider__icon material-symbols-outlined" aria-hidden="true">volume_up</span>
|
|
60
|
+
<input type="range" class="micl-slider-l" min="3.5" max="12.5" value="9.5" step="0.5">
|
|
61
|
+
</div>
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Adding the `disabled` boolean attribute to the `input` element causes the slider to be displayed in a disabled state.
|
|
65
|
+
|
|
66
|
+
The Slider component is aware of the `dir` global attribute that indicates the directionality of text.
|
|
67
|
+
|
|
68
|
+
## Compatibility
|
|
69
|
+
This component uses the `color-mix` CSS functional notation, which might not be supported in your browser. Please check [Browser compatibility](https://developer.mozilla.org/en-US/docs/Web/CSS/color_value/color-mix#browser_compatibility) for details.
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright © 2025 Hermana AS
|
|
3
|
+
//
|
|
4
|
+
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
5
|
+
// of this software and associated documentation files (the "Software"), to deal
|
|
6
|
+
// in the Software without restriction, including without limitation the rights
|
|
7
|
+
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
8
|
+
// copies of the Software, and to permit persons to whom the Software is
|
|
9
|
+
// furnished to do so, subject to the following conditions:
|
|
10
|
+
//
|
|
11
|
+
// The above copyright notice and this permission notice shall be included in all
|
|
12
|
+
// copies or substantial portions of the Software.
|
|
13
|
+
//
|
|
14
|
+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
15
|
+
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
16
|
+
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
17
|
+
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
18
|
+
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
19
|
+
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
20
|
+
// SOFTWARE.
|
|
21
|
+
|
|
22
|
+
export const sliderSelector = 'input[type=range].micl-slider-xs,input[type=range].micl-slider-s,input[type=range].micl-slider-m,input[type=range].micl-slider-l,input[type=range].micl-slider-xl';
|
|
23
|
+
export default (() =>
|
|
24
|
+
{
|
|
25
|
+
const
|
|
26
|
+
tick = String.fromCharCode(8226),
|
|
27
|
+
blank = String.fromCharCode(8201),
|
|
28
|
+
getTickValues = (element: HTMLInputElement): number[] =>
|
|
29
|
+
{
|
|
30
|
+
const
|
|
31
|
+
values: number[] = [],
|
|
32
|
+
max = parseFloat(element.max),
|
|
33
|
+
min = parseFloat(element.min);
|
|
34
|
+
|
|
35
|
+
if (!!element.list && !isNaN(max) && !isNaN(min) && (max > min)) {
|
|
36
|
+
element.list.querySelectorAll<HTMLOptionElement>('option[value]').forEach(option =>
|
|
37
|
+
{
|
|
38
|
+
let value = parseFloat(option.value);
|
|
39
|
+
if (!isNaN(value) && (value >= min) && (value <= max)) {
|
|
40
|
+
values.push(value);
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
return values;
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
return {
|
|
48
|
+
initialize: (element: HTMLInputElement) =>
|
|
49
|
+
{
|
|
50
|
+
element.style.setProperty('--md-sys-slider-max', element.max || '100');
|
|
51
|
+
element.style.setProperty('--md-sys-slider-min', element.min || '0');
|
|
52
|
+
element.style.setProperty('--md-sys-slider-value', element.value);
|
|
53
|
+
element.style.setProperty('--md-sys-slider-tip', JSON.stringify(element.value + ''));
|
|
54
|
+
|
|
55
|
+
const rect = element.getBoundingClientRect(),
|
|
56
|
+
e = document.elementFromPoint(Math.max(rect.x - 1, 0), Math.max(rect.y - 1, 0));
|
|
57
|
+
if (e) {
|
|
58
|
+
element.style.color = window.getComputedStyle(e).getPropertyValue('background-color');
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const
|
|
62
|
+
max = parseFloat(element.max),
|
|
63
|
+
min = parseFloat(element.min),
|
|
64
|
+
percentages = getTickValues(element).sort((a, b) => a - b).map(value => {
|
|
65
|
+
return Math.round(100 * (value - min) / (max - min));
|
|
66
|
+
});
|
|
67
|
+
if (percentages.length > 0) {
|
|
68
|
+
const
|
|
69
|
+
canvas = document.createElement('canvas'),
|
|
70
|
+
ctx = canvas.getContext('2d');
|
|
71
|
+
if (ctx) {
|
|
72
|
+
ctx.font = window.getComputedStyle(element).getPropertyValue('font');
|
|
73
|
+
let blankWidth = ctx.measureText(blank).width,
|
|
74
|
+
tickWidth = ctx.measureText(tick).width,
|
|
75
|
+
totalWidth = rect.width - 10,
|
|
76
|
+
currentWidth = 0,
|
|
77
|
+
tickString = '';
|
|
78
|
+
|
|
79
|
+
percentages.forEach(percentage =>
|
|
80
|
+
{
|
|
81
|
+
let position = (totalWidth * percentage) / 100,
|
|
82
|
+
nrBlanks = Math.round((position - currentWidth) / blankWidth) - 1;
|
|
83
|
+
for (let i = 0; i < nrBlanks; i++) {
|
|
84
|
+
tickString += blank;
|
|
85
|
+
currentWidth += blankWidth;
|
|
86
|
+
}
|
|
87
|
+
tickString += tick;
|
|
88
|
+
currentWidth += tickWidth;
|
|
89
|
+
});
|
|
90
|
+
element.dataset.miclsliderticks = tickString;
|
|
91
|
+
}
|
|
92
|
+
canvas.remove();
|
|
93
|
+
}
|
|
94
|
+
else {
|
|
95
|
+
element.dataset.miclsliderticks = tick;
|
|
96
|
+
}
|
|
97
|
+
},
|
|
98
|
+
input: (event: Event) =>
|
|
99
|
+
{
|
|
100
|
+
if (
|
|
101
|
+
!(event.target as Element).matches(
|
|
102
|
+
'.micl-slider-xs,.micl-slider-s,.micl-slider-m,.micl-slider-l,.micl-slider-xl'
|
|
103
|
+
)
|
|
104
|
+
|| !(event.target instanceof HTMLInputElement)
|
|
105
|
+
|| (event.target as HTMLInputElement).disabled
|
|
106
|
+
) {
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
event.target.style.setProperty('--md-sys-slider-value', event.target.value);
|
|
111
|
+
event.target.style.setProperty('--md-sys-slider-tip', JSON.stringify(event.target.value + ''));
|
|
112
|
+
}
|
|
113
|
+
};
|
|
114
|
+
})();
|