@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,7 +1,9 @@
1
1
  import { LitElement, html, css } from "lit";
2
2
  import { customElement, property, state } from "lit/decorators.js";
3
+ import "../../styles/vars-v2.css";
4
+ import "@reliability-design/icons/rel-icon";
3
5
 
4
- export type TextFieldState = "default" | "focused" | "error" | "disabled";
6
+ export type TextFieldState = "default" | "error" | "disabled";
5
7
  export type TextFieldSize = "small" | "medium" | "large";
6
8
 
7
9
  @customElement("rel-textfield")
@@ -15,28 +17,106 @@ export class RelTextfield extends LitElement {
15
17
  @property({ type: String }) supportingText?: string;
16
18
  @property({ type: String }) state: TextFieldState = "default";
17
19
  @property({ type: String }) size: TextFieldSize = "medium";
20
+ @property({ type: Boolean, reflect: true }) floatingLabel = false;
18
21
 
19
- @state() private internalState: TextFieldState = "default";
22
+ @state() private internalState: "default" | "focused" = "default";
23
+ private _wasError = false;
24
+
25
+ private uniqueId = `textfield-${Math.random().toString(36).slice(2)}`;
20
26
 
21
27
  static styles = css`
22
28
  :host {
23
29
  display: block;
24
- font-family: var(--rel-fontFamilies-primary, sans-serif);
30
+ font-family: var(--rel-fontFamilies-primary);
25
31
  }
26
32
 
27
33
  /* Wrapper */
28
34
  .textfield-wrapper {
29
35
  display: flex;
30
36
  flex-direction: column;
31
- gap: var(--rel-spacing-space-2xs, 4px);
37
+ gap: var(--rel-spacing-space-2xs);
32
38
  width: 100%;
33
39
  }
34
40
 
35
41
  /* Label */
36
42
  .textfield-label {
37
- font-size: var(--rel-fontSizes-sm, 12px);
38
- font-weight: var(--rel-fontWeights-medium, 500);
39
- color: var(--rel-text-heading, #2b2b2b);
43
+ font-size: var(--rel-fontSizes-sm);
44
+ font-weight: var(--rel-fontWeights-medium);
45
+ color: var(--rel-color-text-heading);
46
+ }
47
+
48
+ /* Floating label variant */
49
+ :host([floatinglabel]) .textfield-wrapper {
50
+ gap: 0;
51
+ }
52
+
53
+ :host([floatinglabel]) .textfield-container {
54
+ overflow: visible;
55
+ position: relative;
56
+ padding-top: 0;
57
+ }
58
+
59
+ :host([floatinglabel]) .textfield-label {
60
+ position: absolute;
61
+ left: var(--rel-spacing-space-sm);
62
+ top: 50%;
63
+ transform: translateY(-50%);
64
+ font-size: var(--rel-fontSizes-md);
65
+ font-weight: var(--rel-fontWeights-regular);
66
+ color: var(--rel-color-text-placeholder);
67
+ pointer-events: none;
68
+ transition: top 0.15s ease, font-size 0.15s ease, color 0.15s ease, transform 0.15s ease;
69
+ background: var(--rel-color-bg-primary);
70
+ padding: 0 2px;
71
+ z-index: 1;
72
+ line-height: 1;
73
+ white-space: nowrap;
74
+ }
75
+
76
+ :host([floatinglabel]) .textfield-wrapper.focused .textfield-label,
77
+ :host([floatinglabel]) .textfield-wrapper.has-value .textfield-label {
78
+ top: 0;
79
+ transform: translateY(-50%);
80
+ font-size: var(--rel-fontSizes-xs);
81
+ color: var(--rel-color-text-primary);
82
+ }
83
+
84
+ :host([floatinglabel]) .textfield-wrapper.error .textfield-label {
85
+ color: var(--rel-error-text);
86
+ }
87
+
88
+ :host([floatinglabel]) .textfield-wrapper.focused:not(.error) .textfield-label {
89
+ color: var(--rel-color-text-primary);
90
+ }
91
+
92
+ :host([floatinglabel]) .textfield-wrapper.focused.error .textfield-label,
93
+ :host([floatinglabel]) .textfield-wrapper.has-value.error .textfield-label {
94
+ top: 0;
95
+ transform: translateY(-50%);
96
+ font-size: var(--rel-fontSizes-xs);
97
+ color: var(--rel-error-text);
98
+ }
99
+
100
+ :host([floatinglabel]) .textfield-input {
101
+ padding-top: var(--rel-spacing-space-sm);
102
+ padding-bottom: var(--rel-spacing-space-sm);
103
+ }
104
+
105
+ :host([floatinglabel]) .textfield-input.has-left-icon {
106
+ padding-left: var(--rel-spacing-space-2xl);
107
+ }
108
+
109
+ :host([floatinglabel]) .textfield-label.has-left-icon {
110
+ left: var(--rel-spacing-space-2xl);
111
+ }
112
+
113
+ :host([floatinglabel]) .textfield-wrapper.focused .textfield-label.has-left-icon,
114
+ :host([floatinglabel]) .textfield-wrapper.has-value .textfield-label.has-left-icon {
115
+ left: var(--rel-spacing-space-sm);
116
+ }
117
+
118
+ :host([floatinglabel]) .textfield-wrapper.disabled .textfield-label {
119
+ background: var(--rel-color-bg-tertiary);
40
120
  }
41
121
 
42
122
  /* Container */
@@ -44,14 +124,15 @@ export class RelTextfield extends LitElement {
44
124
  display: flex;
45
125
  align-items: center;
46
126
  position: relative;
47
- border: 1px solid var(--rel-border-default, #ddd);
48
- border-radius: var(--rel-borders-radius-border-radius-200, 8px);
49
- background: var(--rel-colors-white, #fff);
127
+ border: 1px solid var(--rel-color-border-default);
128
+ border-radius: var(--rel-borders-radius-border-radius-100);
129
+ background: var(--rel-color-bg-primary);
130
+ overflow: hidden;
50
131
  transition: border 0.2s;
51
132
  }
52
133
 
53
134
  .textfield-container.disabled {
54
- background: var(--rel-surface-surface, #f5f5f5);
135
+ background: var(--rel-color-bg-tertiary);
55
136
  cursor: not-allowed;
56
137
  }
57
138
 
@@ -60,23 +141,23 @@ export class RelTextfield extends LitElement {
60
141
  flex: 1;
61
142
  border: none;
62
143
  outline: none;
63
- font-size: var(--rel-fontSizes-md, 14px);
64
- padding: 0.5rem 0.75rem;
144
+ font-size: var(--rel-fontSizes-md);
145
+ padding: var(--rel-spacing-space-xs) var(--rel-spacing-space-sm);
65
146
  background: transparent;
66
- font-family: var(--rel-fontFamilies-primary, sans-serif);
67
- color: var(--rel-text-primary, #333);
147
+ font-family: var(--rel-fontFamilies-primary);
148
+ color: var(--rel-color-text-primary);
68
149
  }
69
150
 
70
151
  .textfield-input:disabled {
71
152
  cursor: not-allowed;
72
- color: var(--rel-text-disabled, #9e9e9e);
153
+ color: var(--rel-color-text-disabled);
73
154
  }
74
155
 
75
156
  .icon {
76
157
  position: absolute;
77
158
  display: flex;
78
159
  pointer-events: none;
79
- color: var(--rel-text-placeholder, #a9a9a9);
160
+ color: var(--rel-color-text-placeholder);
80
161
  }
81
162
 
82
163
  .icon.left {
@@ -88,32 +169,32 @@ export class RelTextfield extends LitElement {
88
169
  }
89
170
 
90
171
  .textfield-wrapper.small .textfield-input {
91
- padding: 0.5rem 1rem;
92
- font-size: 0.9rem;
172
+ padding: var(--rel-spacing-space-xs) var(--rel-spacing-space-sm);
173
+ font-size: var(--rel-fontSizes-sm);
93
174
  }
94
175
 
95
176
  .textfield-wrapper.medium .textfield-input {
96
- padding: 0.688rem 1rem;
97
- font-size: 1rem;
177
+ padding: var(--rel-spacing-space-sm) var(--rel-spacing-space-md);
178
+ font-size: var(--rel-fontSizes-md);
98
179
  }
99
180
 
100
181
  .textfield-wrapper.large .textfield-input {
101
- padding: 0.875rem 1rem;
102
- font-size: 1.1rem;
182
+ padding: var(--rel-spacing-space-md) var(--rel-spacing-space-md);
183
+ font-size: var(--rel-fontSizes-lg);
103
184
  }
104
185
 
105
186
  /* Adjust padding when icons are present */
106
187
  .textfield-wrapper .textfield-input.has-left-icon {
107
- padding-left: 2.5rem;
188
+ padding-left: var(--rel-spacing-space-2xl);
108
189
  }
109
190
 
110
191
  .textfield-wrapper .textfield-input.has-right-icon {
111
- padding-right: 2.5rem;
192
+ padding-right: var(--rel-spacing-space-2xl);
112
193
  }
113
194
 
114
195
  /* States */
115
196
  .textfield-wrapper.focused .textfield-container {
116
- border-color: var(--rel-colors-primary-500, #0091ea);
197
+ border-color: var(--rel-color-text-primary);
117
198
  }
118
199
 
119
200
  .textfield-input:focus-visible {
@@ -122,40 +203,53 @@ export class RelTextfield extends LitElement {
122
203
 
123
204
  .textfield-wrapper.focused .textfield-container,
124
205
  .textfield-container:focus-within {
125
- border-color: var(--rel-colors-primary-500, #0091ea);
126
- box-shadow: 0 0 0 1px var(--rel-colors-primary-500, #0091ea);
206
+ border-color: var(--rel-color-text-primary);
207
+ box-shadow: 0 0 0 1px var(--rel-color-text-primary);
127
208
  }
128
209
 
129
210
  .textfield-wrapper.error .textfield-container {
130
- border-color: var(--rel-border-error, #cc0000);
211
+ border-color: var(--rel-error-surface-default);
212
+ box-shadow: none;
131
213
  }
132
214
 
133
215
  .textfield-wrapper.disabled .textfield-container {
134
- border-color: var(--rel-border-default, #ddd);
216
+ border-color: var(--rel-color-border-default);
135
217
  }
136
218
 
137
219
  /* Supporting text */
138
220
  .textfield-support {
139
- font-size: var(--rel-fontSizes-xs, 10px);
140
- color: var(--rel-text-placeholder, #a9a9a9);
221
+ font-size: var(--rel-fontSizes-xs);
222
+ color: var(--rel-color-text-placeholder);
141
223
  }
142
224
 
143
225
  .textfield-support.error {
144
- color: var(--rel-text-error, #cc0000);
226
+ color: var(--rel-error-text);
145
227
  }
146
228
 
147
229
  /* Placeholder */
148
230
  .textfield-input::placeholder {
149
- color: var(--rel-text-placeholder, #a9a9a9);
231
+ color: var(--rel-color-text-placeholder);
232
+ }
233
+
234
+ /* Suppress Chrome autofill blue background */
235
+ .textfield-input:-webkit-autofill,
236
+ .textfield-input:-webkit-autofill:hover,
237
+ .textfield-input:-webkit-autofill:focus {
238
+ -webkit-box-shadow: 0 0 0px 1000px var(--rel-color-bg-primary) inset;
239
+ -webkit-text-fill-color: var(--rel-color-text-primary);
150
240
  }
151
241
  `;
152
242
 
153
- private get currentState(): TextFieldState {
154
- return this.state !== "default" ? this.state : this.internalState;
243
+ connectedCallback() {
244
+ super.connectedCallback();
245
+ const leftIcon = this.getAttribute('lefticon') || this.getAttribute('leftIcon');
246
+ if (leftIcon) this.leftIcon = leftIcon;
247
+ const rightIcon = this.getAttribute('righticon') || this.getAttribute('rightIcon');
248
+ if (rightIcon) this.rightIcon = rightIcon;
155
249
  }
156
250
 
157
251
  private handleFocus() {
158
- if (this.state !== "disabled" && this.state !== "error") {
252
+ if (this.state !== "disabled") {
159
253
  this.internalState = "focused";
160
254
  }
161
255
  }
@@ -169,8 +263,15 @@ export class RelTextfield extends LitElement {
169
263
  private handleInput(e: Event) {
170
264
  const input = e.target as HTMLInputElement;
171
265
  this.value = input.value;
266
+ if (this.state === "error" && this.value) {
267
+ this._wasError = true;
268
+ this.state = "default";
269
+ } else if (this._wasError && !this.value) {
270
+ this._wasError = false;
271
+ this.state = "error";
272
+ }
172
273
  this.dispatchEvent(
173
- new CustomEvent<string>("input-change", {
274
+ new CustomEvent<string>("rel-change", {
174
275
  detail: this.value,
175
276
  bubbles: true,
176
277
  composed: true,
@@ -182,8 +283,10 @@ export class RelTextfield extends LitElement {
182
283
  const wrapperClasses = [
183
284
  "textfield-wrapper",
184
285
  this.size,
185
- this.currentState,
186
- ].join(" ");
286
+ this.state,
287
+ this.internalState === "focused" ? "focused" : "",
288
+ this.value ? "has-value" : "",
289
+ ].filter(Boolean).join(" ");
187
290
 
188
291
  const inputClasses = [
189
292
  "textfield-input",
@@ -195,8 +298,8 @@ export class RelTextfield extends LitElement {
195
298
 
196
299
  return html`
197
300
  <div class="${wrapperClasses}">
198
- ${this.label
199
- ? html`<label class="textfield-label">${this.label}</label>`
301
+ ${!this.floatingLabel && this.label
302
+ ? html`<label class="textfield-label" for="${this.uniqueId}">${this.label}</label>`
200
303
  : ""}
201
304
 
202
305
  <div
@@ -204,12 +307,18 @@ export class RelTextfield extends LitElement {
204
307
  ? "disabled"
205
308
  : ""}"
206
309
  >
310
+ ${this.floatingLabel && this.label
311
+ ? html`<label
312
+ class="textfield-label${this.leftIcon ? ' has-left-icon' : ''}"
313
+ for="${this.uniqueId}">${this.label}</label>`
314
+ : ""}
207
315
  ${this.leftIcon
208
- ? html`<span class="icon left">${this.leftIcon}</span>`
316
+ ? html`<rel-icon name="${this.leftIcon}" size="sm" class="icon left"></rel-icon>`
209
317
  : ""}
210
318
 
211
319
  <input
212
320
  type="${this.type}"
321
+ id="${this.uniqueId}"
213
322
  class="${inputClasses}"
214
323
  placeholder="${this.placeholder}"
215
324
  .value="${this.value}"
@@ -221,7 +330,7 @@ export class RelTextfield extends LitElement {
221
330
  />
222
331
 
223
332
  ${this.rightIcon
224
- ? html`<span class="icon right">${this.rightIcon}</span>`
333
+ ? html`<rel-icon name="${this.rightIcon}" size="sm" class="icon right"></rel-icon>`
225
334
  : ""}
226
335
  </div>
227
336
 
@@ -1,5 +1,6 @@
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
  export type ToastType = "default" | "error";
5
6
 
@@ -9,9 +10,10 @@ export class RelToast extends LitElement {
9
10
  @property({ type: String }) type: ToastType = "default";
10
11
  @property({ type: String }) actionLabel?: string;
11
12
  @property({ type: Number }) duration: number = 4000;
13
+ @property({ type: Boolean, reflect: true }) show = false;
12
14
 
13
15
  @state() private visible: boolean = false;
14
- private timeoutId?: number;
16
+ private _dismissTimer: ReturnType<typeof setTimeout> | null = null;
15
17
 
16
18
  static styles = css`
17
19
  :host {
@@ -23,7 +25,7 @@ export class RelToast extends LitElement {
23
25
  justify-content: center;
24
26
  width: 100%;
25
27
  pointer-events: none;
26
- z-index: 1000;
28
+ z-index: var(--rel-z-toast, 9000);
27
29
  font-family: var(--rel-fontFamilies-primary);
28
30
  }
29
31
 
@@ -31,20 +33,20 @@ export class RelToast extends LitElement {
31
33
  .toast {
32
34
  display: inline-flex;
33
35
  align-items: center;
34
- gap: var(--rel-spacing-space-sm, 12px);
35
- background-color: var(--rel-surface-toast, #404040);
36
- color: var(--rel-colors-white, #fff);
37
- font-family: var(--rel-fontFamilies-primary, sans-serif);
38
- font-size: var(--rel-fontSizes-md, 14px);
39
- border-radius: var(--rel-borders-radius-border-radius-200, 8px);
40
- padding: var(--rel-spacing-space-md, 16px);
36
+ gap: var(--rel-spacing-space-sm);
37
+ background-color: var(--rel-color-text-primary);
38
+ color: var(--rel-color-bg-primary);
39
+ font-family: var(--rel-fontFamilies-primary);
40
+ font-size: var(--rel-fontSizes-md);
41
+ border-radius: var(--rel-borders-radius-border-radius-200);
42
+ padding: var(--rel-spacing-space-md);
41
43
  box-shadow: var(--rel-shadows-shadow-300);
42
44
  pointer-events: all;
43
45
  animation: fadeInUp 0.25s ease;
44
46
  }
45
47
 
46
48
  .toast.error {
47
- background-color: var(--rel-colors-red-secondary);
49
+ background-color: var(--rel-error-surface-default);
48
50
  }
49
51
 
50
52
  .toast.hidden {
@@ -73,7 +75,7 @@ export class RelToast extends LitElement {
73
75
  }
74
76
 
75
77
  .toast-action:focus-visible {
76
- outline: 2px solid var(--rel-colors-white, #fff);
78
+ outline: 2px solid #FFFFFF;
77
79
  outline-offset: 2px;
78
80
  border-radius: 2px;
79
81
  }
@@ -86,8 +88,8 @@ export class RelToast extends LitElement {
86
88
  font-size: 1rem;
87
89
  cursor: pointer;
88
90
  line-height: 1;
89
- padding: var(--rel-spacing-space-2xs, 4px);
90
- margin-left: var(--rel-spacing-space-2xs, 4px);
91
+ padding: var(--rel-spacing-space-2xs);
92
+ margin-left: var(--rel-spacing-space-2xs);
91
93
  }
92
94
 
93
95
  .toast-close:hover {
@@ -95,11 +97,23 @@ export class RelToast extends LitElement {
95
97
  }
96
98
 
97
99
  .toast-close:focus-visible {
98
- outline: 2px solid var(--rel-colors-white, #fff);
100
+ outline: 2px solid #FFFFFF;
99
101
  outline-offset: 2px;
100
102
  border-radius: 2px;
101
103
  }
102
104
 
105
+ /* Dark mode overrides */
106
+ :host-context([data-theme="dark"]) .toast-action,
107
+ :host-context(.dark) .toast-action {
108
+ color: var(--rel-colors-green-tertiary);
109
+ }
110
+
111
+ :host-context([data-theme="dark"]) .toast.error,
112
+ :host-context(.dark) .toast.error {
113
+ background-color: var(--rel-error-surface-default);
114
+ color: #ffffff;
115
+ }
116
+
103
117
  @media (prefers-reduced-motion: reduce) {
104
118
  .toast {
105
119
  animation: none;
@@ -119,25 +133,45 @@ export class RelToast extends LitElement {
119
133
  }
120
134
  `;
121
135
 
122
- show() {
123
- this.visible = true;
124
-
125
- if (this.duration > 0) {
126
- this.timeoutId = window.setTimeout(() => this.close(), this.duration);
136
+ willUpdate(changed: Map<string, unknown>) {
137
+ if (!changed.has('show')) return;
138
+ if (this.show) {
139
+ this.visible = true;
140
+ if (this._dismissTimer) clearTimeout(this._dismissTimer);
141
+ if (this.duration > 0) {
142
+ this._dismissTimer = setTimeout(() => this.close(), this.duration);
143
+ }
144
+ } else {
145
+ this.visible = false;
146
+ if (this._dismissTimer) {
147
+ clearTimeout(this._dismissTimer);
148
+ this._dismissTimer = null;
149
+ }
127
150
  }
128
151
  }
129
152
 
153
+ disconnectedCallback() {
154
+ super.disconnectedCallback();
155
+ if (this._dismissTimer) clearTimeout(this._dismissTimer);
156
+ }
157
+
158
+ open() {
159
+ this.show = true;
160
+ }
161
+
130
162
  close() {
163
+ this.show = false;
131
164
  this.visible = false;
165
+ if (this._dismissTimer) {
166
+ clearTimeout(this._dismissTimer);
167
+ this._dismissTimer = null;
168
+ }
132
169
  this.dispatchEvent(
133
170
  new CustomEvent("dismissed", {
134
171
  bubbles: true,
135
172
  composed: true,
136
173
  })
137
174
  );
138
- if (this.timeoutId) {
139
- clearTimeout(this.timeoutId);
140
- }
141
175
  }
142
176
 
143
177
  private handleActionClick(event: Event) {
@@ -7,7 +7,11 @@ export type TooltipPosition = "above" | "below";
7
7
  export class RelTooltip extends LitElement {
8
8
  @property({ type: String }) text: string = "";
9
9
  @property({ type: String }) position: TooltipPosition = "above";
10
- @property({ type: Boolean }) showTail: boolean = false;
10
+ @property({ type: Boolean, attribute: "show-tail" }) showTail: boolean =
11
+ false;
12
+
13
+ // Stable id so aria-describedby can reference the bubble across renders.
14
+ private uniqueId = Math.random().toString(36).slice(2);
11
15
 
12
16
  static styles = css`
13
17
  :host {
@@ -20,9 +24,10 @@ export class RelTooltip extends LitElement {
20
24
  position: absolute;
21
25
  left: 50%;
22
26
  transform: translateX(-50%);
23
- background-color: var(--rel-surface-toast, #404040);
24
- color: var(--rel-colors-white, #fff);
25
- padding: var(--rel-spacing-space-xs, 8px) var(--rel-spacing-space-sm, 12px);
27
+ background-color: #1a1a1a;
28
+ color: #ffffff;
29
+ padding: var(--rel-spacing-space-xs, 8px)
30
+ var(--rel-spacing-space-sm, 12px);
26
31
  border-radius: var(--rel-borders-radius-border-radius-200, 8px);
27
32
  font-size: var(--rel-fontSizes-sm, 12px);
28
33
  font-family: var(--rel-fontFamilies-primary, sans-serif);
@@ -31,9 +36,11 @@ export class RelTooltip extends LitElement {
31
36
  opacity: 0;
32
37
  pointer-events: none;
33
38
  transition: opacity 0.2s ease;
39
+ overflow: visible;
34
40
  }
35
41
 
36
- :host(:hover) .tooltip-bubble {
42
+ :host(:hover) .tooltip-bubble,
43
+ :host(:focus-within) .tooltip-bubble {
37
44
  opacity: 1;
38
45
  }
39
46
 
@@ -55,23 +62,59 @@ export class RelTooltip extends LitElement {
55
62
  height: 0;
56
63
  border-left: 6px solid transparent;
57
64
  border-right: 6px solid transparent;
65
+ display: block;
58
66
  }
59
67
 
60
68
  .tooltip-bubble.above .tooltip-tail {
61
69
  bottom: -6px;
62
- border-top: 6px solid var(--rel-surface-toast, #404040);
70
+ border-top: 6px solid #1a1a1a;
71
+ border-bottom: none;
63
72
  }
64
73
 
65
74
  .tooltip-bubble.below .tooltip-tail {
66
75
  top: -6px;
67
- border-bottom: 6px solid var(--rel-surface-toast, #404040);
76
+ border-bottom: 6px solid #1a1a1a;
77
+ border-top: none;
78
+ }
79
+
80
+ :host-context([data-theme="dark"]) .tooltip-bubble,
81
+ :host-context(.dark) .tooltip-bubble {
82
+ background-color: #ffffff;
83
+ color: #000000;
84
+ }
85
+
86
+ :host-context([data-theme="dark"]) .tooltip-bubble.above .tooltip-tail,
87
+ :host-context(.dark) .tooltip-bubble.above .tooltip-tail {
88
+ border-top-color: #ffffff;
89
+ }
90
+
91
+ :host-context([data-theme="dark"]) .tooltip-bubble.below .tooltip-tail,
92
+ :host-context(.dark) .tooltip-bubble.below .tooltip-tail {
93
+ border-bottom-color: #ffffff;
68
94
  }
69
95
  `;
70
96
 
97
+ firstUpdated() {
98
+ this.linkTrigger();
99
+ }
100
+
101
+ private linkTrigger() {
102
+ const slot = this.shadowRoot?.querySelector("slot");
103
+ if (!slot) return;
104
+ const trigger = slot.assignedElements()[0];
105
+ if (trigger) {
106
+ trigger.setAttribute("aria-describedby", `tooltip-${this.uniqueId}`);
107
+ }
108
+ }
109
+
71
110
  render() {
72
111
  return html`
73
- <slot></slot>
74
- <div class="tooltip-bubble ${this.position}" role="tooltip" aria-hidden="true">
112
+ <slot @slotchange="${this.linkTrigger}"></slot>
113
+ <div
114
+ class="tooltip-bubble ${this.position}"
115
+ id="tooltip-${this.uniqueId}"
116
+ role="tooltip"
117
+ >
75
118
  ${this.text}
76
119
  ${this.showTail ? html`<span class="tooltip-tail"></span>` : ""}
77
120
  </div>
@@ -0,0 +1,24 @@
1
+ import { LitElement, html, css } from "lit";
2
+ import { customElement } from "lit/decorators.js";
3
+ import "../../styles/vars-v2.css";
4
+
5
+ @customElement("rel-topbar-actions")
6
+ export class RelTopbarActions extends LitElement {
7
+ static styles = css`
8
+ .actions {
9
+ display: inline-flex;
10
+ align-items: center;
11
+ gap: var(--rel-spacing-space-xs, 8px);
12
+ }
13
+ `;
14
+
15
+ render() {
16
+ return html`<div class="actions"><slot></slot></div>`;
17
+ }
18
+ }
19
+
20
+ declare global {
21
+ interface HTMLElementTagNameMap {
22
+ "rel-topbar-actions": RelTopbarActions;
23
+ }
24
+ }
@@ -0,0 +1,47 @@
1
+ import { LitElement, html, css } from "lit";
2
+ import { customElement, property } from "lit/decorators.js";
3
+ import "../../styles/vars-v2.css";
4
+
5
+ @customElement("rel-topbar-brand")
6
+ export class RelTopbarBrand extends LitElement {
7
+ @property({ type: String }) src: string = "";
8
+ @property({ type: String }) href: string = "/";
9
+ @property({ type: String }) label: string = "";
10
+
11
+ static styles = css`
12
+ :host {
13
+ display: inline-flex;
14
+ align-items: center;
15
+ }
16
+
17
+ a {
18
+ display: inline-flex;
19
+ align-items: center;
20
+ gap: var(--rel-spacing-space-xs);
21
+ text-decoration: none;
22
+ color: var(--rel-color-text-primary);
23
+ font-weight: var(--rel-fontWeights-semibold);
24
+ font-size: var(--rel-fontSizes-md);
25
+ }
26
+
27
+ img {
28
+ display: block;
29
+ }
30
+ `;
31
+
32
+ render() {
33
+ return html`
34
+ <a href="${this.href}" aria-label="${this.label || "Home"}">
35
+ ${this.src
36
+ ? html`<img src="${this.src}" alt="${this.label}" />`
37
+ : html`<slot>${this.label}</slot>`}
38
+ </a>
39
+ `;
40
+ }
41
+ }
42
+
43
+ declare global {
44
+ interface HTMLElementTagNameMap {
45
+ "rel-topbar-brand": RelTopbarBrand;
46
+ }
47
+ }