@vaadin/side-nav 24.1.2 → 24.2.0-alpha2

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 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.1.2",
3
+ "version": "24.2.0-alpha2",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
@@ -35,10 +35,10 @@
35
35
  "web-component"
36
36
  ],
37
37
  "dependencies": {
38
- "@vaadin/component-base": "~24.1.2",
39
- "@vaadin/vaadin-lumo-styles": "~24.1.2",
40
- "@vaadin/vaadin-material-styles": "~24.1.2",
41
- "@vaadin/vaadin-themable-mixin": "~24.1.2",
38
+ "@vaadin/component-base": "24.2.0-alpha2",
39
+ "@vaadin/vaadin-lumo-styles": "24.2.0-alpha2",
40
+ "@vaadin/vaadin-material-styles": "24.2.0-alpha2",
41
+ "@vaadin/vaadin-themable-mixin": "24.2.0-alpha2",
42
42
  "lit": "^2.0.0"
43
43
  },
44
44
  "devDependencies": {
@@ -51,5 +51,5 @@
51
51
  "web-types.json",
52
52
  "web-types.lit.json"
53
53
  ],
54
- "gitHead": "2d52aec53816cf8ee5cc4e08344adfb0c0b4d2f7"
54
+ "gitHead": "b8378bd2267728e172012bcb2ea45c3e5a6e590a"
55
55
  }
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * @license
3
- * Copyright (c) 2017 - 2023 Vaadin Ltd.
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,17 @@ 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
- a {
18
+ [part='content'] {
19
+ display: flex;
20
+ align-items: center;
21
+ }
22
+
23
+ [part='link'] {
18
24
  flex: auto;
19
25
  min-width: 0;
20
26
  display: flex;
@@ -28,10 +34,21 @@ export const sideNavItemBaseStyles = css`
28
34
  -webkit-appearance: none;
29
35
  appearance: none;
30
36
  flex: none;
37
+ position: relative;
38
+ margin: 0;
39
+ padding: 0;
40
+ border: 0;
41
+ background: transparent;
31
42
  }
32
43
 
33
- :host(:not([path])) a {
34
- position: relative;
44
+ [part='children'] {
45
+ padding: 0;
46
+ margin: 0;
47
+ list-style-type: none;
48
+ }
49
+
50
+ :host(:not([has-children])) button {
51
+ display: none !important;
35
52
  }
36
53
 
37
54
  :host(:not([path])) button::after {
@@ -56,12 +73,6 @@ export const sideNavItemBaseStyles = css`
56
73
  text-overflow: ellipsis;
57
74
  white-space: nowrap;
58
75
  }
59
-
60
- slot[name='children'] {
61
- /* Needed to make role="list" work */
62
- display: block;
63
- width: 100%;
64
- }
65
76
  `;
66
77
 
67
78
  export const sideNavBaseStyles = css`
@@ -73,32 +84,24 @@ export const sideNavBaseStyles = css`
73
84
  display: none !important;
74
85
  }
75
86
 
76
- summary {
87
+ button {
77
88
  display: flex;
78
89
  align-items: center;
79
- justify-content: space-between;
80
- }
81
-
82
- summary ::slotted([slot='label']) {
83
- display: block;
84
- }
85
-
86
- summary::-webkit-details-marker {
87
- display: none;
88
- }
89
-
90
- summary::marker {
91
- content: '';
92
- }
93
-
94
- summary::after {
95
- display: inline-flex;
96
- align-items: center;
97
- justify-content: center;
90
+ justify-content: inherit;
91
+ width: 100%;
92
+ margin: 0;
93
+ padding: 0;
94
+ background-color: initial;
95
+ color: inherit;
96
+ border: initial;
97
+ outline: none;
98
+ font: inherit;
99
+ text-align: inherit;
98
100
  }
99
101
 
100
- slot {
101
- /* Needed to make role="list" work */
102
- display: block;
102
+ [part='children'] {
103
+ padding: 0;
104
+ margin: 0;
105
+ list-style-type: none;
103
106
  }
104
107
  `;
@@ -0,0 +1,41 @@
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
+ }
@@ -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
+ * @private
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,13 @@
1
1
  /**
2
2
  * @license
3
- * Copyright (c) 2017 - 2023 Vaadin Ltd.
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
7
  import { ElementMixin } from '@vaadin/component-base/src/element-mixin.js';
8
8
  import { PolylitMixin } from '@vaadin/component-base/src/polylit-mixin.js';
9
9
  import { ThemableMixin } from '@vaadin/vaadin-themable-mixin/vaadin-themable-mixin.js';
10
+ import { SideNavChildrenMixin } from './vaadin-side-nav-children-mixin.js';
10
11
 
11
12
  /**
12
13
  * Fired when the `expanded` property changes.
@@ -54,14 +55,39 @@ export type SideNavItemEventMap = HTMLElementEventMap & SideNavItemCustomEventMa
54
55
  * </vaadin-side-nav-item>
55
56
  * ```
56
57
  *
58
+ * ### Styling
59
+ *
60
+ * The following shadow DOM parts are available for styling:
61
+ *
62
+ * Part name | Description
63
+ * ----------------|----------------
64
+ * `content` | The element that wraps link and toggle button
65
+ * `children` | The element that wraps child items
66
+ * `link` | The clickable anchor used for navigation
67
+ * `toggle-button` | The toggle button
68
+ *
69
+ * The following state attributes are available for styling:
70
+ *
71
+ * Attribute | Description
72
+ * ---------------|-------------
73
+ * `expanded` | Set when the element is expanded.
74
+ * `has-children` | Set when the element has child items.
75
+ *
76
+ * See [Styling Components](https://vaadin.com/docs/latest/styling/styling-components) documentation.
77
+ *
57
78
  * @fires {CustomEvent} expanded-changed - Fired when the `expanded` property changes.
58
79
  */
59
- declare class SideNavItem extends ElementMixin(ThemableMixin(PolylitMixin(LitElement))) {
80
+ declare class SideNavItem extends SideNavChildrenMixin(ElementMixin(ThemableMixin(PolylitMixin(LitElement)))) {
60
81
  /**
61
82
  * The path to navigate to
62
83
  */
63
84
  path: string | null | undefined;
64
85
 
86
+ /**
87
+ * The list of alternative paths matching this item
88
+ */
89
+ pathAliases: string[];
90
+
65
91
  /**
66
92
  * Whether to show the child items or not
67
93
  */
@@ -72,7 +98,7 @@ declare class SideNavItem extends ElementMixin(ThemableMixin(PolylitMixin(LitEle
72
98
  * Set when the item is appended to DOM or when navigated back
73
99
  * to the page that contains this item using the browser.
74
100
  */
75
- readonly active: boolean;
101
+ readonly current: boolean;
76
102
 
77
103
  addEventListener<K extends keyof SideNavItemEventMap>(
78
104
  type: K,
@@ -1,14 +1,17 @@
1
1
  /**
2
2
  * @license
3
- * Copyright (c) 2017 - 2023 Vaadin Ltd.
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 { screenReaderOnly } from '@vaadin/a11y-base/src/styles/sr-only-styles.js';
8
9
  import { ElementMixin } from '@vaadin/component-base/src/element-mixin.js';
9
10
  import { PolylitMixin } from '@vaadin/component-base/src/polylit-mixin.js';
11
+ import { matchPaths } from '@vaadin/component-base/src/url-utils.js';
10
12
  import { ThemableMixin } from '@vaadin/vaadin-themable-mixin/vaadin-themable-mixin.js';
11
13
  import { sideNavItemBaseStyles } from './vaadin-side-nav-base-styles.js';
14
+ import { SideNavChildrenMixin } from './vaadin-side-nav-children-mixin.js';
12
15
 
13
16
  function isEnabled() {
14
17
  return window.Vaadin && window.Vaadin.featureFlags && !!window.Vaadin.featureFlags.sideNavComponent;
@@ -49,14 +52,35 @@ function isEnabled() {
49
52
  * </vaadin-side-nav-item>
50
53
  * ```
51
54
  *
55
+ * ### Styling
56
+ *
57
+ * The following shadow DOM parts are available for styling:
58
+ *
59
+ * Part name | Description
60
+ * ----------------|----------------
61
+ * `content` | The element that wraps link and toggle button
62
+ * `children` | The element that wraps child items
63
+ * `link` | The clickable anchor used for navigation
64
+ * `toggle-button` | The toggle button
65
+ *
66
+ * The following state attributes are available for styling:
67
+ *
68
+ * Attribute | Description
69
+ * ---------------|-------------
70
+ * `expanded` | Set when the element is expanded.
71
+ * `has-children` | Set when the element has child items.
72
+ *
73
+ * See [Styling Components](https://vaadin.com/docs/latest/styling/styling-components) documentation.
74
+ *
52
75
  * @fires {CustomEvent} expanded-changed - Fired when the `expanded` property changes.
53
76
  *
54
77
  * @extends LitElement
55
78
  * @mixes PolylitMixin
56
79
  * @mixes ThemableMixin
57
80
  * @mixes ElementMixin
81
+ * @mixes SideNavChildrenMixin
58
82
  */
59
- class SideNavItem extends ElementMixin(ThemableMixin(PolylitMixin(LitElement))) {
83
+ class SideNavItem extends SideNavChildrenMixin(ElementMixin(ThemableMixin(PolylitMixin(LitElement)))) {
60
84
  static get is() {
61
85
  return 'vaadin-side-nav-item';
62
86
  }
@@ -68,6 +92,16 @@ class SideNavItem extends ElementMixin(ThemableMixin(PolylitMixin(LitElement)))
68
92
  */
69
93
  path: String,
70
94
 
95
+ /**
96
+ * The list of alternative paths matching this item
97
+ *
98
+ * @type {!Array<string>}
99
+ */
100
+ pathAliases: {
101
+ type: Array,
102
+ value: () => [],
103
+ },
104
+
71
105
  /**
72
106
  * Whether to show the child items or not
73
107
  *
@@ -87,7 +121,7 @@ class SideNavItem extends ElementMixin(ThemableMixin(PolylitMixin(LitElement)))
87
121
  *
88
122
  * @type {boolean}
89
123
  */
90
- active: {
124
+ current: {
91
125
  type: Boolean,
92
126
  value: false,
93
127
  readOnly: true,
@@ -97,7 +131,13 @@ class SideNavItem extends ElementMixin(ThemableMixin(PolylitMixin(LitElement)))
97
131
  }
98
132
 
99
133
  static get styles() {
100
- return sideNavItemBaseStyles;
134
+ return [screenReaderOnly, sideNavItemBaseStyles];
135
+ }
136
+
137
+ constructor() {
138
+ super();
139
+
140
+ this.__boundUpdateCurrent = this.__updateCurrent.bind(this);
101
141
  }
102
142
 
103
143
  /** @protected */
@@ -110,6 +150,8 @@ class SideNavItem extends ElementMixin(ThemableMixin(PolylitMixin(LitElement)))
110
150
  * @override
111
151
  */
112
152
  firstUpdated() {
153
+ super.firstUpdated();
154
+
113
155
  // By default, if the user hasn't provided a custom role,
114
156
  // the role attribute is set to "listitem".
115
157
  if (!this.hasAttribute('role')) {
@@ -124,84 +166,87 @@ class SideNavItem extends ElementMixin(ThemableMixin(PolylitMixin(LitElement)))
124
166
  updated(props) {
125
167
  super.updated(props);
126
168
 
127
- if (props.has('path')) {
128
- this.__updateActive();
169
+ if (props.has('path') || props.has('pathAliases')) {
170
+ this.__updateCurrent();
129
171
  }
130
172
  }
131
173
 
132
174
  /** @protected */
133
175
  connectedCallback() {
134
176
  super.connectedCallback();
135
- this.__updateActive();
136
- this.__boundUpdateActive = this.__updateActive.bind(this);
137
- window.addEventListener('popstate', this.__boundUpdateActive);
177
+ this.__updateCurrent();
178
+
179
+ window.addEventListener('popstate', this.__boundUpdateCurrent);
138
180
  }
139
181
 
140
182
  /** @protected */
141
183
  disconnectedCallback() {
142
184
  super.disconnectedCallback();
143
- window.removeEventListener('popstate', this.__boundUpdateActive);
185
+ window.removeEventListener('popstate', this.__boundUpdateCurrent);
144
186
  }
145
187
 
146
188
  /** @protected */
147
189
  render() {
148
190
  return html`
149
- <a href="${ifDefined(this.path)}" part="item" aria-current="${this.active ? 'page' : 'false'}">
150
- <slot name="prefix"></slot>
151
- <slot></slot>
152
- <slot name="suffix"></slot>
191
+ <div part="content" @click="${this._onContentClick}">
192
+ <a id="link" href="${ifDefined(this.path)}" part="link" aria-current="${this.current ? 'page' : 'false'}">
193
+ <slot name="prefix"></slot>
194
+ <slot></slot>
195
+ <slot name="suffix"></slot>
196
+ </a>
153
197
  <button
154
198
  part="toggle-button"
155
- @click="${this.__toggleExpanded}"
156
- ?hidden="${!this.querySelector('[slot=children]')}"
199
+ @click="${this._onButtonClick}"
157
200
  aria-controls="children"
158
201
  aria-expanded="${this.expanded}"
159
- aria-label="Toggle child items"
202
+ aria-labelledby="link i18n"
160
203
  ></button>
161
- </a>
162
- <slot name="children" role="list" part="children" id="children" ?hidden="${!this.expanded}"></slot>
204
+ </div>
205
+ <ul part="children" ?hidden="${!this.expanded}">
206
+ <slot name="children"></slot>
207
+ </ul>
208
+ <div class="sr-only" id="i18n">${this.i18n.toggle}</div>
163
209
  `;
164
210
  }
165
211
 
166
212
  /** @private */
167
- __toggleExpanded(e) {
168
- e.preventDefault();
169
- e.stopPropagation();
170
- this.expanded = !this.expanded;
213
+ _onButtonClick(event) {
214
+ // Prevent the event from being handled
215
+ // by the content click listener below
216
+ event.stopPropagation();
217
+ this.__toggleExpanded();
171
218
  }
172
219
 
173
220
  /** @private */
174
- __updateActive() {
175
- if (!this.path && this.path !== '') {
176
- this._setActive(false);
177
- return;
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;
221
+ _onContentClick() {
222
+ // Toggle item expanded state unless the link has a non-empty path
223
+ if (this.path == null && this.hasAttribute('has-children')) {
224
+ this.__toggleExpanded();
183
225
  }
184
226
  }
185
227
 
186
228
  /** @private */
187
- __calculateActive() {
188
- const pathAbsolute = this.path.startsWith('/');
189
- // Absolute path or no base uri in use. No special comparison needed
190
- if (pathAbsolute) {
191
- // Compare an absolute view path
192
- return document.location.pathname === this.path;
229
+ __toggleExpanded() {
230
+ this.expanded = !this.expanded;
231
+ }
232
+
233
+ /** @private */
234
+ __updateCurrent() {
235
+ this._setCurrent(this.__isCurrent());
236
+ if (this.current) {
237
+ this.expanded = this._items.length > 0;
193
238
  }
194
- const hasBaseUri = document.baseURI !== document.location.href;
195
- if (!hasBaseUri) {
196
- // Compare a relative view path (strip the starting slash)
197
- return document.location.pathname.substring(1) === this.path;
239
+ }
240
+
241
+ /** @private */
242
+ __isCurrent() {
243
+ if (this.path == null) {
244
+ return false;
198
245
  }
199
- const pathRelativeToRoot = document.location.pathname;
200
- const basePath = new URL(document.baseURI).pathname;
201
- const pathWithoutBase = pathRelativeToRoot.substring(basePath.length);
202
- const pathRelativeToBase =
203
- basePath !== pathRelativeToRoot && pathRelativeToRoot.startsWith(basePath) ? pathWithoutBase : pathRelativeToRoot;
204
- return pathRelativeToBase === this.path;
246
+ return (
247
+ matchPaths(document.location.pathname, this.path) ||
248
+ this.pathAliases.some((alias) => matchPaths(document.location.pathname, alias))
249
+ );
205
250
  }
206
251
  }
207
252
 
@@ -4,9 +4,13 @@
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 { FocusMixin } from '@vaadin/a11y-base/src/focus-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, type SideNavI18n } from './vaadin-side-nav-children-mixin.js';
12
+
13
+ export type { SideNavI18n };
10
14
 
11
15
  /**
12
16
  * Fired when the `collapsed` property changes.
@@ -48,9 +52,29 @@ export type SideNavEventMap = HTMLElementEventMap & SideNavCustomEventMap;
48
52
  * </vaadin-side-nav>
49
53
  * ```
50
54
  *
55
+ * ### Styling
56
+ *
57
+ * The following shadow DOM parts are available for styling:
58
+ *
59
+ * Part name | Description
60
+ * ----------------|----------------
61
+ * `label` | The label element
62
+ * `children` | The element that wraps child items
63
+ * `toggle-button` | The toggle button
64
+ *
65
+ * The following state attributes are available for styling:
66
+ *
67
+ * Attribute | Description
68
+ * -------------|-------------
69
+ * `collapsed` | Set when the element is collapsed.
70
+ * `focus-ring` | Set when the label is focused using the keyboard.
71
+ * `focused` | Set when the label is focused.
72
+ *
73
+ * See [Styling Components](https://vaadin.com/docs/latest/styling/styling-components) documentation.
74
+ *
51
75
  * @fires {CustomEvent} collapsed-changed - Fired when the `collapsed` property changes.
52
76
  */
53
- declare class SideNav extends ElementMixin(ThemableMixin(PolylitMixin(LitElement))) {
77
+ declare class SideNav extends SideNavChildrenMixin(FocusMixin(ElementMixin(ThemableMixin(PolylitMixin(LitElement))))) {
54
78
  /**
55
79
  * Whether the side nav is collapsible. When enabled, the toggle icon is shown.
56
80
  */