material-inspired-component-library 1.2.1 → 1.3.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 +12 -2
- package/components/bottomsheet/index.scss +18 -16
- package/components/button/README.md +1 -1
- package/components/button/index.scss +57 -80
- package/components/card/index.scss +33 -31
- package/components/checkbox/index.scss +5 -9
- package/components/dialog/index.scss +38 -29
- package/components/iconbutton/README.md +1 -1
- package/components/iconbutton/index.scss +51 -81
- package/components/list/index.scss +45 -33
- package/components/list/index.ts +10 -9
- package/components/menu/README.md +67 -4
- package/components/menu/index.scss +43 -39
- package/components/menu/index.ts +47 -16
- package/components/navigationrail/README.md +99 -0
- package/components/navigationrail/index.scss +157 -0
- package/components/radio/index.scss +28 -15
- package/components/select/index.scss +4 -12
- package/components/sidesheet/index.scss +21 -19
- package/components/switch/index.scss +11 -8
- package/components/textfield/index.scss +16 -7
- package/dist/bottomsheet.css +1 -1
- package/dist/button.css +1 -1
- package/dist/card.css +1 -1
- package/dist/checkbox.css +1 -1
- package/dist/components/menu/index.d.ts +0 -11
- package/dist/dialog.css +1 -1
- package/dist/iconbutton.css +1 -1
- package/dist/list.css +1 -1
- package/dist/menu.css +1 -1
- package/dist/micl.css +1 -1
- package/dist/micl.js +1 -1
- package/dist/navigationrail.css +1 -0
- package/dist/navigationrail.js +1 -0
- package/dist/radio.css +1 -1
- package/dist/select.css +1 -1
- package/dist/sidesheet.css +1 -1
- package/dist/slider.css +1 -1
- package/dist/switch.css +1 -1
- package/dist/textfield.css +1 -1
- package/docs/dialog.html +4 -4
- package/docs/docs.js +2 -2
- package/docs/index.html +3 -1
- package/docs/menu.html +183 -3
- package/docs/micl.css +1 -1
- package/docs/micl.js +1 -1
- package/docs/navigationrail.html +75 -0
- package/micl.ts +10 -8
- package/package.json +7 -7
- package/styles/statelayer.scss +14 -0
- package/styles.scss +1 -1
- package/webpack.config.js +37 -0
- package/styles/ripple.scss +0 -50
|
@@ -4,7 +4,7 @@ This component implements the [Material Design 3 Expressive Menu](https://m3.mat
|
|
|
4
4
|
## Basic Usage
|
|
5
5
|
|
|
6
6
|
### HTML
|
|
7
|
-
The Menu component is an extension of the [List component](../list/README.md). It consists of a `<nav>` element with the `micl-menu` class, which acts as the container for a `<ul>` with the `micl-list` class. The menu can be opened and closed using a control element with the `popovertarget` attribute.
|
|
7
|
+
The Menu component is an extension of the [List component](../list/README.md). It consists of a `<nav>` element with the `micl-menu` class, which acts as the container for a `<ul>` with the `micl-list` class. The menu can be opened and closed using a control element with the `popovertarget` attribute, where the value of the attribute matches the `id` of the menu's `<nav>` element.
|
|
8
8
|
|
|
9
9
|
```HTML
|
|
10
10
|
<nav id="mymenu" class="micl-menu" popover>
|
|
@@ -17,7 +17,7 @@ The Menu component is an extension of the [List component](../list/README.md). I
|
|
|
17
17
|
</ul>
|
|
18
18
|
</nav>
|
|
19
19
|
|
|
20
|
-
<button type="button" popovertarget="mymenu">Open
|
|
20
|
+
<button type="button" popovertarget="mymenu">Open Menu</button>
|
|
21
21
|
```
|
|
22
22
|
|
|
23
23
|
### CSS
|
|
@@ -68,17 +68,18 @@ Since the Menu component is based on the **List component**, all of its list ite
|
|
|
68
68
|
```
|
|
69
69
|
|
|
70
70
|
**Example: A menu with a link**
|
|
71
|
+
|
|
71
72
|
To make a menu item function as a link, wrap its content in an `<a>` tag and set the `tabindex` to `-1`. This ensures the link is clickable but does not interfere with menu navigation.
|
|
72
73
|
|
|
73
74
|
```HTML
|
|
74
75
|
<nav id="mymenu" class="micl-menu" popover>
|
|
75
76
|
<ul class="micl-list">
|
|
76
|
-
<li class="micl-list-item-two
|
|
77
|
+
<li class="micl-list-item-two" tabindex="0">
|
|
77
78
|
<a href="https://www.nytimes.com" tabindex="-1">
|
|
78
79
|
<span class="micl-list-item__icon material-symbols-outlined" aria-hidden="true">newspaper</span>
|
|
79
80
|
<span class="micl-list-item__text">
|
|
80
81
|
<span class="micl-list-item__headline">The New York Times</span>
|
|
81
|
-
<span class="micl-list-item__supporting-text">
|
|
82
|
+
<span class="micl-list-item__supporting-text">Open the front page of The New York Times</span>
|
|
82
83
|
</span>
|
|
83
84
|
</a>
|
|
84
85
|
</li>
|
|
@@ -86,9 +87,71 @@ To make a menu item function as a link, wrap its content in an `<a>` tag and set
|
|
|
86
87
|
</nav>
|
|
87
88
|
```
|
|
88
89
|
|
|
90
|
+
**Example: A menu with a submenu**
|
|
91
|
+
|
|
92
|
+
A menu item may trigger opening a submenu when invoked by a button. Wrap the menu item content in a `<button>` tag, set its `tabindex` to `-1` and add the `popovertarget` attribute that points to the submenu. The button acts then as the control element for the submenu.
|
|
93
|
+
|
|
94
|
+
```HTML
|
|
95
|
+
<nav id="mymenu" class="micl-menu" popover>
|
|
96
|
+
<ul class="micl-list">
|
|
97
|
+
<li class="micl-list-item-one" tabindex="0">
|
|
98
|
+
<button popovertarget="mysubmenu" tabindex="-1">
|
|
99
|
+
<span class="micl-list-item__text">
|
|
100
|
+
<span class="micl-list-item__headline">Item 1</span>
|
|
101
|
+
</span>
|
|
102
|
+
<span class="micl-list-item__icon material-symbols-outlined">arrow_right</span>
|
|
103
|
+
</button>
|
|
104
|
+
<nav id="mysubmenu" class="micl-menu" popover>
|
|
105
|
+
<ul class="micl-list">
|
|
106
|
+
<li class="micl-list-item-one" tabindex="0">
|
|
107
|
+
<span class="micl-list-item__text">
|
|
108
|
+
<span class="micl-list-item__headline">Item 1-1</span>
|
|
109
|
+
</span>
|
|
110
|
+
</li>
|
|
111
|
+
<li class="micl-list-item-one" tabindex="0">
|
|
112
|
+
<span class="micl-list-item__text">
|
|
113
|
+
<span class="micl-list-item__headline">Item 1-2</span>
|
|
114
|
+
</span>
|
|
115
|
+
</li>
|
|
116
|
+
</ul>
|
|
117
|
+
</nav>
|
|
118
|
+
</li>
|
|
119
|
+
<li class="micl-list-item-one" tabindex="0">
|
|
120
|
+
<span class="micl-list-item__text">
|
|
121
|
+
<span class="micl-list-item__headline">Item 2</span>
|
|
122
|
+
</span>
|
|
123
|
+
</li>
|
|
124
|
+
</ul>
|
|
125
|
+
</nav>
|
|
126
|
+
```
|
|
127
|
+
|
|
89
128
|
Adding the `micl-list-item--disabled` class to a menu item causes the item to be displayed in a disabled state.
|
|
90
129
|
|
|
91
130
|
Add the `micl-list-item__divider` class to a menu item to create a divider between the item and the previous menu item.
|
|
92
131
|
|
|
132
|
+
## Customizations
|
|
133
|
+
You can customize the appearance of the Menu 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 menus.
|
|
134
|
+
|
|
135
|
+
| Variable name | Default Value | Description |
|
|
136
|
+
| ------------- | ----- | ----------- |
|
|
137
|
+
| --md-sys-menu-width-max | 280px | The maximum width allowed for a menu |
|
|
138
|
+
| --md-sys-menu-width-min | 112px | The minimum allowed width for a menu |
|
|
139
|
+
|
|
140
|
+
**Example: Changing the maximum width**
|
|
141
|
+
|
|
142
|
+
```HTML
|
|
143
|
+
<div style="--md-sys-menu-width-max:320px">
|
|
144
|
+
<nav id="mymenu" class="micl-menu" popover>
|
|
145
|
+
<ul class="micl-list">
|
|
146
|
+
<li class="micl-list-item-one" tabindex="0">
|
|
147
|
+
<span class="micl-list-item__text">
|
|
148
|
+
<span class="micl-list-item__headline">Menu item</span>
|
|
149
|
+
</span>
|
|
150
|
+
</li>
|
|
151
|
+
</ul>
|
|
152
|
+
</nav>
|
|
153
|
+
</div>
|
|
154
|
+
```
|
|
155
|
+
|
|
93
156
|
## Compatibility
|
|
94
157
|
This component uses **popover anchor positioning** to place the menu next to its invoker. This is a modern CSS feature that may not be fully supported in all browsers. To ensure the menu works in browsers that do not support anchor positioning, wrap the menu and its invoker in a `<div>` element with `position:relative`. Please check [Browser compatibility](https://developer.mozilla.org/en-US/docs/Web/CSS/anchor#browser_compatibility) for details.
|
|
@@ -21,19 +21,25 @@
|
|
|
21
21
|
|
|
22
22
|
@use '../../styles/elevation';
|
|
23
23
|
@use '../../styles/motion';
|
|
24
|
-
@use '../../styles/ripple';
|
|
25
24
|
@use '../../styles/shapes';
|
|
26
|
-
|
|
25
|
+
|
|
26
|
+
:root {
|
|
27
|
+
--md-sys-menu-motion-spatial: #{motion.$md-sys-motion-expressive-default-spatial};
|
|
28
|
+
--md-sys-menu-motion-duration: #{motion.$md-sys-motion-expressive-default-spatial-duration};
|
|
29
|
+
--md-sys-menu-motion-duration-reverse: #{motion.$md-sys-motion-expressive-fast-spatial-duration};
|
|
30
|
+
--md-sys-menu-width-max: 280px;
|
|
31
|
+
--md-sys-menu-width-min: 112px;
|
|
32
|
+
}
|
|
27
33
|
|
|
28
34
|
.micl-menu[popover] {
|
|
29
35
|
position: absolute;
|
|
30
36
|
inset: unset;
|
|
31
37
|
inset-block-start: anchor(end);
|
|
32
38
|
inset-inline-start: anchor(start);
|
|
33
|
-
position-try
|
|
34
|
-
min-width:
|
|
35
|
-
max-width:
|
|
36
|
-
padding
|
|
39
|
+
position-try: most-block-size flip-block, flip-inline;
|
|
40
|
+
min-width: var(--md-sys-menu-width-min);
|
|
41
|
+
max-width: var(--md-sys-menu-width-max);
|
|
42
|
+
padding: 0;
|
|
37
43
|
border: none;
|
|
38
44
|
border-radius: var(--md-sys-shape-corner-extra-small);
|
|
39
45
|
background-color: var(--md-sys-color-surface-container);
|
|
@@ -41,33 +47,25 @@
|
|
|
41
47
|
opacity: 0;
|
|
42
48
|
overflow: hidden;
|
|
43
49
|
transform: scaleY(0);
|
|
44
|
-
transform-origin: top
|
|
50
|
+
transform-origin: left top;
|
|
45
51
|
transition:
|
|
46
|
-
opacity var(--md-sys-motion-duration-
|
|
47
|
-
transform var(--md-sys-motion-duration-
|
|
48
|
-
overlay var(--md-sys-motion-duration-
|
|
49
|
-
display var(--md-sys-motion-duration-
|
|
52
|
+
opacity var(--md-sys-menu-motion-duration-reverse) linear,
|
|
53
|
+
transform var(--md-sys-menu-motion-duration-reverse) var(--md-sys-menu-motion-spatial),
|
|
54
|
+
overlay var(--md-sys-menu-motion-duration-reverse) linear allow-discrete,
|
|
55
|
+
display var(--md-sys-menu-motion-duration-reverse) linear allow-discrete;
|
|
50
56
|
|
|
51
57
|
&:popover-open {
|
|
52
58
|
opacity: 1;
|
|
53
59
|
transform: scaleY(1);
|
|
54
60
|
transition:
|
|
55
|
-
opacity var(--md-sys-motion-duration
|
|
56
|
-
transform var(--md-sys-motion-duration
|
|
57
|
-
overlay var(--md-sys-motion-duration
|
|
58
|
-
display var(--md-sys-motion-duration
|
|
59
|
-
|
|
60
|
-
@position-try-on {
|
|
61
|
-
transform-origin: bottom left;
|
|
62
|
-
}
|
|
61
|
+
opacity var(--md-sys-menu-motion-duration) motion.$md-sys-motion-easing-emphasized-decelerate,
|
|
62
|
+
transform var(--md-sys-menu-motion-duration) var(--md-sys-menu-motion-spatial),
|
|
63
|
+
overlay var(--md-sys-menu-motion-duration) linear allow-discrete,
|
|
64
|
+
display var(--md-sys-menu-motion-duration) linear allow-discrete;
|
|
63
65
|
|
|
64
66
|
@starting-style {
|
|
65
67
|
opacity: 0;
|
|
66
68
|
transform: scaleY(0);
|
|
67
|
-
|
|
68
|
-
@position-try-on {
|
|
69
|
-
transform-origin: bottom left;
|
|
70
|
-
}
|
|
71
69
|
}
|
|
72
70
|
&::backdrop {
|
|
73
71
|
background-color: rgba(0, 0, 0, 0.2);
|
|
@@ -80,12 +78,13 @@
|
|
|
80
78
|
&::backdrop {
|
|
81
79
|
background-color: rgba(0, 0, 0, 0);
|
|
82
80
|
transition:
|
|
83
|
-
background-color var(--md-sys-motion-duration
|
|
84
|
-
overlay var(--md-sys-motion-duration
|
|
85
|
-
display var(--md-sys-motion-duration
|
|
81
|
+
background-color var(--md-sys-menu-motion-duration) linear,
|
|
82
|
+
overlay var(--md-sys-menu-motion-duration) linear allow-discrete,
|
|
83
|
+
display var(--md-sys-menu-motion-duration) linear allow-discrete;
|
|
86
84
|
}
|
|
87
85
|
|
|
88
|
-
.micl-list {
|
|
86
|
+
&> .micl-list {
|
|
87
|
+
--md-sys-list-padding: 4px;
|
|
89
88
|
--md-sys-list-item-one-height: 48px;
|
|
90
89
|
--md-sys-list-item-one-padding: 0;
|
|
91
90
|
--md-sys-list-item-two-padding: 0;
|
|
@@ -95,18 +94,6 @@
|
|
|
95
94
|
.micl-list-item-one,
|
|
96
95
|
.micl-list-item-two,
|
|
97
96
|
.micl-list-item-three {
|
|
98
|
-
&:not(.micl-list-item--disabled) {
|
|
99
|
-
--miclripple: 1;
|
|
100
|
-
|
|
101
|
-
@include ripple.effect;
|
|
102
|
-
|
|
103
|
-
cursor: pointer;
|
|
104
|
-
|
|
105
|
-
&:focus-visible {
|
|
106
|
-
outline-offset: calc(-1 * var(--md-sys-state-focus-indicator-thickness));
|
|
107
|
-
z-index: 1;
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
97
|
&.micl-list-item__divider {
|
|
111
98
|
position: relative;
|
|
112
99
|
margin-block-start: 16px;
|
|
@@ -122,6 +109,23 @@
|
|
|
122
109
|
border-top: var(--md-sys-divider-thickness) solid var(--md-sys-divider-color);
|
|
123
110
|
}
|
|
124
111
|
}
|
|
112
|
+
&> .micl-menu[popover] {
|
|
113
|
+
transform: scaleX(0);
|
|
114
|
+
|
|
115
|
+
&:popover-open {
|
|
116
|
+
transform: scaleX(1);
|
|
117
|
+
|
|
118
|
+
@starting-style {
|
|
119
|
+
transform: scaleX(0);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
&:popover-open {
|
|
123
|
+
&::backdrop {
|
|
124
|
+
background-color: rgba(0, 0, 0, 0);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
125
128
|
}
|
|
126
129
|
}
|
|
127
130
|
}
|
|
131
|
+
|
package/components/menu/index.ts
CHANGED
|
@@ -21,27 +21,15 @@
|
|
|
21
21
|
|
|
22
22
|
export const menuSelector = '.micl-menu[popover]';
|
|
23
23
|
|
|
24
|
-
/**
|
|
25
|
-
* Set the origin for menu transformations just before transitions start.
|
|
26
|
-
* By default, the origin is "top left" (the menu opens just below the invoker, left aligned),
|
|
27
|
-
* but could also be "top right", "bottom left" or "bottom right".
|
|
28
|
-
* When the browser needs to apply a position-try-fallbacks, because there is not enough space
|
|
29
|
-
* for the menu in the default location, then the reverse transformation will be applied from
|
|
30
|
-
* the wrong origin.
|
|
31
|
-
* Therefore, when the menu is open, calculate the transformation origin just before the
|
|
32
|
-
* transitions start. When the menu is closed, do the same just after the 'display:none' has
|
|
33
|
-
* been removed by the browser (the 'toggle' event has then been triggered).
|
|
34
|
-
*/
|
|
35
24
|
export default (() =>
|
|
36
25
|
{
|
|
37
26
|
const getOrigin = (invoker: Element, popover: Element): string =>
|
|
38
27
|
{
|
|
39
|
-
const
|
|
40
|
-
|
|
41
|
-
oldOrigin = window.getComputedStyle(popover).getPropertyValue('transform-origin');
|
|
28
|
+
const invokerRect = invoker.getBoundingClientRect(),
|
|
29
|
+
popoverRect = popover.getBoundingClientRect();
|
|
42
30
|
|
|
43
|
-
return ((
|
|
44
|
-
((
|
|
31
|
+
return ((invokerRect.x > popoverRect.x) ? 'right ' : 'left ') +
|
|
32
|
+
((invokerRect.y > popoverRect.y) ? 'bottom' : 'top');
|
|
45
33
|
};
|
|
46
34
|
|
|
47
35
|
return {
|
|
@@ -72,6 +60,49 @@ export default (() =>
|
|
|
72
60
|
}
|
|
73
61
|
});
|
|
74
62
|
|
|
63
|
+
element.querySelectorAll<HTMLButtonElement>(
|
|
64
|
+
':scope > ul.micl-list > li > button[popovertarget]'
|
|
65
|
+
).forEach(submenuinvoker =>
|
|
66
|
+
{
|
|
67
|
+
if (submenuinvoker.popoverTargetElement?.matches('.micl-menu[popover]')) {
|
|
68
|
+
let hoverTimeout: any,
|
|
69
|
+
id = `--${submenuinvoker.popoverTargetElement.id}`;
|
|
70
|
+
|
|
71
|
+
if (submenuinvoker.popoverTargetElement instanceof HTMLElement) {
|
|
72
|
+
submenuinvoker.style.setProperty('anchor-name', id);
|
|
73
|
+
submenuinvoker.popoverTargetElement.style.insetBlockStart = `anchor(${id} start)`;
|
|
74
|
+
submenuinvoker.popoverTargetElement.style.insetInlineStart = `anchor(${id} end)`;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
submenuinvoker.addEventListener('mouseenter', () =>
|
|
78
|
+
{
|
|
79
|
+
if (submenuinvoker.popoverTargetElement instanceof HTMLElement) {
|
|
80
|
+
submenuinvoker.popoverTargetElement.showPopover();
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
submenuinvoker.addEventListener('mouseleave', () =>
|
|
84
|
+
{
|
|
85
|
+
hoverTimeout = setTimeout(() => {
|
|
86
|
+
if (
|
|
87
|
+
submenuinvoker.popoverTargetElement instanceof HTMLElement
|
|
88
|
+
&& !submenuinvoker.popoverTargetElement.matches(':hover')
|
|
89
|
+
) {
|
|
90
|
+
submenuinvoker.popoverTargetElement.hidePopover();
|
|
91
|
+
}
|
|
92
|
+
}, 100);
|
|
93
|
+
});
|
|
94
|
+
submenuinvoker.popoverTargetElement.addEventListener('mouseenter', () =>
|
|
95
|
+
{
|
|
96
|
+
clearTimeout(hoverTimeout);
|
|
97
|
+
});
|
|
98
|
+
submenuinvoker.popoverTargetElement.addEventListener('mouseleave', () =>
|
|
99
|
+
{
|
|
100
|
+
if (submenuinvoker.popoverTargetElement instanceof HTMLElement) {
|
|
101
|
+
submenuinvoker.popoverTargetElement.hidePopover();
|
|
102
|
+
}
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
});
|
|
75
106
|
}
|
|
76
107
|
};
|
|
77
108
|
})();
|
|
@@ -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 "material-inspired-component-library/dist/sidesheet";
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
### JavaScript
|
|
34
|
+
No custom JavaScript 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,157 @@
|
|
|
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/elevation';
|
|
23
|
+
@use '../../styles/motion';
|
|
24
|
+
@use '../../styles/shapes';
|
|
25
|
+
@use '../../styles/typography';
|
|
26
|
+
|
|
27
|
+
dialog.micl-navigationrail {
|
|
28
|
+
--md-sys-navigationrail-width: 256px;
|
|
29
|
+
--md-sys-navigationrail-maxwidth: 400px;
|
|
30
|
+
--md-sys-navigationrail-spring-buffer: 200px;
|
|
31
|
+
--md-sys-navigationrail-padding-standard: 24px;
|
|
32
|
+
--md-sys-navigationrail-padding-modal: 16px;
|
|
33
|
+
--md-sys-navigationrail-motion-spatial: #{motion.$md-sys-motion-expressive-slow-spatial};
|
|
34
|
+
--md-sys-navigationrail-motion-duration: #{motion.$md-sys-motion-expressive-slow-spatial-duration};
|
|
35
|
+
--md-sys-navigationrail-motion-duration-reverse: #{motion.$md-sys-motion-expressive-default-spatial-duration};
|
|
36
|
+
|
|
37
|
+
box-sizing: border-box;
|
|
38
|
+
display: none;
|
|
39
|
+
flex-direction: column;
|
|
40
|
+
justify-content: space-between;
|
|
41
|
+
min-width: calc(var(--md-sys-navigationrail-width) + var(--md-sys-navigationrail-spring-buffer));
|
|
42
|
+
max-width: calc(var(--md-sys-navigationrail-maxwidth) + var(--md-sys-navigationrail-spring-buffer));
|
|
43
|
+
height: 100%;
|
|
44
|
+
min-height: 100%;
|
|
45
|
+
margin-block: 0;
|
|
46
|
+
margin-inline-start: calc(-1 * (var(--md-sys-navigationrail-maxwidth) + var(--md-sys-navigationrail-spring-buffer)));
|
|
47
|
+
margin-inline-end: auto;
|
|
48
|
+
padding-block-start: 10px;
|
|
49
|
+
padding-block-end: var(--md-sys-navigationrail-padding-standard);
|
|
50
|
+
padding-inline-start: var(--md-sys-navigationrail-spring-buffer);
|
|
51
|
+
padding-inline-end: 0;
|
|
52
|
+
background-color: var(--md-sys-color-surface);
|
|
53
|
+
border: none;
|
|
54
|
+
box-shadow: var(--md-sys-elevation-level0);
|
|
55
|
+
opacity: 0;
|
|
56
|
+
overflow: hidden;
|
|
57
|
+
transition:
|
|
58
|
+
opacity var(--md-sys-navigationrail-motion-duration-reverse) linear,
|
|
59
|
+
margin-inline-start var(--md-sys-navigationrail-motion-duration-reverse) var(--md-sys-navigationrail-motion-spatial),
|
|
60
|
+
overlay var(--md-sys-navigationrail-motion-duration-reverse) linear allow-discrete,
|
|
61
|
+
display var(--md-sys-navigationrail-motion-duration-reverse) linear allow-discrete;
|
|
62
|
+
|
|
63
|
+
.micl-navigationrail__headline {
|
|
64
|
+
display: flex;
|
|
65
|
+
align-items: center;
|
|
66
|
+
padding-inline-start: 12px;
|
|
67
|
+
padding-inline-end: 24px;
|
|
68
|
+
column-gap: 8px;
|
|
69
|
+
color: var(--md-sys-color-on-surface-variant);
|
|
70
|
+
|
|
71
|
+
h1, h2, h3, h4, h5, h6, .micl-heading {
|
|
72
|
+
@include typography.title-large;
|
|
73
|
+
|
|
74
|
+
flex: 1 2 fit-content;
|
|
75
|
+
margin: 0;
|
|
76
|
+
overflow: hidden;
|
|
77
|
+
text-overflow: ellipsis;
|
|
78
|
+
white-space: nowrap;
|
|
79
|
+
user-select: none;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
.micl-navigationrail__content {
|
|
83
|
+
box-sizing: border-box;
|
|
84
|
+
display: flex;
|
|
85
|
+
flex-direction: column;
|
|
86
|
+
flex: 1 1 auto;
|
|
87
|
+
align-items: flex-start;
|
|
88
|
+
row-gap: 8px;
|
|
89
|
+
width: 100%;
|
|
90
|
+
max-width: 100%;
|
|
91
|
+
padding-inline: var(--md-sys-navigationrail-padding-standard);
|
|
92
|
+
overflow: hidden auto;
|
|
93
|
+
}
|
|
94
|
+
.micl-navigationrail__actions {
|
|
95
|
+
box-sizing: border-box;
|
|
96
|
+
display: flex;
|
|
97
|
+
align-items: center;
|
|
98
|
+
justify-content: start;
|
|
99
|
+
width: 100%;
|
|
100
|
+
max-width: 100%;
|
|
101
|
+
height: calc(72px - var(--md-sys-navigationrail-padding-standard));
|
|
102
|
+
margin: auto 0 0 0;
|
|
103
|
+
padding-block-start: 16px;
|
|
104
|
+
overflow: hidden;
|
|
105
|
+
border-block-start: var(--md-sys-divider-thickness) solid var(--md-sys-divider-color);
|
|
106
|
+
|
|
107
|
+
button:active,
|
|
108
|
+
button:focus-visible,
|
|
109
|
+
button:hover {
|
|
110
|
+
color: var(--md-sys-color-primary);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
&::backdrop {
|
|
115
|
+
background-color: rgba(0, 0, 0, 0);
|
|
116
|
+
transition:
|
|
117
|
+
background-color var(--md-sys-navigationrail-motion-duration) linear,
|
|
118
|
+
overlay var(--md-sys-navigationrail-motion-duration) linear allow-discrete,
|
|
119
|
+
display var(--md-sys-navigationrail-motion-duration) linear allow-discrete;
|
|
120
|
+
}
|
|
121
|
+
&[open] {
|
|
122
|
+
background-color: var(--md-sys-color-surface-container-low);
|
|
123
|
+
border-start-end-radius: var(--md-sys-shape-corner-large);
|
|
124
|
+
border-end-end-radius: var(--md-sys-shape-corner-large);
|
|
125
|
+
box-shadow: var(--md-sys-elevation-level1);
|
|
126
|
+
|
|
127
|
+
.micl-navigationrail__headline {
|
|
128
|
+
padding-inline-end: 4px;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
&:popover-open {
|
|
132
|
+
border-inline-end: var(--md-sys-divider-thickness) solid var(--md-sys-divider-color);
|
|
133
|
+
}
|
|
134
|
+
&:popover-open,
|
|
135
|
+
&[open] {
|
|
136
|
+
display: flex;
|
|
137
|
+
margin-inline-start: calc(-1 * var(--md-sys-navigationrail-spring-buffer));
|
|
138
|
+
opacity: 1;
|
|
139
|
+
transition:
|
|
140
|
+
opacity var(--md-sys-navigationrail-motion-duration) linear,
|
|
141
|
+
margin-inline-start var(--md-sys-navigationrail-motion-duration) var(--md-sys-navigationrail-motion-spatial),
|
|
142
|
+
overlay var(--md-sys-navigationrail-motion-duration) linear allow-discrete,
|
|
143
|
+
display var(--md-sys-navigationrail-motion-duration) linear allow-discrete;
|
|
144
|
+
|
|
145
|
+
@starting-style {
|
|
146
|
+
margin-inline-start: calc(-1 * (var(--md-sys-navigationrail-maxwidth) + var(--md-sys-navigationrail-spring-buffer)));
|
|
147
|
+
opacity: 0;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
&[open]::backdrop {
|
|
151
|
+
background-color: rgba(0, 0, 0, 0.2);
|
|
152
|
+
|
|
153
|
+
@starting-style {
|
|
154
|
+
background-color: rgba(0, 0, 0, 0);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|