@vaadin/side-nav 24.1.5 → 24.2.0-alpha10
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 +6 -1
- package/package.json +7 -7
- package/src/vaadin-side-nav-base-styles.js +38 -40
- package/src/vaadin-side-nav-children-mixin.d.ts +46 -0
- package/src/vaadin-side-nav-children-mixin.js +134 -0
- package/src/vaadin-side-nav-item.d.ts +33 -3
- package/src/vaadin-side-nav-item.js +111 -48
- package/src/vaadin-side-nav.d.ts +25 -1
- package/src/vaadin-side-nav.js +95 -17
- package/theme/lumo/vaadin-side-nav-item-styles.js +40 -28
- package/theme/lumo/vaadin-side-nav-item.js +1 -1
- package/theme/lumo/vaadin-side-nav-styles.js +39 -25
- package/theme/lumo/vaadin-side-nav.js +1 -1
- package/theme/material/vaadin-side-nav-item-styles.js +142 -0
- package/theme/material/vaadin-side-nav-item.js +7 -0
- package/theme/material/vaadin-side-nav-styles.js +69 -0
- package/theme/material/vaadin-side-nav.js +3 -2
- package/web-types.json +52 -3
- package/web-types.lit.json +31 -3
package/README.md
CHANGED
|
@@ -49,9 +49,14 @@ import '@vaadin/side-nav';
|
|
|
49
49
|
## Themes
|
|
50
50
|
|
|
51
51
|
Vaadin components come with two built-in [themes](https://vaadin.com/docs/latest/styling), Lumo and Material.
|
|
52
|
-
This component currently does not support Material theme.
|
|
53
52
|
The [main entrypoint](https://github.com/vaadin/web-components/blob/main/packages/side-nav/vaadin-side-nav.js) of the package uses the Lumo theme.
|
|
54
53
|
|
|
54
|
+
To use the Material theme, import the component from the `theme/material` folder:
|
|
55
|
+
|
|
56
|
+
```js
|
|
57
|
+
import '@vaadin/side-nav/theme/material/vaadin-side-nav.js';
|
|
58
|
+
```
|
|
59
|
+
|
|
55
60
|
You can also import the Lumo version of the component explicitly:
|
|
56
61
|
|
|
57
62
|
```js
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vaadin/side-nav",
|
|
3
|
-
"version": "24.
|
|
3
|
+
"version": "24.2.0-alpha10",
|
|
4
4
|
"publishConfig": {
|
|
5
5
|
"access": "public"
|
|
6
6
|
},
|
|
@@ -35,15 +35,15 @@
|
|
|
35
35
|
"web-component"
|
|
36
36
|
],
|
|
37
37
|
"dependencies": {
|
|
38
|
-
"@vaadin/component-base": "
|
|
39
|
-
"@vaadin/vaadin-lumo-styles": "
|
|
40
|
-
"@vaadin/vaadin-material-styles": "
|
|
41
|
-
"@vaadin/vaadin-themable-mixin": "
|
|
38
|
+
"@vaadin/component-base": "24.2.0-alpha10",
|
|
39
|
+
"@vaadin/vaadin-lumo-styles": "24.2.0-alpha10",
|
|
40
|
+
"@vaadin/vaadin-material-styles": "24.2.0-alpha10",
|
|
41
|
+
"@vaadin/vaadin-themable-mixin": "24.2.0-alpha10",
|
|
42
42
|
"lit": "^2.0.0"
|
|
43
43
|
},
|
|
44
44
|
"devDependencies": {
|
|
45
45
|
"@esm-bundle/chai": "^4.3.4",
|
|
46
|
-
"@vaadin/testing-helpers": "^0.
|
|
46
|
+
"@vaadin/testing-helpers": "^0.5.0",
|
|
47
47
|
"lit": "^2.0.0",
|
|
48
48
|
"sinon": "^13.0.2"
|
|
49
49
|
},
|
|
@@ -51,5 +51,5 @@
|
|
|
51
51
|
"web-types.json",
|
|
52
52
|
"web-types.lit.json"
|
|
53
53
|
],
|
|
54
|
-
"gitHead": "
|
|
54
|
+
"gitHead": "ca16b5f88b00ae05fb6d7c7e9874525048e389f0"
|
|
55
55
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @license
|
|
3
|
-
* Copyright (c)
|
|
3
|
+
* Copyright (c) 2023 Vaadin Ltd.
|
|
4
4
|
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
|
|
5
5
|
*/
|
|
6
6
|
import { css } from 'lit';
|
|
@@ -10,11 +10,21 @@ export const sideNavItemBaseStyles = css`
|
|
|
10
10
|
display: block;
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
+
:host([hidden]),
|
|
13
14
|
[hidden] {
|
|
14
15
|
display: none !important;
|
|
15
16
|
}
|
|
16
17
|
|
|
17
|
-
|
|
18
|
+
:host([disabled]) {
|
|
19
|
+
pointer-events: none;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
[part='content'] {
|
|
23
|
+
display: flex;
|
|
24
|
+
align-items: center;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
[part='link'] {
|
|
18
28
|
flex: auto;
|
|
19
29
|
min-width: 0;
|
|
20
30
|
display: flex;
|
|
@@ -28,19 +38,21 @@ export const sideNavItemBaseStyles = css`
|
|
|
28
38
|
-webkit-appearance: none;
|
|
29
39
|
appearance: none;
|
|
30
40
|
flex: none;
|
|
41
|
+
position: relative;
|
|
42
|
+
margin: 0;
|
|
43
|
+
padding: 0;
|
|
44
|
+
border: 0;
|
|
45
|
+
background: transparent;
|
|
31
46
|
}
|
|
32
47
|
|
|
33
|
-
|
|
34
|
-
|
|
48
|
+
[part='children'] {
|
|
49
|
+
padding: 0;
|
|
50
|
+
margin: 0;
|
|
51
|
+
list-style-type: none;
|
|
35
52
|
}
|
|
36
53
|
|
|
37
|
-
:host(:not([
|
|
38
|
-
|
|
39
|
-
position: absolute;
|
|
40
|
-
top: 0;
|
|
41
|
-
right: 0;
|
|
42
|
-
bottom: 0;
|
|
43
|
-
left: 0;
|
|
54
|
+
:host(:not([has-children])) button {
|
|
55
|
+
display: none !important;
|
|
44
56
|
}
|
|
45
57
|
|
|
46
58
|
slot[name='prefix'],
|
|
@@ -56,12 +68,6 @@ export const sideNavItemBaseStyles = css`
|
|
|
56
68
|
text-overflow: ellipsis;
|
|
57
69
|
white-space: nowrap;
|
|
58
70
|
}
|
|
59
|
-
|
|
60
|
-
slot[name='children'] {
|
|
61
|
-
/* Needed to make role="list" work */
|
|
62
|
-
display: block;
|
|
63
|
-
width: 100%;
|
|
64
|
-
}
|
|
65
71
|
`;
|
|
66
72
|
|
|
67
73
|
export const sideNavBaseStyles = css`
|
|
@@ -73,32 +79,24 @@ export const sideNavBaseStyles = css`
|
|
|
73
79
|
display: none !important;
|
|
74
80
|
}
|
|
75
81
|
|
|
76
|
-
|
|
82
|
+
button {
|
|
77
83
|
display: flex;
|
|
78
84
|
align-items: center;
|
|
79
|
-
justify-content:
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
summary::marker {
|
|
91
|
-
content: '';
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
summary::after {
|
|
95
|
-
display: inline-flex;
|
|
96
|
-
align-items: center;
|
|
97
|
-
justify-content: center;
|
|
85
|
+
justify-content: inherit;
|
|
86
|
+
width: 100%;
|
|
87
|
+
margin: 0;
|
|
88
|
+
padding: 0;
|
|
89
|
+
background-color: initial;
|
|
90
|
+
color: inherit;
|
|
91
|
+
border: initial;
|
|
92
|
+
outline: none;
|
|
93
|
+
font: inherit;
|
|
94
|
+
text-align: inherit;
|
|
98
95
|
}
|
|
99
96
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
97
|
+
[part='children'] {
|
|
98
|
+
padding: 0;
|
|
99
|
+
margin: 0;
|
|
100
|
+
list-style-type: none;
|
|
103
101
|
}
|
|
104
102
|
`;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright (c) 2023 Vaadin Ltd.
|
|
4
|
+
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
|
|
5
|
+
*/
|
|
6
|
+
import type { Constructor } from '@open-wc/dedupe-mixin';
|
|
7
|
+
|
|
8
|
+
export interface SideNavI18n {
|
|
9
|
+
toggle: string;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export declare function SideNavChildrenMixin<T extends Constructor<HTMLElement>>(
|
|
13
|
+
base: T,
|
|
14
|
+
): Constructor<SideNavChildrenMixinClass> & T;
|
|
15
|
+
|
|
16
|
+
export declare class SideNavChildrenMixinClass {
|
|
17
|
+
/**
|
|
18
|
+
* The object used to localize this component.
|
|
19
|
+
*
|
|
20
|
+
* To change the default localization, replace the entire
|
|
21
|
+
* `i18n` object with a custom one.
|
|
22
|
+
*
|
|
23
|
+
* The object has the following structure and default values:
|
|
24
|
+
* ```
|
|
25
|
+
* {
|
|
26
|
+
* toggle: 'Toggle child items'
|
|
27
|
+
* }
|
|
28
|
+
* ```
|
|
29
|
+
*/
|
|
30
|
+
i18n: SideNavI18n;
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* List of child items of this component.
|
|
34
|
+
*/
|
|
35
|
+
protected readonly _items: HTMLElement[];
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Name of the slot to be used for children.
|
|
39
|
+
*/
|
|
40
|
+
protected readonly _itemsSlotName: string;
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Count of child items.
|
|
44
|
+
*/
|
|
45
|
+
protected _itemsCount: number;
|
|
46
|
+
}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright (c) 2023 Vaadin Ltd.
|
|
4
|
+
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
|
|
5
|
+
*/
|
|
6
|
+
import { SlotController } from '@vaadin/component-base/src/slot-controller.js';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* A controller that manages the item content children slot.
|
|
10
|
+
*/
|
|
11
|
+
class ChildrenController extends SlotController {
|
|
12
|
+
constructor(host, slotName) {
|
|
13
|
+
super(host, slotName, null, { observe: true, multiple: true });
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* @protected
|
|
18
|
+
* @override
|
|
19
|
+
*/
|
|
20
|
+
initAddedNode() {
|
|
21
|
+
this.host.requestUpdate();
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* @protected
|
|
26
|
+
* @override
|
|
27
|
+
*/
|
|
28
|
+
teardownNode() {
|
|
29
|
+
this.host.requestUpdate();
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* @polymerMixin
|
|
35
|
+
*/
|
|
36
|
+
export const SideNavChildrenMixin = (superClass) =>
|
|
37
|
+
class SideNavChildrenMixin extends superClass {
|
|
38
|
+
static get properties() {
|
|
39
|
+
return {
|
|
40
|
+
/**
|
|
41
|
+
* The object used to localize this component.
|
|
42
|
+
*
|
|
43
|
+
* To change the default localization, replace the entire
|
|
44
|
+
* `i18n` object with a custom one.
|
|
45
|
+
*
|
|
46
|
+
* The object has the following structure and default values:
|
|
47
|
+
* ```
|
|
48
|
+
* {
|
|
49
|
+
* toggle: 'Toggle child items'
|
|
50
|
+
* }
|
|
51
|
+
* ```
|
|
52
|
+
*
|
|
53
|
+
* @type {SideNavI18n}
|
|
54
|
+
* @default {English/US}
|
|
55
|
+
*/
|
|
56
|
+
i18n: {
|
|
57
|
+
type: Object,
|
|
58
|
+
value: () => {
|
|
59
|
+
return {
|
|
60
|
+
toggle: 'Toggle child items',
|
|
61
|
+
};
|
|
62
|
+
},
|
|
63
|
+
},
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Count of child items.
|
|
67
|
+
* @protected
|
|
68
|
+
*/
|
|
69
|
+
_itemsCount: {
|
|
70
|
+
type: Number,
|
|
71
|
+
value: 0,
|
|
72
|
+
},
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
constructor() {
|
|
77
|
+
super();
|
|
78
|
+
|
|
79
|
+
this._childrenController = new ChildrenController(this, this._itemsSlotName);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* List of child items of this component.
|
|
84
|
+
* @protected
|
|
85
|
+
*/
|
|
86
|
+
get _items() {
|
|
87
|
+
return this._childrenController.nodes;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Name of the slot to be used for children.
|
|
92
|
+
* @protected
|
|
93
|
+
*/
|
|
94
|
+
get _itemsSlotName() {
|
|
95
|
+
return 'children';
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/** @protected */
|
|
99
|
+
firstUpdated() {
|
|
100
|
+
super.firstUpdated();
|
|
101
|
+
|
|
102
|
+
// Controller that detects changes to the side-nav items.
|
|
103
|
+
this.addController(this._childrenController);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* @protected
|
|
108
|
+
* @override
|
|
109
|
+
*/
|
|
110
|
+
willUpdate(props) {
|
|
111
|
+
super.willUpdate(props);
|
|
112
|
+
|
|
113
|
+
this._itemsCount = this._items.length;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* @protected
|
|
118
|
+
* @override
|
|
119
|
+
*/
|
|
120
|
+
updated(props) {
|
|
121
|
+
super.updated(props);
|
|
122
|
+
|
|
123
|
+
if (props.has('_itemsCount')) {
|
|
124
|
+
this.toggleAttribute('has-children', this._itemsCount > 0);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// Propagate i18n object to all the child items
|
|
128
|
+
if (props.has('_itemsCount') || props.has('i18n')) {
|
|
129
|
+
this._items.forEach((item) => {
|
|
130
|
+
item.i18n = this.i18n;
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
};
|
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @license
|
|
3
|
-
* Copyright (c)
|
|
3
|
+
* Copyright (c) 2023 Vaadin Ltd.
|
|
4
4
|
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
|
|
5
5
|
*/
|
|
6
6
|
import { LitElement } from 'lit';
|
|
7
|
+
import { DisabledMixin } from '@vaadin/a11y-base/src/disabled-mixin.js';
|
|
7
8
|
import { ElementMixin } from '@vaadin/component-base/src/element-mixin.js';
|
|
8
9
|
import { PolylitMixin } from '@vaadin/component-base/src/polylit-mixin.js';
|
|
9
10
|
import { ThemableMixin } from '@vaadin/vaadin-themable-mixin/vaadin-themable-mixin.js';
|
|
11
|
+
import { SideNavChildrenMixin } from './vaadin-side-nav-children-mixin.js';
|
|
10
12
|
|
|
11
13
|
/**
|
|
12
14
|
* Fired when the `expanded` property changes.
|
|
@@ -54,14 +56,42 @@ export type SideNavItemEventMap = HTMLElementEventMap & SideNavItemCustomEventMa
|
|
|
54
56
|
* </vaadin-side-nav-item>
|
|
55
57
|
* ```
|
|
56
58
|
*
|
|
59
|
+
* ### Styling
|
|
60
|
+
*
|
|
61
|
+
* The following shadow DOM parts are available for styling:
|
|
62
|
+
*
|
|
63
|
+
* Part name | Description
|
|
64
|
+
* ----------------|----------------
|
|
65
|
+
* `content` | The element that wraps link and toggle button
|
|
66
|
+
* `children` | The element that wraps child items
|
|
67
|
+
* `link` | The clickable anchor used for navigation
|
|
68
|
+
* `toggle-button` | The toggle button
|
|
69
|
+
*
|
|
70
|
+
* The following state attributes are available for styling:
|
|
71
|
+
*
|
|
72
|
+
* Attribute | Description
|
|
73
|
+
* ---------------|-------------
|
|
74
|
+
* `disabled` | Set when the element is disabled.
|
|
75
|
+
* `expanded` | Set when the element is expanded.
|
|
76
|
+
* `has-children` | Set when the element has child items.
|
|
77
|
+
*
|
|
78
|
+
* See [Styling Components](https://vaadin.com/docs/latest/styling/styling-components) documentation.
|
|
79
|
+
*
|
|
57
80
|
* @fires {CustomEvent} expanded-changed - Fired when the `expanded` property changes.
|
|
58
81
|
*/
|
|
59
|
-
declare class SideNavItem extends
|
|
82
|
+
declare class SideNavItem extends SideNavChildrenMixin(
|
|
83
|
+
DisabledMixin(ElementMixin(ThemableMixin(PolylitMixin(LitElement)))),
|
|
84
|
+
) {
|
|
60
85
|
/**
|
|
61
86
|
* The path to navigate to
|
|
62
87
|
*/
|
|
63
88
|
path: string | null | undefined;
|
|
64
89
|
|
|
90
|
+
/**
|
|
91
|
+
* The list of alternative paths matching this item
|
|
92
|
+
*/
|
|
93
|
+
pathAliases: string[];
|
|
94
|
+
|
|
65
95
|
/**
|
|
66
96
|
* Whether to show the child items or not
|
|
67
97
|
*/
|
|
@@ -72,7 +102,7 @@ declare class SideNavItem extends ElementMixin(ThemableMixin(PolylitMixin(LitEle
|
|
|
72
102
|
* Set when the item is appended to DOM or when navigated back
|
|
73
103
|
* to the page that contains this item using the browser.
|
|
74
104
|
*/
|
|
75
|
-
readonly
|
|
105
|
+
readonly current: boolean;
|
|
76
106
|
|
|
77
107
|
addEventListener<K extends keyof SideNavItemEventMap>(
|
|
78
108
|
type: K,
|
|
@@ -1,14 +1,18 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @license
|
|
3
|
-
* Copyright (c)
|
|
3
|
+
* Copyright (c) 2023 Vaadin Ltd.
|
|
4
4
|
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
|
|
5
5
|
*/
|
|
6
6
|
import { html, LitElement } from 'lit';
|
|
7
7
|
import { ifDefined } from 'lit/directives/if-defined.js';
|
|
8
|
+
import { DisabledMixin } from '@vaadin/a11y-base/src/disabled-mixin.js';
|
|
9
|
+
import { screenReaderOnly } from '@vaadin/a11y-base/src/styles/sr-only-styles.js';
|
|
8
10
|
import { ElementMixin } from '@vaadin/component-base/src/element-mixin.js';
|
|
9
11
|
import { PolylitMixin } from '@vaadin/component-base/src/polylit-mixin.js';
|
|
12
|
+
import { matchPaths } from '@vaadin/component-base/src/url-utils.js';
|
|
10
13
|
import { ThemableMixin } from '@vaadin/vaadin-themable-mixin/vaadin-themable-mixin.js';
|
|
11
14
|
import { sideNavItemBaseStyles } from './vaadin-side-nav-base-styles.js';
|
|
15
|
+
import { SideNavChildrenMixin } from './vaadin-side-nav-children-mixin.js';
|
|
12
16
|
|
|
13
17
|
function isEnabled() {
|
|
14
18
|
return window.Vaadin && window.Vaadin.featureFlags && !!window.Vaadin.featureFlags.sideNavComponent;
|
|
@@ -49,14 +53,37 @@ function isEnabled() {
|
|
|
49
53
|
* </vaadin-side-nav-item>
|
|
50
54
|
* ```
|
|
51
55
|
*
|
|
56
|
+
* ### Styling
|
|
57
|
+
*
|
|
58
|
+
* The following shadow DOM parts are available for styling:
|
|
59
|
+
*
|
|
60
|
+
* Part name | Description
|
|
61
|
+
* ----------------|----------------
|
|
62
|
+
* `content` | The element that wraps link and toggle button
|
|
63
|
+
* `children` | The element that wraps child items
|
|
64
|
+
* `link` | The clickable anchor used for navigation
|
|
65
|
+
* `toggle-button` | The toggle button
|
|
66
|
+
*
|
|
67
|
+
* The following state attributes are available for styling:
|
|
68
|
+
*
|
|
69
|
+
* Attribute | Description
|
|
70
|
+
* ---------------|-------------
|
|
71
|
+
* `disabled` | Set when the element is disabled.
|
|
72
|
+
* `expanded` | Set when the element is expanded.
|
|
73
|
+
* `has-children` | Set when the element has child items.
|
|
74
|
+
*
|
|
75
|
+
* See [Styling Components](https://vaadin.com/docs/latest/styling/styling-components) documentation.
|
|
76
|
+
*
|
|
52
77
|
* @fires {CustomEvent} expanded-changed - Fired when the `expanded` property changes.
|
|
53
78
|
*
|
|
54
79
|
* @extends LitElement
|
|
55
80
|
* @mixes PolylitMixin
|
|
56
81
|
* @mixes ThemableMixin
|
|
82
|
+
* @mixes DisabledMixin
|
|
57
83
|
* @mixes ElementMixin
|
|
84
|
+
* @mixes SideNavChildrenMixin
|
|
58
85
|
*/
|
|
59
|
-
class SideNavItem extends ElementMixin(ThemableMixin(PolylitMixin(LitElement))) {
|
|
86
|
+
class SideNavItem extends SideNavChildrenMixin(DisabledMixin(ElementMixin(ThemableMixin(PolylitMixin(LitElement))))) {
|
|
60
87
|
static get is() {
|
|
61
88
|
return 'vaadin-side-nav-item';
|
|
62
89
|
}
|
|
@@ -68,6 +95,16 @@ class SideNavItem extends ElementMixin(ThemableMixin(PolylitMixin(LitElement)))
|
|
|
68
95
|
*/
|
|
69
96
|
path: String,
|
|
70
97
|
|
|
98
|
+
/**
|
|
99
|
+
* The list of alternative paths matching this item
|
|
100
|
+
*
|
|
101
|
+
* @type {!Array<string>}
|
|
102
|
+
*/
|
|
103
|
+
pathAliases: {
|
|
104
|
+
type: Array,
|
|
105
|
+
value: () => [],
|
|
106
|
+
},
|
|
107
|
+
|
|
71
108
|
/**
|
|
72
109
|
* Whether to show the child items or not
|
|
73
110
|
*
|
|
@@ -87,7 +124,7 @@ class SideNavItem extends ElementMixin(ThemableMixin(PolylitMixin(LitElement)))
|
|
|
87
124
|
*
|
|
88
125
|
* @type {boolean}
|
|
89
126
|
*/
|
|
90
|
-
|
|
127
|
+
current: {
|
|
91
128
|
type: Boolean,
|
|
92
129
|
value: false,
|
|
93
130
|
readOnly: true,
|
|
@@ -97,7 +134,13 @@ class SideNavItem extends ElementMixin(ThemableMixin(PolylitMixin(LitElement)))
|
|
|
97
134
|
}
|
|
98
135
|
|
|
99
136
|
static get styles() {
|
|
100
|
-
return sideNavItemBaseStyles;
|
|
137
|
+
return [screenReaderOnly, sideNavItemBaseStyles];
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
constructor() {
|
|
141
|
+
super();
|
|
142
|
+
|
|
143
|
+
this.__boundUpdateCurrent = this.__updateCurrent.bind(this);
|
|
101
144
|
}
|
|
102
145
|
|
|
103
146
|
/** @protected */
|
|
@@ -110,6 +153,8 @@ class SideNavItem extends ElementMixin(ThemableMixin(PolylitMixin(LitElement)))
|
|
|
110
153
|
* @override
|
|
111
154
|
*/
|
|
112
155
|
firstUpdated() {
|
|
156
|
+
super.firstUpdated();
|
|
157
|
+
|
|
113
158
|
// By default, if the user hasn't provided a custom role,
|
|
114
159
|
// the role attribute is set to "listitem".
|
|
115
160
|
if (!this.hasAttribute('role')) {
|
|
@@ -124,84 +169,102 @@ class SideNavItem extends ElementMixin(ThemableMixin(PolylitMixin(LitElement)))
|
|
|
124
169
|
updated(props) {
|
|
125
170
|
super.updated(props);
|
|
126
171
|
|
|
127
|
-
if (props.has('path')) {
|
|
128
|
-
this.
|
|
172
|
+
if (props.has('path') || props.has('pathAliases')) {
|
|
173
|
+
this.__updateCurrent();
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// Ensure all the child items are disabled
|
|
177
|
+
if (props.has('disabled') || props.has('_itemsCount')) {
|
|
178
|
+
this._items.forEach((item) => {
|
|
179
|
+
item.disabled = this.disabled;
|
|
180
|
+
});
|
|
129
181
|
}
|
|
130
182
|
}
|
|
131
183
|
|
|
132
184
|
/** @protected */
|
|
133
185
|
connectedCallback() {
|
|
134
186
|
super.connectedCallback();
|
|
135
|
-
this.
|
|
136
|
-
|
|
137
|
-
window.addEventListener('popstate', this.
|
|
187
|
+
this.__updateCurrent();
|
|
188
|
+
|
|
189
|
+
window.addEventListener('popstate', this.__boundUpdateCurrent);
|
|
138
190
|
}
|
|
139
191
|
|
|
140
192
|
/** @protected */
|
|
141
193
|
disconnectedCallback() {
|
|
142
194
|
super.disconnectedCallback();
|
|
143
|
-
window.removeEventListener('popstate', this.
|
|
195
|
+
window.removeEventListener('popstate', this.__boundUpdateCurrent);
|
|
144
196
|
}
|
|
145
197
|
|
|
146
198
|
/** @protected */
|
|
147
199
|
render() {
|
|
148
200
|
return html`
|
|
149
|
-
<
|
|
150
|
-
<
|
|
151
|
-
|
|
152
|
-
|
|
201
|
+
<div part="content" @click="${this._onContentClick}">
|
|
202
|
+
<a
|
|
203
|
+
id="link"
|
|
204
|
+
?disabled="${this.disabled}"
|
|
205
|
+
tabindex="${this.disabled || !this.path ? '-1' : '0'}"
|
|
206
|
+
href="${ifDefined(this.disabled ? null : this.path)}"
|
|
207
|
+
part="link"
|
|
208
|
+
aria-current="${this.current ? 'page' : 'false'}"
|
|
209
|
+
>
|
|
210
|
+
<slot name="prefix"></slot>
|
|
211
|
+
<slot></slot>
|
|
212
|
+
<slot name="suffix"></slot>
|
|
213
|
+
</a>
|
|
153
214
|
<button
|
|
154
215
|
part="toggle-button"
|
|
155
|
-
|
|
156
|
-
|
|
216
|
+
?disabled="${this.disabled}"
|
|
217
|
+
@click="${this._onButtonClick}"
|
|
157
218
|
aria-controls="children"
|
|
158
219
|
aria-expanded="${this.expanded}"
|
|
159
|
-
aria-
|
|
220
|
+
aria-labelledby="link i18n"
|
|
160
221
|
></button>
|
|
161
|
-
</
|
|
162
|
-
<
|
|
222
|
+
</div>
|
|
223
|
+
<ul part="children" role="list" ?hidden="${!this.expanded}" aria-hidden="${this.expanded ? 'false' : 'true'}">
|
|
224
|
+
<slot name="children"></slot>
|
|
225
|
+
</ul>
|
|
226
|
+
<div class="sr-only" id="i18n">${this.i18n.toggle}</div>
|
|
163
227
|
`;
|
|
164
228
|
}
|
|
165
229
|
|
|
166
230
|
/** @private */
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
231
|
+
_onButtonClick(event) {
|
|
232
|
+
// Prevent the event from being handled
|
|
233
|
+
// by the content click listener below
|
|
234
|
+
event.stopPropagation();
|
|
235
|
+
this.__toggleExpanded();
|
|
171
236
|
}
|
|
172
237
|
|
|
173
238
|
/** @private */
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
}
|
|
179
|
-
this._setActive(this.__calculateActive());
|
|
180
|
-
this.toggleAttribute('child-active', document.location.pathname.startsWith(this.path));
|
|
181
|
-
if (this.active) {
|
|
182
|
-
this.expanded = true;
|
|
239
|
+
_onContentClick() {
|
|
240
|
+
// Toggle item expanded state unless the link has a non-empty path
|
|
241
|
+
if (this.path == null && this.hasAttribute('has-children')) {
|
|
242
|
+
this.__toggleExpanded();
|
|
183
243
|
}
|
|
184
244
|
}
|
|
185
245
|
|
|
186
246
|
/** @private */
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
247
|
+
__toggleExpanded() {
|
|
248
|
+
this.expanded = !this.expanded;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
/** @private */
|
|
252
|
+
__updateCurrent() {
|
|
253
|
+
this._setCurrent(this.__isCurrent());
|
|
254
|
+
if (this.current) {
|
|
255
|
+
this.expanded = this._items.length > 0;
|
|
193
256
|
}
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
/** @private */
|
|
260
|
+
__isCurrent() {
|
|
261
|
+
if (this.path == null) {
|
|
262
|
+
return false;
|
|
198
263
|
}
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
basePath !== pathRelativeToRoot && pathRelativeToRoot.startsWith(basePath) ? pathWithoutBase : pathRelativeToRoot;
|
|
204
|
-
return pathRelativeToBase === this.path;
|
|
264
|
+
return (
|
|
265
|
+
matchPaths(document.location.pathname, this.path) ||
|
|
266
|
+
this.pathAliases.some((alias) => matchPaths(document.location.pathname, alias))
|
|
267
|
+
);
|
|
205
268
|
}
|
|
206
269
|
}
|
|
207
270
|
|