@reliability-design/web 1.0.6 → 1.1.1
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 +4 -0
- package/dist/components/rel-alert/rel-alert.ts +83 -13
- package/dist/components/rel-avatar/rel-avatar.ts +3 -2
- package/dist/components/rel-badge/rel-badge.ts +6 -2
- package/dist/components/rel-button/rel-button.ts +64 -8
- package/dist/components/rel-calendar/rel-calendar.ts +3 -3
- package/dist/components/rel-carousel/rel-carousel.ts +10 -11
- package/dist/components/rel-checkbox/rel-checkbox.ts +33 -2
- package/dist/components/rel-chip/rel-chip.ts +63 -5
- package/dist/components/rel-divider/rel-divider.ts +9 -0
- package/dist/components/rel-drawer/rel-drawer-footer.ts +29 -0
- package/dist/components/rel-drawer/rel-drawer.ts +17 -26
- package/dist/components/rel-menu/rel-menu.ts +53 -21
- package/dist/components/rel-menubar/rel-menubar.ts +52 -23
- package/dist/components/rel-nav-menu/rel-nav-menu-item.ts +240 -143
- package/dist/components/rel-nav-menu/rel-nav-menu.ts +227 -331
- package/dist/components/rel-navbar/rel-navbar.ts +13 -0
- package/dist/components/rel-pagination/rel-pagination.ts +11 -6
- package/dist/components/rel-popover/rel-popover.ts +16 -13
- package/dist/components/rel-radio/rel-radio.ts +18 -1
- package/dist/components/rel-resizable/rel-resizable-handle.ts +4 -8
- package/dist/components/rel-select/rel-select.ts +14 -0
- package/dist/components/rel-sheet/rel-sheet.ts +7 -6
- package/dist/components/rel-sidebar/rel-sidebar.ts +13 -5
- package/dist/components/rel-slider/rel-slider.ts +25 -2
- package/dist/components/rel-snackbar/rel-snackbar.ts +46 -16
- package/dist/components/rel-stepper/rel-stepper.ts +32 -21
- package/dist/components/rel-switch/rel-switch.ts +19 -6
- package/dist/components/rel-tab/rel-tab.ts +49 -15
- package/dist/components/rel-table/rel-table-body.ts +5 -0
- package/dist/components/rel-table/rel-table-caption.ts +6 -1
- package/dist/components/rel-table/rel-table-cell.ts +42 -49
- package/dist/components/rel-table/rel-table-footer.ts +5 -0
- package/dist/components/rel-table/rel-table-head.ts +13 -1
- package/dist/components/rel-table/rel-table-header.ts +5 -0
- package/dist/components/rel-table/rel-table-row.ts +7 -3
- package/dist/components/rel-table/rel-table.ts +8 -2
- package/dist/components/rel-textfield/rel-textfield.ts +20 -20
- package/dist/components/rel-toast/rel-toast.ts +31 -10
- package/dist/components/rel-tooltip/rel-tooltip.ts +123 -80
- package/dist/components/rel-topbar/rel-topbar-actions.ts +23 -0
- package/dist/components/rel-topbar/rel-topbar-brand.ts +46 -0
- package/dist/components/rel-topbar/rel-topbar-dropdown-item.ts +33 -0
- package/dist/components/rel-topbar/rel-topbar-dropdown.ts +28 -0
- package/dist/components/rel-topbar/rel-topbar-item.ts +315 -0
- package/dist/components/rel-topbar/rel-topbar-mobile-panel.ts +23 -0
- package/dist/components/rel-topbar/rel-topbar-profile.ts +248 -0
- package/dist/components/rel-topbar/rel-topbar-utilities.ts +23 -0
- package/dist/components/rel-topbar/rel-topbar.ts +329 -0
- package/dist/index.d.mts +278 -289
- package/dist/index.d.ts +278 -289
- package/dist/index.js +4498 -5727
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +4535 -5756
- package/dist/index.mjs.map +1 -1
- package/dist/styles/vars.css +346 -294
- package/dist/{vars-ITL2K3KO.css → vars-TBB4BMSD.css} +346 -294
- package/package.json +2 -1
- package/dist/components/rel-quote-page/README.md +0 -166
- package/dist/components/rel-quote-page/rel-quote-page.ts +0 -816
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
import { LitElement, html, css } from "lit";
|
|
2
|
+
import { customElement, property } from "lit/decorators.js";
|
|
3
|
+
|
|
4
|
+
export type TopbarProfileType = "avatar" | "carrier";
|
|
5
|
+
export type TopbarRewardTier = "gold" | "silver" | "platinum" | "bronze" | "regular";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* `rel-topbar-profile` — Profile / user tile for the topbar right region.
|
|
9
|
+
*
|
|
10
|
+
* type="avatar" Customer avatar with initials circle + optional name/subtitle.
|
|
11
|
+
* type="carrier" Carrier reward tile with reward-tier badge + name/subtitle.
|
|
12
|
+
*/
|
|
13
|
+
@customElement("rel-topbar-profile")
|
|
14
|
+
export class RelTopbarProfile extends LitElement {
|
|
15
|
+
/** Visual mode */
|
|
16
|
+
@property({ type: String, reflect: true }) type: TopbarProfileType = "avatar";
|
|
17
|
+
|
|
18
|
+
/** One or two initials shown in the avatar circle */
|
|
19
|
+
@property({ type: String }) initials?: string;
|
|
20
|
+
|
|
21
|
+
/** Primary display name */
|
|
22
|
+
@property({ type: String }) name?: string;
|
|
23
|
+
|
|
24
|
+
/** Secondary line (role, plan, etc.) */
|
|
25
|
+
@property({ type: String }) subtitle?: string;
|
|
26
|
+
|
|
27
|
+
/** Reward tier — only meaningful when type="carrier" */
|
|
28
|
+
@property({ type: String, reflect: true }) reward: TopbarRewardTier = "regular";
|
|
29
|
+
|
|
30
|
+
/** Compact mode — avatar only, no text */
|
|
31
|
+
@property({ type: Boolean, reflect: true }) compact = false;
|
|
32
|
+
|
|
33
|
+
static styles = css`
|
|
34
|
+
/* ─── Component tokens (reward tier palette) ────────────────────── */
|
|
35
|
+
:host {
|
|
36
|
+
--_profile-radius: var(--rel-borders-radius-border-radius-500, 1000px);
|
|
37
|
+
--_avatar-size: 36px;
|
|
38
|
+
--_avatar-size-sm: 28px;
|
|
39
|
+
--_avatar-bg: var(--rel-colors-green-primary, #00f49c);
|
|
40
|
+
--_avatar-color: var(--rel-colors-gray-primary, #333);
|
|
41
|
+
|
|
42
|
+
/* Reward tier colours (reference existing token palette) */
|
|
43
|
+
--rel-topbar-reward-gold: var(--rel-colors-gold, #d6ba00);
|
|
44
|
+
--rel-topbar-reward-gold-bg: var(--rel-colors-gold-light, #f6efd7);
|
|
45
|
+
--rel-topbar-reward-silver: var(--rel-colors-silver, #bababa);
|
|
46
|
+
--rel-topbar-reward-silver-bg: var(--rel-colors-silver-secondary, #f0f0f0);
|
|
47
|
+
--rel-topbar-reward-platinum: var(--rel-colors-platinum, #8392c9);
|
|
48
|
+
--rel-topbar-reward-platinum-bg: var(--rel-colors-platinum-secondary, #e4e6f2);
|
|
49
|
+
--rel-topbar-reward-bronze: var(--rel-colors-bronze, #7d5300);
|
|
50
|
+
--rel-topbar-reward-bronze-bg: var(--rel-colors-bronze-secondary, #e2dac7);
|
|
51
|
+
--rel-topbar-reward-regular: var(--rel-colors-gray-divider, #bdbdbd);
|
|
52
|
+
--rel-topbar-reward-regular-bg: var(--rel-surface-secondary, #fafafa);
|
|
53
|
+
|
|
54
|
+
display: inline-flex;
|
|
55
|
+
align-items: center;
|
|
56
|
+
gap: var(--rel-spacing-space-xs, 8px);
|
|
57
|
+
font-family: var(--rel-fontFamilies-primary, Poppins, sans-serif);
|
|
58
|
+
cursor: pointer;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/* ─── Avatar circle ─────────────────────────────────────────────── */
|
|
62
|
+
.avatar {
|
|
63
|
+
width: var(--_avatar-size);
|
|
64
|
+
height: var(--_avatar-size);
|
|
65
|
+
border-radius: var(--_profile-radius);
|
|
66
|
+
background: var(--_avatar-bg);
|
|
67
|
+
color: var(--_avatar-color);
|
|
68
|
+
display: flex;
|
|
69
|
+
align-items: center;
|
|
70
|
+
justify-content: center;
|
|
71
|
+
font-size: var(--rel-fontSizes-sm, 12px);
|
|
72
|
+
font-weight: var(--rel-fontWeights-semibold, 600);
|
|
73
|
+
flex-shrink: 0;
|
|
74
|
+
user-select: none;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
:host([compact]) .avatar {
|
|
78
|
+
width: var(--_avatar-size-sm);
|
|
79
|
+
height: var(--_avatar-size-sm);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/* ─── Name / subtitle stack ─────────────────────────────────────── */
|
|
83
|
+
.text-stack {
|
|
84
|
+
display: flex;
|
|
85
|
+
flex-direction: column;
|
|
86
|
+
gap: 0;
|
|
87
|
+
min-width: 0;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
:host([compact]) .text-stack {
|
|
91
|
+
display: none;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
.name {
|
|
95
|
+
font-size: var(--rel-fontSizes-md, 14px);
|
|
96
|
+
font-weight: var(--rel-fontWeights-medium, 500);
|
|
97
|
+
color: var(--rel-text-primary, #333);
|
|
98
|
+
line-height: var(--rel-lineHeights-md, 20px);
|
|
99
|
+
white-space: nowrap;
|
|
100
|
+
overflow: hidden;
|
|
101
|
+
text-overflow: ellipsis;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
.subtitle {
|
|
105
|
+
font-size: var(--rel-fontSizes-sm, 12px);
|
|
106
|
+
font-weight: var(--rel-fontWeights-regular, 400);
|
|
107
|
+
color: var(--rel-text-placeholder, #a9a9a9);
|
|
108
|
+
line-height: var(--rel-lineHeights-sm, 18px);
|
|
109
|
+
white-space: nowrap;
|
|
110
|
+
overflow: hidden;
|
|
111
|
+
text-overflow: ellipsis;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/* ─── Carrier reward badge ──────────────────────────────────────── */
|
|
115
|
+
.reward-badge {
|
|
116
|
+
display: inline-flex;
|
|
117
|
+
align-items: center;
|
|
118
|
+
gap: var(--rel-spacing-space-3xs, 2px);
|
|
119
|
+
padding: 2px var(--rel-spacing-space-2xs, 4px);
|
|
120
|
+
border-radius: var(--rel-borders-radius-border-radius-100, 2px);
|
|
121
|
+
font-size: var(--rel-fontSizes-xs, 10px);
|
|
122
|
+
font-weight: var(--rel-fontWeights-bold, 700);
|
|
123
|
+
text-transform: uppercase;
|
|
124
|
+
letter-spacing: 0.4px;
|
|
125
|
+
flex-shrink: 0;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/* Gold */
|
|
129
|
+
:host([reward="gold"]) .avatar {
|
|
130
|
+
background: var(--rel-topbar-reward-gold-bg);
|
|
131
|
+
color: var(--rel-topbar-reward-gold);
|
|
132
|
+
border: 1.5px solid var(--rel-topbar-reward-gold);
|
|
133
|
+
}
|
|
134
|
+
:host([reward="gold"]) .reward-badge {
|
|
135
|
+
background: var(--rel-topbar-reward-gold-bg);
|
|
136
|
+
color: var(--rel-topbar-reward-gold);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/* Silver */
|
|
140
|
+
:host([reward="silver"]) .avatar {
|
|
141
|
+
background: var(--rel-topbar-reward-silver-bg);
|
|
142
|
+
color: var(--rel-colors-gray-primary, #333);
|
|
143
|
+
border: 1.5px solid var(--rel-topbar-reward-silver);
|
|
144
|
+
}
|
|
145
|
+
:host([reward="silver"]) .reward-badge {
|
|
146
|
+
background: var(--rel-topbar-reward-silver-bg);
|
|
147
|
+
color: var(--rel-colors-gray-primary, #333);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/* Platinum */
|
|
151
|
+
:host([reward="platinum"]) .avatar {
|
|
152
|
+
background: var(--rel-topbar-reward-platinum-bg);
|
|
153
|
+
color: var(--rel-topbar-reward-platinum);
|
|
154
|
+
border: 1.5px solid var(--rel-topbar-reward-platinum);
|
|
155
|
+
}
|
|
156
|
+
:host([reward="platinum"]) .reward-badge {
|
|
157
|
+
background: var(--rel-topbar-reward-platinum-bg);
|
|
158
|
+
color: var(--rel-topbar-reward-platinum);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/* Bronze */
|
|
162
|
+
:host([reward="bronze"]) .avatar {
|
|
163
|
+
background: var(--rel-topbar-reward-bronze-bg);
|
|
164
|
+
color: var(--rel-topbar-reward-bronze);
|
|
165
|
+
border: 1.5px solid var(--rel-topbar-reward-bronze);
|
|
166
|
+
}
|
|
167
|
+
:host([reward="bronze"]) .reward-badge {
|
|
168
|
+
background: var(--rel-topbar-reward-bronze-bg);
|
|
169
|
+
color: var(--rel-topbar-reward-bronze);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/* Regular */
|
|
173
|
+
:host([reward="regular"]) .avatar,
|
|
174
|
+
:host(:not([reward])) .avatar {
|
|
175
|
+
background: var(--rel-topbar-reward-regular-bg);
|
|
176
|
+
color: var(--rel-colors-gray-primary, #333);
|
|
177
|
+
border: 1.5px solid var(--rel-topbar-reward-regular);
|
|
178
|
+
}
|
|
179
|
+
:host([reward="regular"]) .reward-badge {
|
|
180
|
+
background: var(--rel-topbar-reward-regular-bg);
|
|
181
|
+
color: var(--rel-colors-gray-primary, #333);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/* ─── Carrier tile shell ────────────────────────────────────────── */
|
|
185
|
+
.carrier-tile {
|
|
186
|
+
display: flex;
|
|
187
|
+
align-items: center;
|
|
188
|
+
gap: var(--rel-spacing-space-xs, 8px);
|
|
189
|
+
padding: var(--rel-spacing-space-2xs, 4px) var(--rel-spacing-space-xs, 8px);
|
|
190
|
+
border-radius: var(--rel-borders-radius-border-radius-200, 8px);
|
|
191
|
+
background: var(--rel-surface-secondary, #fafafa);
|
|
192
|
+
border: var(--rel-borders-width-stroke-100, 1px) solid var(--rel-border-default, #ddd);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
:host([compact]) .carrier-tile {
|
|
196
|
+
padding: var(--rel-spacing-space-2xs, 4px);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
.carrier-meta {
|
|
200
|
+
display: flex;
|
|
201
|
+
flex-direction: column;
|
|
202
|
+
gap: 1px;
|
|
203
|
+
min-width: 0;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
:host([compact]) .carrier-meta {
|
|
207
|
+
display: none;
|
|
208
|
+
}
|
|
209
|
+
`;
|
|
210
|
+
|
|
211
|
+
render() {
|
|
212
|
+
if (this.type === "carrier") {
|
|
213
|
+
return this._renderCarrier();
|
|
214
|
+
}
|
|
215
|
+
return this._renderAvatar();
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
private _renderAvatar() {
|
|
219
|
+
return html`
|
|
220
|
+
<div class="avatar" aria-hidden="true">
|
|
221
|
+
${this.initials ?? ""}
|
|
222
|
+
</div>
|
|
223
|
+
${!this.compact && (this.name || this.subtitle)
|
|
224
|
+
? html`
|
|
225
|
+
<div class="text-stack">
|
|
226
|
+
${this.name ? html`<span class="name">${this.name}</span>` : null}
|
|
227
|
+
${this.subtitle ? html`<span class="subtitle">${this.subtitle}</span>` : null}
|
|
228
|
+
</div>
|
|
229
|
+
`
|
|
230
|
+
: null}
|
|
231
|
+
`;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
private _renderCarrier() {
|
|
235
|
+
const rewardLabel = this.reward.charAt(0).toUpperCase() + this.reward.slice(1);
|
|
236
|
+
|
|
237
|
+
return html`
|
|
238
|
+
<div class="carrier-tile" role="img" aria-label="${this.name ?? "Profile"} – ${rewardLabel} member">
|
|
239
|
+
<div class="avatar">${this.initials ?? ""}</div>
|
|
240
|
+
<div class="carrier-meta">
|
|
241
|
+
${this.name ? html`<span class="name">${this.name}</span>` : null}
|
|
242
|
+
<span class="reward-badge">${rewardLabel}</span>
|
|
243
|
+
${this.subtitle ? html`<span class="subtitle">${this.subtitle}</span>` : null}
|
|
244
|
+
</div>
|
|
245
|
+
</div>
|
|
246
|
+
`;
|
|
247
|
+
}
|
|
248
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { LitElement, html, css } from "lit";
|
|
2
|
+
import { customElement } from "lit/decorators.js";
|
|
3
|
+
|
|
4
|
+
@customElement("rel-topbar-utilities")
|
|
5
|
+
export class RelTopbarUtilities extends LitElement {
|
|
6
|
+
static styles = css`
|
|
7
|
+
.utilities {
|
|
8
|
+
display: inline-flex;
|
|
9
|
+
align-items: center;
|
|
10
|
+
gap: var(--rel-spacing-space-xs, 8px);
|
|
11
|
+
}
|
|
12
|
+
`;
|
|
13
|
+
|
|
14
|
+
render() {
|
|
15
|
+
return html`<div class="utilities"><slot></slot></div>`;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
declare global {
|
|
20
|
+
interface HTMLElementTagNameMap {
|
|
21
|
+
"rel-topbar-utilities": RelTopbarUtilities;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
@@ -0,0 +1,329 @@
|
|
|
1
|
+
import { LitElement, html, css } from "lit";
|
|
2
|
+
import { customElement, property } from "lit/decorators.js";
|
|
3
|
+
import "./rel-topbar-brand";
|
|
4
|
+
import "./rel-topbar-actions";
|
|
5
|
+
import "./rel-topbar-utilities";
|
|
6
|
+
import "./rel-topbar-mobile-panel";
|
|
7
|
+
|
|
8
|
+
export type TopbarVariant = "customer" | "carrier";
|
|
9
|
+
export type TopbarForceLayout = "auto" | "desktop" | "tablet" | "mobile";
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* `rel-topbar` — Application-level top navigation / header shell.
|
|
13
|
+
*
|
|
14
|
+
* Slots:
|
|
15
|
+
* brand — Logo / wordmark region (left)
|
|
16
|
+
* (default) — Primary nav items (rel-topbar-item elements)
|
|
17
|
+
* actions — CTA / action buttons (customer) or dark-pill area (carrier)
|
|
18
|
+
* utilities — Utility icon buttons (notifications, search, etc.)
|
|
19
|
+
* profile — rel-topbar-profile tile
|
|
20
|
+
* mobile-panel — Collapsed nav panel shown when mobileOpen is true
|
|
21
|
+
*
|
|
22
|
+
* Events:
|
|
23
|
+
* rel-menu-toggle — fired when hamburger is clicked { open: boolean }
|
|
24
|
+
* rel-mobile-open-change — alias of rel-menu-toggle for convenience
|
|
25
|
+
* rel-nav-select — bubbled from child rel-topbar-item { value, label }
|
|
26
|
+
*/
|
|
27
|
+
@customElement("rel-topbar")
|
|
28
|
+
export class RelTopbar extends LitElement {
|
|
29
|
+
/** Visual variant — affects action/profile region styling */
|
|
30
|
+
@property({ type: String, reflect: true }) variant: TopbarVariant = "customer";
|
|
31
|
+
|
|
32
|
+
/** Whether the mobile nav panel is currently open */
|
|
33
|
+
@property({ type: Boolean, reflect: true }) mobileOpen = false;
|
|
34
|
+
|
|
35
|
+
/** Whether to render the hamburger / close toggle button */
|
|
36
|
+
@property({ type: Boolean, reflect: true }) showMenuToggle = false;
|
|
37
|
+
|
|
38
|
+
/** Reduce height to compact (48 px) mode */
|
|
39
|
+
@property({ type: Boolean, reflect: true }) compact = false;
|
|
40
|
+
|
|
41
|
+
/** Stick topbar to top of viewport while scrolling */
|
|
42
|
+
@property({ type: Boolean, reflect: true }) sticky = false;
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Override the responsive layout for demos / storybook.
|
|
46
|
+
* 'auto' respects CSS media queries.
|
|
47
|
+
*/
|
|
48
|
+
@property({ type: String }) forceLayout: TopbarForceLayout = "auto";
|
|
49
|
+
|
|
50
|
+
static styles = css`
|
|
51
|
+
/* ─── Component tokens ─────────────────────────────────────────── */
|
|
52
|
+
:host {
|
|
53
|
+
--rel-topbar-height: 64px;
|
|
54
|
+
--rel-topbar-height-compact: 48px;
|
|
55
|
+
--rel-topbar-bg: var(--rel-colors-white, #fff);
|
|
56
|
+
--rel-topbar-border-color: var(--rel-border-default, #ddd);
|
|
57
|
+
--rel-topbar-item-color: var(--rel-text-primary, #333);
|
|
58
|
+
--rel-topbar-item-active-color: var(--rel-colors-green-tertiary, #00a86b);
|
|
59
|
+
--rel-topbar-item-active-indicator: var(--rel-colors-green-primary, #00f49c);
|
|
60
|
+
--rel-topbar-item-hover-bg: var(--rel-surface-secondary, #fafafa);
|
|
61
|
+
--rel-topbar-divider-color: var(--rel-border-divider, #bdbdbd);
|
|
62
|
+
--rel-topbar-carrier-action-bg: var(--rel-colors-gray-primary, #333);
|
|
63
|
+
--rel-topbar-carrier-action-color: var(--rel-colors-white, #fff);
|
|
64
|
+
--rel-topbar-z: 100;
|
|
65
|
+
|
|
66
|
+
display: block;
|
|
67
|
+
width: 100%;
|
|
68
|
+
font-family: var(--rel-fontFamilies-primary, Poppins, sans-serif);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/* ─── Sticky host ───────────────────────────────────────────────── */
|
|
72
|
+
:host([sticky]) {
|
|
73
|
+
position: sticky;
|
|
74
|
+
top: 0;
|
|
75
|
+
z-index: var(--rel-topbar-z);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/* ─── Bar shell ─────────────────────────────────────────────────── */
|
|
79
|
+
.topbar {
|
|
80
|
+
display: flex;
|
|
81
|
+
align-items: center;
|
|
82
|
+
height: var(--rel-topbar-height);
|
|
83
|
+
background: var(--rel-topbar-bg);
|
|
84
|
+
border-bottom: var(--rel-borders-width-stroke-100, 1px) solid var(--rel-topbar-border-color);
|
|
85
|
+
padding: 0 var(--rel-spacing-space-xl, 24px);
|
|
86
|
+
box-sizing: border-box;
|
|
87
|
+
gap: var(--rel-spacing-space-md, 16px);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
:host([compact]) .topbar {
|
|
91
|
+
height: var(--rel-topbar-height-compact);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/* ─── Brand ─────────────────────────────────────────────────────── */
|
|
95
|
+
.brand-region {
|
|
96
|
+
display: flex;
|
|
97
|
+
align-items: center;
|
|
98
|
+
flex-shrink: 0;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/* ─── Nav (primary, desktop) ────────────────────────────────────── */
|
|
102
|
+
.nav-region {
|
|
103
|
+
display: flex;
|
|
104
|
+
align-items: center;
|
|
105
|
+
flex: 1;
|
|
106
|
+
gap: var(--rel-spacing-space-2xs, 4px);
|
|
107
|
+
min-width: 0;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/* ─── Right side cluster ────────────────────────────────────────── */
|
|
111
|
+
.right-region {
|
|
112
|
+
display: flex;
|
|
113
|
+
align-items: center;
|
|
114
|
+
flex-shrink: 0;
|
|
115
|
+
gap: var(--rel-spacing-space-xs, 8px);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
.actions-region,
|
|
119
|
+
.utilities-region,
|
|
120
|
+
.profile-region {
|
|
121
|
+
display: contents;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/* Vertical divider between util/profile when both are populated */
|
|
125
|
+
.divider {
|
|
126
|
+
width: var(--rel-borders-width-stroke-100, 1px);
|
|
127
|
+
height: 24px;
|
|
128
|
+
background: var(--rel-topbar-divider-color);
|
|
129
|
+
flex-shrink: 0;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/* ─── Carrier action pill wrapper ───────────────────────────────── */
|
|
133
|
+
:host([variant="carrier"]) .actions-region ::slotted(*) {
|
|
134
|
+
display: flex;
|
|
135
|
+
align-items: center;
|
|
136
|
+
gap: var(--rel-spacing-space-xs, 8px);
|
|
137
|
+
background: var(--rel-topbar-carrier-action-bg);
|
|
138
|
+
color: var(--rel-topbar-carrier-action-color);
|
|
139
|
+
padding: var(--rel-spacing-space-2xs, 4px) var(--rel-spacing-space-sm, 12px);
|
|
140
|
+
border-radius: var(--rel-borders-radius-border-radius-500, 1000px);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/* ─── Mobile toggle button ──────────────────────────────────────── */
|
|
144
|
+
.menu-toggle {
|
|
145
|
+
display: none;
|
|
146
|
+
align-items: center;
|
|
147
|
+
justify-content: center;
|
|
148
|
+
width: 40px;
|
|
149
|
+
height: 40px;
|
|
150
|
+
padding: 0;
|
|
151
|
+
border: none;
|
|
152
|
+
background: transparent;
|
|
153
|
+
border-radius: var(--rel-borders-radius-border-radius-200, 8px);
|
|
154
|
+
cursor: pointer;
|
|
155
|
+
color: var(--rel-topbar-item-color);
|
|
156
|
+
flex-shrink: 0;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
.menu-toggle:focus-visible {
|
|
160
|
+
outline: 2px solid var(--rel-topbar-item-active-indicator);
|
|
161
|
+
outline-offset: 2px;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
.menu-toggle:hover {
|
|
165
|
+
background: var(--rel-topbar-item-hover-bg);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
.menu-toggle svg {
|
|
169
|
+
display: block;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/* ─── Mobile panel ──────────────────────────────────────────────── */
|
|
173
|
+
.mobile-panel {
|
|
174
|
+
display: none;
|
|
175
|
+
background: var(--rel-topbar-bg);
|
|
176
|
+
border-bottom: var(--rel-borders-width-stroke-100, 1px) solid var(--rel-topbar-border-color);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
.mobile-panel.open {
|
|
180
|
+
display: block;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/* ─── Responsive: auto mode (CSS media queries) ─────────────────── */
|
|
184
|
+
@media (max-width: 768px) {
|
|
185
|
+
.topbar:not(.force-desktop) .nav-region {
|
|
186
|
+
display: none;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
.topbar:not(.force-desktop) .menu-toggle {
|
|
190
|
+
display: flex;
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
@media (min-width: 769px) {
|
|
195
|
+
.topbar:not(.force-mobile):not(.force-tablet) .menu-toggle {
|
|
196
|
+
display: none;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/* ─── forceLayout overrides ─────────────────────────────────────── */
|
|
201
|
+
.topbar.force-mobile .nav-region,
|
|
202
|
+
.topbar.force-tablet .nav-region {
|
|
203
|
+
display: none;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
.topbar.force-mobile .menu-toggle,
|
|
207
|
+
.topbar.force-tablet .menu-toggle {
|
|
208
|
+
display: flex;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
.topbar.force-desktop .nav-region {
|
|
212
|
+
display: flex;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
.topbar.force-desktop .menu-toggle {
|
|
216
|
+
display: none;
|
|
217
|
+
}
|
|
218
|
+
`;
|
|
219
|
+
|
|
220
|
+
private onRouterSlotChange(e: Event) {
|
|
221
|
+
const slot = e.target as HTMLSlotElement;
|
|
222
|
+
for (const el of slot.assignedElements({ flatten: true })) {
|
|
223
|
+
const tag = (el as HTMLElement).tagName.toLowerCase();
|
|
224
|
+
const targetSlot =
|
|
225
|
+
tag === "rel-topbar-brand" ? "brand" :
|
|
226
|
+
tag === "rel-topbar-actions" ? "actions" :
|
|
227
|
+
tag === "rel-topbar-utilities" ? "utilities" :
|
|
228
|
+
tag === "rel-topbar-profile" ? "profile" :
|
|
229
|
+
tag === "rel-topbar-mobile-panel" ? "mobile-panel" :
|
|
230
|
+
"nav";
|
|
231
|
+
if ((el as HTMLElement).slot !== targetSlot) {
|
|
232
|
+
(el as HTMLElement).slot = targetSlot;
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
private get layoutClass(): string {
|
|
238
|
+
if (this.forceLayout === "auto") return "";
|
|
239
|
+
return `force-${this.forceLayout}`;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
private handleMenuToggle() {
|
|
243
|
+
this.mobileOpen = !this.mobileOpen;
|
|
244
|
+
const detail = { open: this.mobileOpen };
|
|
245
|
+
this.dispatchEvent(
|
|
246
|
+
new CustomEvent("rel-menu-toggle", { detail, bubbles: true, composed: true })
|
|
247
|
+
);
|
|
248
|
+
this.dispatchEvent(
|
|
249
|
+
new CustomEvent("rel-mobile-open-change", { detail, bubbles: true, composed: true })
|
|
250
|
+
);
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
render() {
|
|
254
|
+
const topbarClass = ["topbar", this.layoutClass].filter(Boolean).join(" ");
|
|
255
|
+
const menuLabel = this.mobileOpen ? "Close menu" : "Open menu";
|
|
256
|
+
|
|
257
|
+
return html`
|
|
258
|
+
<!-- router: auto-assigns slot names by tagName; stays empty after redistribution -->
|
|
259
|
+
<slot style="display:none" @slotchange=${this.onRouterSlotChange}></slot>
|
|
260
|
+
|
|
261
|
+
<header>
|
|
262
|
+
<div class=${topbarClass} role="banner">
|
|
263
|
+
<div class="brand-region">
|
|
264
|
+
<slot name="brand"></slot>
|
|
265
|
+
</div>
|
|
266
|
+
|
|
267
|
+
<nav class="nav-region" aria-label="Primary">
|
|
268
|
+
<slot name="nav"></slot>
|
|
269
|
+
</nav>
|
|
270
|
+
|
|
271
|
+
<div class="right-region">
|
|
272
|
+
<div class="actions-region">
|
|
273
|
+
<slot name="actions"></slot>
|
|
274
|
+
</div>
|
|
275
|
+
<div class="utilities-region">
|
|
276
|
+
<slot name="utilities"></slot>
|
|
277
|
+
</div>
|
|
278
|
+
<div class="profile-region">
|
|
279
|
+
<slot name="profile"></slot>
|
|
280
|
+
</div>
|
|
281
|
+
|
|
282
|
+
${this.showMenuToggle
|
|
283
|
+
? html`
|
|
284
|
+
<button
|
|
285
|
+
class="menu-toggle"
|
|
286
|
+
aria-label=${menuLabel}
|
|
287
|
+
aria-expanded=${this.mobileOpen}
|
|
288
|
+
aria-controls="mobile-panel"
|
|
289
|
+
@click=${this.handleMenuToggle}
|
|
290
|
+
>
|
|
291
|
+
${this.mobileOpen ? _iconClose() : _iconMenu()}
|
|
292
|
+
</button>
|
|
293
|
+
`
|
|
294
|
+
: null}
|
|
295
|
+
</div>
|
|
296
|
+
</div>
|
|
297
|
+
</header>
|
|
298
|
+
|
|
299
|
+
<div
|
|
300
|
+
class="mobile-panel ${this.mobileOpen ? "open" : ""}"
|
|
301
|
+
id="mobile-panel"
|
|
302
|
+
role="region"
|
|
303
|
+
aria-label="Mobile navigation"
|
|
304
|
+
>
|
|
305
|
+
<slot name="mobile-panel"></slot>
|
|
306
|
+
</div>
|
|
307
|
+
`;
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
/* ─── Inline SVG helpers ──────────────────────────────────────────────── */
|
|
312
|
+
|
|
313
|
+
function _iconMenu() {
|
|
314
|
+
return html`
|
|
315
|
+
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" aria-hidden="true" focusable="false">
|
|
316
|
+
<rect x="2" y="5" width="16" height="1.5" rx="0.75" fill="currentColor"/>
|
|
317
|
+
<rect x="2" y="9.25" width="16" height="1.5" rx="0.75" fill="currentColor"/>
|
|
318
|
+
<rect x="2" y="13.5" width="16" height="1.5" rx="0.75" fill="currentColor"/>
|
|
319
|
+
</svg>
|
|
320
|
+
`;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
function _iconClose() {
|
|
324
|
+
return html`
|
|
325
|
+
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" aria-hidden="true" focusable="false">
|
|
326
|
+
<path d="M5 5L15 15M15 5L5 15" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
|
|
327
|
+
</svg>
|
|
328
|
+
`;
|
|
329
|
+
}
|