@reliability-design/web 1.4.6 → 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.
Files changed (146) hide show
  1. package/CHANGELOG.md +211 -49
  2. package/dist/components/rel-accordion/README.md +132 -132
  3. package/dist/components/rel-alert/README.md +145 -141
  4. package/dist/components/rel-autocomplete/README.md +156 -61
  5. package/dist/components/rel-avatar/README.md +116 -130
  6. package/dist/components/rel-badge/README.md +98 -93
  7. package/dist/components/rel-breadcrumb/README.md +96 -102
  8. package/dist/components/rel-button/README.md +160 -130
  9. package/dist/components/rel-calendar/README.md +109 -109
  10. package/dist/components/rel-card/README.md +75 -94
  11. package/dist/components/rel-cardmedia/README.md +100 -127
  12. package/dist/components/rel-carousel/README.md +184 -166
  13. package/dist/components/rel-checkbox/README.md +93 -75
  14. package/dist/components/rel-chip/README.md +132 -121
  15. package/dist/components/rel-container/README.md +107 -99
  16. package/dist/components/rel-date-picker/README.md +200 -289
  17. package/dist/components/rel-divider/README.md +78 -100
  18. package/dist/components/rel-drawer/README.md +187 -200
  19. package/dist/components/rel-kbd/README.md +141 -151
  20. package/dist/components/rel-menu/README.md +178 -151
  21. package/dist/components/rel-menubar/README.md +218 -329
  22. package/dist/components/rel-otp/README.md +170 -440
  23. package/dist/components/rel-pagination/README.md +100 -424
  24. package/dist/components/rel-popover/README.md +203 -568
  25. package/dist/components/rel-progress/README.md +88 -110
  26. package/dist/components/rel-radio/README.md +131 -138
  27. package/dist/components/rel-range-picker/README.md +162 -0
  28. package/dist/components/rel-resizable/README.md +199 -140
  29. package/dist/components/rel-segmented-button/README.md +155 -141
  30. package/dist/components/rel-select/README.md +154 -93
  31. package/dist/components/rel-sheet/README.md +168 -224
  32. package/dist/components/rel-skeleton/README.md +103 -0
  33. package/dist/components/rel-slider/README.md +130 -135
  34. package/dist/components/rel-snackbar/README.md +162 -134
  35. package/dist/components/rel-stepper/README.md +158 -160
  36. package/dist/components/rel-switch/README.md +108 -102
  37. package/dist/components/rel-tab/README.md +171 -163
  38. package/dist/components/rel-textfield/README.md +159 -69
  39. package/dist/components/rel-time-picker/README.md +122 -157
  40. package/dist/components/rel-toast/README.md +157 -148
  41. package/dist/components/rel-tooltip/README.md +108 -111
  42. package/dist/components/rel-upload/README.md +220 -0
  43. package/dist/index.d.mts +1147 -340
  44. package/dist/index.d.ts +1147 -340
  45. package/dist/index.js +3451 -3163
  46. package/dist/index.js.map +1 -1
  47. package/dist/index.mjs +3416 -3118
  48. package/dist/index.mjs.map +1 -1
  49. package/dist/styles/vars.css +877 -866
  50. package/dist/{vars-XNMTSRVN.css → vars-JHTUGNRG.css} +877 -866
  51. package/package.json +3 -2
  52. package/dist/components/rel-accordion/rel-accordion.ts +0 -336
  53. package/dist/components/rel-alert/rel-alert.ts +0 -305
  54. package/dist/components/rel-autocomplete/rel-autocomplete.ts +0 -720
  55. package/dist/components/rel-avatar/rel-avatar.ts +0 -142
  56. package/dist/components/rel-badge/rel-badge.ts +0 -175
  57. package/dist/components/rel-breadcrumb/rel-breadcrumb.ts +0 -106
  58. package/dist/components/rel-button/rel-button.ts +0 -560
  59. package/dist/components/rel-calendar/index.ts +0 -1
  60. package/dist/components/rel-calendar/rel-calendar.ts +0 -565
  61. package/dist/components/rel-card/rel-card.ts +0 -88
  62. package/dist/components/rel-cardmedia/rel-cardmedia.ts +0 -88
  63. package/dist/components/rel-carousel/index.ts +0 -1
  64. package/dist/components/rel-carousel/rel-carousel.ts +0 -723
  65. package/dist/components/rel-chart/README.md +0 -286
  66. package/dist/components/rel-chart/index.ts +0 -1
  67. package/dist/components/rel-chart/rel-chart.ts +0 -754
  68. package/dist/components/rel-checkbox/rel-checkbox.ts +0 -382
  69. package/dist/components/rel-chip/rel-chip.ts +0 -483
  70. package/dist/components/rel-container/rel-container.ts +0 -95
  71. package/dist/components/rel-date-picker/date-format.test.ts +0 -43
  72. package/dist/components/rel-date-picker/date-format.ts +0 -47
  73. package/dist/components/rel-date-picker/date-parse.test.ts +0 -116
  74. package/dist/components/rel-date-picker/date-parse.ts +0 -178
  75. package/dist/components/rel-date-picker/index.ts +0 -1
  76. package/dist/components/rel-date-picker/rel-date-picker.ts +0 -1676
  77. package/dist/components/rel-divider/rel-divider.ts +0 -54
  78. package/dist/components/rel-drawer/index.ts +0 -1
  79. package/dist/components/rel-drawer/rel-drawer-footer.ts +0 -27
  80. package/dist/components/rel-drawer/rel-drawer.ts +0 -607
  81. package/dist/components/rel-kbd/rel-kbd.ts +0 -188
  82. package/dist/components/rel-layout/rel-layout.ts +0 -49
  83. package/dist/components/rel-menu/rel-menu.ts +0 -541
  84. package/dist/components/rel-menubar/index.ts +0 -2
  85. package/dist/components/rel-menubar/rel-menubar-menu.css +0 -3
  86. package/dist/components/rel-menubar/rel-menubar-menu.ts +0 -17
  87. package/dist/components/rel-menubar/rel-menubar.ts +0 -777
  88. package/dist/components/rel-nav-menu/README.md +0 -289
  89. package/dist/components/rel-nav-menu/index.ts +0 -3
  90. package/dist/components/rel-nav-menu/rel-nav-menu-item.ts +0 -243
  91. package/dist/components/rel-nav-menu/rel-nav-menu-panel.ts +0 -72
  92. package/dist/components/rel-nav-menu/rel-nav-menu.ts +0 -270
  93. package/dist/components/rel-navbar/README.md +0 -126
  94. package/dist/components/rel-navbar/rel-navbar.ts +0 -105
  95. package/dist/components/rel-otp/index.ts +0 -1
  96. package/dist/components/rel-otp/rel-otp.ts +0 -527
  97. package/dist/components/rel-pagination/index.ts +0 -1
  98. package/dist/components/rel-pagination/rel-pagination.ts +0 -522
  99. package/dist/components/rel-popover/index.ts +0 -2
  100. package/dist/components/rel-popover/rel-popover-content.ts +0 -81
  101. package/dist/components/rel-popover/rel-popover.ts +0 -475
  102. package/dist/components/rel-progress/rel-progress.ts +0 -234
  103. package/dist/components/rel-radio/rel-radio.ts +0 -481
  104. package/dist/components/rel-range-picker/rel-range-picker.ts +0 -1688
  105. package/dist/components/rel-resizable/index.ts +0 -3
  106. package/dist/components/rel-resizable/rel-resizable-group.ts +0 -405
  107. package/dist/components/rel-resizable/rel-resizable-handle.ts +0 -150
  108. package/dist/components/rel-resizable/rel-resizable-panel.ts +0 -58
  109. package/dist/components/rel-segmented-button/rel-segmented-button.ts +0 -217
  110. package/dist/components/rel-select/rel-select.ts +0 -1416
  111. package/dist/components/rel-sheet/rel-sheet.ts +0 -610
  112. package/dist/components/rel-sidebar/README.md +0 -209
  113. package/dist/components/rel-sidebar/rel-sidebar.ts +0 -1191
  114. package/dist/components/rel-skeleton/rel-skeleton.ts +0 -143
  115. package/dist/components/rel-slider/rel-slider.ts +0 -673
  116. package/dist/components/rel-snackbar/rel-snackbar.ts +0 -329
  117. package/dist/components/rel-stepper/rel-stepper.ts +0 -567
  118. package/dist/components/rel-switch/rel-switch.ts +0 -199
  119. package/dist/components/rel-tab/rel-tab.ts +0 -264
  120. package/dist/components/rel-table/README.md +0 -178
  121. package/dist/components/rel-table/rel-table-body.ts +0 -22
  122. package/dist/components/rel-table/rel-table-caption.ts +0 -30
  123. package/dist/components/rel-table/rel-table-cell.ts +0 -43
  124. package/dist/components/rel-table/rel-table-footer.ts +0 -24
  125. package/dist/components/rel-table/rel-table-head.ts +0 -47
  126. package/dist/components/rel-table/rel-table-header.ts +0 -22
  127. package/dist/components/rel-table/rel-table-row.ts +0 -37
  128. package/dist/components/rel-table/rel-table.ts +0 -62
  129. package/dist/components/rel-textfield/rel-textfield.ts +0 -1054
  130. package/dist/components/rel-time-picker/rel-time-picker.ts +0 -1482
  131. package/dist/components/rel-time-picker/time-parse.test.ts +0 -67
  132. package/dist/components/rel-time-picker/time-parse.ts +0 -85
  133. package/dist/components/rel-toast/rel-toast.ts +0 -206
  134. package/dist/components/rel-tooltip/rel-tooltip.ts +0 -193
  135. package/dist/components/rel-topbar/rel-topbar-actions.ts +0 -27
  136. package/dist/components/rel-topbar/rel-topbar-brand.ts +0 -54
  137. package/dist/components/rel-topbar/rel-topbar-dropdown-item.ts +0 -69
  138. package/dist/components/rel-topbar/rel-topbar-dropdown.ts +0 -31
  139. package/dist/components/rel-topbar/rel-topbar-item.ts +0 -324
  140. package/dist/components/rel-topbar/rel-topbar-mobile-panel.ts +0 -27
  141. package/dist/components/rel-topbar/rel-topbar-profile.ts +0 -243
  142. package/dist/components/rel-topbar/rel-topbar-utilities.ts +0 -27
  143. package/dist/components/rel-topbar/rel-topbar.ts +0 -446
  144. package/dist/components/rel-upload/UPLOAD-DESIGN-CHANGES.md +0 -134
  145. package/dist/components/rel-upload/rel-upload-actions.ts +0 -163
  146. package/dist/components/rel-upload/rel-upload.ts +0 -968
@@ -0,0 +1,220 @@
1
+ # rel-upload
2
+
3
+ File upload: `<rel-upload>` renders the dropzone, the format / size hint, the status alert and the per-file list (Figma "↳ File Upload" — `Upload Dropzone`, `Upload File Row`, `Upload Modal`), and `<rel-upload-actions>` renders the Cancel / Upload footer as a separate element so it can sit in `<rel-modal>`'s `footer` slot and stay pinned while the file list scrolls. Use it wherever users attach one or more files and need per-file progress, success and failure states; the pair runs either **controlled** (the component picks and validates, your app transfers) or **self-driving** (pass an `uploader` and the component runs the whole batch). For a plain native file field without a list, use `<input type="file">` directly.
4
+
5
+ ## Usage
6
+
7
+ ```html
8
+ <!-- Controlled mode: pick + validate here, transfer in your app -->
9
+ <rel-upload
10
+ id="docs"
11
+ accept=".pdf,.png,.jpg"
12
+ max-size="10MB"
13
+ multiple>
14
+ </rel-upload>
15
+ <rel-upload-actions for="docs"></rel-upload-actions>
16
+ <script>
17
+ const upload = document.getElementById('docs');
18
+
19
+ upload.addEventListener('rel-upload', async (e) => {
20
+ for (const entry of e.detail.files) {
21
+ // Write progress / result back onto `files` — the rows re-render from it.
22
+ const patch = (partial) => {
23
+ upload.files = upload.files.map((f) => (f.id === entry.id ? { ...f, ...partial } : f));
24
+ };
25
+ patch({ status: 'uploading', progress: 0, loaded: 0 });
26
+ try {
27
+ await sendToServer(entry.file, (loaded) =>
28
+ patch({ loaded, progress: Math.round((loaded / entry.size) * 100) }));
29
+ patch({ status: 'success', progress: 100, loaded: entry.size });
30
+ } catch (err) {
31
+ patch({ status: 'error', error: err.message });
32
+ }
33
+ }
34
+ });
35
+ </script>
36
+
37
+ <!-- Self-driving mode: hand over an uploader and listen for the outcome -->
38
+ <rel-upload id="photos" accept="image/*" max-size="5MB" multiple concurrency="2"></rel-upload>
39
+ <rel-upload-actions for="photos"></rel-upload-actions>
40
+ <script>
41
+ const photos = document.getElementById('photos');
42
+ photos.uploader = async (file, { signal, onProgress }) => {
43
+ // Any transport works; honour `signal` so Cancel aborts, call `onProgress(loaded, total)` for the bar.
44
+ await putWithProgress('/api/photos', file, { signal, onProgress });
45
+ };
46
+ photos.addEventListener('rel-complete', (e) => {
47
+ console.log('done', e.detail.succeeded.length, 'failed', e.detail.failed.length);
48
+ });
49
+ </script>
50
+
51
+ <!-- Inside rel-modal: body scrolls, footer stays pinned; the actions find the sibling upload automatically -->
52
+ <rel-modal open side="center" title="Upload documents">
53
+ <rel-upload accept=".pdf" max-size="10MB" multiple></rel-upload>
54
+ <rel-upload-actions slot="footer"></rel-upload-actions>
55
+ </rel-modal>
56
+
57
+ <!-- Single file, custom copy, forward-only footer -->
58
+ <rel-upload
59
+ id="avatar"
60
+ accept=".png,.jpg"
61
+ max-size="2MB"
62
+ dropzone-title="Drop your avatar here"
63
+ browse-label="Choose image"
64
+ hint="PNG or JPG, square, up to 2 MB">
65
+ </rel-upload>
66
+ <rel-upload-actions for="avatar" no-cancel></rel-upload-actions>
67
+
68
+ <!-- Disabled -->
69
+ <rel-upload disabled></rel-upload>
70
+ ```
71
+
72
+ <!-- readme-gen:start -->
73
+
74
+ <!-- Generated from rel-upload.ts, rel-upload-actions.ts — do not edit; run readme-generate.mjs -->
75
+
76
+ ## Elements
77
+
78
+ | Element | Class | Source |
79
+ | --- | --- | --- |
80
+ | `<rel-upload>` | `RelUpload` | `rel-upload.ts` |
81
+ | `<rel-upload-actions>` | `RelUploadActions` | `rel-upload-actions.ts` |
82
+
83
+ ## API
84
+
85
+ ### `<rel-upload>`
86
+
87
+ #### Attributes / Properties
88
+
89
+ | Property | Attribute | Type | Default | Description |
90
+ | --- | --- | --- | --- | --- |
91
+ | `accept` | `accept` | `string` | `""` | Comma-separated accept list, e.g. ".pdf,.png,.jpg" or "image/*". |
92
+ | `maxSize` | `max-size` | `string` | `""` | Max size per file as a human string ("10MB", "500kb"). Empty → no limit. |
93
+ | `multiple` | `multiple` | `boolean` | `false` | Allow more than one file. |
94
+ | `disabled` | `disabled` | `boolean` | `false` | Disables the whole control. Reflected. |
95
+ | `hint` | `hint` | `string` | `""` | Overrides the derived "PDF, PNG or JPG - up to 10 MB per file" hint. |
96
+ | `dropzoneTitle` | `dropzone-title` | `string` | `"Drag and drop your files here"` | Dropzone headline. |
97
+ | `browseLabel` | `browse-label` | `string` | `"Browse files"` | Browse button label. |
98
+ | `files` | — | `UploadFile[]` | `[]` | The file list. Set it to drive the component in controlled mode; the component also maintains it itself as files are added and removed. Property only. |
99
+ | `uploader` | — | `(file: File, ctx: UploaderContext) => Promise<void>` | `undefined` | Supply an `Uploader` `(file, { file, signal, onProgress }) => Promise<void>` to switch to self-driving mode: the component runs the batch itself with `concurrency`, progress, the delay alert, abort and retry. Unset (default) the component only emits intents. Property only. |
100
+ | `concurrency` | `concurrency` | `number` | `3` | Max files transferred at once in self-driving mode. |
101
+ | `delayThreshold` | `delay-threshold` | `number` | `15000` | ms of continuous uploading before the "taking longer than usual" alert. |
102
+
103
+ #### Public fields / accessors
104
+
105
+ | Name | Type | Description |
106
+ | --- | --- | --- |
107
+ | `phase` | `UploadPhase` | Aggregate state. Read-only; corresponds to the Figma modal variants. `failed` means there is nothing left to send AND something errored — while any row is still `ready` the primary action is Upload, not Retry. |
108
+ | `canUpload` | `boolean` | Whether the primary action is enabled: not disabled, nothing in flight, and either `ready` rows exist or (in the failed phase) at least one failed row can be retried. |
109
+ | `primaryLabel` | `string` | Footer button label — "Upload", "Upload 3 files" or "Retry upload". |
110
+ | `state` | `UploadState` | The snapshot `<rel-upload-actions>` renders from. |
111
+
112
+ #### Methods
113
+
114
+ | Method | Signature | Description |
115
+ | --- | --- | --- |
116
+ | `upload` | `upload()` | Start (or resume) the batch. Emits `rel-upload`; runs it when `uploader` is set. |
117
+ | `retry` | `retry()` | Re-queue every retryable failed row and send again. |
118
+ | `cancel` | `cancel()` | Abort anything in flight and emit `rel-cancel`. Does not clear the list. |
119
+ | `removeFile` | `removeFile(id: string)` | Remove one row by id. |
120
+ | `reset` | `reset()` | Clear everything and abort in-flight transfers. |
121
+
122
+ ### Events
123
+
124
+ | Event | Detail | Bubbles / composed | Description |
125
+ | --- | --- | --- | --- |
126
+ | `rel-cancel` | — | bubbles / composed | Fired when Cancel is pressed (or `cancel()` is called); in-flight transfers are aborted, the list is kept. No detail. |
127
+ | `rel-change` | `{ files: this.files }` | bubbles / composed | Fired whenever the file list changes (files added, a row removed, `reset()`). Detail: `{ files: UploadFile[] }`. |
128
+ | `rel-complete` | `{ succeeded: this.files.filter((f) => f.status === "success"), failed: this.failedFiles, }` | bubbles / composed | Fired when the whole self-driving batch settles without being cancelled. Detail: `{ succeeded: UploadFile[], failed: UploadFile[] }`. |
129
+ | `rel-file-complete` | `{ id: item.id, file: item.file }` | bubbles / composed | Fired when one file finishes uploading in self-driving mode. Detail: `{ id: string, file: File }`. |
130
+ | `rel-file-error` | `{ id: item.id, file: item.file, error: message }` | bubbles / composed | Fired when one file's `uploader` rejects in self-driving mode. Detail: `{ id: string, file: File, error: string }`. |
131
+ | `rel-files-added` | `{ added, rejected }` | bubbles / composed | Fired after files are picked or dropped and validated. Detail: `{ added: UploadFile[], rejected: UploadRejection[] }`. |
132
+ | `rel-invalid` | `{ rejected }` | bubbles / composed | Fired just before `rel-files-added` when at least one picked file fails `accept` / `max-size`. Detail: `{ rejected: UploadRejection[] }`. |
133
+ | `rel-progress` | `{ id: item.id, progress, loaded, total: t }` | bubbles / composed | Fired on every progress tick in self-driving mode. Detail: `{ id: string, progress: number (0–100), loaded: number, total: number }` (bytes). |
134
+ | `rel-remove` | `{ id, file: target?.file }` | bubbles / composed | Fired when a row is removed via its delete button or `removeFile()`. Detail: `{ id: string, file?: File }`. |
135
+ | `rel-retry` | `{ files: requeued }` | bubbles / composed | Fired when Retry re-queues the retryable failed rows. Detail: `{ files: UploadFile[] }`. |
136
+ | `rel-state` | `s / this.state` | bubbles / composed | Fired when the derived `state` snapshot changes (and once after connecting). Detail: `UploadState`. |
137
+ | `rel-upload` | `{ files: targets }` | bubbles / composed | Fired when Upload is pressed (or `upload()` is called) with the rows still `ready`; in controlled mode the app performs the transfer. Detail: `{ files: UploadFile[] }`. |
138
+
139
+ ### Slots
140
+
141
+ | Slot | Description |
142
+ | --- | --- |
143
+ | — | This element has no slots. |
144
+
145
+ ### CSS Parts
146
+
147
+ | Part | Description |
148
+ | --- | --- |
149
+ | — | None exposed. |
150
+
151
+ ### Tokens / CSS Variables
152
+
153
+ #### Component hooks
154
+
155
+ Set these on the host (or an ancestor) to customise the element without touching design tokens.
156
+
157
+ | Custom property | Default | Description |
158
+ | --- | --- | --- |
159
+ | `--rel-upload-browse-hover-bg` | `var(--rel-action-neutral-outline-hover, var(--rel-bg-surface-elevated))` | Background of the Browse button on hover; falls back to `--rel-action-neutral-outline-hover`, then `--rel-bg-surface-elevated` |
160
+
161
+ ### `<rel-upload-actions>`
162
+
163
+ #### Attributes / Properties
164
+
165
+ | Property | Attribute | Type | Default | Description |
166
+ | --- | --- | --- | --- | --- |
167
+ | `for` | `for` | `string` | `""` | Id of the `<rel-upload>` to control. Omit to auto-discover a sibling. |
168
+ | `cancelLabel` | `cancel-label` | `string` | `"Cancel"` | Cancel button label. |
169
+ | `noCancel` | `no-cancel` | `boolean` | `false` | Hides Cancel for flows that only ever move forward. |
170
+
171
+ ### Events
172
+
173
+ | Event | Detail | Bubbles / composed | Description |
174
+ | --- | --- | --- | --- |
175
+ | `rel-cancel` | — | bubbles / composed | Fired when the Cancel button is pressed, after `cancel()` has been called on the target `<rel-upload>`. No detail. |
176
+
177
+ ### Slots
178
+
179
+ | Slot | Description |
180
+ | --- | --- |
181
+ | — | This element has no slots. |
182
+
183
+ ### CSS Parts
184
+
185
+ | Part | Description |
186
+ | --- | --- |
187
+ | `base` | The right-aligned flex row holding the buttons |
188
+ | `cancel` | The secondary Cancel `rel-button` (absent when `no-cancel` is set) |
189
+ | `primary` | The primary Upload / Retry `rel-button` |
190
+
191
+ <!-- readme-gen:end -->
192
+
193
+ ## Accessibility
194
+
195
+ - The dropzone itself is a mouse / drag-and-drop affordance only — it has no role and is not focusable, so no interactive control is nested inside another. The **Browse** `<button type="button">` is the only focusable control in the dropzone; it opens the hidden native `<input type="file">` (which carries `accept` and `multiple`).
196
+ - The file rows container is `role="list"`. Each row shows a status icon with `role="img"` and an accessible name ("Uploaded" / "Failed") and a remove button labelled "Remove {file name}"; remove buttons are disabled while `disabled` is set.
197
+ - Browse and remove buttons show a 2px `--rel-border-focus` focus ring on `:focus-visible`.
198
+ - In `<rel-upload-actions>` the primary button is `disabled` whenever `canUpload` is false and, while a batch is in flight, shows `rel-button`'s `loading` state with `aria-label="Uploading"`.
199
+ - Screen-reader announcements go through the shared `announce()` live region: "N file(s) added" (polite) and "N file(s) rejected" (assertive) after a pick or drop; the list heading whenever it changes — "Selected files (3)", "Uploading 3 files" (polite) or "3 files - 2 failed" (assertive); the alert text ("This is taking longer than usual…" / "We couldn't upload…") assertively when it appears; "{name} removed" after `removeFile()`; "File list cleared" after `reset()`.
200
+ - Status colours are backed by the tokens `--rel-text-caption`, `--rel-text-success` and `--rel-text-danger` rather than the Figma hexes (see `UPLOAD-DESIGN-CHANGES.md` for the contrast rationale).
201
+
202
+ | Key | Action |
203
+ | --- | --- |
204
+ | `Tab` / `Shift+Tab` | Move between Browse, each row's Remove button and the footer buttons (all native buttons) |
205
+ | `Enter` / `Space` on Browse | Open the native file picker |
206
+ | `Enter` / `Space` on Remove | Remove that row (`rel-remove` + `rel-change`) |
207
+ | `Enter` / `Space` on Upload / Retry / Cancel | Call `upload()` / `retry()` / `cancel()` on the target `<rel-upload>` |
208
+
209
+ ## Notes / Edge Cases
210
+
211
+ - **Modes.** Without `uploader` the component only emits intents (`rel-upload`, `rel-retry`, `rel-cancel`, `rel-remove`) and your app must write `status` / `progress` / `loaded` / `error` back onto `files` (replace the array — rows are keyed by `id`). With `uploader` the component patches `files` itself, runs up to `concurrency` transfers at once, emits `rel-progress` / `rel-file-complete` / `rel-file-error` per file and `rel-complete` when the batch settles.
212
+ - **Validation happens on pick.** Files failing `accept` or `max-size` are kept as `error` rows ("unsupported file type" / "file exceeds the 10MB limit") — that is how the Figma Failure state surfaces them — and are reported in `rel-invalid` and `rel-files-added.rejected`. `retry()` skips rows that would fail validation again (and rows without a `file` object); if every failed row is unretryable the Retry button stays disabled — remove the row instead.
213
+ - **`accept` tokens** are `.ext` (name suffix), `type/*` (MIME prefix) or an exact MIME type; the hint line is derived from them ("PDF, PNG or JPG") plus `max-size` ("- up to 10MB per file"). `max-size` accepts `b` / `kb` / `mb` / `gb` (decimal: 1MB = 1,000,000 bytes); an unparseable string means no limit.
214
+ - **Same-named files replace** the existing entry instead of duplicating it. Without `multiple` only the first picked file is taken and it replaces the whole list.
215
+ - **Phase.** `phase` is `failed` only when nothing is left to send AND something errored; while any row is still `ready` the primary action remains "Upload N files". `delayed` is entered after `delay-threshold` ms of continuous uploading (`0` disables the "taking longer than usual" alert) and shows a subtle warning `rel-alert`; failures show a subtle destructive `rel-alert`.
216
+ - **Cancel** aborts the `AbortSignal` handed to `uploader` and returns in-flight rows to `ready`; it keeps the list. `reset()` aborts and clears everything.
217
+ - **`rel-state` is de-duplicated** — it only fires when the derived snapshot (`phase`, counts, `canUpload`, `primaryLabel`) changes, so progress ticks do not spam `<rel-upload-actions>`; it also fires once right after connecting.
218
+ - **Pairing.** `<rel-upload-actions>` resolves its target by `for` (an id in the same DOM root), else the first `<rel-upload>` in its parent element, else the first in its root. It listens for `rel-state` on its root node, so both elements must share the same document or shadow root — slotting the actions into `<rel-modal>`'s footer is fine because slotting does not change the root.
219
+ - **Figma deviations.** The Browse button (not the whole dropzone) is the focusable control; the drag-over frame (solid brand border + 8% brand tint) and the disabled state (60% opacity) have no Figma frame; three colours were moved to the correct token layer for contrast. Details in `UPLOAD-DESIGN-CHANGES.md`.
220
+ - The Browse hover background reads `--rel-upload-browse-hover-bg` and falls back to `--rel-action-neutral-outline-hover`, then `--rel-bg-surface-elevated`.