@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
@@ -24,8 +24,6 @@ export class RelOtp extends LitElement {
24
24
  @property({ type: String }) size: "sm" | "md" | "lg" = "md";
25
25
 
26
26
  @state() private cells: string[] = [];
27
- @state() private focusedIndex: number = -1;
28
-
29
27
  @query(".hidden-input") private hiddenInput?: HTMLInputElement;
30
28
 
31
29
  private isUpdatingFromValue = false;
@@ -38,30 +36,30 @@ export class RelOtp extends LitElement {
38
36
 
39
37
  :host([disabled]) {
40
38
  opacity: 0.6;
41
- pointer-events: none;
39
+ cursor: not-allowed;
42
40
  }
43
41
 
44
42
  .otp-root {
45
43
  display: inline-flex;
46
44
  align-items: center;
47
- gap: var(--rel-spacing-space-2xs, 8px);
45
+ gap: var(--rel-spacing-space-2xs);
48
46
  position: relative;
49
47
  }
50
48
 
51
49
  .otp-group {
52
50
  display: flex;
53
- gap: var(--rel-spacing-space-2xs, 8px);
51
+ gap: var(--rel-spacing-space-2xs);
54
52
  }
55
53
 
56
54
  .otp-separator {
57
55
  display: flex;
58
56
  align-items: center;
59
57
  justify-content: center;
60
- color: var(--rel-colors-gray-divider);
58
+ color: var(--rel-gray-divider-default);
61
59
  font-size: var(--rel-fontSizes-lg);
62
60
  font-weight: var(--rel-fontWeights-semibold);
63
61
  user-select: none;
64
- padding: 0 var(--rel-spacing-space-3xs, 4px);
62
+ padding: 0 var(--rel-spacing-space-3xs);
65
63
  }
66
64
 
67
65
  .otp-separator.hidden {
@@ -71,17 +69,17 @@ export class RelOtp extends LitElement {
71
69
  .otp-cell {
72
70
  width: 40px;
73
71
  height: 48px;
74
- border: var(--rel-borders-width-stroke-100) solid var(--rel-border-default);
72
+ border: var(--rel-borders-width-stroke-100) solid var(--rel-color-border-default);
75
73
  border-radius: var(--rel-borders-radius-border-radius-200);
76
- background: var(--rel-colors-white);
74
+ background: var(--rel-color-bg-primary);
77
75
  font-family: var(--rel-fontFamilies-primary);
78
76
  font-size: var(--rel-fontSizes-lg);
79
77
  font-weight: var(--rel-fontWeights-medium);
80
78
  text-align: center;
81
- color: var(--rel-text-primary);
79
+ color: var(--rel-color-text-primary);
82
80
  outline: none;
83
81
  transition: border-color 0.15s ease, box-shadow 0.15s ease;
84
- caret-color: var(--rel-colors-gray-primary);
82
+ caret-color: var(--rel-color-text-primary);
85
83
  }
86
84
 
87
85
  /* Size variants */
@@ -98,27 +96,31 @@ export class RelOtp extends LitElement {
98
96
  }
99
97
 
100
98
  .otp-cell::placeholder {
101
- color: var(--rel-colors-gray-placeholder);
99
+ color: var(--rel-color-text-placeholder);
102
100
  opacity: 0.6;
103
101
  }
104
102
 
105
103
  .otp-cell:hover:not(:disabled):not(:read-only) {
106
- border-color: var(--rel-colors-gray-primary);
104
+ border-color: var(--rel-color-text-primary);
107
105
  }
108
106
 
109
107
  .otp-cell:focus-visible {
110
- border-color: var(--rel-colors-gray-heading);
111
- box-shadow: 0 0 0 2px var(--rel-border-default);
108
+ border-color: var(--rel-color-text-heading);
109
+ box-shadow: 0 0 0 2px var(--rel-color-border-default);
112
110
  }
113
111
 
114
112
  .otp-cell:disabled {
115
- background: var(--rel-colors-gray-light);
116
- color: var(--rel-colors-gray-disabled-text);
113
+ background: var(--rel-color-bg-secondary);
114
+ color: var(--rel-color-text-disabled);
115
+ cursor: not-allowed;
116
+ }
117
+
118
+ :host([disabled]) .otp-cell {
117
119
  cursor: not-allowed;
118
120
  }
119
121
 
120
122
  .otp-cell:read-only {
121
- background: var(--rel-colors-gray-surface);
123
+ background: var(--rel-color-bg-tertiary);
122
124
  cursor: default;
123
125
  }
124
126
 
@@ -146,6 +148,7 @@ export class RelOtp extends LitElement {
146
148
  transition: none;
147
149
  }
148
150
  }
151
+
149
152
  `;
150
153
 
151
154
  connectedCallback() {
@@ -382,16 +385,10 @@ export class RelOtp extends LitElement {
382
385
 
383
386
  private handleFocus(e: FocusEvent) {
384
387
  const input = e.currentTarget as HTMLInputElement;
385
- const index = parseInt(input.dataset.index || "0", 10);
386
-
387
- this.focusedIndex = index;
388
-
389
- // Select all text on focus for easy replacement
390
388
  input.select();
391
389
  }
392
390
 
393
391
  private handleBlur() {
394
- this.focusedIndex = -1;
395
392
  }
396
393
 
397
394
  private focusCell(index: number) {
@@ -458,7 +455,7 @@ export class RelOtp extends LitElement {
458
455
  data-index="${index}"
459
456
  type="${this.mask ? "password" : "text"}"
460
457
  inputmode="${this.getInputMode()}"
461
- autocomplete="${index === 0 ? this.autocomplete : "off"}"
458
+ autocomplete="new-password"
462
459
  pattern="${this.pattern || ""}"
463
460
  placeholder="${this.placeholder}"
464
461
  maxlength="1"
@@ -1,15 +1,21 @@
1
1
  import { LitElement, html, css } from "lit";
2
2
  import { customElement, property, state } from "lit/decorators.js";
3
+ import "../../styles/vars-v2.css";
3
4
 
4
5
  type DensityType = "comfortable" | "compact";
5
6
  type VariantType = "default" | "icons";
6
- type PageItem = { type: "page"; value: number } | { type: "ellipsis" } | { type: "prev" } | { type: "next" };
7
+ type PageItem =
8
+ | { type: "page"; value: number }
9
+ | { type: "ellipsis" }
10
+ | { type: "spacer" }
11
+ | { type: "prev" }
12
+ | { type: "next" };
7
13
 
8
14
  @customElement("rel-pagination")
9
15
  export class RelPagination extends LitElement {
10
16
  @property({ type: Number }) page?: number;
11
17
  @property({ type: Number }) defaultPage = 1;
12
- @property({ type: Number }) totalPages!: number;
18
+ @property({ type: Number }) totalPages = 0;
13
19
  @property({ type: Number }) siblingCount = 1;
14
20
  @property({ type: Number }) boundaryCount = 1;
15
21
  @property({ type: Boolean, reflect: true }) disabled = false;
@@ -30,6 +36,7 @@ export class RelPagination extends LitElement {
30
36
  display: flex;
31
37
  align-items: center;
32
38
  gap: var(--rel-spacing-space-2xs);
39
+ min-width: max-content;
33
40
  }
34
41
 
35
42
  nav.compact {
@@ -41,35 +48,47 @@ export class RelPagination extends LitElement {
41
48
  display: inline-flex;
42
49
  align-items: center;
43
50
  justify-content: center;
44
- min-width: 40px;
51
+ width: 40px;
45
52
  height: 40px;
46
- padding: 0 var(--rel-spacing-space-sm);
53
+ padding: 0;
47
54
  font-family: var(--rel-fontFamilies-primary);
48
55
  font-size: var(--rel-fontSizes-sm);
49
56
  font-weight: var(--rel-fontWeights-medium);
50
- color: var(--rel-colors-gray-heading);
51
- background: var(--rel-colors-white);
52
- border: var(--rel-borders-width-stroke-100) solid var(--rel-colors-gray-border);
57
+ color: var(--rel-color-text-heading);
58
+ background: var(--rel-color-bg-primary);
59
+ border: var(--rel-borders-width-stroke-100) solid var(--rel-color-border-default);
53
60
  border-radius: var(--rel-borders-radius-border-radius-200);
54
61
  cursor: pointer;
55
- transition: all 0.15s ease;
62
+ transition: background 0.15s ease, border-color 0.15s ease, color 0.15s ease;
56
63
  text-decoration: none;
57
64
  outline: none;
58
65
  user-select: none;
59
66
  }
60
67
 
68
+ button.prev-btn, button.next-btn,
69
+ a.prev-btn, a.next-btn {
70
+ width: auto;
71
+ padding: 0 var(--rel-spacing-space-sm);
72
+ }
73
+
61
74
  nav.compact button,
62
75
  nav.compact a {
63
- min-width: 32px;
76
+ width: 32px;
64
77
  height: 32px;
65
- padding: 0 var(--rel-spacing-space-xs);
78
+ padding: 0;
66
79
  font-size: var(--rel-fontSizes-xs);
67
80
  }
68
81
 
82
+ nav.compact button.prev-btn, nav.compact button.next-btn,
83
+ nav.compact a.prev-btn, nav.compact a.next-btn {
84
+ width: auto;
85
+ padding: 0 var(--rel-spacing-space-xs);
86
+ }
87
+
69
88
  button:hover:not(:disabled),
70
89
  a:hover:not([aria-disabled="true"]) {
71
- background: var(--rel-surface-secondary, #fafafa);
72
- border-color: var(--rel-colors-gray-primary, #333);
90
+ background: var(--rel-color-bg-secondary);
91
+ border-color: var(--rel-color-text-primary);
73
92
  }
74
93
 
75
94
  button:focus-visible,
@@ -82,40 +101,50 @@ export class RelPagination extends LitElement {
82
101
  a[aria-disabled="true"] {
83
102
  opacity: 0.5;
84
103
  cursor: not-allowed;
85
- pointer-events: none;
86
104
  }
87
105
 
88
106
  button[aria-current="page"],
89
107
  a[aria-current="page"] {
90
- background: var(--rel-colors-gray-heading);
91
- color: var(--rel-colors-white);
92
- border-color: var(--rel-colors-gray-heading);
108
+ background: var(--rel-color-bg-selected);
109
+ color: var(--rel-color-text-primary);
110
+ border-color: var(--rel-color-text-heading);
93
111
  font-weight: var(--rel-fontWeights-semibold);
94
112
  }
95
113
 
96
114
  button[aria-current="page"]:hover,
97
115
  a[aria-current="page"]:hover {
98
- background: var(--rel-colors-gray-primary);
99
- border-color: var(--rel-colors-gray-primary);
116
+ background: var(--rel-color-bg-selected);
117
+ border-color: var(--rel-color-text-heading);
100
118
  }
101
119
 
102
120
  .ellipsis {
103
121
  display: inline-flex;
104
122
  align-items: center;
105
123
  justify-content: center;
106
- min-width: 40px;
124
+ width: 40px;
107
125
  height: 40px;
108
- color: var(--rel-colors-gray-placeholder);
126
+ color: var(--rel-color-text-placeholder);
109
127
  font-size: var(--rel-fontSizes-sm);
110
128
  user-select: none;
111
129
  }
112
130
 
113
131
  nav.compact .ellipsis {
114
- min-width: 32px;
132
+ width: 32px;
115
133
  height: 32px;
116
134
  font-size: var(--rel-fontSizes-xs);
117
135
  }
118
136
 
137
+ .pagination-spacer {
138
+ display: inline-flex;
139
+ width: 40px;
140
+ height: 40px;
141
+ }
142
+
143
+ nav.compact .pagination-spacer {
144
+ width: 32px;
145
+ height: 32px;
146
+ }
147
+
119
148
  .icon {
120
149
  width: 16px;
121
150
  height: 16px;
@@ -127,6 +156,10 @@ export class RelPagination extends LitElement {
127
156
  height: 14px;
128
157
  }
129
158
 
159
+ :host([disabled]) {
160
+ cursor: not-allowed;
161
+ }
162
+
130
163
  :host([disabled]) nav {
131
164
  opacity: 0.5;
132
165
  pointer-events: none;
@@ -135,6 +168,19 @@ export class RelPagination extends LitElement {
135
168
 
136
169
  connectedCallback() {
137
170
  super.connectedCallback();
171
+ // Manually parse numeric attributes in case they were set before upgrade
172
+ const totalPagesAttr = this.getAttribute('total-pages');
173
+ if (totalPagesAttr) {
174
+ this.totalPages = Number(totalPagesAttr);
175
+ }
176
+ const pageAttr = this.getAttribute('page');
177
+ if (pageAttr) {
178
+ this.page = Number(pageAttr);
179
+ }
180
+ const defaultPageAttr = this.getAttribute('default-page');
181
+ if (defaultPageAttr) {
182
+ this.defaultPage = Number(defaultPageAttr);
183
+ }
138
184
  this.currentPage = this.page ?? this.defaultPage;
139
185
  }
140
186
 
@@ -153,49 +199,39 @@ export class RelPagination extends LitElement {
153
199
  const items: PageItem[] = [];
154
200
  const current = this.activePage;
155
201
  const total = this.totalPages;
156
- const sibling = this.siblingCount;
157
- const boundary = this.boundaryCount;
202
+
203
+ if (total <= 0) return items;
158
204
 
159
205
  items.push({ type: "prev" });
160
206
 
161
- // Calculate page ranges
162
- const leftBoundary = Array.from({ length: Math.min(boundary, total) }, (_, i) => i + 1);
163
- const rightBoundary = Array.from(
164
- { length: Math.min(boundary, total) },
165
- (_, i) => total - boundary + i + 1
166
- ).filter((p) => p > 0);
167
-
168
- const leftSiblings = Array.from(
169
- { length: sibling },
170
- (_, i) => current - sibling + i
171
- ).filter((p) => p > boundary && p < current);
172
-
173
- const rightSiblings = Array.from(
174
- { length: sibling },
175
- (_, i) => current + i + 1
176
- ).filter((p) => p > current && p <= total - boundary);
177
-
178
- // Combine all visible pages
179
- const visiblePages = new Set([
180
- ...leftBoundary,
181
- ...leftSiblings,
182
- current,
183
- ...rightSiblings,
184
- ...rightBoundary,
185
- ]);
186
-
187
- const sortedPages = Array.from(visiblePages).sort((a, b) => a - b);
188
-
189
- // Build items with ellipsis
190
- sortedPages.forEach((page, index) => {
191
- if (index > 0) {
192
- const prevPage = sortedPages[index - 1];
193
- if (page - prevPage > 1) {
194
- items.push({ type: "ellipsis" });
195
- }
207
+ if (total <= 7) {
208
+ for (let i = 1; i <= total; i++) {
209
+ items.push({ type: "page", value: i });
196
210
  }
197
- items.push({ type: "page", value: page });
198
- });
211
+ } else if (current <= 4) {
212
+ // Near start: [1, 2, 3, 4, 5] [ellipsis] [last]
213
+ for (let i = 1; i <= 5; i++) {
214
+ items.push({ type: "page", value: i });
215
+ }
216
+ items.push({ type: "ellipsis" });
217
+ items.push({ type: "page", value: total });
218
+ } else if (current >= total - 3) {
219
+ // Near end: [1] [ellipsis] [last-4, last-3, last-2, last-1, last]
220
+ items.push({ type: "page", value: 1 });
221
+ items.push({ type: "ellipsis" });
222
+ for (let i = total - 4; i <= total; i++) {
223
+ items.push({ type: "page", value: i });
224
+ }
225
+ } else {
226
+ // Middle: [1] [ellipsis] [current-1, current, current+1] [ellipsis] [last]
227
+ items.push({ type: "page", value: 1 });
228
+ items.push({ type: "ellipsis" });
229
+ items.push({ type: "page", value: current - 1 });
230
+ items.push({ type: "page", value: current });
231
+ items.push({ type: "page", value: current + 1 });
232
+ items.push({ type: "ellipsis" });
233
+ items.push({ type: "page", value: total });
234
+ }
199
235
 
200
236
  items.push({ type: "next" });
201
237
 
@@ -222,6 +258,11 @@ export class RelPagination extends LitElement {
222
258
  );
223
259
  }
224
260
 
261
+ private getHref(page: number): string {
262
+ if (!this.hrefTemplate) return "#";
263
+ return this.hrefTemplate.replace(/\{page\}/, String(page));
264
+ }
265
+
225
266
  private handleKeyDown(event: KeyboardEvent, callback: () => void) {
226
267
  if (event.key === "Enter" || event.key === " ") {
227
268
  event.preventDefault();
@@ -249,6 +290,10 @@ export class RelPagination extends LitElement {
249
290
  const current = this.activePage;
250
291
  const useLinks = !!this.hrefTemplate;
251
292
 
293
+ if (item.type === "spacer") {
294
+ return html`<span class="pagination-spacer"></span>`;
295
+ }
296
+
252
297
  if (item.type === "ellipsis") {
253
298
  return html`<span class="ellipsis">…</span>`;
254
299
  }
@@ -258,10 +303,11 @@ export class RelPagination extends LitElement {
258
303
  const targetPage = current - 1;
259
304
 
260
305
  if (useLinks) {
261
- const href = disabled ? "#" : this.hrefTemplate.replace("{page}", String(targetPage));
306
+ const href = disabled ? "#" : this.getHref(targetPage);
262
307
  return html`
263
308
  <a
264
309
  href="${href}"
310
+ class="prev-btn"
265
311
  aria-label="Go to previous page"
266
312
  aria-disabled="${disabled}"
267
313
  @click="${(e: Event) => {
@@ -281,6 +327,7 @@ export class RelPagination extends LitElement {
281
327
  return html`
282
328
  <button
283
329
  type="button"
330
+ class="prev-btn"
284
331
  aria-label="Go to previous page"
285
332
  ?disabled="${disabled}"
286
333
  @click="${() => this.handlePageChange(targetPage)}"
@@ -295,10 +342,11 @@ export class RelPagination extends LitElement {
295
342
  const targetPage = current + 1;
296
343
 
297
344
  if (useLinks) {
298
- const href = disabled ? "#" : this.hrefTemplate.replace("{page}", String(targetPage));
345
+ const href = disabled ? "#" : this.getHref(targetPage);
299
346
  return html`
300
347
  <a
301
348
  href="${href}"
349
+ class="next-btn"
302
350
  aria-label="Go to next page"
303
351
  aria-disabled="${disabled}"
304
352
  @click="${(e: Event) => {
@@ -318,6 +366,7 @@ export class RelPagination extends LitElement {
318
366
  return html`
319
367
  <button
320
368
  type="button"
369
+ class="next-btn"
321
370
  aria-label="Go to next page"
322
371
  ?disabled="${disabled}"
323
372
  @click="${() => this.handlePageChange(targetPage)}"
@@ -332,7 +381,7 @@ export class RelPagination extends LitElement {
332
381
  const isCurrent = pageNum === current;
333
382
 
334
383
  if (useLinks) {
335
- const href = this.hrefTemplate.replace("{page}", String(pageNum));
384
+ const href = this.getHref(pageNum);
336
385
  return html`
337
386
  <a
338
387
  href="${href}"
@@ -1,5 +1,6 @@
1
1
  import { LitElement, html, css } from "lit";
2
2
  import { customElement, property } from "lit/decorators.js";
3
+ import "../../styles/vars-v2.css";
3
4
 
4
5
  @customElement("rel-popover-content")
5
6
  export class RelPopoverContent extends LitElement {
@@ -13,14 +14,14 @@ export class RelPopoverContent extends LitElement {
13
14
  }
14
15
 
15
16
  .popover-content {
16
- background: var(--rel-colors-white);
17
- border: var(--rel-borders-width-stroke-100) solid var(--rel-colors-gray-border);
17
+ background: var(--rel-color-bg-primary);
18
+ border: var(--rel-borders-width-stroke-100) solid var(--rel-color-border-default);
18
19
  border-radius: var(--rel-borders-radius-border-radius-200);
19
20
  box-shadow: var(--rel-shadows-shadow-300);
20
21
  padding: var(--rel-spacing-space-md);
21
22
  font-family: var(--rel-fontFamilies-primary);
22
23
  font-size: var(--rel-fontSizes-sm);
23
- color: var(--rel-colors-gray-heading);
24
+ color: var(--rel-color-text-heading);
24
25
  }
25
26
 
26
27
  .popover-content ::slotted(h1),
@@ -31,12 +32,12 @@ export class RelPopoverContent extends LitElement {
31
32
  .popover-content ::slotted(h6) {
32
33
  margin: 0 0 var(--rel-spacing-space-xs);
33
34
  font-weight: var(--rel-fontWeights-semibold);
34
- color: var(--rel-colors-gray-heading);
35
+ color: var(--rel-color-text-heading);
35
36
  }
36
37
 
37
38
  .popover-content ::slotted(p) {
38
39
  margin: 0 0 var(--rel-spacing-space-sm);
39
- color: var(--rel-colors-gray-placeholder);
40
+ color: var(--rel-color-text-placeholder);
40
41
  line-height: var(--rel-lineHeights-md);
41
42
  }
42
43
 
@@ -1,5 +1,6 @@
1
1
  import { LitElement, html, css } from "lit";
2
2
  import { customElement, property, state, query } from "lit/decorators.js";
3
+ import "../../styles/vars-v2.css";
3
4
 
4
5
  type SideType = "top" | "right" | "bottom" | "left";
5
6
  type AlignType = "start" | "center" | "end";
@@ -35,6 +36,7 @@ export class RelPopover extends LitElement {
35
36
 
36
37
  private triggerEl: HTMLElement | null = null;
37
38
  private returnFocusEl: HTMLElement | null = null;
39
+ private uniqueId = `popover-${Math.random().toString(36).slice(2)}`;
38
40
 
39
41
  /* =======================
40
42
  * STYLES
@@ -47,8 +49,12 @@ export class RelPopover extends LitElement {
47
49
  }
48
50
 
49
51
  :host([disabled]) {
50
- pointer-events: none;
51
52
  opacity: 0.6;
53
+ cursor: not-allowed;
54
+ }
55
+
56
+ :host([disabled]) * {
57
+ pointer-events: none;
52
58
  }
53
59
 
54
60
  .content-wrapper {
@@ -81,10 +87,8 @@ export class RelPopover extends LitElement {
81
87
  }
82
88
 
83
89
  firstUpdated() {
84
- // Resolve trigger once from light DOM
85
- this.triggerEl = this.querySelector(
86
- '[slot="trigger"]',
87
- ) as HTMLElement | null;
90
+ // triggerEl is resolved via onTriggerSlotChange; call aria in case slot
91
+ // content was already assigned before firstUpdated
88
92
  this.applyTriggerAria();
89
93
  }
90
94
 
@@ -134,18 +138,13 @@ export class RelPopover extends LitElement {
134
138
 
135
139
  const path = e.composedPath();
136
140
 
137
- // Toggle ONLY if pointerdown originated from trigger
138
- const triggerNode = path.find(
139
- (n) => n instanceof HTMLElement && n.getAttribute?.("slot") === "trigger",
140
- ) as HTMLElement | undefined;
141
-
142
- if (!triggerNode) return;
141
+ // Toggle ONLY if pointerdown originated from the trigger element
142
+ if (!this.triggerEl || !path.includes(this.triggerEl)) return;
143
143
 
144
144
  // Prevent this interaction from becoming an "outside click"
145
145
  e.preventDefault();
146
146
  e.stopPropagation();
147
147
 
148
- this.triggerEl = triggerNode;
149
148
  this.setOpen(!this.activeOpen);
150
149
  };
151
150
 
@@ -239,7 +238,8 @@ export class RelPopover extends LitElement {
239
238
  }
240
239
 
241
240
  private restoreFocus() {
242
- this.triggerEl?.focus();
241
+ (this.returnFocusEl ?? this.triggerEl)?.focus();
242
+ this.returnFocusEl = null;
243
243
  }
244
244
 
245
245
  private trapFocusLoop(e: KeyboardEvent) {
@@ -278,12 +278,25 @@ export class RelPopover extends LitElement {
278
278
  const t = this.triggerEl.getBoundingClientRect();
279
279
  const c = this.contentWrapper.getBoundingClientRect();
280
280
 
281
- let top = t.bottom + this.sideOffset;
282
- let left = t.left + t.width / 2 - c.width / 2;
281
+ let top = 0;
282
+ let left = 0;
283
283
 
284
- if (this.side === "top") top = t.top - c.height - this.sideOffset;
285
- if (this.side === "left") left = t.left - c.width - this.sideOffset;
286
- if (this.side === "right") left = t.right + this.sideOffset;
284
+ if (this.side === "top") {
285
+ top = t.top - c.height - this.sideOffset;
286
+ left = t.left + t.width / 2 - c.width / 2;
287
+ }
288
+ if (this.side === "bottom") {
289
+ top = t.bottom + this.sideOffset;
290
+ left = t.left + t.width / 2 - c.width / 2;
291
+ }
292
+ if (this.side === "left") {
293
+ top = t.top + t.height / 2 - c.height / 2;
294
+ left = t.left - c.width - this.sideOffset;
295
+ }
296
+ if (this.side === "right") {
297
+ top = t.top + t.height / 2 - c.height / 2;
298
+ left = t.right + this.sideOffset;
299
+ }
287
300
 
288
301
  top = Math.max(8, Math.min(top, window.innerHeight - c.height - 8));
289
302
  left = Math.max(8, Math.min(left, window.innerWidth - c.width - 8));
@@ -295,10 +308,18 @@ export class RelPopover extends LitElement {
295
308
  * ARIA
296
309
  * ======================= */
297
310
 
311
+ private onTriggerSlotChange(e: Event) {
312
+ const slot = e.target as HTMLSlotElement;
313
+ const first = slot.assignedElements({ flatten: true })[0] as HTMLElement | undefined;
314
+ this.triggerEl = first ?? null;
315
+ this.applyTriggerAria();
316
+ }
317
+
298
318
  private applyTriggerAria() {
299
319
  if (!this.triggerEl) return;
300
320
  this.triggerEl.setAttribute("aria-haspopup", "dialog");
301
321
  this.triggerEl.setAttribute("aria-expanded", String(this.activeOpen));
322
+ this.triggerEl.setAttribute("aria-controls", this.uniqueId);
302
323
  }
303
324
 
304
325
  /* =======================
@@ -307,14 +328,15 @@ export class RelPopover extends LitElement {
307
328
 
308
329
  render() {
309
330
  return html`
310
- <slot name="trigger"></slot>
331
+ <slot @slotchange=${this.onTriggerSlotChange}></slot>
311
332
 
312
333
  <div
334
+ id="${this.uniqueId}"
313
335
  class="content-wrapper"
314
336
  style="${this.contentStyle}"
315
337
  ?open="${this.activeOpen}"
316
338
  >
317
- <slot></slot>
339
+ <slot name="content"></slot>
318
340
  </div>
319
341
  `;
320
342
  }