@vaadin/side-nav 24.1.0-alpha9
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/LICENSE +190 -0
- package/README.md +76 -0
- package/enable.js +3 -0
- package/package.json +55 -0
- package/src/vaadin-side-nav-base-styles.js +104 -0
- package/src/vaadin-side-nav-item.d.ts +96 -0
- package/src/vaadin-side-nav-item.js +198 -0
- package/src/vaadin-side-nav.d.ts +81 -0
- package/src/vaadin-side-nav.js +140 -0
- package/theme/lumo/vaadin-side-nav-item.js +7 -0
- package/theme/lumo/vaadin-side-nav-styles.js +164 -0
- package/theme/lumo/vaadin-side-nav.js +7 -0
- package/theme/material/vaadin-side-nav.js +7 -0
- package/vaadin-side-nav-item.d.ts +1 -0
- package/vaadin-side-nav-item.js +2 -0
- package/vaadin-side-nav.d.ts +1 -0
- package/vaadin-side-nav.js +2 -0
- package/web-types.json +142 -0
- package/web-types.lit.json +76 -0
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright (c) 2017 - 2023 Vaadin Ltd.
|
|
4
|
+
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
|
|
5
|
+
*/
|
|
6
|
+
import { html, LitElement } from 'lit';
|
|
7
|
+
import { ifDefined } from 'lit/directives/if-defined.js';
|
|
8
|
+
import { ElementMixin } from '@vaadin/component-base/src/element-mixin.js';
|
|
9
|
+
import { PolylitMixin } from '@vaadin/component-base/src/polylit-mixin.js';
|
|
10
|
+
import { ThemableMixin } from '@vaadin/vaadin-themable-mixin/vaadin-themable-mixin.js';
|
|
11
|
+
import { sideNavItemBaseStyles } from './vaadin-side-nav-base-styles.js';
|
|
12
|
+
|
|
13
|
+
function isEnabled() {
|
|
14
|
+
return window.Vaadin && window.Vaadin.featureFlags && !!window.Vaadin.featureFlags.sideNavComponent;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* A navigation item to be used within `<vaadin-side-nav>`. Represents a navigation target.
|
|
19
|
+
* Not intended to be used separately.
|
|
20
|
+
*
|
|
21
|
+
* ```html
|
|
22
|
+
* <vaadin-side-nav-item>
|
|
23
|
+
* Item 1
|
|
24
|
+
* <vaadin-side-nav-item path="/path1" slot="children">
|
|
25
|
+
* Child item 1
|
|
26
|
+
* </vaadin-side-nav-item>
|
|
27
|
+
* <vaadin-side-nav-item path="/path2" slot="children">
|
|
28
|
+
* Child item 2
|
|
29
|
+
* </vaadin-side-nav-item>
|
|
30
|
+
* </vaadin-side-nav-item>
|
|
31
|
+
* ```
|
|
32
|
+
*
|
|
33
|
+
* ### Customization
|
|
34
|
+
*
|
|
35
|
+
* You can configure the item by using `slot` names.
|
|
36
|
+
*
|
|
37
|
+
* Slot name | Description
|
|
38
|
+
* ----------|-------------
|
|
39
|
+
* `prefix` | A slot for content before the label (e.g. an icon).
|
|
40
|
+
* `suffix` | A slot for content after the label (e.g. an icon).
|
|
41
|
+
*
|
|
42
|
+
* #### Example:
|
|
43
|
+
*
|
|
44
|
+
* ```html
|
|
45
|
+
* <vaadin-side-nav-item>
|
|
46
|
+
* <vaadin-icon icon="vaadin:chart" slot="prefix"></vaadin-icon>
|
|
47
|
+
* Item
|
|
48
|
+
* <span theme="badge primary" slot="suffix">Suffix</span>
|
|
49
|
+
* </vaadin-side-nav-item>
|
|
50
|
+
* ```
|
|
51
|
+
*
|
|
52
|
+
* @fires {CustomEvent} expanded-changed - Fired when the `expanded` property changes.
|
|
53
|
+
*
|
|
54
|
+
* @extends LitElement
|
|
55
|
+
* @mixes PolylitMixin
|
|
56
|
+
* @mixes ThemableMixin
|
|
57
|
+
* @mixes ElementMixin
|
|
58
|
+
*/
|
|
59
|
+
class SideNavItem extends ElementMixin(ThemableMixin(PolylitMixin(LitElement))) {
|
|
60
|
+
static get is() {
|
|
61
|
+
return 'vaadin-side-nav-item';
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
static get observers() {
|
|
65
|
+
return ['__pathChanged(path)'];
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
static get properties() {
|
|
69
|
+
return {
|
|
70
|
+
/**
|
|
71
|
+
* The path to navigate to
|
|
72
|
+
*/
|
|
73
|
+
path: String,
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Whether to show the child items or not
|
|
77
|
+
*
|
|
78
|
+
* @type {boolean}
|
|
79
|
+
*/
|
|
80
|
+
expanded: {
|
|
81
|
+
type: Boolean,
|
|
82
|
+
value: false,
|
|
83
|
+
notify: true,
|
|
84
|
+
reflectToAttribute: true,
|
|
85
|
+
},
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Whether the path of the item matches the current path.
|
|
89
|
+
* Set when the item is appended to DOM or when navigated back
|
|
90
|
+
* to the page that contains this item using the browser.
|
|
91
|
+
*
|
|
92
|
+
* @type {boolean}
|
|
93
|
+
*/
|
|
94
|
+
active: {
|
|
95
|
+
type: Boolean,
|
|
96
|
+
value: false,
|
|
97
|
+
readOnly: true,
|
|
98
|
+
reflectToAttribute: true,
|
|
99
|
+
},
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
static get styles() {
|
|
104
|
+
return sideNavItemBaseStyles;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/** @protected */
|
|
108
|
+
get _button() {
|
|
109
|
+
return this.shadowRoot.querySelector('button');
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/** @protected */
|
|
113
|
+
connectedCallback() {
|
|
114
|
+
super.connectedCallback();
|
|
115
|
+
this.setAttribute('role', 'listitem');
|
|
116
|
+
this.__updateActive();
|
|
117
|
+
this.__boundUpdateActive = this.__updateActive.bind(this);
|
|
118
|
+
window.addEventListener('popstate', this.__boundUpdateActive);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/** @protected */
|
|
122
|
+
disconnectedCallback() {
|
|
123
|
+
super.disconnectedCallback();
|
|
124
|
+
window.removeEventListener('popstate', this.__boundUpdateActive);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/** @protected */
|
|
128
|
+
render() {
|
|
129
|
+
return html`
|
|
130
|
+
<a href="${ifDefined(this.path)}" part="item" aria-current="${this.active ? 'page' : 'false'}">
|
|
131
|
+
<slot name="prefix"></slot>
|
|
132
|
+
<slot></slot>
|
|
133
|
+
<slot name="suffix"></slot>
|
|
134
|
+
<button
|
|
135
|
+
part="toggle-button"
|
|
136
|
+
@click="${this.__toggleExpanded}"
|
|
137
|
+
?hidden="${!this.querySelector('[slot=children]')}"
|
|
138
|
+
aria-controls="children"
|
|
139
|
+
aria-expanded="${this.expanded}"
|
|
140
|
+
aria-label="Toggle child items"
|
|
141
|
+
></button>
|
|
142
|
+
</a>
|
|
143
|
+
<slot name="children" role="list" part="children" id="children" ?hidden="${!this.expanded}"></slot>
|
|
144
|
+
`;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/** @private */
|
|
148
|
+
__pathChanged() {
|
|
149
|
+
this.__updateActive();
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/** @private */
|
|
153
|
+
__toggleExpanded(e) {
|
|
154
|
+
e.preventDefault();
|
|
155
|
+
e.stopPropagation();
|
|
156
|
+
this.expanded = !this.expanded;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/** @private */
|
|
160
|
+
__updateActive() {
|
|
161
|
+
if (!this.path && this.path !== '') {
|
|
162
|
+
this._setActive(false);
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
this._setActive(this.__calculateActive());
|
|
166
|
+
this.toggleAttribute('child-active', document.location.pathname.startsWith(this.path));
|
|
167
|
+
if (this.active) {
|
|
168
|
+
this.expanded = true;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/** @private */
|
|
173
|
+
__calculateActive() {
|
|
174
|
+
const pathAbsolute = this.path.startsWith('/');
|
|
175
|
+
// Absolute path or no base uri in use. No special comparison needed
|
|
176
|
+
if (pathAbsolute) {
|
|
177
|
+
// Compare an absolute view path
|
|
178
|
+
return document.location.pathname === this.path;
|
|
179
|
+
}
|
|
180
|
+
const hasBaseUri = document.baseURI !== document.location.href;
|
|
181
|
+
if (!hasBaseUri) {
|
|
182
|
+
// Compare a relative view path (strip the starting slash)
|
|
183
|
+
return document.location.pathname.substring(1) === this.path;
|
|
184
|
+
}
|
|
185
|
+
const pathRelativeToRoot = document.location.pathname;
|
|
186
|
+
const basePath = new URL(document.baseURI).pathname;
|
|
187
|
+
const pathWithoutBase = pathRelativeToRoot.substring(basePath.length);
|
|
188
|
+
const pathRelativeToBase =
|
|
189
|
+
basePath !== pathRelativeToRoot && pathRelativeToRoot.startsWith(basePath) ? pathWithoutBase : pathRelativeToRoot;
|
|
190
|
+
return pathRelativeToBase === this.path;
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
if (isEnabled()) {
|
|
195
|
+
customElements.define(SideNavItem.is, SideNavItem);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
export { SideNavItem };
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright (c) 2017 - 2023 Vaadin Ltd.
|
|
4
|
+
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
|
|
5
|
+
*/
|
|
6
|
+
import { LitElement } from 'lit';
|
|
7
|
+
import { ElementMixin } from '@vaadin/component-base/src/element-mixin.js';
|
|
8
|
+
import { PolylitMixin } from '@vaadin/component-base/src/polylit-mixin.js';
|
|
9
|
+
import { ThemableMixin } from '@vaadin/vaadin-themable-mixin/vaadin-themable-mixin.js';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Fired when the `collapsed` property changes.
|
|
13
|
+
*/
|
|
14
|
+
export type SideNavCollapsedChangedEvent = CustomEvent<{ value: boolean }>;
|
|
15
|
+
|
|
16
|
+
export interface SideNavCustomEventMap {
|
|
17
|
+
'collapsed-changed': SideNavCollapsedChangedEvent;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export type SideNavEventMap = HTMLElementEventMap & SideNavCustomEventMap;
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* `<vaadin-side-nav>` is a Web Component for navigation menus.
|
|
24
|
+
* ```html
|
|
25
|
+
* <vaadin-side-nav>
|
|
26
|
+
* <vaadin-side-nav-item>Item 1</vaadin-side-nav-item>
|
|
27
|
+
* <vaadin-side-nav-item>Item 2</vaadin-side-nav-item>
|
|
28
|
+
* <vaadin-side-nav-item>Item 3</vaadin-side-nav-item>
|
|
29
|
+
* <vaadin-side-nav-item>Item 4</vaadin-side-nav-item>
|
|
30
|
+
* </vaadin-side-nav>
|
|
31
|
+
* ```
|
|
32
|
+
*
|
|
33
|
+
* ### Customization
|
|
34
|
+
*
|
|
35
|
+
* You can configure the component by using `slot` names.
|
|
36
|
+
*
|
|
37
|
+
* Slot name | Description
|
|
38
|
+
* ----------|-------------
|
|
39
|
+
* `label` | The label (text) inside the side nav.
|
|
40
|
+
*
|
|
41
|
+
* #### Example:
|
|
42
|
+
* ```html
|
|
43
|
+
* <vaadin-side-nav>
|
|
44
|
+
* <span slot="label">Main menu</span>
|
|
45
|
+
* <vaadin-side-nav-item>Item</vaadin-side-nav-item>
|
|
46
|
+
* </vaadin-side-nav>
|
|
47
|
+
* ```
|
|
48
|
+
*
|
|
49
|
+
* @fires {CustomEvent} collapsed-changed - Fired when the `collapsed` property changes.
|
|
50
|
+
*/
|
|
51
|
+
declare class SideNav extends ElementMixin(ThemableMixin(PolylitMixin(LitElement))) {
|
|
52
|
+
/**
|
|
53
|
+
* Whether the side nav is collapsible. When enabled, the toggle icon is shown.
|
|
54
|
+
*/
|
|
55
|
+
collapsible: boolean;
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Whether the side nav is collapsed. When collapsed, the items are hidden.
|
|
59
|
+
*/
|
|
60
|
+
collapsed: boolean;
|
|
61
|
+
|
|
62
|
+
addEventListener<K extends keyof SideNavEventMap>(
|
|
63
|
+
type: K,
|
|
64
|
+
listener: (this: SideNav, ev: SideNavEventMap[K]) => void,
|
|
65
|
+
options?: AddEventListenerOptions | boolean,
|
|
66
|
+
): void;
|
|
67
|
+
|
|
68
|
+
removeEventListener<K extends keyof SideNavEventMap>(
|
|
69
|
+
type: K,
|
|
70
|
+
listener: (this: SideNav, ev: SideNavEventMap[K]) => void,
|
|
71
|
+
options?: EventListenerOptions | boolean,
|
|
72
|
+
): void;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
declare global {
|
|
76
|
+
interface HTMLElementTagNameMap {
|
|
77
|
+
'vaadin-side-nav': SideNav;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export { SideNav };
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright (c) 2017 - 2023 Vaadin Ltd.
|
|
4
|
+
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { html, LitElement } from 'lit';
|
|
8
|
+
import { ElementMixin } from '@vaadin/component-base/src/element-mixin.js';
|
|
9
|
+
import { PolylitMixin } from '@vaadin/component-base/src/polylit-mixin.js';
|
|
10
|
+
import { generateUniqueId } from '@vaadin/component-base/src/unique-id-utils.js';
|
|
11
|
+
import { ThemableMixin } from '@vaadin/vaadin-themable-mixin/vaadin-themable-mixin.js';
|
|
12
|
+
import { sideNavBaseStyles } from './vaadin-side-nav-base-styles.js';
|
|
13
|
+
|
|
14
|
+
function isEnabled() {
|
|
15
|
+
return window.Vaadin && window.Vaadin.featureFlags && !!window.Vaadin.featureFlags.sideNavComponent;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* `<vaadin-side-nav>` is a Web Component for navigation menus.
|
|
20
|
+
* ```html
|
|
21
|
+
* <vaadin-side-nav>
|
|
22
|
+
* <vaadin-side-nav-item>Item 1</vaadin-side-nav-item>
|
|
23
|
+
* <vaadin-side-nav-item>Item 2</vaadin-side-nav-item>
|
|
24
|
+
* <vaadin-side-nav-item>Item 3</vaadin-side-nav-item>
|
|
25
|
+
* <vaadin-side-nav-item>Item 4</vaadin-side-nav-item>
|
|
26
|
+
* </vaadin-side-nav>
|
|
27
|
+
* ```
|
|
28
|
+
*
|
|
29
|
+
* ### Customization
|
|
30
|
+
*
|
|
31
|
+
* You can configure the component by using `slot` names.
|
|
32
|
+
*
|
|
33
|
+
* Slot name | Description
|
|
34
|
+
* ----------|-------------
|
|
35
|
+
* `label` | The label (text) inside the side nav.
|
|
36
|
+
*
|
|
37
|
+
* #### Example:
|
|
38
|
+
* ```html
|
|
39
|
+
* <vaadin-side-nav>
|
|
40
|
+
* <span slot="label">Main menu</span>
|
|
41
|
+
* <vaadin-side-nav-item>Item</vaadin-side-nav-item>
|
|
42
|
+
* </vaadin-side-nav>
|
|
43
|
+
* ```
|
|
44
|
+
*
|
|
45
|
+
* @fires {CustomEvent} collapsed-changed - Fired when the `collapsed` property changes.
|
|
46
|
+
*
|
|
47
|
+
* @extends LitElement
|
|
48
|
+
* @mixes PolylitMixin
|
|
49
|
+
* @mixes ThemableMixin
|
|
50
|
+
* @mixes ElementMixin
|
|
51
|
+
*/
|
|
52
|
+
class SideNav extends ElementMixin(ThemableMixin(PolylitMixin(LitElement))) {
|
|
53
|
+
static get is() {
|
|
54
|
+
return 'vaadin-side-nav';
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
static get properties() {
|
|
58
|
+
return {
|
|
59
|
+
/**
|
|
60
|
+
* Whether the side nav is collapsible. When enabled, the toggle icon is shown.
|
|
61
|
+
*
|
|
62
|
+
* @type {boolean}
|
|
63
|
+
*/
|
|
64
|
+
collapsible: {
|
|
65
|
+
type: Boolean,
|
|
66
|
+
value: false,
|
|
67
|
+
reflectToAttribute: true,
|
|
68
|
+
},
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Whether the side nav is collapsed. When collapsed, the items are hidden.
|
|
72
|
+
*
|
|
73
|
+
* @type {boolean}
|
|
74
|
+
*/
|
|
75
|
+
collapsed: {
|
|
76
|
+
type: Boolean,
|
|
77
|
+
value: false,
|
|
78
|
+
notify: true,
|
|
79
|
+
reflectToAttribute: true,
|
|
80
|
+
},
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
static get styles() {
|
|
85
|
+
return sideNavBaseStyles;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/** @protected */
|
|
89
|
+
firstUpdated() {
|
|
90
|
+
super.ready();
|
|
91
|
+
|
|
92
|
+
// By default, if the user hasn't provided a custom role,
|
|
93
|
+
// the role attribute is set to "navigation".
|
|
94
|
+
if (!this.hasAttribute('role')) {
|
|
95
|
+
this.setAttribute('role', 'navigation');
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/** @protected */
|
|
100
|
+
render() {
|
|
101
|
+
const label = this.querySelector('[slot="label"]');
|
|
102
|
+
if (label && this.collapsible) {
|
|
103
|
+
return html`
|
|
104
|
+
<details ?open="${!this.collapsed}" @toggle="${this.__toggleCollapsed}">${this.__renderBody(label)}</details>
|
|
105
|
+
`;
|
|
106
|
+
}
|
|
107
|
+
return this.__renderBody(label);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/** @private */
|
|
111
|
+
__renderBody(label) {
|
|
112
|
+
if (label) {
|
|
113
|
+
if (!label.id) label.id = `side-nav-label-${generateUniqueId()}`;
|
|
114
|
+
this.setAttribute('aria-labelledby', label.id);
|
|
115
|
+
} else {
|
|
116
|
+
this.removeAttribute('aria-labelledby');
|
|
117
|
+
}
|
|
118
|
+
return html`
|
|
119
|
+
<summary part="label" ?hidden="${label == null}">
|
|
120
|
+
<slot name="label" @slotchange="${() => this.requestUpdate()}"></slot>
|
|
121
|
+
</summary>
|
|
122
|
+
<slot role="list"></slot>
|
|
123
|
+
`;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/** @private */
|
|
127
|
+
__toggleCollapsed(e) {
|
|
128
|
+
this.collapsed = !e.target.open;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
if (isEnabled()) {
|
|
133
|
+
customElements.define(SideNav.is, SideNav);
|
|
134
|
+
} else {
|
|
135
|
+
console.warn(
|
|
136
|
+
'WARNING: The side-nav component is currently an experimental feature and needs to be explicitly enabled. To enable the component, `import "@vaadin/side-nav/enable.js"` *before* importing the side-nav module itself.',
|
|
137
|
+
);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
export { SideNav };
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
import '@vaadin/vaadin-lumo-styles/color.js';
|
|
2
|
+
import '@vaadin/vaadin-lumo-styles/typography.js';
|
|
3
|
+
import '@vaadin/vaadin-lumo-styles/sizing.js';
|
|
4
|
+
import '@vaadin/vaadin-lumo-styles/spacing.js';
|
|
5
|
+
import '@vaadin/vaadin-lumo-styles/style.js';
|
|
6
|
+
import '@vaadin/vaadin-lumo-styles/font-icons.js';
|
|
7
|
+
import { css, registerStyles } from '@vaadin/vaadin-themable-mixin/vaadin-themable-mixin.js';
|
|
8
|
+
|
|
9
|
+
export const sideNavItemStyles = css`
|
|
10
|
+
a {
|
|
11
|
+
gap: var(--lumo-space-xs);
|
|
12
|
+
padding: var(--lumo-space-s);
|
|
13
|
+
padding-inline-start: calc(var(--lumo-space-s) + var(--_child-indent, 0px));
|
|
14
|
+
border-radius: var(--lumo-border-radius-m);
|
|
15
|
+
transition: background-color 140ms, color 140ms;
|
|
16
|
+
cursor: var(--lumo-clickable-cursor, default);
|
|
17
|
+
min-height: var(--lumo-icon-size-m);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
button {
|
|
21
|
+
border: 0;
|
|
22
|
+
margin: calc((var(--lumo-icon-size-m) - var(--lumo-size-s)) / 2) 0;
|
|
23
|
+
margin-inline-end: calc(var(--lumo-space-xs) * -1);
|
|
24
|
+
padding: 0;
|
|
25
|
+
background: transparent;
|
|
26
|
+
font: inherit;
|
|
27
|
+
color: var(--lumo-tertiary-text-color);
|
|
28
|
+
width: var(--lumo-size-s);
|
|
29
|
+
height: var(--lumo-size-s);
|
|
30
|
+
cursor: var(--lumo-clickable-cursor, default);
|
|
31
|
+
transition: color 140ms;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
@media (any-hover: hover) {
|
|
35
|
+
a:hover {
|
|
36
|
+
color: var(--lumo-header-text-color);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
button:hover {
|
|
40
|
+
color: var(--lumo-body-text-color);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
a:active:focus {
|
|
45
|
+
background-color: var(--lumo-contrast-5pct);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
button::before {
|
|
49
|
+
font-family: lumo-icons;
|
|
50
|
+
content: var(--lumo-icons-dropdown);
|
|
51
|
+
font-size: 1.5em;
|
|
52
|
+
line-height: var(--lumo-size-s);
|
|
53
|
+
display: inline-block;
|
|
54
|
+
transform: rotate(-90deg);
|
|
55
|
+
transition: transform 140ms;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
:host([expanded]) button::before {
|
|
59
|
+
transform: none;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
@supports selector(:focus-visible) {
|
|
63
|
+
a,
|
|
64
|
+
button {
|
|
65
|
+
outline: none;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
a:focus-visible,
|
|
69
|
+
button:focus-visible {
|
|
70
|
+
border-radius: var(--lumo-border-radius-m);
|
|
71
|
+
box-shadow: 0 0 0 2px var(--lumo-primary-color-50pct);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
a:active {
|
|
76
|
+
color: var(--lumo-header-text-color);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
slot:not([name]) {
|
|
80
|
+
margin: 0 var(--lumo-space-xs);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
slot[name='prefix']::slotted(:is(vaadin-icon, [class*='icon'])) {
|
|
84
|
+
color: var(--lumo-contrast-60pct);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
:host([active]) slot[name='prefix']::slotted(:is(vaadin-icon, [class*='icon'])) {
|
|
88
|
+
color: inherit;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
slot[name='children'] {
|
|
92
|
+
--_child-indent: calc(var(--_child-indent-2, 0px) + var(--vaadin-side-nav-child-indent, var(--lumo-space-l)));
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
slot[name='children']::slotted(*) {
|
|
96
|
+
--_child-indent-2: var(--_child-indent);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
:host([active]) a {
|
|
100
|
+
color: var(--lumo-primary-text-color);
|
|
101
|
+
background-color: var(--lumo-primary-color-10pct);
|
|
102
|
+
}
|
|
103
|
+
`;
|
|
104
|
+
|
|
105
|
+
registerStyles('vaadin-side-nav-item', sideNavItemStyles, { moduleId: 'lumo-side-nav-item' });
|
|
106
|
+
|
|
107
|
+
export const sideNavStyles = css`
|
|
108
|
+
:host {
|
|
109
|
+
font-family: var(--lumo-font-family);
|
|
110
|
+
font-size: var(--lumo-font-size-m);
|
|
111
|
+
font-weight: 500;
|
|
112
|
+
line-height: var(--lumo-line-height-xs);
|
|
113
|
+
color: var(--lumo-body-text-color);
|
|
114
|
+
-webkit-tap-highlight-color: transparent;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
summary {
|
|
118
|
+
cursor: var(--lumo-clickable-cursor, default);
|
|
119
|
+
border-radius: var(--lumo-border-radius-m);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
summary ::slotted([slot='label']) {
|
|
123
|
+
font-size: var(--lumo-font-size-s);
|
|
124
|
+
color: var(--lumo-secondary-text-color);
|
|
125
|
+
margin: var(--lumo-space-s);
|
|
126
|
+
border-radius: inherit;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
summary::after {
|
|
130
|
+
font-family: lumo-icons;
|
|
131
|
+
color: var(--lumo-tertiary-text-color);
|
|
132
|
+
font-size: var(--lumo-icon-size-m);
|
|
133
|
+
width: var(--lumo-size-s);
|
|
134
|
+
height: var(--lumo-size-s);
|
|
135
|
+
transition: transform 140ms;
|
|
136
|
+
margin: 0 var(--lumo-space-xs);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
:host([collapsible]) summary::after {
|
|
140
|
+
content: var(--lumo-icons-dropdown);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
@media (any-hover: hover) {
|
|
144
|
+
summary:hover::after {
|
|
145
|
+
color: var(--lumo-body-text-color);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
:host([collapsed]) summary::after {
|
|
150
|
+
transform: rotate(-90deg);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
@supports selector(:focus-visible) {
|
|
154
|
+
summary {
|
|
155
|
+
outline: none;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
summary:focus-visible {
|
|
159
|
+
box-shadow: 0 0 0 2px var(--lumo-primary-color-50pct);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
`;
|
|
163
|
+
|
|
164
|
+
registerStyles('vaadin-side-nav', sideNavStyles, { moduleId: 'lumo-side-nav' });
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './src/vaadin-side-nav-item.js';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './src/vaadin-side-nav.js';
|