@c2n/side-nav 0.0.6

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 ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 Nguyen Thai Vinh
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,19 @@
1
+ # @c2n/side-nav
2
+
3
+ `c2-side-nav` is an app-shell layout with a navigation drawer beside the page content. It pushes the content on large screens (`side` mode) and slides over it with a backdrop on small ones (`over` mode), switching automatically at 600px and 960px.
4
+
5
+ ```html
6
+ <c2-side-nav opened style="height: 100dvh">
7
+ <nav slot="side-nav-content">…</nav>
8
+ <main>
9
+ <button side-nav-toggle>Menu</button>
10
+
11
+ </main>
12
+ </c2-side-nav>
13
+ ```
14
+
15
+ - Attributes: `opened`, `desktop-mode` / `tablet-mode` (`side` | `over`), `responsive-tablet`, `position` (`start` | `end`).
16
+ - Any element with a `side-nav-toggle` attribute anywhere inside (page header or drawer) toggles the drawer; `toggle()` does the same from code. A non-zero `--c2-side-nav__close--width` gives an icon rail; hide labels with `c2-side-nav:not([opened]) .label { display: none }`.
17
+ - Event: `opened-change` (`detail.opened`) for user and breakpoint driven changes.
18
+ - Over mode: backdrop, page scroll lock, Escape to close, dialog semantics and focus management. Set `--c2-side-nav__over--position: absolute` to keep it inside a positioned parent.
19
+ - Theming: `--c2-side-nav--*` (widths, background, padding, borders, radius, transition), `__divider--*`, `__over--*`, `__backdrop--*`, `__scrollbar--color`.
@@ -0,0 +1,223 @@
1
+ import { LitElement, html, nothing, unsafeCSS } from "lit";
2
+ import { customElement, property, query, state } from "lit/decorators.js";
3
+ import { isServer } from "lit-html/is-server.js";
4
+ import { classMap } from "lit/directives/class-map.js";
5
+ import { BreakpointsObserver } from "@c2n/core/controllers/breakpoints-observer.js";
6
+ import { Breakpoints, smartFixedPosition } from "@c2n/core/dom-helper.js";
7
+ //#region src/side-nav.scss?inline
8
+ var side_nav_default = "/* ex : var((width: 24px), width, c2-checkbox) returns var(--c2-checkbox-width, 24px) */\n:host {\n display: flex;\n align-items: stretch;\n min-width: 0;\n}\n\n:host([hidden]) {\n display: none;\n}\n\n.c2-side-nav {\n display: flex;\n flex: 1 1 auto;\n min-width: 0;\n align-items: stretch;\n}\n.c2-side-nav.end {\n flex-direction: row-reverse;\n}\n\n.drawer {\n position: relative;\n flex: none;\n box-sizing: border-box;\n overflow: hidden;\n width: var(--c2-side-nav__close--width,0px);\n z-index: 1;\n transition: width var(--c2-side-nav--transition-duration,250ms) cubic-bezier(0, 0, 0, 1), transform var(--c2-side-nav--transition-duration,250ms) cubic-bezier(0, 0, 0, 1), visibility 0s linear var(--c2-side-nav--transition-duration,250ms);\n}\n\n:host([opened]) .drawer {\n width: var(--c2-side-nav__open--width,240px);\n}\n\n.c2-side-nav:not(.ready) .drawer {\n transition: none;\n}\n\n.panel {\n box-sizing: border-box;\n width: var(--c2-side-nav__open--width,240px);\n height: 100%;\n overflow-y: auto;\n overflow-x: hidden;\n overscroll-behavior: contain;\n scrollbar-width: thin;\n scrollbar-color: var(--c2-side-nav__scrollbar--color,rgba(0, 0, 0, 0.2)) transparent;\n background-color: var(--c2-side-nav--background-color,#fafafa);\n color: var(--c2-side-nav--color);\n border-top-left-radius: var(--c2-side-nav--border-top-left-radius);\n border-top-right-radius: var(--c2-side-nav--border-top-right-radius);\n border-bottom-left-radius: var(--c2-side-nav--border-bottom-left-radius);\n border-bottom-right-radius: var(--c2-side-nav--border-bottom-right-radius);\n padding-top: var(--c2-side-nav--padding-top,16px);\n padding-right: var(--c2-side-nav--padding-right,16px);\n padding-bottom: var(--c2-side-nav--padding-bottom,16px);\n padding-left: var(--c2-side-nav--padding-left,16px);\n border-top: var(--c2-side-nav--border-top);\n border-bottom: var(--c2-side-nav--border-bottom);\n border-right: var(--c2-side-nav--border-right);\n border-left: var(--c2-side-nav--border-left);\n border-inline-end: var(--c2-side-nav__divider--width,1px) solid var(--c2-side-nav__divider--color,#e4e4e7);\n transition: background-color var(--c2-side-nav--transition-duration,250ms) cubic-bezier(0.2, 0, 0, 1);\n}\n\n.c2-side-nav.end .panel {\n border-inline-end: none;\n border-inline-start: var(--c2-side-nav__divider--width,1px) solid var(--c2-side-nav__divider--color,#e4e4e7);\n}\n\n.content {\n flex: 1 1 auto;\n min-width: 0;\n}\n\n.c2-side-nav.over .drawer {\n position: var(--c2-side-nav__over--position,fixed);\n top: var(--c2-side-nav__over--top,0px);\n left: 0;\n height: var(--c2-side-nav__over--height,100dvh);\n width: var(--c2-side-nav__over--width,var(--c2-side-nav__open--width,240px));\n z-index: calc(var(--c2-side-nav__backdrop--z-index,1000) + 1);\n transform: translateX(-100%);\n visibility: hidden;\n overflow: visible;\n}\n.c2-side-nav.over.end .drawer {\n left: auto;\n right: 0;\n transform: translateX(100%);\n}\n.c2-side-nav.over .panel {\n width: 100%;\n border-inline-end: none;\n border-inline-start: none;\n background-color: var(--c2-side-nav__over--background-color,var(--c2-side-nav--background-color,#fafafa));\n border-top-left-radius: var(--c2-side-nav__over--border-top-left-radius,var(--c2-side-nav--border-top-left-radius));\n border-top-right-radius: var(--c2-side-nav__over--border-top-right-radius,var(--c2-side-nav--border-top-right-radius));\n border-bottom-left-radius: var(--c2-side-nav__over--border-bottom-left-radius,var(--c2-side-nav--border-bottom-left-radius));\n border-bottom-right-radius: var(--c2-side-nav__over--border-bottom-right-radius,var(--c2-side-nav--border-bottom-right-radius));\n padding-top: var(--c2-side-nav__over--padding-top,var(--c2-side-nav--padding-top,16px));\n padding-right: var(--c2-side-nav__over--padding-right,var(--c2-side-nav--padding-right,16px));\n padding-bottom: var(--c2-side-nav__over--padding-bottom,var(--c2-side-nav--padding-bottom,16px));\n padding-left: var(--c2-side-nav__over--padding-left,var(--c2-side-nav--padding-left,16px));\n box-shadow: var(--c2-side-nav__over--box-shadow,0 12px 40px rgba(0, 0, 0, 0.18));\n}\n\n:host([opened]) .c2-side-nav.over .drawer {\n transform: none;\n visibility: visible;\n transition: transform var(--c2-side-nav--transition-duration,250ms) cubic-bezier(0, 0, 0, 1), visibility 0s;\n}\n\n.backdrop {\n position: var(--c2-side-nav__over--position,fixed);\n inset: 0;\n z-index: var(--c2-side-nav__backdrop--z-index,1000);\n background-color: var(--c2-side-nav__backdrop--background-color,rgba(0, 0, 0, 0.32));\n backdrop-filter: var(--c2-side-nav__backdrop--backdrop-filter,none);\n animation: c2-side-nav-fade-in var(--c2-side-nav--transition-duration,250ms) cubic-bezier(0.2, 0, 0, 1);\n}\n\n@keyframes c2-side-nav-fade-in {\n from {\n opacity: 0;\n }\n}";
9
+ //#endregion
10
+ //#region \0@oxc-project+runtime@0.148.0/helpers/esm/decorate.js
11
+ function __decorate(decorators, target, key, desc) {
12
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
13
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
14
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
15
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
16
+ }
17
+ //#endregion
18
+ //#region src/side-nav.ts
19
+ var BREAKPOINT_QUERIES = [
20
+ Breakpoints.Phone,
21
+ Breakpoints.Tablet,
22
+ Breakpoints.Desktop
23
+ ];
24
+ var FOCUSABLE = "a[href], button:not([disabled]), input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex=\"-1\"])";
25
+ var SideNav = class SideNav extends LitElement {
26
+ constructor(..._args) {
27
+ super(..._args);
28
+ this.opened = false;
29
+ this.desktopMode = "side";
30
+ this.tabletMode = "over";
31
+ this.responsiveTablet = false;
32
+ this.position = "start";
33
+ this.breakpoint = "desktop";
34
+ this.ready = false;
35
+ this.preferredOpened = false;
36
+ this.previouslyFocused = null;
37
+ this.scrollLocked = false;
38
+ this.breakpointsController = new BreakpointsObserver(this, BREAKPOINT_QUERIES, (isInitial) => this.handleBreakpoint(isInitial));
39
+ this.handleBackdropClick = () => {
40
+ this.setOpened(false, true);
41
+ };
42
+ this.handleKeydown = (event) => {
43
+ if (event.key === "Escape" && this.opened && this.mode === "over") {
44
+ event.preventDefault();
45
+ this.setOpened(false, true);
46
+ }
47
+ };
48
+ }
49
+ static {
50
+ this.styles = unsafeCSS(side_nav_default);
51
+ }
52
+ /** The mode in effect for the current viewport: `over` on phones, otherwise `tablet-mode` / `desktop-mode`. */
53
+ get mode() {
54
+ if (this.breakpoint === "phone") return "over";
55
+ if (this.breakpoint === "tablet") return this.tabletMode;
56
+ return this.desktopMode;
57
+ }
58
+ /** Opens or closes the drawer as a user action would (fires `opened-change`). */
59
+ toggle(opened = !this.opened) {
60
+ this.setOpened(opened, true);
61
+ }
62
+ connectedCallback() {
63
+ super.connectedCallback();
64
+ this.preferredOpened = this.opened;
65
+ }
66
+ disconnectedCallback() {
67
+ super.disconnectedCallback();
68
+ window.removeEventListener("keydown", this.handleKeydown);
69
+ this.lockScroll(false);
70
+ }
71
+ collapsesAt(breakpoint) {
72
+ if (breakpoint === "phone") return true;
73
+ if (breakpoint === "tablet") return this.tabletMode === "over" || this.responsiveTablet;
74
+ return this.desktopMode === "over";
75
+ }
76
+ /**
77
+ * On load the `opened` attribute is meant for the layout the author designed for: it is ignored on phones and for the
78
+ * implicit tablet overlay, but honoured when an overlay mode was chosen explicitly (`desktop-mode="over"`).
79
+ */
80
+ closesOnLoadAt(breakpoint) {
81
+ if (breakpoint === "phone") return true;
82
+ if (breakpoint === "tablet") return this.responsiveTablet || this.tabletMode === "over" && !this.hasAttribute("tablet-mode");
83
+ return false;
84
+ }
85
+ handleBreakpoint(isInitial) {
86
+ const query = this.breakpointsController.currentBreakpoint;
87
+ this.breakpoint = query === Breakpoints.Phone ? "phone" : query === Breakpoints.Tablet ? "tablet" : "desktop";
88
+ if (isInitial) {
89
+ this.preferredOpened = this.opened;
90
+ if (this.closesOnLoadAt(this.breakpoint)) this.opened = false;
91
+ this.updateComplete.then(() => requestAnimationFrame(() => this.ready = true));
92
+ return;
93
+ }
94
+ if (this.collapsesAt(this.breakpoint)) {
95
+ if (this.opened) {
96
+ this.preferredOpened = true;
97
+ this.setOpened(false, true);
98
+ }
99
+ } else if (this.opened !== this.preferredOpened) this.setOpened(this.preferredOpened, true);
100
+ }
101
+ setOpened(opened, emit) {
102
+ if (this.opened === opened) return;
103
+ this.opened = opened;
104
+ if (this.mode === "side") this.preferredOpened = opened;
105
+ if (emit) this.dispatchEvent(new CustomEvent("opened-change", {
106
+ bubbles: true,
107
+ composed: true,
108
+ detail: { opened }
109
+ }));
110
+ }
111
+ /** Click delegation for `[side-nav-toggle]` elements anywhere in the light DOM (drawer or content). */
112
+ handleToggleClick(event) {
113
+ const path = event.composedPath();
114
+ const triggerIndex = path.findIndex((node) => node instanceof Element && node.hasAttribute("side-nav-toggle"));
115
+ if (triggerIndex === -1 || path[triggerIndex] === this) return;
116
+ if (path.slice(triggerIndex + 1).find((node) => node instanceof Element && node.localName === this.localName) !== this) return;
117
+ this.toggle();
118
+ }
119
+ lockScroll(lock) {
120
+ if (lock === this.scrollLocked) return;
121
+ if (lock && (isServer || getComputedStyle(this.drawer).position !== "fixed")) return;
122
+ this.scrollLocked = lock;
123
+ smartFixedPosition(lock);
124
+ }
125
+ syncOverlayState() {
126
+ const overlayOpen = this.opened && this.mode === "over";
127
+ window.removeEventListener("keydown", this.handleKeydown);
128
+ if (overlayOpen) {
129
+ window.addEventListener("keydown", this.handleKeydown);
130
+ this.lockScroll(true);
131
+ this.previouslyFocused = document.activeElement;
132
+ this.focusDrawer();
133
+ } else {
134
+ this.lockScroll(false);
135
+ const focused = document.activeElement;
136
+ if (focused && this.previouslyFocused instanceof HTMLElement && (focused === this || this.contains(focused))) this.previouslyFocused.focus();
137
+ this.previouslyFocused = null;
138
+ }
139
+ }
140
+ focusDrawer() {
141
+ const slot = this.renderRoot.querySelector("slot[name=\"side-nav-content\"]");
142
+ for (const element of slot?.assignedElements({ flatten: true }) ?? []) {
143
+ const target = element.matches(FOCUSABLE) ? element : element.querySelector(FOCUSABLE);
144
+ if (target instanceof HTMLElement) {
145
+ target.focus();
146
+ return;
147
+ }
148
+ }
149
+ this.drawer.focus();
150
+ }
151
+ /** In side mode a collapsed drawer with a non-zero closed width is a rail and must stay interactive. */
152
+ get drawerInert() {
153
+ if (this.opened) return false;
154
+ if (this.mode === "over") return true;
155
+ if (isServer) return true;
156
+ return (parseFloat(getComputedStyle(this).getPropertyValue("--c2-side-nav__close--width")) || 0) === 0;
157
+ }
158
+ updated(changed) {
159
+ this.drawer.toggleAttribute("inert", this.drawerInert);
160
+ this.contentElement.toggleAttribute("inert", this.opened && this.mode === "over");
161
+ if (changed.has("opened") || changed.has("breakpoint") || changed.has("desktopMode") || changed.has("tabletMode")) {
162
+ if ((changed.has("opened") ? changed.get("opened") && this.mode === "over" : void 0) !== (this.opened && this.mode === "over") || changed.has("breakpoint")) this.syncOverlayState();
163
+ }
164
+ }
165
+ render() {
166
+ const over = this.mode === "over";
167
+ const overlayOpen = over && this.opened;
168
+ return html`
169
+ <div class="c2-side-nav ${classMap({
170
+ over,
171
+ ready: this.ready,
172
+ end: this.position === "end"
173
+ })}" @click=${this.handleToggleClick}>
174
+ ${overlayOpen ? html`<div class="backdrop" part="backdrop" @click=${this.handleBackdropClick}></div>` : nothing}
175
+ <div
176
+ class="drawer"
177
+ part="drawer"
178
+ role=${over ? "dialog" : nothing}
179
+ aria-modal=${over ? "true" : nothing}
180
+ aria-hidden=${!this.opened && over ? "true" : nothing}
181
+ tabindex="-1"
182
+ >
183
+ <div class="panel" part="panel">
184
+ <slot name="side-nav-content"></slot>
185
+ </div>
186
+ </div>
187
+ <div class="content" part="content">
188
+ <slot></slot>
189
+ </div>
190
+ </div>
191
+ `;
192
+ }
193
+ };
194
+ __decorate([property({
195
+ type: Boolean,
196
+ reflect: true
197
+ })], SideNav.prototype, "opened", void 0);
198
+ __decorate([property({
199
+ type: String,
200
+ reflect: true,
201
+ attribute: "desktop-mode"
202
+ })], SideNav.prototype, "desktopMode", void 0);
203
+ __decorate([property({
204
+ type: String,
205
+ reflect: true,
206
+ attribute: "tablet-mode"
207
+ })], SideNav.prototype, "tabletMode", void 0);
208
+ __decorate([property({
209
+ type: Boolean,
210
+ reflect: true,
211
+ attribute: "responsive-tablet"
212
+ })], SideNav.prototype, "responsiveTablet", void 0);
213
+ __decorate([property({
214
+ type: String,
215
+ reflect: true
216
+ })], SideNav.prototype, "position", void 0);
217
+ __decorate([state()], SideNav.prototype, "breakpoint", void 0);
218
+ __decorate([state()], SideNav.prototype, "ready", void 0);
219
+ __decorate([query(".drawer")], SideNav.prototype, "drawer", void 0);
220
+ __decorate([query(".content")], SideNav.prototype, "contentElement", void 0);
221
+ SideNav = __decorate([customElement("c2-side-nav")], SideNav);
222
+ //#endregion
223
+ export { SideNav };
package/package.json ADDED
@@ -0,0 +1,60 @@
1
+ {
2
+ "name": "@c2n/side-nav",
3
+ "version": "0.0.6",
4
+ "type": "module",
5
+ "main": "dist/side-nav.js",
6
+ "exports": {
7
+ ".": {
8
+ "default": "./dist/side-nav.js",
9
+ "types": "./types/src/side-nav.d.ts"
10
+ },
11
+ "./custom-elements.json": "./custom-elements.json"
12
+ },
13
+ "files": [
14
+ "dist",
15
+ "types"
16
+ ],
17
+ "keywords": [
18
+ "side-nav",
19
+ "web component",
20
+ "lit"
21
+ ],
22
+ "author": "code2nguyen@gmail.com",
23
+ "publishConfig": {
24
+ "registry": "https://registry.npmjs.org",
25
+ "access": "public"
26
+ },
27
+ "repository": {
28
+ "type": "git",
29
+ "url": "https://github.com/code2nguyen/web-components.git"
30
+ },
31
+ "scripts": {
32
+ "dev": "vite",
33
+ "build": "wireit",
34
+ "build:only": "vite build",
35
+ "type-check": "wireit"
36
+ },
37
+ "wireit": {
38
+ "type-check": {
39
+ "dependencies": [
40
+ "../../core:build"
41
+ ],
42
+ "command": "tsc -p tsconfig.lib.json --composite false"
43
+ },
44
+ "build": {
45
+ "dependencies": [
46
+ "type-check"
47
+ ],
48
+ "command": "vite build"
49
+ }
50
+ },
51
+ "dependencies": {
52
+ "@c2n/core": "0.0.6",
53
+ "lit": "3.3.3"
54
+ },
55
+ "devDependencies": {
56
+ "@c2n/config": "*"
57
+ },
58
+ "customElements": "custom-elements.json",
59
+ "gitHead": "2921054bc75299da66dad11c1e374246ff88a51e"
60
+ }
@@ -0,0 +1,126 @@
1
+ import { LitElement, type PropertyValues } from 'lit';
2
+ export type DisplayMode = 'over' | 'side';
3
+ export type SideNavPosition = 'start' | 'end';
4
+ export type SideNavBreakpoint = 'phone' | 'tablet' | 'desktop';
5
+ export interface OpenedChangeEventDetail {
6
+ opened: boolean;
7
+ }
8
+ /**
9
+ * A layout shell with a navigation drawer beside the page content. The drawer goes in the `side-nav-content` slot, the
10
+ * page in the default slot, and the host stretches both to its own height. In `side` mode the drawer collapses in the
11
+ * flow and pushes the content; in `over` mode it slides over the content with a backdrop, locks page scrolling, traps
12
+ * focus as a dialog and closes on Escape or a backdrop click. Phones always use `over`; `tablet-mode` and
13
+ * `desktop-mode` pick the mode for the two larger breakpoints (600px and 960px), and the drawer remembers whether it
14
+ * was open when the viewport crosses them.
15
+ *
16
+ * Any element inside the side nav (drawer or content) carrying a `side-nav-toggle` attribute toggles the nearest enclosing side nav when
17
+ * clicked, so a menu button in the page or a collapse button in the drawer header needs no script. With a non-zero
18
+ * `--c2-side-nav__close--width` the collapsed drawer becomes an icon rail; the reflected `opened` attribute lets your
19
+ * CSS hide the labels in that state (`c2-side-nav:not([opened]) .label { display: none }`).
20
+ *
21
+ * @tag c2-side-nav
22
+ *
23
+ * @slot side-nav-content - The drawer content: a `c2-list` of links, a logo, anything.
24
+ * @slot default - The page content beside the drawer.
25
+ *
26
+ * @event {CustomEvent<OpenedChangeEventDetail>} opened-change - Fired when the user or a breakpoint change opens or closes the drawer (not when `opened` is set from code). `detail.opened` is the new state.
27
+ *
28
+ * @cssproperty {pixel} [--c2-side-nav__open--width=240px]
29
+ * @cssproperty {pixel} [--c2-side-nav__close--width=0px] - Width of the collapsed drawer; a non-zero value gives a rail.
30
+ * @cssproperty {time} [--c2-side-nav--transition-duration=250ms]
31
+ *
32
+ * @cssproperty {color} [--c2-side-nav--background-color=#fafafa]
33
+ * @cssproperty {color} --c2-side-nav--color
34
+ *
35
+ * @cssproperty {border-radius} --c2-side-nav--border-top-left-radius
36
+ * @cssproperty {border-radius} --c2-side-nav--border-top-right-radius
37
+ * @cssproperty {border-radius} --c2-side-nav--border-bottom-left-radius
38
+ * @cssproperty {border-radius} --c2-side-nav--border-bottom-right-radius
39
+ *
40
+ * @cssproperty {padding} [--c2-side-nav--padding-top=16px]
41
+ * @cssproperty {padding} [--c2-side-nav--padding-right=16px]
42
+ * @cssproperty {padding} [--c2-side-nav--padding-bottom=16px]
43
+ * @cssproperty {padding} [--c2-side-nav--padding-left=16px]
44
+ *
45
+ * @cssproperty {border} --c2-side-nav--border-top
46
+ * @cssproperty {border} --c2-side-nav--border-right
47
+ * @cssproperty {border} --c2-side-nav--border-bottom
48
+ * @cssproperty {border} --c2-side-nav--border-left
49
+ *
50
+ * @cssproperty {pixel} [--c2-side-nav__divider--width=1px] - Line between the drawer and the content, on whichever side the drawer is.
51
+ * @cssproperty {color} [--c2-side-nav__divider--color=#e4e4e7]
52
+ *
53
+ * @cssproperty {color} [--c2-side-nav__scrollbar--color=rgba(0, 0, 0, 0.2)]
54
+ *
55
+ * @cssproperty {position} [--c2-side-nav__over--position=fixed] - `absolute` keeps the overlay inside a positioned parent.
56
+ * @cssproperty {pixel} [--c2-side-nav__over--top=0px]
57
+ * @cssproperty {pixel} [--c2-side-nav__over--height=100dvh]
58
+ * @cssproperty {pixel} --c2-side-nav__over--width - Defaults to the open width.
59
+ * @cssproperty {color} --c2-side-nav__over--background-color - Defaults to the drawer background.
60
+ * @cssproperty {box-shadow} [--c2-side-nav__over--box-shadow=0 12px 40px rgba(0, 0, 0, 0.18)]
61
+ * @cssproperty {border-radius} --c2-side-nav__over--border-top-left-radius
62
+ * @cssproperty {border-radius} --c2-side-nav__over--border-top-right-radius
63
+ * @cssproperty {border-radius} --c2-side-nav__over--border-bottom-left-radius
64
+ * @cssproperty {border-radius} --c2-side-nav__over--border-bottom-right-radius
65
+ * @cssproperty {padding} --c2-side-nav__over--padding-top
66
+ * @cssproperty {padding} --c2-side-nav__over--padding-right
67
+ * @cssproperty {padding} --c2-side-nav__over--padding-bottom
68
+ * @cssproperty {padding} --c2-side-nav__over--padding-left
69
+ *
70
+ * @cssproperty {color} [--c2-side-nav__backdrop--background-color=rgba(0, 0, 0, 0.32)]
71
+ * @cssproperty {filter} [--c2-side-nav__backdrop--backdrop-filter=none]
72
+ * @cssproperty {number} [--c2-side-nav__backdrop--z-index=1000]
73
+ */
74
+ export declare class SideNav extends LitElement {
75
+ static styles: import("lit").CSSResult;
76
+ /** Whether the drawer is open. On phones (and on tablets unless `tablet-mode` is set explicitly) the initial value is ignored and the drawer starts closed. */
77
+ opened: boolean;
78
+ /** How the drawer behaves at 960px and up: `side` pushes the content, `over` slides above it. */
79
+ desktopMode: DisplayMode;
80
+ /** How the drawer behaves between 600px and 959px. */
81
+ tabletMode: DisplayMode;
82
+ /** Also collapse the drawer on tablets in `side` mode when the viewport shrinks to that size. */
83
+ responsiveTablet: boolean;
84
+ /** Which edge the drawer is on: `start` (left in LTR) or `end`. */
85
+ position: SideNavPosition;
86
+ private breakpoint;
87
+ private ready;
88
+ private drawer;
89
+ private contentElement;
90
+ /** The state the user wants in `side` mode, restored when the viewport grows back. */
91
+ private preferredOpened;
92
+ private previouslyFocused;
93
+ private scrollLocked;
94
+ private breakpointsController;
95
+ /** The mode in effect for the current viewport: `over` on phones, otherwise `tablet-mode` / `desktop-mode`. */
96
+ get mode(): DisplayMode;
97
+ /** Opens or closes the drawer as a user action would (fires `opened-change`). */
98
+ toggle(opened?: boolean): void;
99
+ connectedCallback(): void;
100
+ disconnectedCallback(): void;
101
+ private collapsesAt;
102
+ /**
103
+ * On load the `opened` attribute is meant for the layout the author designed for: it is ignored on phones and for the
104
+ * implicit tablet overlay, but honoured when an overlay mode was chosen explicitly (`desktop-mode="over"`).
105
+ */
106
+ private closesOnLoadAt;
107
+ private handleBreakpoint;
108
+ private setOpened;
109
+ private handleBackdropClick;
110
+ private handleKeydown;
111
+ /** Click delegation for `[side-nav-toggle]` elements anywhere in the light DOM (drawer or content). */
112
+ private handleToggleClick;
113
+ private lockScroll;
114
+ private syncOverlayState;
115
+ private focusDrawer;
116
+ /** In side mode a collapsed drawer with a non-zero closed width is a rail and must stay interactive. */
117
+ private get drawerInert();
118
+ protected updated(changed: PropertyValues): void;
119
+ render(): import("lit-html").TemplateResult<1>;
120
+ }
121
+ declare global {
122
+ interface HTMLElementTagNameMap {
123
+ 'c2-side-nav': SideNav;
124
+ }
125
+ }
126
+ //# sourceMappingURL=side-nav.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"side-nav.d.ts","sourceRoot":"","sources":["../../src/side-nav.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAA4B,KAAK,cAAc,EAAE,MAAM,KAAK,CAAA;AAQ/E,MAAM,MAAM,WAAW,GAAG,MAAM,GAAG,MAAM,CAAA;AACzC,MAAM,MAAM,eAAe,GAAG,OAAO,GAAG,KAAK,CAAA;AAC7C,MAAM,MAAM,iBAAiB,GAAG,OAAO,GAAG,QAAQ,GAAG,SAAS,CAAA;AAE9D,MAAM,WAAW,uBAAuB;IACtC,MAAM,EAAE,OAAO,CAAA;CAChB;AAKD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiEG;AACH,qBACa,OAAQ,SAAQ,UAAU;IACrC,OAAgB,MAAM,0BAAoB;IAE1C,+JAA+J;IACnH,MAAM,UAAQ;IAE1D,iGAAiG;IAC3B,WAAW,EAAE,WAAW,CAAS;IAEvG,sDAAsD;IACe,UAAU,EAAE,WAAW,CAAS;IAErG,iGAAiG;IACrB,gBAAgB,UAAQ;IAEpG,mEAAmE;IACxB,QAAQ,EAAE,eAAe,CAAU;IAErE,OAAO,CAAC,UAAU,CAA+B;IACjD,OAAO,CAAC,KAAK,CAAQ;IAEZ,OAAO,CAAC,MAAM,CAAc;IAC3B,OAAO,CAAC,cAAc,CAAc;IAEvD,sFAAsF;IACtF,OAAO,CAAC,eAAe,CAAQ;IAC/B,OAAO,CAAC,iBAAiB,CAAuB;IAChD,OAAO,CAAC,YAAY,CAAQ;IAE5B,OAAO,CAAC,qBAAqB,CAAqG;IAElI,+GAA+G;IAC/G,IAAI,IAAI,IAAI,WAAW,CAItB;IAED,iFAAiF;IACjF,MAAM,CAAC,MAAM,UAAe;IAInB,iBAAiB,IAAI,IAAI;IAKzB,oBAAoB,IAAI,IAAI;IAMrC,OAAO,CAAC,WAAW;IAMnB;;;OAGG;IACH,OAAO,CAAC,cAAc;IAMtB,OAAO,CAAC,gBAAgB;IAqBxB,OAAO,CAAC,SAAS;IASjB,OAAO,CAAC,mBAAmB,CAE1B;IAED,OAAO,CAAC,aAAa,CAKpB;IAED,uGAAuG;IACvG,OAAO,CAAC,iBAAiB;IAWzB,OAAO,CAAC,UAAU;IAQlB,OAAO,CAAC,gBAAgB;IAkBxB,OAAO,CAAC,WAAW;IAYnB,wGAAwG;IACxG,OAAO,KAAK,WAAW,GAMtB;cAEkB,OAAO,CAAC,OAAO,EAAE,cAAc,GAAG,IAAI;IAYhD,MAAM;CAwBhB;AAED,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,qBAAqB;QAC7B,aAAa,EAAE,OAAO,CAAA;KACvB;CACF"}