material-inspired-component-library 6.0.7 → 7.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/README.md +11 -2
- package/components/appbar/index.scss +4 -4
- package/components/button/README.md +16 -1
- package/components/button/index.scss +6 -0
- package/components/button/index.ts +17 -0
- package/components/card/README.md +82 -11
- package/components/card/index.scss +169 -209
- package/components/datepicker/README.md +3 -2
- package/components/datepicker/index.ts +0 -8
- package/components/iconbutton/README.md +11 -0
- package/components/iconbutton/index.scss +12 -0
- package/components/iconbutton/index.ts +15 -0
- package/components/navigationrail/README.md +64 -59
- package/components/navigationrail/index.scss +197 -300
- package/components/navigationrail/index.ts +55 -38
- package/components/snackbar/README.md +126 -0
- package/components/snackbar/index.scss +94 -73
- package/components/snackbar/index.ts +90 -28
- package/components/timepicker/README.md +2 -1
- package/components/timepicker/index.ts +0 -3
- package/dist/appbar.css +1 -1
- package/dist/button.css +1 -1
- package/dist/card.css +1 -1
- package/dist/components/navigationrail/index.d.ts +2 -1
- package/dist/components/snackbar/index.d.ts +1 -0
- package/dist/iconbutton.css +1 -1
- package/dist/micl.css +1 -1
- package/dist/micl.js +1 -1
- package/dist/navigationrail.css +1 -1
- package/dist/snackbar.css +1 -1
- package/docs/button.html +43 -15
- package/docs/card.html +6 -5
- package/docs/datepicker.html +4 -4
- package/docs/iconbutton.html +22 -8
- package/docs/index.html +31 -18
- package/docs/micl.css +1 -1
- package/docs/micl.js +1 -1
- package/docs/navigationrail.html +76 -22
- package/docs/snackbar.html +31 -14
- package/docs/timepicker.html +12 -3
- package/package.json +1 -1
- package/docs/snackbar1.html +0 -159
- package/docs/snackbar2.html +0 -159
|
@@ -21,50 +21,67 @@
|
|
|
21
21
|
|
|
22
22
|
export const navigationrailSelector = '.micl-navigationrail';
|
|
23
23
|
|
|
24
|
-
|
|
24
|
+
interface NavigationRailState {
|
|
25
|
+
boundHandleClick: (event: Event) => void;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const navigationrailStates = new WeakMap<HTMLElement, NavigationRailState>();
|
|
29
|
+
|
|
30
|
+
export default
|
|
25
31
|
{
|
|
26
|
-
|
|
27
|
-
|
|
32
|
+
initialize: (element: HTMLElement): void =>
|
|
33
|
+
{
|
|
34
|
+
if (!element.matches(navigationrailSelector) || element.dataset.miclinitialized) {
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
element.dataset.miclinitialized = '1';
|
|
39
|
+
|
|
40
|
+
const boundHandleClick = (event: Event): void =>
|
|
28
41
|
{
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|| !event.target.matches('label.micl-navigationrail__item[for]')
|
|
33
|
-
) {
|
|
34
|
-
return;
|
|
35
|
-
}
|
|
36
|
-
const input = document.getElementById(event.target.htmlFor) as HTMLInputElement;
|
|
37
|
-
if (!input) {
|
|
38
|
-
return;
|
|
39
|
-
}
|
|
42
|
+
const clickedItem = (event.target as HTMLElement).closest<HTMLAnchorElement>(
|
|
43
|
+
'a.micl-navigationrail__item'
|
|
44
|
+
);
|
|
40
45
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
input.checked = !input.checked;
|
|
47
|
-
}
|
|
46
|
+
if (clickedItem) {
|
|
47
|
+
const allItems = element.querySelectorAll<HTMLAnchorElement>(
|
|
48
|
+
'a.micl-navigationrail__item'
|
|
49
|
+
);
|
|
50
|
+
allItems.forEach(item => item.removeAttribute('aria-current'));
|
|
48
51
|
|
|
49
|
-
|
|
50
|
-
if (el instanceof HTMLAnchorElement) {
|
|
51
|
-
el.click();
|
|
52
|
-
}
|
|
53
|
-
break;
|
|
54
|
-
default:
|
|
52
|
+
clickedItem.setAttribute('aria-current', 'page');
|
|
55
53
|
}
|
|
56
|
-
}
|
|
54
|
+
};
|
|
57
55
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
56
|
+
navigationrailStates.set(element, { boundHandleClick });
|
|
57
|
+
|
|
58
|
+
element.addEventListener('click', boundHandleClick);
|
|
59
|
+
},
|
|
61
60
|
|
|
62
|
-
|
|
61
|
+
keydown: (event: Event): void =>
|
|
62
|
+
{
|
|
63
|
+
if (
|
|
64
|
+
!(event instanceof KeyboardEvent)
|
|
65
|
+
|| !(event.target instanceof HTMLElement)
|
|
66
|
+
|| !event.target.matches('.micl-navigationrail__item')
|
|
67
|
+
) {
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
63
70
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
});
|
|
71
|
+
if (event.keyCode === 32 || event.key === ' ') {
|
|
72
|
+
event.preventDefault();
|
|
73
|
+
event.target.click();
|
|
68
74
|
}
|
|
69
|
-
}
|
|
70
|
-
|
|
75
|
+
},
|
|
76
|
+
|
|
77
|
+
cleanup: (element: HTMLElement): void =>
|
|
78
|
+
{
|
|
79
|
+
const state = navigationrailStates.get(element);
|
|
80
|
+
|
|
81
|
+
if (state) {
|
|
82
|
+
element.removeEventListener('click', state.boundHandleClick);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
delete element.dataset.miclinitialized;
|
|
86
|
+
}
|
|
87
|
+
};
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
# Snackbar
|
|
2
|
+
This component implements the [Material Design 3 Expressive Snackbar](https://m3.material.io/components/snackbar/overview) design. Snackbars are short, temporary notifications that appear at the bottom of the screen.
|
|
3
|
+
|
|
4
|
+
## Basic Usage
|
|
5
|
+
|
|
6
|
+
### HTML
|
|
7
|
+
To create a simple snackbar, use a `<div>` element with the `micl-snackbar` class. Inside, use a `<span>` element with the `micl-snackbar__supporting-text` class to contain the snackbar's notification.
|
|
8
|
+
|
|
9
|
+
```HTML
|
|
10
|
+
<div class="micl-snackbar" popover="manual" data-micldelay="3000" role="status" aria-atomic="true">
|
|
11
|
+
<span class="micl-snackbar__supporting-text">All changes saved</span>
|
|
12
|
+
</div>
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
The `data-micldelay` attribute contains the number of milliseconds the snackbar remains visible.
|
|
16
|
+
|
|
17
|
+
### CSS
|
|
18
|
+
Import the snackbar styles into your project:
|
|
19
|
+
|
|
20
|
+
```CSS
|
|
21
|
+
@use "material-inspired-component-library/dist/snackbar";
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
Or import all MICL styles:
|
|
25
|
+
```CSS
|
|
26
|
+
@use "material-inspired-component-library/styles";
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
### JavaScript
|
|
30
|
+
This component requires JavaScript to enable auto-dismissing the snackbar.
|
|
31
|
+
|
|
32
|
+
```JavaScript
|
|
33
|
+
import micl from "material-inspired-component-library/dist/micl";
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
This will initialize any Snackbar component, including those that will be added to the DOM later on.
|
|
37
|
+
|
|
38
|
+
### Live Demo
|
|
39
|
+
A live example of the [Snackbar component](https://henkpb.github.io/micl/snackbar.html) is available to interact with.
|
|
40
|
+
|
|
41
|
+
## Variants
|
|
42
|
+
You can add an action to the snackbar by including a [MICL button](../button/README.md) to the markup:
|
|
43
|
+
|
|
44
|
+
```HTML
|
|
45
|
+
<div class="micl-snackbar" popover="manual" data-micldelay="3000" role="status" aria-atomic="true">
|
|
46
|
+
<span class="micl-snackbar__supporting-text">Your document has been saved</span>
|
|
47
|
+
<button type="button" class="micl-button-text-s">Undo</button>
|
|
48
|
+
</div>
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
You can also add a close button that dismisses the snackbar when pressed:
|
|
52
|
+
|
|
53
|
+
```HTML
|
|
54
|
+
<div id="mysnackbar" class="micl-snackbar" popover="manual" data-micldelay="7000" role="status" aria-atomic="true">
|
|
55
|
+
<span class="micl-snackbar__supporting-text">Your document has been saved</span>
|
|
56
|
+
<button type="button" class="micl-iconbutton-standard-s material-symbols-outlined" popovertarget="mysnackbar" popovertargetaction="hide">close</button>
|
|
57
|
+
</div>
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Add the `micl-snackbar--two` class to the snackbar to increase its height so that it can accommodate two lines of text.
|
|
61
|
+
|
|
62
|
+
```HTML
|
|
63
|
+
<div class="micl-snackbar micl-snackbar--two" popover="manual" data-micldelay="5000" role="status" aria-atomic="true">
|
|
64
|
+
<span>
|
|
65
|
+
<span class="micl-snackbar__supporting-text">Message sent</span>
|
|
66
|
+
<span class="micl-snackbar__supporting-text">An email has been sent to Alice</span>
|
|
67
|
+
</span>
|
|
68
|
+
</div>
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
The Snackbar component respects the `dir` global attribute, automatically adjusting its layout for right-to-left (RTL) languages when `dir="rtl"` is applied to an ancestor element.
|
|
72
|
+
|
|
73
|
+
## Customizations
|
|
74
|
+
You can customize the appearance of the Snackbar component by overriding its global CSS variables. These variables are declared on the `:root` pseudo-class and can be changed on any appropriate parent element to affect its child snackbars.
|
|
75
|
+
|
|
76
|
+
| Variable name | Default Value | Description |
|
|
77
|
+
| ------------- | ------------- | ----------- |
|
|
78
|
+
| --md-comp-snackbar-margin | 24px | The distance between the snackbar and the bottom of the device |
|
|
79
|
+
| --md-comp-snackbar-padding | 16px | The inner padding of the snackbar |
|
|
80
|
+
|
|
81
|
+
The Snackbar component supports the following CSS variables, as defined in the [Material Design 3 Expressive Snackbar Specification](https://m3.material.io/components/snackbar/specs):
|
|
82
|
+
|
|
83
|
+
```CSS
|
|
84
|
+
--md-comp-snackbar-container-color
|
|
85
|
+
--md-comp-snackbar-container-elevation
|
|
86
|
+
--md-comp-snackbar-container-shape
|
|
87
|
+
--md-comp-snackbar-with-single-line-container-height
|
|
88
|
+
--md-comp-snackbar-with-two-lines-container-height
|
|
89
|
+
--md-comp-snackbar-action-label-text-color
|
|
90
|
+
--md-comp-snackbar-icon-size
|
|
91
|
+
--md-comp-snackbar-icon-color
|
|
92
|
+
--md-comp-snackbar-supporting-text-color
|
|
93
|
+
--md-comp-snackbar-action-hover-label-text-color
|
|
94
|
+
--md-comp-snackbar-action-focus-label-text-color
|
|
95
|
+
--md-comp-snackbar-action-pressed-label-text-color
|
|
96
|
+
--md-comp-snackbar-icon-hover-icon-color
|
|
97
|
+
--md-comp-snackbar-icon-focus-icon-color
|
|
98
|
+
--md-comp-snackbar-icon-pressed-icon-color
|
|
99
|
+
--md-comp-snackbar-action-hover-state-layer-color
|
|
100
|
+
--md-comp-snackbar-action-hover-state-layer-opacity
|
|
101
|
+
--md-comp-snackbar-action-focus-state-layer-color
|
|
102
|
+
--md-comp-snackbar-action-focus-state-layer-opacity
|
|
103
|
+
--md-comp-snackbar-action-pressed-state-layer-color
|
|
104
|
+
--md-comp-snackbar-action-pressed-state-layer-opacity
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
**Example: Changing the padding of the snackbar**
|
|
108
|
+
|
|
109
|
+
```HTML
|
|
110
|
+
<div style="--md-comp-snackbar-padding:24px">
|
|
111
|
+
<div class="micl-snackbar" popover="manual" data-micldelay="3000" role="status" aria-atomic="true">
|
|
112
|
+
<span class="micl-snackbar__supporting-text">All changes saved</span>
|
|
113
|
+
</div>
|
|
114
|
+
</div>
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
To change the amount of rounding of the snackbar's corners, you could for example add a CSS rule to your stylesheet:
|
|
118
|
+
|
|
119
|
+
```CSS
|
|
120
|
+
.micl-snackbar {
|
|
121
|
+
--md-comp-snackbar-container-shape: 24px;
|
|
122
|
+
}
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
## Compatibility
|
|
126
|
+
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.
|
|
@@ -20,123 +20,144 @@
|
|
|
20
20
|
// SOFTWARE.
|
|
21
21
|
|
|
22
22
|
@use '../../foundations';
|
|
23
|
+
@use '../../foundations/layout';
|
|
23
24
|
@use '../../styles/elevation';
|
|
24
25
|
@use '../../styles/motion';
|
|
25
26
|
@use '../../styles/shapes';
|
|
26
27
|
@use '../../styles/typography';
|
|
27
28
|
|
|
28
29
|
:root {
|
|
29
|
-
--md-comp-snackbar-with-single-line-container-height: 48px;
|
|
30
|
-
--md-comp-snackbar-with-two-lines-container-height: 68px;
|
|
31
30
|
--md-comp-snackbar-margin: 24px;
|
|
31
|
+
--md-comp-snackbar-padding: 16px;
|
|
32
|
+
--md-comp-snackbar-dir-factor: 1;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
[dir=rtl] {
|
|
36
|
+
--md-comp-snackbar-dir-factor: -1;
|
|
32
37
|
}
|
|
33
38
|
|
|
34
39
|
.micl-snackbar {
|
|
40
|
+
--md-comp-snackbar-motion-spatial: #{motion.$md-sys-motion-expressive-default-spatial};
|
|
41
|
+
--md-comp-snackbar-motion-duration: #{motion.$md-sys-motion-expressive-default-spatial-duration};
|
|
42
|
+
--md-comp-snackbar-motion-duration-reverse: #{motion.$md-sys-motion-expressive-fast-spatial-duration};
|
|
43
|
+
--statelayer-color: var(--md-sys-color-inverse-primary);
|
|
44
|
+
--statelayer-opacity: 0;
|
|
45
|
+
|
|
35
46
|
box-sizing: border-box;
|
|
36
47
|
position: fixed;
|
|
37
48
|
display: flex;
|
|
38
49
|
align-items: center;
|
|
50
|
+
justify-content: space-between;
|
|
39
51
|
column-gap: 4px;
|
|
52
|
+
|
|
40
53
|
block-size: 0;
|
|
54
|
+
inline-size: max-content;
|
|
55
|
+
max-inline-size: min(layout.$md-sys-window-expanded-max, calc(100vw - (2 * var(--md-sys-layout-window-margin))));
|
|
56
|
+
min-inline-size: 300px;
|
|
57
|
+
|
|
41
58
|
inset: auto;
|
|
42
|
-
inset-block-end: calc(var(--md-comp-snackbar-margin) + env(safe-area-inset-bottom));
|
|
59
|
+
inset-block-end: calc(var(--md-comp-snackbar-margin, 24px) + env(safe-area-inset-bottom));
|
|
43
60
|
inset-inline-start: 50%;
|
|
44
61
|
margin: 0;
|
|
45
62
|
padding-block: 0;
|
|
46
|
-
padding-inline: 16px
|
|
63
|
+
padding-inline: var(--md-comp-snackbar-padding, 16px);
|
|
64
|
+
|
|
47
65
|
border: none;
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
box-shadow: var(--md-sys-elevation-level3);
|
|
66
|
+
outline: none;
|
|
67
|
+
border-radius: var(--md-comp-snackbar-container-shape, var(--md-sys-shape-corner-extra-small));
|
|
68
|
+
box-shadow: var(--md-comp-snackbar-container-elevation, var(--md-sys-elevation-level3));
|
|
69
|
+
background-color: var(--md-comp-snackbar-container-color, var(--md-sys-color-inverse-surface));
|
|
70
|
+
|
|
71
|
+
background-image: linear-gradient(rgb(from var(--statelayer-color) r g b / var(--statelayer-opacity)));
|
|
72
|
+
background-repeat: no-repeat;
|
|
73
|
+
background-size: 100%;
|
|
74
|
+
|
|
51
75
|
opacity: 0;
|
|
52
76
|
overflow-y: hidden;
|
|
53
|
-
translate: -50%
|
|
77
|
+
translate: calc(var(--md-comp-snackbar-dir-factor, 1) * -50%);
|
|
78
|
+
|
|
54
79
|
transition:
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
display
|
|
58
|
-
overlay
|
|
59
|
-
|
|
80
|
+
block-size var(--md-comp-snackbar-motion-duration-reverse) var(--md-comp-snackbar-motion-spatial),
|
|
81
|
+
opacity var(--md-comp-snackbar-motion-duration-reverse) linear,
|
|
82
|
+
display var(--md-comp-snackbar-motion-duration-reverse) linear allow-discrete,
|
|
83
|
+
overlay var(--md-comp-snackbar-motion-duration-reverse) linear allow-discrete,
|
|
84
|
+
--statelayer-opacity var(--md-comp-snackbar-motion-duration) linear;
|
|
85
|
+
|
|
86
|
+
&:hover {
|
|
87
|
+
--statelayer-color: var(--md-comp-snackbar-action-hover-state-layer-color, var(--md-sys-color-inverse-primary));
|
|
88
|
+
--statelayer-opacity: var(--md-comp-snackbar-action-hover-state-layer-opacity, var(--md-sys-state-hover-state-layer-opacity));
|
|
89
|
+
}
|
|
90
|
+
&:focus-visible {
|
|
91
|
+
--statelayer-color: var(--md-comp-snackbar-action-focus-state-layer-color, var(--md-sys-color-inverse-primary));
|
|
92
|
+
--statelayer-opacity: var(--md-comp-snackbar-action-focus-state-layer-opacity, var(--md-sys-state-focus-state-layer-opacity));
|
|
93
|
+
}
|
|
94
|
+
&:active {
|
|
95
|
+
--statelayer-color: var(--md-comp-snackbar-action-pressed-state-layer-color, var(--md-sys-color-inverse-primary));
|
|
96
|
+
--statelayer-opacity: var(--md-comp-snackbar-action-pressed-state-layer-opacity, var(--md-sys-state-pressed-state-layer-opacity));
|
|
97
|
+
}
|
|
98
|
+
|
|
60
99
|
&:popover-open {
|
|
61
|
-
block-size: var(--md-comp-snackbar-with-single-line-container-height);
|
|
100
|
+
block-size: var(--md-comp-snackbar-with-single-line-container-height, 48px);
|
|
62
101
|
opacity: 1;
|
|
63
102
|
transition:
|
|
64
|
-
|
|
65
|
-
|
|
103
|
+
block-size var(--md-comp-snackbar-motion-duration) var(--md-comp-snackbar-motion-spatial),
|
|
104
|
+
opacity var(--md-comp-snackbar-motion-duration-reverse) linear,
|
|
105
|
+
--statelayer-opacity var(--md-comp-snackbar-motion-duration) linear;
|
|
66
106
|
|
|
67
107
|
@starting-style {
|
|
68
108
|
block-size: 0;
|
|
69
109
|
opacity: 0;
|
|
70
110
|
}
|
|
71
111
|
}
|
|
112
|
+
&.micl-snackbar--two:popover-open {
|
|
113
|
+
block-size: var(--md-comp-snackbar-with-two-lines-container-height, 68px);
|
|
114
|
+
}
|
|
72
115
|
|
|
73
116
|
.micl-snackbar__supporting-text {
|
|
74
117
|
@include typography.body-medium;
|
|
118
|
+
display: flex;
|
|
119
|
+
flex-direction: column;
|
|
120
|
+
color: var(--md-comp-snackbar-supporting-text-color, var(--md-sys-color-inverse-on-surface));
|
|
121
|
+
}
|
|
75
122
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
&+ * {
|
|
79
|
-
margin-inline-start: 16px;
|
|
80
|
-
}
|
|
123
|
+
&:has(button) {
|
|
124
|
+
padding-inline-end: 4px;
|
|
81
125
|
}
|
|
126
|
+
|
|
82
127
|
button {
|
|
83
|
-
--statelayer-color: var(--md-sys-color-inverse-primary);
|
|
84
|
-
color: var(--md-sys-color-inverse-primary);
|
|
128
|
+
--statelayer-color: var(--md-comp-snackbar-action-label-text-color, var(--md-sys-color-inverse-primary));
|
|
129
|
+
color: var(--md-comp-snackbar-action-label-text-color, var(--md-sys-color-inverse-primary));
|
|
130
|
+
|
|
131
|
+
&:hover {
|
|
132
|
+
color: var(--md-comp-snackbar-action-hover-label-text-color, var(--md-sys-color-inverse-primary));
|
|
133
|
+
}
|
|
134
|
+
&:focus-visible {
|
|
135
|
+
color: var(--md-comp-snackbar-action-focus-label-text-color, var(--md-sys-color-inverse-primary));
|
|
136
|
+
}
|
|
137
|
+
&:active {
|
|
138
|
+
color: var(--md-comp-snackbar-action-pressed-label-text-color, var(--md-sys-color-inverse-primary));
|
|
139
|
+
}
|
|
85
140
|
}
|
|
86
141
|
button[class*='micl-iconbutton'] {
|
|
87
|
-
--statelayer-color: var(--md-sys-color-inverse-on-surface);
|
|
88
|
-
color: var(--md-sys-color-inverse-on-surface);
|
|
89
|
-
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
.micl-snackbarZ {
|
|
93
|
-
position: fixed;
|
|
94
|
-
display: grid;
|
|
95
|
-
grid-template-rows: 0fr;
|
|
96
|
-
align-content: flex-end;
|
|
97
|
-
inset: auto;
|
|
98
|
-
inset-block-end: 24px;
|
|
99
|
-
inset-block-end: calc(var(--md-comp-snackbar-margin) + env(safe-area-inset-bottom));
|
|
100
|
-
inset-inline-start: 50%;
|
|
101
|
-
margin: 0;
|
|
102
|
-
padding: 0;
|
|
103
|
-
border: none;
|
|
104
|
-
background: transparent;
|
|
105
|
-
opacity: 0;
|
|
106
|
-
overflow: visible;
|
|
107
|
-
translate: -50% 0;
|
|
108
|
-
transition:
|
|
109
|
-
opacity 300ms ease-out,
|
|
110
|
-
grid-template-rows 0s linear 300ms,
|
|
111
|
-
display 300ms allow-discrete,
|
|
112
|
-
overlay 300ms allow-discrete;
|
|
142
|
+
--statelayer-color: var(--md-comp-snackbar-icon-color, var(--md-sys-color-inverse-on-surface));
|
|
143
|
+
color: var(--md-comp-snackbar-icon-color, var(--md-sys-color-inverse-on-surface));
|
|
144
|
+
font-size: var(--md-comp-snackbar-icon-size, var(--md-sys-icon-size, 24px));
|
|
113
145
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
grid-template-rows: 0fr;
|
|
123
|
-
opacity: 0;
|
|
146
|
+
&:hover {
|
|
147
|
+
color: var(--md-comp-snackbar-icon-hover-icon-color, var(--md-sys-color-inverse-on-surface));
|
|
148
|
+
}
|
|
149
|
+
&:focus-visible {
|
|
150
|
+
color: var(--md-comp-snackbar-icon-focus-icon-color, var(--md-sys-color-inverse-on-surface));
|
|
151
|
+
}
|
|
152
|
+
&:active {
|
|
153
|
+
color: var(--md-comp-snackbar-icon-pressed-icon-color, var(--md-sys-color-inverse-on-surface));
|
|
124
154
|
}
|
|
125
155
|
}
|
|
156
|
+
}
|
|
126
157
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
background: darkgray;
|
|
132
|
-
color: white;
|
|
133
|
-
overflow: hidden;
|
|
134
|
-
|
|
135
|
-
.micl-snackbar__content {
|
|
136
|
-
display: flex;
|
|
137
|
-
padding: 16px;
|
|
138
|
-
justify-content: space-between;
|
|
139
|
-
align-items: center;
|
|
140
|
-
}
|
|
158
|
+
@media (max-width: layout.$md-sys-window-compact-max) {
|
|
159
|
+
.micl-snackbar {
|
|
160
|
+
inline-size: calc(100vw - (2 * var(--md-sys-layout-window-margin)));
|
|
161
|
+
min-inline-size: auto;
|
|
141
162
|
}
|
|
142
163
|
}
|
|
@@ -21,36 +21,98 @@
|
|
|
21
21
|
|
|
22
22
|
export const snackbarSelector = '.micl-snackbar';
|
|
23
23
|
|
|
24
|
-
|
|
24
|
+
interface SnackbarState {
|
|
25
|
+
timeoutid?: number;
|
|
26
|
+
delay: number;
|
|
27
|
+
boundHandleToggle: (event: Event) => void;
|
|
28
|
+
boundClearTimer: () => void;
|
|
29
|
+
boundHandleMouseLeave: () => void;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const snackbarStates = new WeakMap<HTMLElement, SnackbarState>();
|
|
33
|
+
|
|
34
|
+
const clearTimer = (element: HTMLElement): void =>
|
|
35
|
+
{
|
|
36
|
+
const state = snackbarStates.get(element);
|
|
37
|
+
if (state && state.timeoutid) {
|
|
38
|
+
clearTimeout(state.timeoutid);
|
|
39
|
+
state.timeoutid = undefined;
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
const startTimer = (element: HTMLElement): void =>
|
|
25
44
|
{
|
|
26
|
-
|
|
27
|
-
|
|
45
|
+
clearTimer(element);
|
|
46
|
+
const state = snackbarStates.get(element);
|
|
47
|
+
if (!state) return;
|
|
48
|
+
|
|
49
|
+
state.timeoutid = window.setTimeout(() => {
|
|
50
|
+
if (element.isConnected && element.matches(':popover-open')) {
|
|
51
|
+
element.hidePopover();
|
|
52
|
+
}
|
|
53
|
+
}, state.delay);
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
export default
|
|
57
|
+
{
|
|
58
|
+
initialize: (element: HTMLElement): void =>
|
|
59
|
+
{
|
|
60
|
+
if (!element.matches(snackbarSelector) || element.dataset.miclinitialized) {
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const delayAttr = element.dataset.micldelay;
|
|
65
|
+
const delay = delayAttr ? parseInt(delayAttr, 10) : 0;
|
|
66
|
+
|
|
67
|
+
if (isNaN(delay) || delay <= 0) return;
|
|
68
|
+
|
|
69
|
+
element.dataset.miclinitialized = '1';
|
|
70
|
+
|
|
71
|
+
// Create bound handlers specifically for THIS snackbar.
|
|
72
|
+
const boundClearTimer = () => clearTimer(element);
|
|
73
|
+
|
|
74
|
+
const boundHandleToggle = (event: Event): void =>
|
|
28
75
|
{
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
) {
|
|
33
|
-
return;
|
|
76
|
+
const toggleEvent = event as ToggleEvent;
|
|
77
|
+
if (toggleEvent.newState === 'open') {
|
|
78
|
+
startTimer(element);
|
|
34
79
|
}
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
delete element.dataset.micltimeoutid;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
if ((event as ToggleEvent).oldState === 'closed') {
|
|
48
|
-
// The snackbar has just opened.
|
|
49
|
-
timeoutid = window.setTimeout(() => { element.hidePopover(); }, delay);
|
|
50
|
-
element.dataset.micltimeoutid = `${timeoutid}`;
|
|
51
|
-
}
|
|
52
|
-
});
|
|
80
|
+
else {
|
|
81
|
+
clearTimer(element);
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
const boundHandleMouseLeave = (): void =>
|
|
86
|
+
{
|
|
87
|
+
if (element.matches(':popover-open')) {
|
|
88
|
+
startTimer(element);
|
|
53
89
|
}
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
// Save these references in the WeakMap.
|
|
93
|
+
snackbarStates.set(element, {
|
|
94
|
+
delay,
|
|
95
|
+
boundHandleToggle,
|
|
96
|
+
boundClearTimer,
|
|
97
|
+
boundHandleMouseLeave
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
element.addEventListener('toggle', boundHandleToggle);
|
|
101
|
+
element.addEventListener('mouseenter', boundClearTimer);
|
|
102
|
+
element.addEventListener('mouseleave', boundHandleMouseLeave);
|
|
103
|
+
},
|
|
104
|
+
|
|
105
|
+
cleanup: (element: HTMLElement): void =>
|
|
106
|
+
{
|
|
107
|
+
const state = snackbarStates.get(element);
|
|
108
|
+
|
|
109
|
+
if (state) {
|
|
110
|
+
clearTimer(element);
|
|
111
|
+
element.removeEventListener('toggle', state.boundHandleToggle);
|
|
112
|
+
element.removeEventListener('mouseenter', state.boundClearTimer);
|
|
113
|
+
element.removeEventListener('mouseleave', state.boundHandleMouseLeave);
|
|
114
|
+
snackbarStates.delete(element);
|
|
54
115
|
}
|
|
55
|
-
|
|
56
|
-
}
|
|
116
|
+
delete element.dataset.miclinitialized;
|
|
117
|
+
}
|
|
118
|
+
};
|
|
@@ -83,7 +83,8 @@ By default, the layout is **vertical**. To switch to a **horizontal** layout (si
|
|
|
83
83
|
To allow users to toggle between the text inputs and the analog dial, add a button to the `micl-dialog__actions` container:
|
|
84
84
|
|
|
85
85
|
- Class: `micl-timepicker__inputmode`
|
|
86
|
-
- Data Attribute: `data-
|
|
86
|
+
- Data Attribute: `data-miclicon="keyboard"` (defines the icon to show).
|
|
87
|
+
- Data Attribute: `data-micliconselected="schedule"` (defines the icon to show when toggled).
|
|
87
88
|
|
|
88
89
|
### Integration
|
|
89
90
|
You can trigger the Time picker component from standard input fields or buttons.
|
|
@@ -299,9 +299,6 @@ export default (() =>
|
|
|
299
299
|
|
|
300
300
|
mode?.addEventListener('click', () =>
|
|
301
301
|
{
|
|
302
|
-
const icon = mode.textContent;
|
|
303
|
-
mode.textContent = mode.dataset.miclalt || icon;
|
|
304
|
-
mode.dataset.miclalt = icon;
|
|
305
302
|
dial?.classList.toggle('micl-hidden');
|
|
306
303
|
inputs.forEach(input =>
|
|
307
304
|
{
|