@reliability-design/web 1.4.3 → 1.4.5
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/dist/components/rel-accordion/rel-accordion.ts +13 -1
- package/dist/components/rel-autocomplete/README.md +1 -0
- package/dist/components/rel-autocomplete/rel-autocomplete.ts +56 -1
- package/dist/components/rel-button/rel-button.ts +18 -2
- package/dist/components/rel-calendar/rel-calendar.ts +23 -1
- package/dist/components/rel-carousel/rel-carousel.ts +20 -0
- package/dist/components/rel-checkbox/README.md +69 -22
- package/dist/components/rel-checkbox/rel-checkbox.ts +119 -9
- package/dist/components/rel-chip/rel-chip.ts +29 -0
- package/dist/components/rel-date-picker/rel-date-picker.ts +156 -13
- package/dist/components/rel-drawer/rel-drawer.ts +22 -58
- package/dist/components/rel-menu/rel-menu.ts +124 -15
- package/dist/components/rel-menubar/rel-menubar.ts +221 -63
- package/dist/components/rel-nav-menu/rel-nav-menu.ts +39 -3
- package/dist/components/rel-otp/rel-otp.ts +3 -3
- package/dist/components/rel-pagination/README.md +14 -0
- package/dist/components/rel-pagination/rel-pagination.ts +30 -14
- package/dist/components/rel-popover/rel-popover.ts +31 -3
- package/dist/components/rel-range-picker/rel-range-picker.ts +251 -3
- package/dist/components/rel-resizable/rel-resizable-group.ts +20 -0
- package/dist/components/rel-segmented-button/rel-segmented-button.ts +50 -12
- package/dist/components/rel-select/README.md +1 -0
- package/dist/components/rel-select/rel-select.ts +1385 -1170
- package/dist/components/rel-sheet/rel-sheet.ts +22 -1
- package/dist/components/rel-sidebar/rel-sidebar.ts +43 -39
- package/dist/components/rel-stepper/rel-stepper.ts +44 -1
- package/dist/components/rel-tab/rel-tab.ts +32 -7
- package/dist/components/rel-time-picker/rel-time-picker.ts +338 -206
- package/dist/components/rel-tooltip/rel-tooltip.ts +30 -0
- package/dist/components/rel-topbar/rel-topbar-actions.ts +1 -1
- package/dist/components/rel-topbar/rel-topbar-brand.ts +1 -1
- package/dist/components/rel-topbar/rel-topbar-dropdown-item.ts +1 -1
- package/dist/components/rel-topbar/rel-topbar-dropdown.ts +1 -1
- package/dist/components/rel-topbar/rel-topbar-item.ts +318 -319
- package/dist/components/rel-topbar/rel-topbar-mobile-panel.ts +1 -1
- package/dist/components/rel-topbar/rel-topbar-profile.ts +224 -224
- package/dist/components/rel-topbar/rel-topbar-utilities.ts +1 -1
- package/dist/components/rel-topbar/rel-topbar.ts +440 -440
- package/dist/components/rel-upload/UPLOAD-DESIGN-CHANGES.md +134 -0
- package/dist/components/rel-upload/rel-upload-actions.ts +163 -0
- package/dist/components/rel-upload/rel-upload.ts +943 -0
- package/dist/index.d.mts +491 -18
- package/dist/index.d.ts +491 -18
- package/dist/index.js +2748 -410
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +2953 -627
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
|
@@ -1,446 +1,446 @@
|
|
|
1
|
-
import { LitElement, html, css } from "lit";
|
|
2
|
-
import { customElement, property, state } from "lit/decorators.js";
|
|
3
|
-
import "@reliability-design/icons/rel-icon";
|
|
4
|
-
import "./rel-topbar-brand";
|
|
5
|
-
import "./rel-topbar-actions";
|
|
6
|
-
import "./rel-topbar-utilities";
|
|
7
|
-
import "./rel-topbar-mobile-panel";
|
|
8
|
-
import "./rel-topbar-dropdown";
|
|
9
|
-
import "./rel-topbar-dropdown-item";
|
|
10
|
-
|
|
11
|
-
export type TopbarVariant = "customer" | "carrier";
|
|
12
|
-
export type TopbarForceLayout = "auto" | "desktop" | "tablet" | "mobile";
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* `rel-topbar` — Application-level top navigation / header shell.
|
|
16
|
-
*
|
|
17
|
-
* @deprecated This component's fixed slot structure, hardcoded breakpoint,
|
|
18
|
-
* and built-in overflow logic are too rigid for most real layouts. Use the
|
|
19
|
-
* "
|
|
20
|
-
* rel-menu/rel-popover, rel-drawer, and rel-button. Still supported, but
|
|
21
|
-
* will be removed in a future major version.
|
|
22
|
-
*
|
|
23
|
-
* Slots:
|
|
24
|
-
* brand — Logo / wordmark region (left)
|
|
25
|
-
* (default) — Primary nav items (rel-topbar-item elements)
|
|
26
|
-
* actions — CTA / action buttons (customer) or dark-pill area (carrier)
|
|
27
|
-
* utilities — Utility icon buttons (notifications, search, etc.)
|
|
28
|
-
* profile — rel-topbar-profile tile
|
|
29
|
-
* mobile-panel — Collapsed nav panel shown when mobileOpen is true
|
|
30
|
-
*
|
|
31
|
-
* Events:
|
|
32
|
-
* rel-menu-toggle — fired when hamburger is clicked { open: boolean }
|
|
33
|
-
* rel-mobile-open-change — alias of rel-menu-toggle for convenience
|
|
34
|
-
* rel-nav-select — bubbled from child rel-topbar-item { value, label }
|
|
35
|
-
*/
|
|
36
|
-
@customElement("rel-topbar")
|
|
37
|
-
export class RelTopbar extends LitElement {
|
|
38
|
-
/** Visual variant — affects action/profile region styling */
|
|
39
|
-
@property({ type: String, reflect: true }) variant: TopbarVariant = "customer";
|
|
40
|
-
|
|
41
|
-
/** Whether the mobile nav panel is currently open */
|
|
42
|
-
@property({ type: Boolean, reflect: true }) mobileOpen = false;
|
|
43
|
-
|
|
44
|
-
/** Whether to render the hamburger / close toggle button */
|
|
45
|
-
@property({ type: Boolean, reflect: true }) showMenuToggle = false;
|
|
46
|
-
|
|
47
|
-
/** Reduce height to compact (48 px) mode */
|
|
48
|
-
@property({ type: Boolean, reflect: true }) compact = false;
|
|
49
|
-
|
|
50
|
-
/** Stick topbar to top of viewport while scrolling */
|
|
51
|
-
@property({ type: Boolean, reflect: true }) sticky = false;
|
|
52
|
-
|
|
53
|
-
/**
|
|
54
|
-
* Override the responsive layout for demos / storybook.
|
|
55
|
-
* 'auto' respects CSS media queries.
|
|
56
|
-
*/
|
|
57
|
-
@property({ type: String }) forceLayout: TopbarForceLayout = "auto";
|
|
58
|
-
|
|
59
|
-
private _overflowItems: Element[] = [];
|
|
60
|
-
@state() private _overflowCount: number = 0;
|
|
61
|
-
@state() private _overflowItemsData: Array<{ label: string; href: string; disabled: boolean }> = [];
|
|
62
|
-
private _resizeObserver?: ResizeObserver;
|
|
63
|
-
private _resizeTimer?: number;
|
|
64
|
-
private _measuring = false;
|
|
65
|
-
private _lastNavWidth = 0;
|
|
66
|
-
|
|
67
|
-
static styles = css`
|
|
68
|
-
/* ─── Component tokens ─────────────────────────────────────────── */
|
|
69
|
-
:host {
|
|
70
|
-
color: var(--rel-text-default);
|
|
71
|
-
--rel-topbar-height: 64px;
|
|
72
|
-
--rel-topbar-height-compact: 48px;
|
|
73
|
-
--rel-topbar-bg: var(--rel-bg-surface);
|
|
74
|
-
--rel-topbar-border-color: var(--rel-border-default);
|
|
75
|
-
--rel-topbar-item-color: var(--rel-text-default);
|
|
76
|
-
--rel-topbar-item-active-color: var(--rel-text-success);
|
|
77
|
-
--rel-topbar-item-active-indicator: var(--rel-bg-brand);
|
|
78
|
-
--rel-topbar-item-hover-bg: var(--rel-bg-surface-elevated);
|
|
79
|
-
--rel-topbar-divider-color: var(--rel-border-default);
|
|
80
|
-
--rel-topbar-carrier-action-bg: var(--rel-text-default);
|
|
81
|
-
--rel-topbar-carrier-action-color: var(--rel-bg-surface);
|
|
82
|
-
--rel-topbar-z: 100;
|
|
83
|
-
|
|
84
|
-
display: block;
|
|
85
|
-
width: 100%;
|
|
86
|
-
font-family: var(--rel-fonts-font-family, Poppins, sans-serif);
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
/* ─── Sticky host ───────────────────────────────────────────────── */
|
|
90
|
-
:host([sticky]) {
|
|
91
|
-
position: sticky;
|
|
92
|
-
top: 0;
|
|
93
|
-
z-index: var(--rel-topbar-z);
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
/* ─── Bar shell ─────────────────────────────────────────────────── */
|
|
97
|
-
.topbar {
|
|
98
|
-
display: flex;
|
|
99
|
-
align-items: center;
|
|
100
|
-
height: var(--rel-topbar-height);
|
|
101
|
-
background: var(--rel-topbar-bg);
|
|
102
|
-
border-bottom: var(--rel-stroke-sm, 1px) solid var(--rel-topbar-border-color);
|
|
103
|
-
padding: 0 var(--rel-spacing-xl, 24px);
|
|
104
|
-
box-sizing: border-box;
|
|
105
|
-
gap: var(--rel-spacing-md, 16px);
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
:host([compact]) .topbar {
|
|
109
|
-
height: var(--rel-topbar-height-compact);
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
/* ─── Brand ─────────────────────────────────────────────────────── */
|
|
113
|
-
.brand-region {
|
|
114
|
-
display: flex;
|
|
115
|
-
align-items: center;
|
|
116
|
-
flex-shrink: 0;
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
/* ─── Nav (primary, desktop) ────────────────────────────────────── */
|
|
120
|
-
.nav-region {
|
|
121
|
-
display: flex;
|
|
122
|
-
align-items: center;
|
|
123
|
-
flex: 1 1 0;
|
|
124
|
-
gap: var(--rel-spacing-2xs, 4px);
|
|
125
|
-
min-width: 0;
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
/* ─── Right side cluster ────────────────────────────────────────── */
|
|
129
|
-
.right-region {
|
|
130
|
-
display: flex;
|
|
131
|
-
align-items: center;
|
|
132
|
-
flex-shrink: 0;
|
|
133
|
-
margin-left: auto;
|
|
134
|
-
gap: var(--rel-spacing-xs, 8px);
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
.actions-region,
|
|
138
|
-
.utilities-region,
|
|
139
|
-
.profile-region {
|
|
140
|
-
display: contents;
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
/* Vertical divider between util/profile when both are populated */
|
|
144
|
-
.divider {
|
|
145
|
-
width: var(--rel-stroke-sm, 1px);
|
|
146
|
-
height: 24px;
|
|
147
|
-
background: var(--rel-topbar-divider-color);
|
|
148
|
-
flex-shrink: 0;
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
/* ─── Carrier action pill wrapper ───────────────────────────────── */
|
|
152
|
-
:host([variant="carrier"]) .actions-region ::slotted(*) {
|
|
153
|
-
display: flex;
|
|
154
|
-
align-items: center;
|
|
155
|
-
gap: var(--rel-spacing-xs, 8px);
|
|
156
|
-
background: var(--rel-topbar-carrier-action-bg);
|
|
157
|
-
color: var(--rel-topbar-carrier-action-color);
|
|
158
|
-
padding: var(--rel-spacing-2xs, 4px) var(--rel-spacing-sm, 12px);
|
|
159
|
-
border-radius: var(--rel-border-radius-full-rounded, 1000px);
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
/* ─── Mobile toggle button ──────────────────────────────────────── */
|
|
163
|
-
.menu-toggle {
|
|
164
|
-
display: none;
|
|
165
|
-
align-items: center;
|
|
166
|
-
justify-content: center;
|
|
167
|
-
width: 40px;
|
|
168
|
-
height: 40px;
|
|
169
|
-
padding: 0;
|
|
170
|
-
border: none;
|
|
171
|
-
background: transparent;
|
|
172
|
-
border-radius: var(--rel-border-radius-sm, 8px);
|
|
173
|
-
cursor: pointer;
|
|
174
|
-
color: var(--rel-topbar-item-color);
|
|
175
|
-
flex-shrink: 0;
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
.menu-toggle:focus-visible {
|
|
179
|
-
outline: 2px solid var(--rel-topbar-item-active-indicator);
|
|
180
|
-
outline-offset: 2px;
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
.menu-toggle:hover {
|
|
184
|
-
background: var(--rel-topbar-item-hover-bg);
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
.menu-toggle svg {
|
|
188
|
-
display: block;
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
/* ─── Mobile panel ──────────────────────────────────────────────── */
|
|
192
|
-
.mobile-panel {
|
|
193
|
-
display: none;
|
|
194
|
-
background: var(--rel-topbar-bg);
|
|
195
|
-
border-bottom: var(--rel-stroke-sm, 1px) solid var(--rel-topbar-border-color);
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
.mobile-panel.open {
|
|
199
|
-
display: block;
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
.mobile-overflow-nav {
|
|
203
|
-
display: flex;
|
|
204
|
-
flex-direction: column;
|
|
205
|
-
padding: var(--rel-spacing-xs, 8px) 0;
|
|
206
|
-
border-top: 1px solid var(--rel-topbar-border-color);
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
.mobile-overflow-item {
|
|
210
|
-
display: block;
|
|
211
|
-
padding: var(--rel-spacing-xs, 8px) var(--rel-spacing-xl, 24px);
|
|
212
|
-
color: var(--rel-topbar-item-color);
|
|
1
|
+
import { LitElement, html, css } from "lit";
|
|
2
|
+
import { customElement, property, state } from "lit/decorators.js";
|
|
3
|
+
import "@reliability-design/icons/rel-icon";
|
|
4
|
+
import "./rel-topbar-brand";
|
|
5
|
+
import "./rel-topbar-actions";
|
|
6
|
+
import "./rel-topbar-utilities";
|
|
7
|
+
import "./rel-topbar-mobile-panel";
|
|
8
|
+
import "./rel-topbar-dropdown";
|
|
9
|
+
import "./rel-topbar-dropdown-item";
|
|
10
|
+
|
|
11
|
+
export type TopbarVariant = "customer" | "carrier";
|
|
12
|
+
export type TopbarForceLayout = "auto" | "desktop" | "tablet" | "mobile";
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* `rel-topbar` — Application-level top navigation / header shell.
|
|
16
|
+
*
|
|
17
|
+
* @deprecated This component's fixed slot structure, hardcoded breakpoint,
|
|
18
|
+
* and built-in overflow logic are too rigid for most real layouts. Use the
|
|
19
|
+
* "Navbar" recipe at /docs/recipes/navbar instead, composed from
|
|
20
|
+
* rel-menu/rel-popover, rel-drawer, and rel-button. Still supported, but
|
|
21
|
+
* will be removed in a future major version.
|
|
22
|
+
*
|
|
23
|
+
* Slots:
|
|
24
|
+
* brand — Logo / wordmark region (left)
|
|
25
|
+
* (default) — Primary nav items (rel-topbar-item elements)
|
|
26
|
+
* actions — CTA / action buttons (customer) or dark-pill area (carrier)
|
|
27
|
+
* utilities — Utility icon buttons (notifications, search, etc.)
|
|
28
|
+
* profile — rel-topbar-profile tile
|
|
29
|
+
* mobile-panel — Collapsed nav panel shown when mobileOpen is true
|
|
30
|
+
*
|
|
31
|
+
* Events:
|
|
32
|
+
* rel-menu-toggle — fired when hamburger is clicked { open: boolean }
|
|
33
|
+
* rel-mobile-open-change — alias of rel-menu-toggle for convenience
|
|
34
|
+
* rel-nav-select — bubbled from child rel-topbar-item { value, label }
|
|
35
|
+
*/
|
|
36
|
+
@customElement("rel-topbar")
|
|
37
|
+
export class RelTopbar extends LitElement {
|
|
38
|
+
/** Visual variant — affects action/profile region styling */
|
|
39
|
+
@property({ type: String, reflect: true }) variant: TopbarVariant = "customer";
|
|
40
|
+
|
|
41
|
+
/** Whether the mobile nav panel is currently open */
|
|
42
|
+
@property({ type: Boolean, reflect: true }) mobileOpen = false;
|
|
43
|
+
|
|
44
|
+
/** Whether to render the hamburger / close toggle button */
|
|
45
|
+
@property({ type: Boolean, reflect: true }) showMenuToggle = false;
|
|
46
|
+
|
|
47
|
+
/** Reduce height to compact (48 px) mode */
|
|
48
|
+
@property({ type: Boolean, reflect: true }) compact = false;
|
|
49
|
+
|
|
50
|
+
/** Stick topbar to top of viewport while scrolling */
|
|
51
|
+
@property({ type: Boolean, reflect: true }) sticky = false;
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Override the responsive layout for demos / storybook.
|
|
55
|
+
* 'auto' respects CSS media queries.
|
|
56
|
+
*/
|
|
57
|
+
@property({ type: String }) forceLayout: TopbarForceLayout = "auto";
|
|
58
|
+
|
|
59
|
+
private _overflowItems: Element[] = [];
|
|
60
|
+
@state() private _overflowCount: number = 0;
|
|
61
|
+
@state() private _overflowItemsData: Array<{ label: string; href: string; disabled: boolean }> = [];
|
|
62
|
+
private _resizeObserver?: ResizeObserver;
|
|
63
|
+
private _resizeTimer?: number;
|
|
64
|
+
private _measuring = false;
|
|
65
|
+
private _lastNavWidth = 0;
|
|
66
|
+
|
|
67
|
+
static styles = css`
|
|
68
|
+
/* ─── Component tokens ─────────────────────────────────────────── */
|
|
69
|
+
:host {
|
|
70
|
+
color: var(--rel-text-default);
|
|
71
|
+
--rel-topbar-height: 64px;
|
|
72
|
+
--rel-topbar-height-compact: 48px;
|
|
73
|
+
--rel-topbar-bg: var(--rel-bg-surface);
|
|
74
|
+
--rel-topbar-border-color: var(--rel-border-default);
|
|
75
|
+
--rel-topbar-item-color: var(--rel-text-default);
|
|
76
|
+
--rel-topbar-item-active-color: var(--rel-text-success);
|
|
77
|
+
--rel-topbar-item-active-indicator: var(--rel-bg-brand);
|
|
78
|
+
--rel-topbar-item-hover-bg: var(--rel-bg-surface-elevated);
|
|
79
|
+
--rel-topbar-divider-color: var(--rel-border-default);
|
|
80
|
+
--rel-topbar-carrier-action-bg: var(--rel-text-default);
|
|
81
|
+
--rel-topbar-carrier-action-color: var(--rel-bg-surface);
|
|
82
|
+
--rel-topbar-z: 100;
|
|
83
|
+
|
|
84
|
+
display: block;
|
|
85
|
+
width: 100%;
|
|
86
|
+
font-family: var(--rel-fonts-font-family, Poppins, sans-serif);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/* ─── Sticky host ───────────────────────────────────────────────── */
|
|
90
|
+
:host([sticky]) {
|
|
91
|
+
position: sticky;
|
|
92
|
+
top: 0;
|
|
93
|
+
z-index: var(--rel-topbar-z);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/* ─── Bar shell ─────────────────────────────────────────────────── */
|
|
97
|
+
.topbar {
|
|
98
|
+
display: flex;
|
|
99
|
+
align-items: center;
|
|
100
|
+
height: var(--rel-topbar-height);
|
|
101
|
+
background: var(--rel-topbar-bg);
|
|
102
|
+
border-bottom: var(--rel-stroke-sm, 1px) solid var(--rel-topbar-border-color);
|
|
103
|
+
padding: 0 var(--rel-spacing-xl, 24px);
|
|
104
|
+
box-sizing: border-box;
|
|
105
|
+
gap: var(--rel-spacing-md, 16px);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
:host([compact]) .topbar {
|
|
109
|
+
height: var(--rel-topbar-height-compact);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/* ─── Brand ─────────────────────────────────────────────────────── */
|
|
113
|
+
.brand-region {
|
|
114
|
+
display: flex;
|
|
115
|
+
align-items: center;
|
|
116
|
+
flex-shrink: 0;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/* ─── Nav (primary, desktop) ────────────────────────────────────── */
|
|
120
|
+
.nav-region {
|
|
121
|
+
display: flex;
|
|
122
|
+
align-items: center;
|
|
123
|
+
flex: 1 1 0;
|
|
124
|
+
gap: var(--rel-spacing-2xs, 4px);
|
|
125
|
+
min-width: 0;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/* ─── Right side cluster ────────────────────────────────────────── */
|
|
129
|
+
.right-region {
|
|
130
|
+
display: flex;
|
|
131
|
+
align-items: center;
|
|
132
|
+
flex-shrink: 0;
|
|
133
|
+
margin-left: auto;
|
|
134
|
+
gap: var(--rel-spacing-xs, 8px);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
.actions-region,
|
|
138
|
+
.utilities-region,
|
|
139
|
+
.profile-region {
|
|
140
|
+
display: contents;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/* Vertical divider between util/profile when both are populated */
|
|
144
|
+
.divider {
|
|
145
|
+
width: var(--rel-stroke-sm, 1px);
|
|
146
|
+
height: 24px;
|
|
147
|
+
background: var(--rel-topbar-divider-color);
|
|
148
|
+
flex-shrink: 0;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/* ─── Carrier action pill wrapper ───────────────────────────────── */
|
|
152
|
+
:host([variant="carrier"]) .actions-region ::slotted(*) {
|
|
153
|
+
display: flex;
|
|
154
|
+
align-items: center;
|
|
155
|
+
gap: var(--rel-spacing-xs, 8px);
|
|
156
|
+
background: var(--rel-topbar-carrier-action-bg);
|
|
157
|
+
color: var(--rel-topbar-carrier-action-color);
|
|
158
|
+
padding: var(--rel-spacing-2xs, 4px) var(--rel-spacing-sm, 12px);
|
|
159
|
+
border-radius: var(--rel-border-radius-full-rounded, 1000px);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/* ─── Mobile toggle button ──────────────────────────────────────── */
|
|
163
|
+
.menu-toggle {
|
|
164
|
+
display: none;
|
|
165
|
+
align-items: center;
|
|
166
|
+
justify-content: center;
|
|
167
|
+
width: 40px;
|
|
168
|
+
height: 40px;
|
|
169
|
+
padding: 0;
|
|
170
|
+
border: none;
|
|
171
|
+
background: transparent;
|
|
172
|
+
border-radius: var(--rel-border-radius-sm, 8px);
|
|
173
|
+
cursor: pointer;
|
|
174
|
+
color: var(--rel-topbar-item-color);
|
|
175
|
+
flex-shrink: 0;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
.menu-toggle:focus-visible {
|
|
179
|
+
outline: 2px solid var(--rel-topbar-item-active-indicator);
|
|
180
|
+
outline-offset: 2px;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
.menu-toggle:hover {
|
|
184
|
+
background: var(--rel-topbar-item-hover-bg);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
.menu-toggle svg {
|
|
188
|
+
display: block;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/* ─── Mobile panel ──────────────────────────────────────────────── */
|
|
192
|
+
.mobile-panel {
|
|
193
|
+
display: none;
|
|
194
|
+
background: var(--rel-topbar-bg);
|
|
195
|
+
border-bottom: var(--rel-stroke-sm, 1px) solid var(--rel-topbar-border-color);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
.mobile-panel.open {
|
|
199
|
+
display: block;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
.mobile-overflow-nav {
|
|
203
|
+
display: flex;
|
|
204
|
+
flex-direction: column;
|
|
205
|
+
padding: var(--rel-spacing-xs, 8px) 0;
|
|
206
|
+
border-top: 1px solid var(--rel-topbar-border-color);
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
.mobile-overflow-item {
|
|
210
|
+
display: block;
|
|
211
|
+
padding: var(--rel-spacing-xs, 8px) var(--rel-spacing-xl, 24px);
|
|
212
|
+
color: var(--rel-topbar-item-color);
|
|
213
213
|
text-decoration: none;
|
|
214
214
|
font-family: var(--rel-body-md-medium-font-family);
|
|
215
215
|
font-weight: var(--rel-body-md-medium-font-weight);
|
|
216
216
|
font-size: var(--rel-body-md-medium-font-size);
|
|
217
217
|
line-height: var(--rel-body-md-medium-line-height);
|
|
218
218
|
letter-spacing: var(--rel-body-md-medium-letter-spacing);
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
.mobile-overflow-item:hover {
|
|
222
|
-
background: var(--rel-topbar-item-hover-bg);
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
.mobile-overflow-item.disabled {
|
|
226
|
-
opacity: 0.45;
|
|
227
|
-
pointer-events: none;
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
/* ─── Responsive: auto mode (CSS media queries) ─────────────────── */
|
|
231
|
-
@media (max-width: 768px) {
|
|
232
|
-
.topbar:not(.force-desktop) .nav-region {
|
|
233
|
-
display: none;
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
.topbar:not(.force-desktop) .menu-toggle {
|
|
237
|
-
display: flex;
|
|
238
|
-
}
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
@media (min-width: 769px) {
|
|
242
|
-
.topbar:not(.force-mobile):not(.force-tablet) .menu-toggle {
|
|
243
|
-
display: none;
|
|
244
|
-
}
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
/* ─── forceLayout overrides ─────────────────────────────────────── */
|
|
248
|
-
.topbar.force-mobile .nav-region,
|
|
249
|
-
.topbar.force-tablet .nav-region {
|
|
250
|
-
display: none;
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
.topbar.force-mobile .menu-toggle,
|
|
254
|
-
.topbar.force-tablet .menu-toggle {
|
|
255
|
-
display: flex;
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
.topbar.force-desktop .nav-region {
|
|
259
|
-
display: flex;
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
.topbar.force-desktop .menu-toggle {
|
|
263
|
-
display: none;
|
|
264
|
-
}
|
|
265
|
-
`;
|
|
266
|
-
|
|
267
|
-
override firstUpdated() {
|
|
268
|
-
const navRegion = this.renderRoot.querySelector(".nav-region") as HTMLElement;
|
|
269
|
-
if (navRegion) {
|
|
270
|
-
this._resizeObserver = new ResizeObserver(() => {
|
|
271
|
-
clearTimeout(this._resizeTimer);
|
|
272
|
-
this._resizeTimer = window.setTimeout(() => this._measureNav(), 150);
|
|
273
|
-
});
|
|
274
|
-
this._resizeObserver.observe(navRegion);
|
|
275
|
-
}
|
|
276
|
-
requestAnimationFrame(() => this._measureNav());
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
override disconnectedCallback() {
|
|
280
|
-
super.disconnectedCallback();
|
|
281
|
-
this._resizeObserver?.disconnect();
|
|
282
|
-
clearTimeout(this._resizeTimer);
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
private _measureNav() {
|
|
286
|
-
if (this._measuring) return;
|
|
287
|
-
this._measuring = true;
|
|
288
|
-
const navRegion = this.renderRoot.querySelector(".nav-region") as HTMLElement;
|
|
289
|
-
if (!navRegion) return;
|
|
290
|
-
const navSlot = this.renderRoot.querySelector('slot[name="nav"]') as HTMLSlotElement;
|
|
291
|
-
if (!navSlot) return;
|
|
292
|
-
const items = Array.from(navSlot.assignedElements()).filter(
|
|
293
|
-
el => el.tagName.toLowerCase() !== "rel-topbar-item" || !el.hasAttribute("data-more")
|
|
294
|
-
);
|
|
295
|
-
const navWidth = navRegion.getBoundingClientRect().width;
|
|
296
|
-
if (Math.abs(navWidth - this._lastNavWidth) < 2) return;
|
|
297
|
-
this._lastNavWidth = navWidth;
|
|
298
|
-
const available = navWidth - 120;
|
|
299
|
-
|
|
300
|
-
let accumulated = 0;
|
|
301
|
-
let firstOverflowIndex = items.length;
|
|
302
|
-
for (let i = 0; i < items.length; i++) {
|
|
303
|
-
accumulated += items[i].getBoundingClientRect().width;
|
|
304
|
-
if (accumulated > available) {
|
|
305
|
-
firstOverflowIndex = i;
|
|
306
|
-
break;
|
|
307
|
-
}
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
const overflowingElements = Array.from(items).slice(firstOverflowIndex);
|
|
311
|
-
this._overflowItems = overflowingElements;
|
|
312
|
-
this._overflowCount = overflowingElements.length;
|
|
313
|
-
this._overflowItemsData = overflowingElements.map(el => ({
|
|
314
|
-
label: el.getAttribute("label") ?? "",
|
|
315
|
-
href: el.getAttribute("href") ?? "",
|
|
316
|
-
disabled: el.hasAttribute("disabled"),
|
|
317
|
-
}));
|
|
318
|
-
|
|
319
|
-
for (const el of items) {
|
|
320
|
-
if (overflowingElements.includes(el)) {
|
|
321
|
-
el.setAttribute("hidden", "");
|
|
322
|
-
} else {
|
|
323
|
-
el.removeAttribute("hidden");
|
|
324
|
-
}
|
|
325
|
-
}
|
|
326
|
-
|
|
327
|
-
this._measuring = false;
|
|
328
|
-
}
|
|
329
|
-
|
|
330
|
-
private onRouterSlotChange(e: Event) {
|
|
331
|
-
const slot = e.target as HTMLSlotElement;
|
|
332
|
-
for (const el of slot.assignedElements({ flatten: true })) {
|
|
333
|
-
const tag = (el as HTMLElement).tagName.toLowerCase();
|
|
334
|
-
const targetSlot =
|
|
335
|
-
tag === "rel-topbar-brand" ? "brand" :
|
|
336
|
-
tag === "rel-topbar-actions" ? "actions" :
|
|
337
|
-
tag === "rel-topbar-utilities" ? "utilities" :
|
|
338
|
-
tag === "rel-topbar-profile" ? "profile" :
|
|
339
|
-
tag === "rel-topbar-mobile-panel" ? "mobile-panel" :
|
|
340
|
-
"nav";
|
|
341
|
-
if ((el as HTMLElement).slot !== targetSlot) {
|
|
342
|
-
(el as HTMLElement).slot = targetSlot;
|
|
343
|
-
}
|
|
344
|
-
}
|
|
345
|
-
}
|
|
346
|
-
|
|
347
|
-
private get layoutClass(): string {
|
|
348
|
-
if (this.forceLayout === "auto") return "";
|
|
349
|
-
return `force-${this.forceLayout}`;
|
|
350
|
-
}
|
|
351
|
-
|
|
352
|
-
private handleMenuToggle() {
|
|
353
|
-
this.mobileOpen = !this.mobileOpen;
|
|
354
|
-
const detail = { open: this.mobileOpen };
|
|
355
|
-
this.dispatchEvent(
|
|
356
|
-
new CustomEvent("rel-menu-toggle", { detail, bubbles: true, composed: true })
|
|
357
|
-
);
|
|
358
|
-
this.dispatchEvent(
|
|
359
|
-
new CustomEvent("rel-mobile-open-change", { detail, bubbles: true, composed: true })
|
|
360
|
-
);
|
|
361
|
-
}
|
|
362
|
-
|
|
363
|
-
render() {
|
|
364
|
-
const topbarClass = ["topbar", this.layoutClass].filter(Boolean).join(" ");
|
|
365
|
-
const menuLabel = this.mobileOpen ? "Close menu" : "Open menu";
|
|
366
|
-
|
|
367
|
-
return html`
|
|
368
|
-
<!-- router: auto-assigns slot names by tagName; stays empty after redistribution -->
|
|
369
|
-
<slot style="display:none" @slotchange=${this.onRouterSlotChange}></slot>
|
|
370
|
-
|
|
371
|
-
<header>
|
|
372
|
-
<div class=${topbarClass} role="banner">
|
|
373
|
-
<div class="brand-region">
|
|
374
|
-
<slot name="brand"></slot>
|
|
375
|
-
</div>
|
|
376
|
-
|
|
377
|
-
<nav class="nav-region" aria-label="Primary">
|
|
378
|
-
<slot name="nav" @slotchange=${this._measureNav}></slot>
|
|
379
|
-
${this._overflowCount > 0 ? html`
|
|
380
|
-
<rel-topbar-item label="More" expandable data-more>
|
|
381
|
-
<rel-topbar-dropdown slot="dropdown">
|
|
382
|
-
${this._overflowItemsData.map(item => html`
|
|
383
|
-
<rel-topbar-dropdown-item
|
|
384
|
-
label=${item.label}
|
|
385
|
-
href=${item.href}
|
|
386
|
-
?disabled=${item.disabled}
|
|
387
|
-
></rel-topbar-dropdown-item>
|
|
388
|
-
`)}
|
|
389
|
-
</rel-topbar-dropdown>
|
|
390
|
-
</rel-topbar-item>
|
|
391
|
-
` : null}
|
|
392
|
-
</nav>
|
|
393
|
-
|
|
394
|
-
<div class="right-region">
|
|
395
|
-
<div class="actions-region">
|
|
396
|
-
<slot name="actions"></slot>
|
|
397
|
-
</div>
|
|
398
|
-
<div class="utilities-region">
|
|
399
|
-
<slot name="utilities"></slot>
|
|
400
|
-
</div>
|
|
401
|
-
<div class="profile-region">
|
|
402
|
-
<slot name="profile"></slot>
|
|
403
|
-
</div>
|
|
404
|
-
|
|
405
|
-
${this.showMenuToggle
|
|
406
|
-
? html`
|
|
407
|
-
<button
|
|
408
|
-
class="menu-toggle"
|
|
409
|
-
aria-label=${menuLabel}
|
|
410
|
-
aria-expanded=${this.mobileOpen}
|
|
411
|
-
aria-controls="mobile-panel"
|
|
412
|
-
@click=${this.handleMenuToggle}
|
|
413
|
-
>
|
|
414
|
-
${this.mobileOpen
|
|
415
|
-
? html`<rel-icon name="close" size="md"></rel-icon>`
|
|
416
|
-
: html`<rel-icon name="hamburger" size="md"></rel-icon>`}
|
|
417
|
-
</button>
|
|
418
|
-
`
|
|
419
|
-
: null}
|
|
420
|
-
</div>
|
|
421
|
-
</div>
|
|
422
|
-
</header>
|
|
423
|
-
|
|
424
|
-
<div
|
|
425
|
-
class="mobile-panel ${this.mobileOpen ? "open" : ""}"
|
|
426
|
-
id="mobile-panel"
|
|
427
|
-
role="region"
|
|
428
|
-
aria-label="Mobile navigation"
|
|
429
|
-
>
|
|
430
|
-
<slot name="mobile-panel"></slot>
|
|
431
|
-
${this._overflowItemsData.length > 0 ? html`
|
|
432
|
-
<div class="mobile-overflow-nav">
|
|
433
|
-
${this._overflowItemsData.map(item => html`
|
|
434
|
-
<a
|
|
435
|
-
class="mobile-overflow-item ${item.disabled ? "disabled" : ""}"
|
|
436
|
-
href=${item.href}
|
|
437
|
-
?aria-disabled=${item.disabled}
|
|
438
|
-
>${item.label}</a>
|
|
439
|
-
`)}
|
|
440
|
-
</div>
|
|
441
|
-
` : null}
|
|
442
|
-
</div>
|
|
443
|
-
`;
|
|
444
|
-
}
|
|
445
|
-
}
|
|
446
|
-
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
.mobile-overflow-item:hover {
|
|
222
|
+
background: var(--rel-topbar-item-hover-bg);
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
.mobile-overflow-item.disabled {
|
|
226
|
+
opacity: 0.45;
|
|
227
|
+
pointer-events: none;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/* ─── Responsive: auto mode (CSS media queries) ─────────────────── */
|
|
231
|
+
@media (max-width: 768px) {
|
|
232
|
+
.topbar:not(.force-desktop) .nav-region {
|
|
233
|
+
display: none;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
.topbar:not(.force-desktop) .menu-toggle {
|
|
237
|
+
display: flex;
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
@media (min-width: 769px) {
|
|
242
|
+
.topbar:not(.force-mobile):not(.force-tablet) .menu-toggle {
|
|
243
|
+
display: none;
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
/* ─── forceLayout overrides ─────────────────────────────────────── */
|
|
248
|
+
.topbar.force-mobile .nav-region,
|
|
249
|
+
.topbar.force-tablet .nav-region {
|
|
250
|
+
display: none;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
.topbar.force-mobile .menu-toggle,
|
|
254
|
+
.topbar.force-tablet .menu-toggle {
|
|
255
|
+
display: flex;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
.topbar.force-desktop .nav-region {
|
|
259
|
+
display: flex;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
.topbar.force-desktop .menu-toggle {
|
|
263
|
+
display: none;
|
|
264
|
+
}
|
|
265
|
+
`;
|
|
266
|
+
|
|
267
|
+
override firstUpdated() {
|
|
268
|
+
const navRegion = this.renderRoot.querySelector(".nav-region") as HTMLElement;
|
|
269
|
+
if (navRegion) {
|
|
270
|
+
this._resizeObserver = new ResizeObserver(() => {
|
|
271
|
+
clearTimeout(this._resizeTimer);
|
|
272
|
+
this._resizeTimer = window.setTimeout(() => this._measureNav(), 150);
|
|
273
|
+
});
|
|
274
|
+
this._resizeObserver.observe(navRegion);
|
|
275
|
+
}
|
|
276
|
+
requestAnimationFrame(() => this._measureNav());
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
override disconnectedCallback() {
|
|
280
|
+
super.disconnectedCallback();
|
|
281
|
+
this._resizeObserver?.disconnect();
|
|
282
|
+
clearTimeout(this._resizeTimer);
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
private _measureNav() {
|
|
286
|
+
if (this._measuring) return;
|
|
287
|
+
this._measuring = true;
|
|
288
|
+
const navRegion = this.renderRoot.querySelector(".nav-region") as HTMLElement;
|
|
289
|
+
if (!navRegion) return;
|
|
290
|
+
const navSlot = this.renderRoot.querySelector('slot[name="nav"]') as HTMLSlotElement;
|
|
291
|
+
if (!navSlot) return;
|
|
292
|
+
const items = Array.from(navSlot.assignedElements()).filter(
|
|
293
|
+
el => el.tagName.toLowerCase() !== "rel-topbar-item" || !el.hasAttribute("data-more")
|
|
294
|
+
);
|
|
295
|
+
const navWidth = navRegion.getBoundingClientRect().width;
|
|
296
|
+
if (Math.abs(navWidth - this._lastNavWidth) < 2) return;
|
|
297
|
+
this._lastNavWidth = navWidth;
|
|
298
|
+
const available = navWidth - 120;
|
|
299
|
+
|
|
300
|
+
let accumulated = 0;
|
|
301
|
+
let firstOverflowIndex = items.length;
|
|
302
|
+
for (let i = 0; i < items.length; i++) {
|
|
303
|
+
accumulated += items[i].getBoundingClientRect().width;
|
|
304
|
+
if (accumulated > available) {
|
|
305
|
+
firstOverflowIndex = i;
|
|
306
|
+
break;
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
const overflowingElements = Array.from(items).slice(firstOverflowIndex);
|
|
311
|
+
this._overflowItems = overflowingElements;
|
|
312
|
+
this._overflowCount = overflowingElements.length;
|
|
313
|
+
this._overflowItemsData = overflowingElements.map(el => ({
|
|
314
|
+
label: el.getAttribute("label") ?? "",
|
|
315
|
+
href: el.getAttribute("href") ?? "",
|
|
316
|
+
disabled: el.hasAttribute("disabled"),
|
|
317
|
+
}));
|
|
318
|
+
|
|
319
|
+
for (const el of items) {
|
|
320
|
+
if (overflowingElements.includes(el)) {
|
|
321
|
+
el.setAttribute("hidden", "");
|
|
322
|
+
} else {
|
|
323
|
+
el.removeAttribute("hidden");
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
this._measuring = false;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
private onRouterSlotChange(e: Event) {
|
|
331
|
+
const slot = e.target as HTMLSlotElement;
|
|
332
|
+
for (const el of slot.assignedElements({ flatten: true })) {
|
|
333
|
+
const tag = (el as HTMLElement).tagName.toLowerCase();
|
|
334
|
+
const targetSlot =
|
|
335
|
+
tag === "rel-topbar-brand" ? "brand" :
|
|
336
|
+
tag === "rel-topbar-actions" ? "actions" :
|
|
337
|
+
tag === "rel-topbar-utilities" ? "utilities" :
|
|
338
|
+
tag === "rel-topbar-profile" ? "profile" :
|
|
339
|
+
tag === "rel-topbar-mobile-panel" ? "mobile-panel" :
|
|
340
|
+
"nav";
|
|
341
|
+
if ((el as HTMLElement).slot !== targetSlot) {
|
|
342
|
+
(el as HTMLElement).slot = targetSlot;
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
private get layoutClass(): string {
|
|
348
|
+
if (this.forceLayout === "auto") return "";
|
|
349
|
+
return `force-${this.forceLayout}`;
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
private handleMenuToggle() {
|
|
353
|
+
this.mobileOpen = !this.mobileOpen;
|
|
354
|
+
const detail = { open: this.mobileOpen };
|
|
355
|
+
this.dispatchEvent(
|
|
356
|
+
new CustomEvent("rel-menu-toggle", { detail, bubbles: true, composed: true })
|
|
357
|
+
);
|
|
358
|
+
this.dispatchEvent(
|
|
359
|
+
new CustomEvent("rel-mobile-open-change", { detail, bubbles: true, composed: true })
|
|
360
|
+
);
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
render() {
|
|
364
|
+
const topbarClass = ["topbar", this.layoutClass].filter(Boolean).join(" ");
|
|
365
|
+
const menuLabel = this.mobileOpen ? "Close menu" : "Open menu";
|
|
366
|
+
|
|
367
|
+
return html`
|
|
368
|
+
<!-- router: auto-assigns slot names by tagName; stays empty after redistribution -->
|
|
369
|
+
<slot style="display:none" @slotchange=${this.onRouterSlotChange}></slot>
|
|
370
|
+
|
|
371
|
+
<header>
|
|
372
|
+
<div class=${topbarClass} role="banner">
|
|
373
|
+
<div class="brand-region">
|
|
374
|
+
<slot name="brand"></slot>
|
|
375
|
+
</div>
|
|
376
|
+
|
|
377
|
+
<nav class="nav-region" aria-label="Primary">
|
|
378
|
+
<slot name="nav" @slotchange=${this._measureNav}></slot>
|
|
379
|
+
${this._overflowCount > 0 ? html`
|
|
380
|
+
<rel-topbar-item label="More" expandable data-more>
|
|
381
|
+
<rel-topbar-dropdown slot="dropdown">
|
|
382
|
+
${this._overflowItemsData.map(item => html`
|
|
383
|
+
<rel-topbar-dropdown-item
|
|
384
|
+
label=${item.label}
|
|
385
|
+
href=${item.href}
|
|
386
|
+
?disabled=${item.disabled}
|
|
387
|
+
></rel-topbar-dropdown-item>
|
|
388
|
+
`)}
|
|
389
|
+
</rel-topbar-dropdown>
|
|
390
|
+
</rel-topbar-item>
|
|
391
|
+
` : null}
|
|
392
|
+
</nav>
|
|
393
|
+
|
|
394
|
+
<div class="right-region">
|
|
395
|
+
<div class="actions-region">
|
|
396
|
+
<slot name="actions"></slot>
|
|
397
|
+
</div>
|
|
398
|
+
<div class="utilities-region">
|
|
399
|
+
<slot name="utilities"></slot>
|
|
400
|
+
</div>
|
|
401
|
+
<div class="profile-region">
|
|
402
|
+
<slot name="profile"></slot>
|
|
403
|
+
</div>
|
|
404
|
+
|
|
405
|
+
${this.showMenuToggle
|
|
406
|
+
? html`
|
|
407
|
+
<button
|
|
408
|
+
class="menu-toggle"
|
|
409
|
+
aria-label=${menuLabel}
|
|
410
|
+
aria-expanded=${this.mobileOpen}
|
|
411
|
+
aria-controls="mobile-panel"
|
|
412
|
+
@click=${this.handleMenuToggle}
|
|
413
|
+
>
|
|
414
|
+
${this.mobileOpen
|
|
415
|
+
? html`<rel-icon name="close" size="md"></rel-icon>`
|
|
416
|
+
: html`<rel-icon name="hamburger" size="md"></rel-icon>`}
|
|
417
|
+
</button>
|
|
418
|
+
`
|
|
419
|
+
: null}
|
|
420
|
+
</div>
|
|
421
|
+
</div>
|
|
422
|
+
</header>
|
|
423
|
+
|
|
424
|
+
<div
|
|
425
|
+
class="mobile-panel ${this.mobileOpen ? "open" : ""}"
|
|
426
|
+
id="mobile-panel"
|
|
427
|
+
role="region"
|
|
428
|
+
aria-label="Mobile navigation"
|
|
429
|
+
>
|
|
430
|
+
<slot name="mobile-panel"></slot>
|
|
431
|
+
${this._overflowItemsData.length > 0 ? html`
|
|
432
|
+
<div class="mobile-overflow-nav">
|
|
433
|
+
${this._overflowItemsData.map(item => html`
|
|
434
|
+
<a
|
|
435
|
+
class="mobile-overflow-item ${item.disabled ? "disabled" : ""}"
|
|
436
|
+
href=${item.href}
|
|
437
|
+
?aria-disabled=${item.disabled}
|
|
438
|
+
>${item.label}</a>
|
|
439
|
+
`)}
|
|
440
|
+
</div>
|
|
441
|
+
` : null}
|
|
442
|
+
</div>
|
|
443
|
+
`;
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
|