@reliability-design/web 1.0.6 → 1.2.0

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