@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
@@ -1,331 +1,227 @@
1
- import { LitElement, html, css } from "lit";
2
- import { customElement, property, state, query } from "lit/decorators.js";
3
- import { RelNavMenuItem } from "./rel-nav-menu-item";
4
-
5
- type DensityType = "comfortable" | "compact";
6
-
7
- @customElement("rel-nav-menu")
8
- export class RelNavMenu extends LitElement {
9
- @property({ type: String, reflect: true }) open = "";
10
- @property({ type: Boolean, reflect: true }) disabled = false;
11
- @property({ type: String }) density: DensityType = "comfortable";
12
-
13
- @state() private items: RelNavMenuItem[] = [];
14
- @state() private focusedIndex = 0;
15
- @state() private hoverSwitchEnabled = false;
16
-
17
- @query(".nav-container") private container!: HTMLElement;
18
-
19
- static styles = css`
20
- :host {
21
- display: block;
22
- font-family: var(--rel-fontFamilies-primary);
23
- }
24
-
25
- .nav-container {
26
- display: flex;
27
- align-items: center;
28
- gap: var(--rel-spacing-space-3xs);
29
- padding: var(--rel-spacing-space-2xs);
30
- background: var(--rel-colors-gray-light);
31
- border-radius: var(--rel-borders-radius-border-radius-200);
32
- border: var(--rel-borders-width-stroke-100) solid var(--rel-colors-gray-border);
33
- }
34
-
35
- .nav-container.compact {
36
- padding: var(--rel-spacing-space-3xs);
37
- gap: 0;
38
- }
39
-
40
- .dropdown-panel {
41
- position: fixed;
42
- min-width: 320px;
43
- max-width: 600px;
44
- background: var(--rel-colors-white);
45
- border: var(--rel-borders-width-stroke-100) solid var(--rel-colors-gray-border);
46
- border-radius: var(--rel-borders-radius-border-radius-200);
47
- box-shadow: var(--rel-shadows-shadow-300);
48
- padding: var(--rel-spacing-space-xs);
49
- z-index: 1000;
50
- display: none;
51
- }
52
-
53
- .dropdown-panel.open {
54
- display: block;
55
- }
56
-
57
- :host([disabled]) .nav-container {
58
- opacity: 0.5;
59
- pointer-events: none;
60
- }
61
- `;
62
-
63
- connectedCallback() {
64
- super.connectedCallback();
65
- this.discoverItems();
66
- document.addEventListener("click", this.handleOutsideClick);
67
- }
68
-
69
- disconnectedCallback() {
70
- super.disconnectedCallback();
71
- document.removeEventListener("click", this.handleOutsideClick);
72
- }
73
-
74
- private discoverItems() {
75
- const slot = this.shadowRoot?.querySelector("slot");
76
- if (slot) {
77
- const nodes = slot.assignedElements() as RelNavMenuItem[];
78
- this.items = nodes.filter((el) => el.tagName === "REL-NAV-MENU-ITEM");
79
- }
80
- }
81
-
82
- private handleSlotChange() {
83
- this.discoverItems();
84
- }
85
-
86
- private handleOutsideClick = (e: MouseEvent) => {
87
- const path = e.composedPath() as HTMLElement[];
88
- const isInside = path.some(
89
- (el) =>
90
- el === this ||
91
- el.classList?.contains("dropdown-panel") ||
92
- el.tagName === "REL-NAV-MENU-ITEM"
93
- );
94
- if (!isInside && this.open) {
95
- this.closePanel();
96
- }
97
- };
98
-
99
- private openPanel(itemId: string) {
100
- if (this.disabled) return;
101
-
102
- const wasOpen = this.open === itemId;
103
- this.open = wasOpen ? "" : itemId;
104
-
105
- if (!wasOpen) {
106
- this.hoverSwitchEnabled = true;
107
- }
108
-
109
- this.dispatchEvent(
110
- new CustomEvent("rel-nav-open-change", {
111
- detail: { openId: this.open },
112
- bubbles: true,
113
- composed: true,
114
- })
115
- );
116
-
117
- if (this.open) {
118
- this.updateComplete.then(() => this.positionPanel(itemId));
119
- }
120
- }
121
-
122
- private closePanel() {
123
- if (this.open) {
124
- this.open = "";
125
- this.hoverSwitchEnabled = false;
126
- this.dispatchEvent(
127
- new CustomEvent("rel-nav-open-change", {
128
- detail: { openId: "" },
129
- bubbles: true,
130
- composed: true,
131
- })
132
- );
133
- }
134
- }
135
-
136
- private positionPanel(itemId: string) {
137
- const item = this.items.find((i) => i.itemId === itemId);
138
- if (!item) return;
139
-
140
- const trigger = item.shadowRoot?.querySelector(".trigger") as HTMLElement;
141
- const panel = this.shadowRoot?.querySelector(
142
- `.dropdown-panel[data-item-id="${itemId}"]`
143
- ) as HTMLElement;
144
-
145
- if (!trigger || !panel) return;
146
-
147
- const triggerRect = trigger.getBoundingClientRect();
148
- const panelRect = panel.getBoundingClientRect();
149
- const viewportWidth = window.innerWidth;
150
- const viewportHeight = window.innerHeight;
151
-
152
- // Default: position below trigger, start-aligned
153
- let top = triggerRect.bottom + 8;
154
- let left = triggerRect.left;
155
-
156
- // Handle right overflow
157
- if (left + panelRect.width > viewportWidth) {
158
- left = viewportWidth - panelRect.width - 16;
159
- }
160
-
161
- // Handle left overflow
162
- if (left < 0) {
163
- left = 16;
164
- }
165
-
166
- // Handle bottom overflow (flip above if needed)
167
- if (top + panelRect.height > viewportHeight) {
168
- const spaceAbove = triggerRect.top;
169
- const spaceBelow = viewportHeight - triggerRect.bottom;
170
-
171
- if (spaceAbove > spaceBelow && spaceAbove > panelRect.height) {
172
- top = triggerRect.top - panelRect.height - 8;
173
- }
174
- }
175
-
176
- panel.style.top = `${top}px`;
177
- panel.style.left = `${left}px`;
178
- }
179
-
180
- private handleItemClick(item: RelNavMenuItem, event: Event) {
181
- if (this.disabled || item.disabled) return;
182
-
183
- // If item has href and no content, let default behavior happen
184
- if (item.href && !this.hasItemContent(item)) {
185
- return;
186
- }
187
-
188
- event.preventDefault();
189
-
190
- // If item has content, toggle panel
191
- if (this.hasItemContent(item)) {
192
- this.openPanel(item.itemId);
193
- }
194
- }
195
-
196
- private handleItemMouseEnter(item: RelNavMenuItem) {
197
- if (this.disabled || item.disabled) return;
198
-
199
- // Hover switching: if a panel is open, hovering another trigger switches to it
200
- if (this.open && this.hoverSwitchEnabled && this.hasItemContent(item)) {
201
- this.openPanel(item.itemId);
202
- }
203
- }
204
-
205
- private handleKeyDown(event: KeyboardEvent) {
206
- if (this.disabled) return;
207
-
208
- const { key } = event;
209
- const focusableItems = this.items.filter((item) => !item.disabled);
210
-
211
- if (key === "ArrowRight") {
212
- event.preventDefault();
213
- this.focusedIndex = Math.min(focusableItems.length - 1, this.focusedIndex + 1);
214
- this.focusItem(this.focusedIndex);
215
- } else if (key === "ArrowLeft") {
216
- event.preventDefault();
217
- this.focusedIndex = Math.max(0, this.focusedIndex - 1);
218
- this.focusItem(this.focusedIndex);
219
- } else if (key === "Enter" || key === " ") {
220
- event.preventDefault();
221
- const item = focusableItems[this.focusedIndex];
222
- if (item && this.hasItemContent(item)) {
223
- this.openPanel(item.itemId);
224
- }
225
- } else if (key === "Escape") {
226
- event.preventDefault();
227
- if (this.open) {
228
- this.closePanel();
229
- const openItem = this.items.find((i) => i.itemId === this.open);
230
- if (openItem) {
231
- const trigger = openItem.shadowRoot?.querySelector(".trigger") as HTMLElement;
232
- trigger?.focus();
233
- }
234
- }
235
- } else if (key === "ArrowDown" && this.open) {
236
- event.preventDefault();
237
- // Move focus into the panel
238
- const panel = this.shadowRoot?.querySelector(
239
- `.dropdown-panel[data-item-id="${this.open}"]`
240
- ) as HTMLElement;
241
- if (panel) {
242
- const firstFocusable = panel.querySelector("a, button") as HTMLElement;
243
- firstFocusable?.focus();
244
- }
245
- }
246
- }
247
-
248
- private focusItem(index: number) {
249
- const focusableItems = this.items.filter((item) => !item.disabled);
250
- const item = focusableItems[index];
251
- if (item) {
252
- const trigger = item.shadowRoot?.querySelector(".trigger") as HTMLElement;
253
- trigger?.focus();
254
- }
255
- }
256
-
257
- private hasItemContent(item: RelNavMenuItem): boolean {
258
- const slot = item.shadowRoot?.querySelector("slot");
259
- if (!slot) return false;
260
- const nodes = slot.assignedNodes({ flatten: true });
261
- return nodes.some(
262
- (node) => node.nodeType === Node.ELEMENT_NODE || node.textContent?.trim()
263
- );
264
- }
265
-
266
- private renderDropdownPanel(item: RelNavMenuItem) {
267
- if (!this.hasItemContent(item)) return null;
268
-
269
- const isOpen = this.open === item.itemId;
270
-
271
- return html`
272
- <div
273
- class="dropdown-panel ${isOpen ? "open" : ""}"
274
- data-item-id="${item.itemId}"
275
- id="${item.panelId}"
276
- role="menu"
277
- @click="${(e: Event) => e.stopPropagation()}"
278
- >
279
- ${this.getItemContent(item)}
280
- </div>
281
- `;
282
- }
283
-
284
- private getItemContent(item: RelNavMenuItem) {
285
- const slot = item.shadowRoot?.querySelector("slot");
286
- if (!slot) return null;
287
-
288
- const nodes = slot.assignedNodes({ flatten: true });
289
- const elements = nodes.filter((node) => node.nodeType === Node.ELEMENT_NODE) as Element[];
290
-
291
- // Clone and return the content
292
- return elements.map((el) => {
293
- const clone = el.cloneNode(true);
294
- return clone;
295
- });
296
- }
297
-
298
- render() {
299
- return html`
300
- <nav
301
- class="nav-container ${this.density === "compact" ? "compact" : ""}"
302
- role="navigation"
303
- @keydown="${this.handleKeyDown}"
304
- >
305
- <slot @slotchange="${this.handleSlotChange}"></slot>
306
- </nav>
307
-
308
- ${this.items.map((item) => {
309
- // Set up click and hover handlers by attaching to the trigger
310
- this.updateComplete.then(() => {
311
- const trigger = item.shadowRoot?.querySelector(".trigger") as HTMLElement;
312
- if (trigger) {
313
- // Remove old listeners if any
314
- trigger.onclick = (e) => this.handleItemClick(item, e);
315
- trigger.onmouseenter = () => this.handleItemMouseEnter(item);
316
-
317
- // Update active state
318
- trigger.classList.toggle("active", this.open === item.itemId);
319
-
320
- // Update ARIA attributes
321
- if (this.hasItemContent(item)) {
322
- trigger.setAttribute("aria-expanded", this.open === item.itemId ? "true" : "false");
323
- }
324
- }
325
- });
326
-
327
- return this.renderDropdownPanel(item);
328
- })}
329
- `;
330
- }
331
- }
1
+ import { LitElement, html, css } from "lit";
2
+ import { customElement, property, state, query } from "lit/decorators.js";
3
+ import { RelNavMenuItem } from "./rel-nav-menu-item";
4
+
5
+ type DensityType = "comfortable" | "compact";
6
+
7
+ @customElement("rel-nav-menu")
8
+ export class RelNavMenu extends LitElement {
9
+ @property({ type: String, reflect: true }) open = "";
10
+ @property({ type: Boolean, reflect: true }) disabled = false;
11
+ @property({ type: String }) density: DensityType = "comfortable";
12
+
13
+ @state() private items: RelNavMenuItem[] = [];
14
+ @state() private focusedIndex = 0;
15
+ @state() private hoverSwitchEnabled = false;
16
+
17
+ @query(".nav-container") private container!: HTMLElement;
18
+
19
+ static styles = css`
20
+ :host {
21
+ display: block;
22
+ font-family: var(--rel-fontFamilies-primary);
23
+ }
24
+
25
+ .nav-container {
26
+ display: flex;
27
+ align-items: center;
28
+ gap: var(--rel-spacing-space-3xs);
29
+ padding: var(--rel-spacing-space-2xs);
30
+ background: var(--rel-colors-gray-light);
31
+ border-radius: var(--rel-borders-radius-border-radius-200);
32
+ border: var(--rel-borders-width-stroke-100) solid var(--rel-colors-gray-border);
33
+ }
34
+
35
+ .nav-container.compact {
36
+ padding: var(--rel-spacing-space-3xs);
37
+ gap: 0;
38
+ }
39
+
40
+ :host([disabled]) .nav-container {
41
+ opacity: 0.5;
42
+ pointer-events: none;
43
+ }
44
+ `;
45
+
46
+ // ── Lifecycle ──────────────────────────────────────────────────────────────
47
+
48
+ connectedCallback() {
49
+ super.connectedCallback();
50
+ this.discoverItems();
51
+ document.addEventListener("click", this.handleOutsideClick);
52
+ // Delegate item interactions via composed events — no shadow DOM mutation.
53
+ this.addEventListener("rel-nav-item-click", this.handleNavItemClick as EventListener);
54
+ this.addEventListener("rel-nav-item-hover", this.handleNavItemHover as EventListener);
55
+ }
56
+
57
+ disconnectedCallback() {
58
+ super.disconnectedCallback();
59
+ document.removeEventListener("click", this.handleOutsideClick);
60
+ this.removeEventListener("rel-nav-item-click", this.handleNavItemClick as EventListener);
61
+ this.removeEventListener("rel-nav-item-hover", this.handleNavItemHover as EventListener);
62
+ }
63
+
64
+ // ── Item discovery ─────────────────────────────────────────────────────────
65
+
66
+ private discoverItems() {
67
+ const slot = this.shadowRoot?.querySelector("slot");
68
+ if (slot) {
69
+ const nodes = slot.assignedElements() as RelNavMenuItem[];
70
+ this.items = nodes.filter((el) => el.tagName === "REL-NAV-MENU-ITEM");
71
+ }
72
+ }
73
+
74
+ private handleSlotChange() {
75
+ this.discoverItems();
76
+ }
77
+
78
+ // ── Outside-click dismissal ────────────────────────────────────────────────
79
+
80
+ private handleOutsideClick = (e: MouseEvent) => {
81
+ const path = e.composedPath() as HTMLElement[];
82
+ const isInside = path.some(
83
+ (el) => el === this || el.tagName === "REL-NAV-MENU-ITEM"
84
+ );
85
+ if (!isInside && this.open) {
86
+ this.closePanel();
87
+ }
88
+ };
89
+
90
+ // ── Delegated item event handlers ──────────────────────────────────────────
91
+
92
+ private handleNavItemClick = (e: Event) => {
93
+ const { itemId, hasContent } = (e as CustomEvent).detail;
94
+ const item = this.items.find((i) => i.itemId === itemId);
95
+ if (!item || this.disabled || item.disabled) return;
96
+ // Plain link with no panel content — let the browser navigate.
97
+ if (item.href && !hasContent) return;
98
+ e.preventDefault();
99
+ if (hasContent) this.openPanel(itemId);
100
+ };
101
+
102
+ private handleNavItemHover = (e: Event) => {
103
+ const { itemId } = (e as CustomEvent).detail;
104
+ const item = this.items.find((i) => i.itemId === itemId);
105
+ if (!item || this.disabled || item.disabled) return;
106
+ if (this.open && this.hoverSwitchEnabled && item.hasContent) {
107
+ this.openPanel(itemId);
108
+ }
109
+ };
110
+
111
+ // ── Panel open/close ───────────────────────────────────────────────────────
112
+
113
+ private openPanel(itemId: string) {
114
+ if (this.disabled) return;
115
+
116
+ // Close the currently open item before switching.
117
+ if (this.open && this.open !== itemId) {
118
+ const prev = this.items.find((i) => i.itemId === this.open);
119
+ if (prev) prev.open = false;
120
+ }
121
+
122
+ const wasOpen = this.open === itemId;
123
+ this.open = wasOpen ? "" : itemId;
124
+
125
+ const item = this.items.find((i) => i.itemId === itemId);
126
+ if (item) item.open = !wasOpen;
127
+
128
+ if (!wasOpen) {
129
+ this.hoverSwitchEnabled = true;
130
+ }
131
+
132
+ this.dispatchEvent(
133
+ new CustomEvent("rel-nav-open-change", {
134
+ detail: { openId: this.open },
135
+ bubbles: true,
136
+ composed: true,
137
+ })
138
+ );
139
+ }
140
+
141
+ private closePanel() {
142
+ if (this.open) {
143
+ const openItem = this.items.find((i) => i.itemId === this.open);
144
+ if (openItem) openItem.open = false;
145
+ this.open = "";
146
+ this.hoverSwitchEnabled = false;
147
+ this.dispatchEvent(
148
+ new CustomEvent("rel-nav-open-change", {
149
+ detail: { openId: "" },
150
+ bubbles: true,
151
+ composed: true,
152
+ })
153
+ );
154
+ }
155
+ }
156
+
157
+ // ── Keyboard navigation ────────────────────────────────────────────────────
158
+
159
+ private handleKeyDown(event: KeyboardEvent) {
160
+ if (this.disabled) return;
161
+
162
+ const { key } = event;
163
+ const focusableItems = this.items.filter((item) => !item.disabled);
164
+
165
+ if (key === "ArrowRight") {
166
+ event.preventDefault();
167
+ this.focusedIndex = Math.min(focusableItems.length - 1, this.focusedIndex + 1);
168
+ this.focusItem(this.focusedIndex);
169
+ } else if (key === "ArrowLeft") {
170
+ event.preventDefault();
171
+ this.focusedIndex = Math.max(0, this.focusedIndex - 1);
172
+ this.focusItem(this.focusedIndex);
173
+ } else if (key === "Enter" || key === " ") {
174
+ event.preventDefault();
175
+ const item = focusableItems[this.focusedIndex];
176
+ if (item && item.hasContent) {
177
+ this.openPanel(item.itemId);
178
+ }
179
+ } else if (key === "Escape") {
180
+ event.preventDefault();
181
+ if (this.open) {
182
+ // Capture before closePanel() clears this.open.
183
+ const openItemId = this.open;
184
+ this.closePanel();
185
+ const openItem = this.items.find((i) => i.itemId === openItemId);
186
+ if (openItem) {
187
+ const trigger = openItem.shadowRoot?.querySelector(".trigger") as HTMLElement;
188
+ trigger?.focus();
189
+ }
190
+ }
191
+ } else if (key === "ArrowDown" && this.open) {
192
+ event.preventDefault();
193
+ // Move focus into the open panel (now inside the item's shadow root).
194
+ const openItem = this.items.find((i) => i.itemId === this.open);
195
+ if (openItem) {
196
+ const panel = openItem.shadowRoot?.querySelector(".panel") as HTMLElement;
197
+ if (panel) {
198
+ const firstFocusable = panel.querySelector("a, button") as HTMLElement;
199
+ firstFocusable?.focus();
200
+ }
201
+ }
202
+ }
203
+ }
204
+
205
+ private focusItem(index: number) {
206
+ const focusableItems = this.items.filter((item) => !item.disabled);
207
+ const item = focusableItems[index];
208
+ if (item) {
209
+ const trigger = item.shadowRoot?.querySelector(".trigger") as HTMLElement;
210
+ trigger?.focus();
211
+ }
212
+ }
213
+
214
+ // ── Render ─────────────────────────────────────────────────────────────────
215
+
216
+ render() {
217
+ return html`
218
+ <nav
219
+ class="nav-container ${this.density === "compact" ? "compact" : ""}"
220
+ role="navigation"
221
+ @keydown="${this.handleKeyDown}"
222
+ >
223
+ <slot @slotchange="${this.handleSlotChange}"></slot>
224
+ </nav>
225
+ `;
226
+ }
227
+ }
@@ -1,8 +1,21 @@
1
1
  import { LitElement, html, css } from "lit";
2
2
  import { customElement, property } from "lit/decorators.js";
3
3
 
4
+ /**
5
+ * @deprecated rel-navbar is deprecated and will be removed in a future release.
6
+ * Use rel-topbar and its sub-components instead:
7
+ * rel-topbar, rel-topbar-brand, rel-topbar-actions, rel-topbar-utilities,
8
+ * rel-topbar-item, rel-topbar-dropdown, rel-topbar-dropdown-item
9
+ */
4
10
  @customElement("rel-navbar")
5
11
  export class RelNavbar extends LitElement {
12
+ constructor() {
13
+ super();
14
+ console.warn(
15
+ "[rel-navbar] rel-navbar is deprecated. Use rel-topbar instead."
16
+ );
17
+ }
18
+
6
19
  @property({ type: Boolean, reflect: true })
7
20
  sticky = false;
8
21