@reliability-design/web 1.4.5 → 1.4.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +211 -49
- package/dist/components/rel-accordion/README.md +132 -132
- package/dist/components/rel-alert/README.md +145 -141
- package/dist/components/rel-autocomplete/README.md +156 -61
- package/dist/components/rel-avatar/README.md +116 -130
- package/dist/components/rel-badge/README.md +98 -93
- package/dist/components/rel-breadcrumb/README.md +96 -102
- package/dist/components/rel-button/README.md +160 -130
- package/dist/components/rel-calendar/README.md +109 -109
- package/dist/components/rel-card/README.md +75 -94
- package/dist/components/rel-cardmedia/README.md +100 -127
- package/dist/components/rel-carousel/README.md +184 -166
- package/dist/components/rel-checkbox/README.md +93 -75
- package/dist/components/rel-chip/README.md +132 -121
- package/dist/components/rel-container/README.md +107 -99
- package/dist/components/rel-date-picker/README.md +200 -289
- package/dist/components/rel-divider/README.md +78 -100
- package/dist/components/rel-drawer/README.md +187 -200
- package/dist/components/rel-kbd/README.md +141 -151
- package/dist/components/rel-menu/README.md +178 -151
- package/dist/components/rel-menubar/README.md +218 -329
- package/dist/components/rel-otp/README.md +170 -440
- package/dist/components/rel-pagination/README.md +100 -424
- package/dist/components/rel-popover/README.md +203 -568
- package/dist/components/rel-progress/README.md +88 -110
- package/dist/components/rel-radio/README.md +131 -135
- package/dist/components/rel-range-picker/README.md +162 -0
- package/dist/components/rel-resizable/README.md +199 -140
- package/dist/components/rel-segmented-button/README.md +155 -141
- package/dist/components/rel-select/README.md +154 -93
- package/dist/components/rel-sheet/README.md +168 -224
- package/dist/components/rel-skeleton/README.md +103 -0
- package/dist/components/rel-slider/README.md +130 -135
- package/dist/components/rel-snackbar/README.md +162 -134
- package/dist/components/rel-stepper/README.md +158 -160
- package/dist/components/rel-switch/README.md +108 -102
- package/dist/components/rel-tab/README.md +171 -163
- package/dist/components/rel-textfield/README.md +159 -69
- package/dist/components/rel-time-picker/README.md +122 -157
- package/dist/components/rel-toast/README.md +157 -148
- package/dist/components/rel-tooltip/README.md +108 -103
- package/dist/components/rel-upload/README.md +220 -0
- package/dist/index.d.mts +1387 -350
- package/dist/index.d.ts +1387 -350
- package/dist/index.js +5863 -3600
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +5864 -3592
- package/dist/index.mjs.map +1 -1
- package/dist/styles/vars.css +877 -866
- package/dist/{vars-XNMTSRVN.css → vars-JHTUGNRG.css} +877 -866
- package/package.json +4 -3
- package/dist/components/rel-accordion/rel-accordion.ts +0 -336
- package/dist/components/rel-alert/rel-alert.ts +0 -305
- package/dist/components/rel-autocomplete/rel-autocomplete.ts +0 -689
- package/dist/components/rel-avatar/rel-avatar.ts +0 -142
- package/dist/components/rel-badge/rel-badge.ts +0 -170
- package/dist/components/rel-breadcrumb/rel-breadcrumb.ts +0 -106
- package/dist/components/rel-button/rel-button.ts +0 -560
- package/dist/components/rel-calendar/index.ts +0 -1
- package/dist/components/rel-calendar/rel-calendar.ts +0 -519
- package/dist/components/rel-card/rel-card.ts +0 -88
- package/dist/components/rel-cardmedia/rel-cardmedia.ts +0 -88
- package/dist/components/rel-carousel/index.ts +0 -1
- package/dist/components/rel-carousel/rel-carousel.ts +0 -700
- package/dist/components/rel-chart/README.md +0 -286
- package/dist/components/rel-chart/index.ts +0 -1
- package/dist/components/rel-chart/rel-chart.ts +0 -754
- package/dist/components/rel-checkbox/rel-checkbox.ts +0 -382
- package/dist/components/rel-chip/rel-chip.ts +0 -472
- package/dist/components/rel-container/rel-container.ts +0 -95
- package/dist/components/rel-date-picker/date-format.test.ts +0 -43
- package/dist/components/rel-date-picker/date-format.ts +0 -47
- package/dist/components/rel-date-picker/index.ts +0 -1
- package/dist/components/rel-date-picker/rel-date-picker.ts +0 -1237
- package/dist/components/rel-divider/rel-divider.ts +0 -54
- package/dist/components/rel-drawer/index.ts +0 -1
- package/dist/components/rel-drawer/rel-drawer-footer.ts +0 -27
- package/dist/components/rel-drawer/rel-drawer.ts +0 -604
- package/dist/components/rel-kbd/rel-kbd.ts +0 -188
- package/dist/components/rel-layout/rel-layout.ts +0 -49
- package/dist/components/rel-menu/rel-menu.ts +0 -535
- package/dist/components/rel-menubar/index.ts +0 -2
- package/dist/components/rel-menubar/rel-menubar-menu.css +0 -3
- package/dist/components/rel-menubar/rel-menubar-menu.ts +0 -17
- package/dist/components/rel-menubar/rel-menubar.ts +0 -777
- package/dist/components/rel-nav-menu/README.md +0 -289
- package/dist/components/rel-nav-menu/index.ts +0 -3
- package/dist/components/rel-nav-menu/rel-nav-menu-item.ts +0 -243
- package/dist/components/rel-nav-menu/rel-nav-menu-panel.ts +0 -72
- package/dist/components/rel-nav-menu/rel-nav-menu.ts +0 -270
- package/dist/components/rel-navbar/README.md +0 -126
- package/dist/components/rel-navbar/rel-navbar.ts +0 -105
- package/dist/components/rel-otp/index.ts +0 -1
- package/dist/components/rel-otp/rel-otp.ts +0 -517
- package/dist/components/rel-pagination/index.ts +0 -1
- package/dist/components/rel-pagination/rel-pagination.ts +0 -509
- package/dist/components/rel-popover/index.ts +0 -2
- package/dist/components/rel-popover/rel-popover-content.ts +0 -81
- package/dist/components/rel-popover/rel-popover.ts +0 -444
- package/dist/components/rel-progress/rel-progress.ts +0 -225
- package/dist/components/rel-radio/rel-radio.ts +0 -305
- package/dist/components/rel-range-picker/rel-range-picker.ts +0 -1331
- package/dist/components/rel-resizable/index.ts +0 -3
- package/dist/components/rel-resizable/rel-resizable-group.ts +0 -405
- package/dist/components/rel-resizable/rel-resizable-handle.ts +0 -150
- package/dist/components/rel-resizable/rel-resizable-panel.ts +0 -58
- package/dist/components/rel-segmented-button/rel-segmented-button.ts +0 -213
- package/dist/components/rel-select/rel-select.ts +0 -1385
- package/dist/components/rel-sheet/rel-sheet.ts +0 -465
- package/dist/components/rel-sidebar/README.md +0 -209
- package/dist/components/rel-sidebar/rel-sidebar.ts +0 -1185
- package/dist/components/rel-skeleton/rel-skeleton.ts +0 -134
- package/dist/components/rel-slider/rel-slider.ts +0 -670
- package/dist/components/rel-snackbar/rel-snackbar.ts +0 -325
- package/dist/components/rel-stepper/rel-stepper.ts +0 -565
- package/dist/components/rel-switch/rel-switch.ts +0 -197
- package/dist/components/rel-tab/rel-tab.ts +0 -264
- package/dist/components/rel-table/README.md +0 -178
- package/dist/components/rel-table/rel-table-body.ts +0 -22
- package/dist/components/rel-table/rel-table-caption.ts +0 -30
- package/dist/components/rel-table/rel-table-cell.ts +0 -43
- package/dist/components/rel-table/rel-table-footer.ts +0 -24
- package/dist/components/rel-table/rel-table-head.ts +0 -47
- package/dist/components/rel-table/rel-table-header.ts +0 -22
- package/dist/components/rel-table/rel-table-row.ts +0 -37
- package/dist/components/rel-table/rel-table.ts +0 -62
- package/dist/components/rel-textfield/rel-textfield.ts +0 -866
- package/dist/components/rel-time-picker/rel-time-picker.ts +0 -1126
- package/dist/components/rel-toast/rel-toast.ts +0 -204
- package/dist/components/rel-tooltip/rel-tooltip.ts +0 -190
- package/dist/components/rel-topbar/rel-topbar-actions.ts +0 -27
- package/dist/components/rel-topbar/rel-topbar-brand.ts +0 -54
- package/dist/components/rel-topbar/rel-topbar-dropdown-item.ts +0 -69
- package/dist/components/rel-topbar/rel-topbar-dropdown.ts +0 -31
- package/dist/components/rel-topbar/rel-topbar-item.ts +0 -324
- package/dist/components/rel-topbar/rel-topbar-mobile-panel.ts +0 -27
- package/dist/components/rel-topbar/rel-topbar-profile.ts +0 -243
- package/dist/components/rel-topbar/rel-topbar-utilities.ts +0 -27
- package/dist/components/rel-topbar/rel-topbar.ts +0 -446
- package/dist/components/rel-upload/UPLOAD-DESIGN-CHANGES.md +0 -134
- package/dist/components/rel-upload/rel-upload-actions.ts +0 -163
- package/dist/components/rel-upload/rel-upload.ts +0 -943
|
@@ -1,446 +0,0 @@
|
|
|
1
|
-
import { LitElement, html, css } from "lit";
|
|
2
|
-
import { customElement, property, state } from "lit/decorators.js";
|
|
3
|
-
import "@reliability-design/icons/rel-icon";
|
|
4
|
-
import "./rel-topbar-brand";
|
|
5
|
-
import "./rel-topbar-actions";
|
|
6
|
-
import "./rel-topbar-utilities";
|
|
7
|
-
import "./rel-topbar-mobile-panel";
|
|
8
|
-
import "./rel-topbar-dropdown";
|
|
9
|
-
import "./rel-topbar-dropdown-item";
|
|
10
|
-
|
|
11
|
-
export type TopbarVariant = "customer" | "carrier";
|
|
12
|
-
export type TopbarForceLayout = "auto" | "desktop" | "tablet" | "mobile";
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* `rel-topbar` — Application-level top navigation / header shell.
|
|
16
|
-
*
|
|
17
|
-
* @deprecated This component's fixed slot structure, hardcoded breakpoint,
|
|
18
|
-
* and built-in overflow logic are too rigid for most real layouts. Use the
|
|
19
|
-
* "Navbar" recipe at /docs/recipes/navbar instead, composed from
|
|
20
|
-
* rel-menu/rel-popover, rel-drawer, and rel-button. Still supported, but
|
|
21
|
-
* will be removed in a future major version.
|
|
22
|
-
*
|
|
23
|
-
* Slots:
|
|
24
|
-
* brand — Logo / wordmark region (left)
|
|
25
|
-
* (default) — Primary nav items (rel-topbar-item elements)
|
|
26
|
-
* actions — CTA / action buttons (customer) or dark-pill area (carrier)
|
|
27
|
-
* utilities — Utility icon buttons (notifications, search, etc.)
|
|
28
|
-
* profile — rel-topbar-profile tile
|
|
29
|
-
* mobile-panel — Collapsed nav panel shown when mobileOpen is true
|
|
30
|
-
*
|
|
31
|
-
* Events:
|
|
32
|
-
* rel-menu-toggle — fired when hamburger is clicked { open: boolean }
|
|
33
|
-
* rel-mobile-open-change — alias of rel-menu-toggle for convenience
|
|
34
|
-
* rel-nav-select — bubbled from child rel-topbar-item { value, label }
|
|
35
|
-
*/
|
|
36
|
-
@customElement("rel-topbar")
|
|
37
|
-
export class RelTopbar extends LitElement {
|
|
38
|
-
/** Visual variant — affects action/profile region styling */
|
|
39
|
-
@property({ type: String, reflect: true }) variant: TopbarVariant = "customer";
|
|
40
|
-
|
|
41
|
-
/** Whether the mobile nav panel is currently open */
|
|
42
|
-
@property({ type: Boolean, reflect: true }) mobileOpen = false;
|
|
43
|
-
|
|
44
|
-
/** Whether to render the hamburger / close toggle button */
|
|
45
|
-
@property({ type: Boolean, reflect: true }) showMenuToggle = false;
|
|
46
|
-
|
|
47
|
-
/** Reduce height to compact (48 px) mode */
|
|
48
|
-
@property({ type: Boolean, reflect: true }) compact = false;
|
|
49
|
-
|
|
50
|
-
/** Stick topbar to top of viewport while scrolling */
|
|
51
|
-
@property({ type: Boolean, reflect: true }) sticky = false;
|
|
52
|
-
|
|
53
|
-
/**
|
|
54
|
-
* Override the responsive layout for demos / storybook.
|
|
55
|
-
* 'auto' respects CSS media queries.
|
|
56
|
-
*/
|
|
57
|
-
@property({ type: String }) forceLayout: TopbarForceLayout = "auto";
|
|
58
|
-
|
|
59
|
-
private _overflowItems: Element[] = [];
|
|
60
|
-
@state() private _overflowCount: number = 0;
|
|
61
|
-
@state() private _overflowItemsData: Array<{ label: string; href: string; disabled: boolean }> = [];
|
|
62
|
-
private _resizeObserver?: ResizeObserver;
|
|
63
|
-
private _resizeTimer?: number;
|
|
64
|
-
private _measuring = false;
|
|
65
|
-
private _lastNavWidth = 0;
|
|
66
|
-
|
|
67
|
-
static styles = css`
|
|
68
|
-
/* ─── Component tokens ─────────────────────────────────────────── */
|
|
69
|
-
:host {
|
|
70
|
-
color: var(--rel-text-default);
|
|
71
|
-
--rel-topbar-height: 64px;
|
|
72
|
-
--rel-topbar-height-compact: 48px;
|
|
73
|
-
--rel-topbar-bg: var(--rel-bg-surface);
|
|
74
|
-
--rel-topbar-border-color: var(--rel-border-default);
|
|
75
|
-
--rel-topbar-item-color: var(--rel-text-default);
|
|
76
|
-
--rel-topbar-item-active-color: var(--rel-text-success);
|
|
77
|
-
--rel-topbar-item-active-indicator: var(--rel-bg-brand);
|
|
78
|
-
--rel-topbar-item-hover-bg: var(--rel-bg-surface-elevated);
|
|
79
|
-
--rel-topbar-divider-color: var(--rel-border-default);
|
|
80
|
-
--rel-topbar-carrier-action-bg: var(--rel-text-default);
|
|
81
|
-
--rel-topbar-carrier-action-color: var(--rel-bg-surface);
|
|
82
|
-
--rel-topbar-z: 100;
|
|
83
|
-
|
|
84
|
-
display: block;
|
|
85
|
-
width: 100%;
|
|
86
|
-
font-family: var(--rel-fonts-font-family, Poppins, sans-serif);
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
/* ─── Sticky host ───────────────────────────────────────────────── */
|
|
90
|
-
:host([sticky]) {
|
|
91
|
-
position: sticky;
|
|
92
|
-
top: 0;
|
|
93
|
-
z-index: var(--rel-topbar-z);
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
/* ─── Bar shell ─────────────────────────────────────────────────── */
|
|
97
|
-
.topbar {
|
|
98
|
-
display: flex;
|
|
99
|
-
align-items: center;
|
|
100
|
-
height: var(--rel-topbar-height);
|
|
101
|
-
background: var(--rel-topbar-bg);
|
|
102
|
-
border-bottom: var(--rel-stroke-sm, 1px) solid var(--rel-topbar-border-color);
|
|
103
|
-
padding: 0 var(--rel-spacing-xl, 24px);
|
|
104
|
-
box-sizing: border-box;
|
|
105
|
-
gap: var(--rel-spacing-md, 16px);
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
:host([compact]) .topbar {
|
|
109
|
-
height: var(--rel-topbar-height-compact);
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
/* ─── Brand ─────────────────────────────────────────────────────── */
|
|
113
|
-
.brand-region {
|
|
114
|
-
display: flex;
|
|
115
|
-
align-items: center;
|
|
116
|
-
flex-shrink: 0;
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
/* ─── Nav (primary, desktop) ────────────────────────────────────── */
|
|
120
|
-
.nav-region {
|
|
121
|
-
display: flex;
|
|
122
|
-
align-items: center;
|
|
123
|
-
flex: 1 1 0;
|
|
124
|
-
gap: var(--rel-spacing-2xs, 4px);
|
|
125
|
-
min-width: 0;
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
/* ─── Right side cluster ────────────────────────────────────────── */
|
|
129
|
-
.right-region {
|
|
130
|
-
display: flex;
|
|
131
|
-
align-items: center;
|
|
132
|
-
flex-shrink: 0;
|
|
133
|
-
margin-left: auto;
|
|
134
|
-
gap: var(--rel-spacing-xs, 8px);
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
.actions-region,
|
|
138
|
-
.utilities-region,
|
|
139
|
-
.profile-region {
|
|
140
|
-
display: contents;
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
/* Vertical divider between util/profile when both are populated */
|
|
144
|
-
.divider {
|
|
145
|
-
width: var(--rel-stroke-sm, 1px);
|
|
146
|
-
height: 24px;
|
|
147
|
-
background: var(--rel-topbar-divider-color);
|
|
148
|
-
flex-shrink: 0;
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
/* ─── Carrier action pill wrapper ───────────────────────────────── */
|
|
152
|
-
:host([variant="carrier"]) .actions-region ::slotted(*) {
|
|
153
|
-
display: flex;
|
|
154
|
-
align-items: center;
|
|
155
|
-
gap: var(--rel-spacing-xs, 8px);
|
|
156
|
-
background: var(--rel-topbar-carrier-action-bg);
|
|
157
|
-
color: var(--rel-topbar-carrier-action-color);
|
|
158
|
-
padding: var(--rel-spacing-2xs, 4px) var(--rel-spacing-sm, 12px);
|
|
159
|
-
border-radius: var(--rel-border-radius-full-rounded, 1000px);
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
/* ─── Mobile toggle button ──────────────────────────────────────── */
|
|
163
|
-
.menu-toggle {
|
|
164
|
-
display: none;
|
|
165
|
-
align-items: center;
|
|
166
|
-
justify-content: center;
|
|
167
|
-
width: 40px;
|
|
168
|
-
height: 40px;
|
|
169
|
-
padding: 0;
|
|
170
|
-
border: none;
|
|
171
|
-
background: transparent;
|
|
172
|
-
border-radius: var(--rel-border-radius-sm, 8px);
|
|
173
|
-
cursor: pointer;
|
|
174
|
-
color: var(--rel-topbar-item-color);
|
|
175
|
-
flex-shrink: 0;
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
.menu-toggle:focus-visible {
|
|
179
|
-
outline: 2px solid var(--rel-topbar-item-active-indicator);
|
|
180
|
-
outline-offset: 2px;
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
.menu-toggle:hover {
|
|
184
|
-
background: var(--rel-topbar-item-hover-bg);
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
.menu-toggle svg {
|
|
188
|
-
display: block;
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
/* ─── Mobile panel ──────────────────────────────────────────────── */
|
|
192
|
-
.mobile-panel {
|
|
193
|
-
display: none;
|
|
194
|
-
background: var(--rel-topbar-bg);
|
|
195
|
-
border-bottom: var(--rel-stroke-sm, 1px) solid var(--rel-topbar-border-color);
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
.mobile-panel.open {
|
|
199
|
-
display: block;
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
.mobile-overflow-nav {
|
|
203
|
-
display: flex;
|
|
204
|
-
flex-direction: column;
|
|
205
|
-
padding: var(--rel-spacing-xs, 8px) 0;
|
|
206
|
-
border-top: 1px solid var(--rel-topbar-border-color);
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
.mobile-overflow-item {
|
|
210
|
-
display: block;
|
|
211
|
-
padding: var(--rel-spacing-xs, 8px) var(--rel-spacing-xl, 24px);
|
|
212
|
-
color: var(--rel-topbar-item-color);
|
|
213
|
-
text-decoration: none;
|
|
214
|
-
font-family: var(--rel-body-md-medium-font-family);
|
|
215
|
-
font-weight: var(--rel-body-md-medium-font-weight);
|
|
216
|
-
font-size: var(--rel-body-md-medium-font-size);
|
|
217
|
-
line-height: var(--rel-body-md-medium-line-height);
|
|
218
|
-
letter-spacing: var(--rel-body-md-medium-letter-spacing);
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
.mobile-overflow-item:hover {
|
|
222
|
-
background: var(--rel-topbar-item-hover-bg);
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
.mobile-overflow-item.disabled {
|
|
226
|
-
opacity: 0.45;
|
|
227
|
-
pointer-events: none;
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
/* ─── Responsive: auto mode (CSS media queries) ─────────────────── */
|
|
231
|
-
@media (max-width: 768px) {
|
|
232
|
-
.topbar:not(.force-desktop) .nav-region {
|
|
233
|
-
display: none;
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
.topbar:not(.force-desktop) .menu-toggle {
|
|
237
|
-
display: flex;
|
|
238
|
-
}
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
@media (min-width: 769px) {
|
|
242
|
-
.topbar:not(.force-mobile):not(.force-tablet) .menu-toggle {
|
|
243
|
-
display: none;
|
|
244
|
-
}
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
/* ─── forceLayout overrides ─────────────────────────────────────── */
|
|
248
|
-
.topbar.force-mobile .nav-region,
|
|
249
|
-
.topbar.force-tablet .nav-region {
|
|
250
|
-
display: none;
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
.topbar.force-mobile .menu-toggle,
|
|
254
|
-
.topbar.force-tablet .menu-toggle {
|
|
255
|
-
display: flex;
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
.topbar.force-desktop .nav-region {
|
|
259
|
-
display: flex;
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
.topbar.force-desktop .menu-toggle {
|
|
263
|
-
display: none;
|
|
264
|
-
}
|
|
265
|
-
`;
|
|
266
|
-
|
|
267
|
-
override firstUpdated() {
|
|
268
|
-
const navRegion = this.renderRoot.querySelector(".nav-region") as HTMLElement;
|
|
269
|
-
if (navRegion) {
|
|
270
|
-
this._resizeObserver = new ResizeObserver(() => {
|
|
271
|
-
clearTimeout(this._resizeTimer);
|
|
272
|
-
this._resizeTimer = window.setTimeout(() => this._measureNav(), 150);
|
|
273
|
-
});
|
|
274
|
-
this._resizeObserver.observe(navRegion);
|
|
275
|
-
}
|
|
276
|
-
requestAnimationFrame(() => this._measureNav());
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
override disconnectedCallback() {
|
|
280
|
-
super.disconnectedCallback();
|
|
281
|
-
this._resizeObserver?.disconnect();
|
|
282
|
-
clearTimeout(this._resizeTimer);
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
private _measureNav() {
|
|
286
|
-
if (this._measuring) return;
|
|
287
|
-
this._measuring = true;
|
|
288
|
-
const navRegion = this.renderRoot.querySelector(".nav-region") as HTMLElement;
|
|
289
|
-
if (!navRegion) return;
|
|
290
|
-
const navSlot = this.renderRoot.querySelector('slot[name="nav"]') as HTMLSlotElement;
|
|
291
|
-
if (!navSlot) return;
|
|
292
|
-
const items = Array.from(navSlot.assignedElements()).filter(
|
|
293
|
-
el => el.tagName.toLowerCase() !== "rel-topbar-item" || !el.hasAttribute("data-more")
|
|
294
|
-
);
|
|
295
|
-
const navWidth = navRegion.getBoundingClientRect().width;
|
|
296
|
-
if (Math.abs(navWidth - this._lastNavWidth) < 2) return;
|
|
297
|
-
this._lastNavWidth = navWidth;
|
|
298
|
-
const available = navWidth - 120;
|
|
299
|
-
|
|
300
|
-
let accumulated = 0;
|
|
301
|
-
let firstOverflowIndex = items.length;
|
|
302
|
-
for (let i = 0; i < items.length; i++) {
|
|
303
|
-
accumulated += items[i].getBoundingClientRect().width;
|
|
304
|
-
if (accumulated > available) {
|
|
305
|
-
firstOverflowIndex = i;
|
|
306
|
-
break;
|
|
307
|
-
}
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
const overflowingElements = Array.from(items).slice(firstOverflowIndex);
|
|
311
|
-
this._overflowItems = overflowingElements;
|
|
312
|
-
this._overflowCount = overflowingElements.length;
|
|
313
|
-
this._overflowItemsData = overflowingElements.map(el => ({
|
|
314
|
-
label: el.getAttribute("label") ?? "",
|
|
315
|
-
href: el.getAttribute("href") ?? "",
|
|
316
|
-
disabled: el.hasAttribute("disabled"),
|
|
317
|
-
}));
|
|
318
|
-
|
|
319
|
-
for (const el of items) {
|
|
320
|
-
if (overflowingElements.includes(el)) {
|
|
321
|
-
el.setAttribute("hidden", "");
|
|
322
|
-
} else {
|
|
323
|
-
el.removeAttribute("hidden");
|
|
324
|
-
}
|
|
325
|
-
}
|
|
326
|
-
|
|
327
|
-
this._measuring = false;
|
|
328
|
-
}
|
|
329
|
-
|
|
330
|
-
private onRouterSlotChange(e: Event) {
|
|
331
|
-
const slot = e.target as HTMLSlotElement;
|
|
332
|
-
for (const el of slot.assignedElements({ flatten: true })) {
|
|
333
|
-
const tag = (el as HTMLElement).tagName.toLowerCase();
|
|
334
|
-
const targetSlot =
|
|
335
|
-
tag === "rel-topbar-brand" ? "brand" :
|
|
336
|
-
tag === "rel-topbar-actions" ? "actions" :
|
|
337
|
-
tag === "rel-topbar-utilities" ? "utilities" :
|
|
338
|
-
tag === "rel-topbar-profile" ? "profile" :
|
|
339
|
-
tag === "rel-topbar-mobile-panel" ? "mobile-panel" :
|
|
340
|
-
"nav";
|
|
341
|
-
if ((el as HTMLElement).slot !== targetSlot) {
|
|
342
|
-
(el as HTMLElement).slot = targetSlot;
|
|
343
|
-
}
|
|
344
|
-
}
|
|
345
|
-
}
|
|
346
|
-
|
|
347
|
-
private get layoutClass(): string {
|
|
348
|
-
if (this.forceLayout === "auto") return "";
|
|
349
|
-
return `force-${this.forceLayout}`;
|
|
350
|
-
}
|
|
351
|
-
|
|
352
|
-
private handleMenuToggle() {
|
|
353
|
-
this.mobileOpen = !this.mobileOpen;
|
|
354
|
-
const detail = { open: this.mobileOpen };
|
|
355
|
-
this.dispatchEvent(
|
|
356
|
-
new CustomEvent("rel-menu-toggle", { detail, bubbles: true, composed: true })
|
|
357
|
-
);
|
|
358
|
-
this.dispatchEvent(
|
|
359
|
-
new CustomEvent("rel-mobile-open-change", { detail, bubbles: true, composed: true })
|
|
360
|
-
);
|
|
361
|
-
}
|
|
362
|
-
|
|
363
|
-
render() {
|
|
364
|
-
const topbarClass = ["topbar", this.layoutClass].filter(Boolean).join(" ");
|
|
365
|
-
const menuLabel = this.mobileOpen ? "Close menu" : "Open menu";
|
|
366
|
-
|
|
367
|
-
return html`
|
|
368
|
-
<!-- router: auto-assigns slot names by tagName; stays empty after redistribution -->
|
|
369
|
-
<slot style="display:none" @slotchange=${this.onRouterSlotChange}></slot>
|
|
370
|
-
|
|
371
|
-
<header>
|
|
372
|
-
<div class=${topbarClass} role="banner">
|
|
373
|
-
<div class="brand-region">
|
|
374
|
-
<slot name="brand"></slot>
|
|
375
|
-
</div>
|
|
376
|
-
|
|
377
|
-
<nav class="nav-region" aria-label="Primary">
|
|
378
|
-
<slot name="nav" @slotchange=${this._measureNav}></slot>
|
|
379
|
-
${this._overflowCount > 0 ? html`
|
|
380
|
-
<rel-topbar-item label="More" expandable data-more>
|
|
381
|
-
<rel-topbar-dropdown slot="dropdown">
|
|
382
|
-
${this._overflowItemsData.map(item => html`
|
|
383
|
-
<rel-topbar-dropdown-item
|
|
384
|
-
label=${item.label}
|
|
385
|
-
href=${item.href}
|
|
386
|
-
?disabled=${item.disabled}
|
|
387
|
-
></rel-topbar-dropdown-item>
|
|
388
|
-
`)}
|
|
389
|
-
</rel-topbar-dropdown>
|
|
390
|
-
</rel-topbar-item>
|
|
391
|
-
` : null}
|
|
392
|
-
</nav>
|
|
393
|
-
|
|
394
|
-
<div class="right-region">
|
|
395
|
-
<div class="actions-region">
|
|
396
|
-
<slot name="actions"></slot>
|
|
397
|
-
</div>
|
|
398
|
-
<div class="utilities-region">
|
|
399
|
-
<slot name="utilities"></slot>
|
|
400
|
-
</div>
|
|
401
|
-
<div class="profile-region">
|
|
402
|
-
<slot name="profile"></slot>
|
|
403
|
-
</div>
|
|
404
|
-
|
|
405
|
-
${this.showMenuToggle
|
|
406
|
-
? html`
|
|
407
|
-
<button
|
|
408
|
-
class="menu-toggle"
|
|
409
|
-
aria-label=${menuLabel}
|
|
410
|
-
aria-expanded=${this.mobileOpen}
|
|
411
|
-
aria-controls="mobile-panel"
|
|
412
|
-
@click=${this.handleMenuToggle}
|
|
413
|
-
>
|
|
414
|
-
${this.mobileOpen
|
|
415
|
-
? html`<rel-icon name="close" size="md"></rel-icon>`
|
|
416
|
-
: html`<rel-icon name="hamburger" size="md"></rel-icon>`}
|
|
417
|
-
</button>
|
|
418
|
-
`
|
|
419
|
-
: null}
|
|
420
|
-
</div>
|
|
421
|
-
</div>
|
|
422
|
-
</header>
|
|
423
|
-
|
|
424
|
-
<div
|
|
425
|
-
class="mobile-panel ${this.mobileOpen ? "open" : ""}"
|
|
426
|
-
id="mobile-panel"
|
|
427
|
-
role="region"
|
|
428
|
-
aria-label="Mobile navigation"
|
|
429
|
-
>
|
|
430
|
-
<slot name="mobile-panel"></slot>
|
|
431
|
-
${this._overflowItemsData.length > 0 ? html`
|
|
432
|
-
<div class="mobile-overflow-nav">
|
|
433
|
-
${this._overflowItemsData.map(item => html`
|
|
434
|
-
<a
|
|
435
|
-
class="mobile-overflow-item ${item.disabled ? "disabled" : ""}"
|
|
436
|
-
href=${item.href}
|
|
437
|
-
?aria-disabled=${item.disabled}
|
|
438
|
-
>${item.label}</a>
|
|
439
|
-
`)}
|
|
440
|
-
</div>
|
|
441
|
-
` : null}
|
|
442
|
-
</div>
|
|
443
|
-
`;
|
|
444
|
-
}
|
|
445
|
-
}
|
|
446
|
-
|
|
@@ -1,134 +0,0 @@
|
|
|
1
|
-
# rel-upload — implementation vs. Figma, and what to update in the design
|
|
2
|
-
|
|
3
|
-
**For:** design team · **From:** engineering · **Date:** 2026-08-09
|
|
4
|
-
**Figma reference:** Reliability DS V1.0 → "↳ File Upload" page (node `8160:917`)
|
|
5
|
-
— `Upload Dropzone` (`8160:2615`), `Upload File Row` (`8160:2566`), `Upload Modal` (`8160:2622`)
|
|
6
|
-
**Shipped:** `<rel-upload>` + `<rel-upload-actions>` in `packages/web/src/components/rel-upload/`
|
|
7
|
-
|
|
8
|
-
This replaces the previous version of this doc, which was written against the
|
|
9
|
-
outdated sandbox design. Per our agreement, engineering adapted the design where
|
|
10
|
-
it had gaps or conflicted with how the design system already works — everything
|
|
11
|
-
that differs is listed here so the Figma file can be brought in line.
|
|
12
|
-
|
|
13
|
-
---
|
|
14
|
-
|
|
15
|
-
## What matches the Figma exactly
|
|
16
|
-
|
|
17
|
-
- **Dropzone**: 16px radius, dashed border, 32/24 padding, circular icon badge,
|
|
18
|
-
"Drag and drop your files here" (16/24 medium) over the format/size hint
|
|
19
|
-
(12/20), pill "Browse files" button.
|
|
20
|
-
- **File rows**: 8px radius, 12/16 padding, document icon → name (14/22) + meta
|
|
21
|
-
(12/20) → trailing status icon + remove. All four states (Ready, Uploading,
|
|
22
|
-
Success, Error) including the tinted error row.
|
|
23
|
-
- **Progress bar**: 4px, fully rounded, green fill, with the
|
|
24
|
-
`45% - 2.4 MB of 5.1 MB` meta line.
|
|
25
|
-
- **Modal**: composed from the existing `<rel-modal>` — title, supporting text,
|
|
26
|
-
close ×, scrolling body, pinned footer. All six variants are reproduced.
|
|
27
|
-
- **Copy**: every string in the design ships verbatim, including
|
|
28
|
-
`Selected files (3)`, `Uploading 3 files`, `3 files - 2 failed`,
|
|
29
|
-
`Upload 3 files`, `Retry upload`, and both alert messages.
|
|
30
|
-
|
|
31
|
-
The hint row is driven by real props: formats come from `accept`, the size limit
|
|
32
|
-
from `max-size`. The design's "PDF, PNG or JPG" / "10 MB" are example values.
|
|
33
|
-
|
|
34
|
-
## Colour changes the design should adopt
|
|
35
|
-
|
|
36
|
-
All three are contrast or token-layer corrections. Every hex in the design
|
|
37
|
-
already exists in `vars.css`, but three of them are used at the wrong layer.
|
|
38
|
-
|
|
39
|
-
### 1. Secondary text `#7D7D7D` → `--rel-text-caption` `#525252`
|
|
40
|
-
|
|
41
|
-
`#7D7D7D` (gray-400) scores **4.12:1 on white — below the 4.5:1 gate**, and in
|
|
42
|
-
the token system gray-400 is not a body-text token at all. Used for the dropzone
|
|
43
|
-
hint and the file-row meta line. `--rel-text-caption` (`#525252`) scores
|
|
44
|
-
**7.81:1**.
|
|
45
|
-
|
|
46
|
-
### 2. Success text `#008655` → `--rel-text-success` `#006440`
|
|
47
|
-
|
|
48
|
-
Green-600 passes (4.62:1) but the system's success-text token is green-700
|
|
49
|
-
(**7.25:1**), and the green rule fixes success text/icons at `#006440`. Affects
|
|
50
|
-
the "…- Uploaded" meta line and the success check icon.
|
|
51
|
-
|
|
52
|
-
### 3. Dashed dropzone border `#C2C2C2` → `--rel-border-accent` `#8C8C8C`
|
|
53
|
-
|
|
54
|
-
`#C2C2C2` scores **1.78:1**, under the 3:1 gate for UI boundaries, and gray-200
|
|
55
|
-
has no border token. `--rel-border-accent` (gray-350) scores **3.36:1**.
|
|
56
|
-
|
|
57
|
-
Also note: the error row border uses `--rel-border-danger` (`#CE3131`, 5.11:1)
|
|
58
|
-
rather than the design's `#FF5252` (3.19:1) — red-500 has no border token.
|
|
59
|
-
And the error meta on the tinted row (`#CE3131` on `#FFEBEB`) lands at
|
|
60
|
-
**4.46:1**, marginally under. That pairing is inherited from the existing
|
|
61
|
-
`rel-alert` subtle-error style, so it is a system-wide question rather than
|
|
62
|
-
something this component introduced — flagging it, not changing it here.
|
|
63
|
-
|
|
64
|
-
## Structural changes
|
|
65
|
-
|
|
66
|
-
### 4. The footer is a separate element, not part of the upload body
|
|
67
|
-
|
|
68
|
-
In the Figma the Cancel/Upload row sits in the Modal Sheet, outside the slot
|
|
69
|
-
holding the upload body. We matched that: `<rel-upload>` renders the alert,
|
|
70
|
-
dropzone and file list; `<rel-upload-actions>` renders the buttons and goes in
|
|
71
|
-
`<rel-modal>`'s footer slot. This is what keeps the footer **pinned while the
|
|
72
|
-
file list scrolls** — with five files the body scrolls and the buttons stay put.
|
|
73
|
-
The actions element finds its `<rel-upload>` sibling automatically.
|
|
74
|
-
|
|
75
|
-
### 5. The dropzone is not itself a button
|
|
76
|
-
|
|
77
|
-
The design puts a "Browse files" button inside the dropzone. Making the whole
|
|
78
|
-
dropzone clickable *and* focusable would nest interactive controls inside each
|
|
79
|
-
other, which is an accessibility violation. The dropzone stays a drag-and-drop
|
|
80
|
-
target and a mouse click affordance, but "Browse files" is the only focusable
|
|
81
|
-
control — so keyboard and screen-reader users get exactly one clear action.
|
|
82
|
-
|
|
83
|
-
### 6. Buttons and the alert are the standard DS components
|
|
84
|
-
|
|
85
|
-
Cancel/Upload are `rel-button`, and both alerts are `rel-alert` (warning and
|
|
86
|
-
error, subtle). They inherit the system's colours, states and focus rings rather
|
|
87
|
-
than the frame's local styles. One visible consequence: the design draws the
|
|
88
|
-
buttons as pills, which `rel-button` supports, but the disabled Upload uses the
|
|
89
|
-
system's disabled fill rather than the frame's dimmed grey. If design prefers the
|
|
90
|
-
dimmed look, that is a change to the *button* spec, not to upload.
|
|
91
|
-
|
|
92
|
-
## Behaviour the design doesn't specify
|
|
93
|
-
|
|
94
|
-
### 7. Validation failures render as error rows
|
|
95
|
-
|
|
96
|
-
The Figma Failure state shows rows reading "Upload failed - file exceeds the
|
|
97
|
-
10 MB limit" and "Upload failed - unsupported file type". Both are knowable
|
|
98
|
-
*before* any transfer, so we surface rejected files as error rows immediately on
|
|
99
|
-
drop rather than silently discarding them. The wording is unchanged.
|
|
100
|
-
|
|
101
|
-
### 8. Retry only re-sends what can succeed
|
|
102
|
-
|
|
103
|
-
A file rejected for format or size will fail identically every time, so
|
|
104
|
-
"Retry upload" skips it — and if *every* failure is of that kind the button is
|
|
105
|
-
disabled. Removing the offending row returns the card to the normal Upload state.
|
|
106
|
-
|
|
107
|
-
### 9. "Upload" wins over "Retry" while anything is still pending
|
|
108
|
-
|
|
109
|
-
If some rows failed but others are still ready, the primary action stays
|
|
110
|
-
"Upload N files". Retry only appears once there is nothing left to send. The
|
|
111
|
-
Figma Multi-upload and Failure frames are both consistent with this.
|
|
112
|
-
|
|
113
|
-
## States still missing from the Figma
|
|
114
|
-
|
|
115
|
-
These are implemented, but no frame specs them:
|
|
116
|
-
|
|
117
|
-
| State | What ships today |
|
|
118
|
-
| ----- | ---------------- |
|
|
119
|
-
| **Drag-over** | Dashed border becomes solid brand green, surface takes an 8% brand tint |
|
|
120
|
-
| **Disabled** | Whole card at 60% opacity, nothing interactive |
|
|
121
|
-
| **Dark mode** | Works automatically — every colour is a semantic token |
|
|
122
|
-
|
|
123
|
-
A frame for each (drag-over especially) would make the set complete.
|
|
124
|
-
|
|
125
|
-
## Suggested Figma additions
|
|
126
|
-
|
|
127
|
-
1. Drag-over variant
|
|
128
|
-
2. Disabled variant
|
|
129
|
-
3. Dark reference frame
|
|
130
|
-
4. Apply the three colour corrections above to the shared styles
|
|
131
|
-
|
|
132
|
-
Questions and pushback welcome — none of this is precious, but items 1–3 and 5
|
|
133
|
-
fix measurable contrast or accessibility problems, so if the design goes a
|
|
134
|
-
different way it needs to solve those rather than reverting.
|