@vmz/ui 0.3.7 → 0.3.9

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 (68) hide show
  1. package/README.md +2 -0
  2. package/contracts/token-requirements.v0.json +58 -1
  3. package/package.json +5 -2
  4. package/presets/web-surface.v0.json +14 -12
  5. package/src/components/Accordion.vmz +49 -16
  6. package/src/components/Alert.vmz +125 -28
  7. package/src/components/AppShell.vmz +121 -56
  8. package/src/components/Autocomplete.vmz +136 -29
  9. package/src/components/Avatar.vmz +20 -12
  10. package/src/components/Badge.vmz +18 -26
  11. package/src/components/Breadcrumb.vmz +28 -14
  12. package/src/components/BulkActions.vmz +13 -8
  13. package/src/components/Button.vmz +68 -44
  14. package/src/components/Callout.vmz +128 -27
  15. package/src/components/Card.vmz +67 -46
  16. package/src/components/Checkbox.vmz +81 -5
  17. package/src/components/CodeBlock.vmz +59 -27
  18. package/src/components/Collapse.vmz +64 -11
  19. package/src/components/ConsoleShell.vmz +249 -69
  20. package/src/components/Content.vmz +15 -1
  21. package/src/components/DataTable.vmz +123 -23
  22. package/src/components/DatePicker.vmz +171 -79
  23. package/src/components/Descriptions.vmz +90 -0
  24. package/src/components/Dialog.vmz +133 -50
  25. package/src/components/Divider.vmz +8 -1
  26. package/src/components/Drawer.vmz +131 -40
  27. package/src/components/Dropdown.vmz +147 -49
  28. package/src/components/Empty.vmz +56 -14
  29. package/src/components/Field.vmz +78 -23
  30. package/src/components/FilterBar.vmz +34 -4
  31. package/src/components/Flex.vmz +3 -1
  32. package/src/components/Footer.vmz +6 -3
  33. package/src/components/Form.vmz +41 -0
  34. package/src/components/FormItem.vmz +2 -3
  35. package/src/components/Grid.vmz +6 -3
  36. package/src/components/Header.vmz +193 -6
  37. package/src/components/Layout.vmz +3 -1
  38. package/src/components/Link.vmz +20 -9
  39. package/src/components/List.vmz +35 -13
  40. package/src/components/Menu.vmz +198 -25
  41. package/src/components/Message.vmz +167 -0
  42. package/src/components/Notification.vmz +132 -37
  43. package/src/components/Pagination.vmz +186 -20
  44. package/src/components/Popconfirm.vmz +287 -0
  45. package/src/components/Popover.vmz +152 -34
  46. package/src/components/Progress.vmz +16 -5
  47. package/src/components/Prose.vmz +131 -32
  48. package/src/components/QueryForm.vmz +31 -9
  49. package/src/components/RadioGroup.vmz +91 -11
  50. package/src/components/Result.vmz +60 -27
  51. package/src/components/Segmented.vmz +46 -11
  52. package/src/components/Select.vmz +174 -87
  53. package/src/components/Sider.vmz +120 -7
  54. package/src/components/Skeleton.vmz +9 -7
  55. package/src/components/Space.vmz +9 -3
  56. package/src/components/Spinner.vmz +5 -4
  57. package/src/components/Steps.vmz +40 -25
  58. package/src/components/Switch.vmz +48 -15
  59. package/src/components/Table.vmz +130 -25
  60. package/src/components/Tabs.vmz +28 -13
  61. package/src/components/Tag.vmz +29 -12
  62. package/src/components/TextArea.vmz +32 -25
  63. package/src/components/Timeline.vmz +65 -30
  64. package/src/components/Toc.vmz +45 -29
  65. package/src/components/Tooltip.vmz +70 -21
  66. package/src/components/Tree.vmz +48 -25
  67. package/src/components/Typography.vmz +23 -8
  68. package/src/components/Upload.vmz +191 -96
@@ -5,8 +5,9 @@
5
5
  </label>
6
6
  <div data-vmz-upload="zone" class="vmz-ui-upload__zone">
7
7
  <input :id="controlId" type="file" :accept="accept" :multiple="multiple" :disabled="inputDisabled" :aria-invalid="error ? 'true' : 'false'" :aria-describedby="describedBy" :data-vmz-upload-directory="directoryAttr" @change="onPick" class="vmz-ui-upload__control" />
8
- <p data-vmz-upload="hint" class="vmz-ui-upload__hint">
9
- {{ hint || zoneHint }}
8
+ <Icon name="action.upload" size="lg" decorative />
9
+ <p v-if="hint" data-vmz-upload="hint" class="vmz-ui-upload__hint">
10
+ {{ hint }}
10
11
  </p>
11
12
  </div>
12
13
  <ul v-if="hasItems" data-vmz-upload="queue" class="vmz-ui-upload__queue">
@@ -24,7 +25,9 @@
24
25
  {{ emptyText }}
25
26
  </p>
26
27
  <div v-if="showProgress" data-vmz-upload="progress" role="progressbar" aria-valuemin="0" aria-valuemax="100" :aria-valuenow="progress" :aria-label="progressLabel" class="vmz-ui-upload__progress">
27
- <div class="vmz-ui-upload__bar" :style="progressStyle" />
28
+ <div class="vmz-ui-upload__track">
29
+ <div class="vmz-ui-upload__bar" :style="progressStyle" />
30
+ </div>
28
31
  <span data-vmz-upload="pct" class="vmz-ui-upload__pct">
29
32
  {{ progressLabel }}
30
33
  </span>
@@ -36,11 +39,17 @@
36
39
  {{ resultText }}
37
40
  </p>
38
41
  <div v-if="showActions" data-vmz-upload="actions" class="vmz-ui-upload__actions">
39
- <button v-if="canCancel" type="button" data-vmz-upload="cancel" @click="onCancelClick" class="vmz-ui-upload__cancel">
40
- {{ cancelLabel }}
42
+ <button v-if="canCancel" type="button" data-vmz-upload="cancel" :aria-label="cancelLabel" @click="onCancelClick" class="vmz-ui-upload__cancel">
43
+ <Icon name="action.close" size="sm" decorative />
44
+ <span v-if="cancelLabel">
45
+ {{ cancelLabel }}
46
+ </span>
41
47
  </button>
42
- <button v-if="canResume" type="button" data-vmz-upload="resume" @click="onResumeClick" class="vmz-ui-upload__resume">
43
- {{ resumeLabel }}
48
+ <button v-if="canResume" type="button" data-vmz-upload="resume" :aria-label="resumeLabel" @click="onResumeClick" class="vmz-ui-upload__resume">
49
+ <Icon name="action.chevron-right" size="sm" decorative />
50
+ <span v-if="resumeLabel">
51
+ {{ resumeLabel }}
52
+ </span>
44
53
  </button>
45
54
  </div>
46
55
  <p v-if="description" :id="descriptionId" class="vmz-ui-upload__description">
@@ -55,21 +64,49 @@
55
64
  .vmz-ui-upload {
56
65
  display: flex;
57
66
  flex-direction: column;
58
- gap: 0.375rem;
67
+ gap: 6px;
68
+ width: 100%;
59
69
  max-width: 28rem;
60
70
  }
61
71
 
62
72
  .vmz-ui-upload__label {
63
- font: inherit;
73
+ font-size: 14px;
64
74
  font-weight: 600;
75
+ line-height: 22px;
76
+ color: var(--vmz-text-ink);
65
77
  }
66
78
 
67
79
  .vmz-ui-upload__zone {
68
80
  position: relative;
81
+ display: flex;
82
+ flex-direction: column;
83
+ align-items: center;
84
+ justify-content: center;
85
+ gap: 8px;
86
+ box-sizing: border-box;
87
+ min-height: 96px;
88
+ padding: 20px 24px;
69
89
  border: 1px dashed var(--vmz-line-default);
70
- border-radius: var(--vmz-radius-md, 6px);
71
- background: var(--vmz-surface-paper);
72
- padding: var(--vmz-density-control-padding-y) var(--vmz-density-control-padding-x);
90
+ border-radius: var(--vmz-radius-lg);
91
+ background: var(--vmz-surface-mist);
92
+ text-align: center;
93
+ transition:
94
+ border-color var(--vmz-motion-control-duration) var(--vmz-motion-control-easing),
95
+ background-color var(--vmz-motion-control-duration) var(--vmz-motion-control-easing);
96
+ }
97
+
98
+ .vmz-ui-upload__zone:hover {
99
+ border-color: var(--vmz-action-primary-background);
100
+ background: color-mix(
101
+ in srgb,
102
+ var(--vmz-action-primary-background) 4%,
103
+ var(--vmz-surface-paper)
104
+ );
105
+ }
106
+
107
+ .vmz-ui-upload__zone:focus-within {
108
+ border-color: var(--vmz-action-primary-background);
109
+ box-shadow: 0 0 0 3px var(--vmz-focus-ring);
73
110
  }
74
111
 
75
112
  .vmz-ui-upload.is-dragover .vmz-ui-upload__zone {
@@ -82,26 +119,33 @@
82
119
  }
83
120
 
84
121
  .vmz-ui-upload__control {
85
- font: inherit;
122
+ position: absolute;
123
+ inset: 0;
86
124
  width: 100%;
87
- padding: 0.125rem 0;
88
- color: inherit;
125
+ height: 100%;
126
+ margin: 0;
127
+ padding: 0;
128
+ border: 0;
129
+ opacity: 0;
130
+ cursor: pointer;
89
131
  outline: none;
90
132
  }
91
133
 
92
- .vmz-ui-upload__control:focus-visible {
93
- box-shadow: 0 0 0 3px var(--vmz-focus-ring);
94
- }
95
-
96
134
  .vmz-ui-upload__control:disabled {
97
- opacity: 0.55;
98
135
  cursor: not-allowed;
99
136
  }
100
137
 
138
+ .vmz-ui-upload__zone .vmz-ui-icon {
139
+ color: var(--vmz-text-tertiary);
140
+ pointer-events: none;
141
+ }
142
+
101
143
  .vmz-ui-upload__hint {
102
- margin: 0.375rem 0 0;
103
- font-size: 0.88rem;
144
+ margin: 0;
145
+ font-size: 14px;
146
+ line-height: 22px;
104
147
  color: var(--vmz-text-steel);
148
+ pointer-events: none;
105
149
  }
106
150
 
107
151
  .vmz-ui-upload__queue {
@@ -110,22 +154,31 @@
110
154
  padding: 0;
111
155
  display: flex;
112
156
  flex-direction: column;
113
- gap: 0.375rem;
157
+ border: 1px solid var(--vmz-line-subtle);
158
+ border-radius: var(--vmz-radius-lg);
159
+ background: var(--vmz-surface-paper);
160
+ overflow: hidden;
114
161
  }
115
162
 
116
163
  .vmz-ui-upload__item {
117
164
  display: grid;
118
165
  grid-template-columns: 1fr auto;
119
- gap: 0.125rem 0.75rem;
120
- padding: 0.375rem 0.5rem;
121
- border: 1px solid var(--vmz-line-default);
122
- border-radius: var(--vmz-radius-md, 6px);
166
+ align-items: center;
167
+ gap: 2px 12px;
168
+ min-height: 40px;
169
+ box-sizing: border-box;
170
+ padding: var(--vmz-density-compact-padding-y) var(--vmz-density-compact-padding-x);
171
+ border-bottom: 1px solid var(--vmz-line-subtle);
123
172
  background: var(--vmz-surface-paper);
124
173
  }
125
174
 
175
+ .vmz-ui-upload__item:last-child {
176
+ border-bottom: none;
177
+ }
178
+
126
179
  .vmz-ui-upload__item-name {
127
- font-family: var(--vmz-font-mono);
128
- font-size: 0.88rem;
180
+ font-size: 14px;
181
+ line-height: 22px;
129
182
  color: var(--vmz-text-ink);
130
183
  overflow: hidden;
131
184
  text-overflow: ellipsis;
@@ -133,17 +186,18 @@
133
186
  }
134
187
 
135
188
  .vmz-ui-upload__item-meta {
136
- font-size: 0.8rem;
137
- color: var(--vmz-text-steel);
189
+ font-size: 12px;
190
+ line-height: 20px;
191
+ color: var(--vmz-text-tertiary);
138
192
  white-space: nowrap;
139
193
  }
140
194
 
141
195
  .vmz-ui-upload__item-bar {
142
196
  grid-column: 1 / -1;
143
- height: 0.35rem;
144
- border-radius: var(--vmz-radius-md, 6px);
197
+ height: 2px;
198
+ border-radius: 1px;
145
199
  background: var(--vmz-action-primary-background);
146
- min-width: 0.25rem;
200
+ min-width: 2px;
147
201
  transition: width var(--vmz-motion-control-duration) var(--vmz-motion-control-easing);
148
202
  }
149
203
 
@@ -151,6 +205,10 @@
151
205
  color: var(--vmz-status-danger-foreground);
152
206
  }
153
207
 
208
+ .vmz-ui-upload[data-vmz-upload-status="error"] .vmz-ui-upload__zone {
209
+ border-color: var(--vmz-status-danger-accent);
210
+ }
211
+
154
212
  /* The native file input already announces "no file chosen"; keep this node for
155
213
  assistive tech without repeating the same line visually. */
156
214
  .vmz-ui-upload__empty {
@@ -169,28 +227,37 @@
169
227
  display: grid;
170
228
  grid-template-columns: 1fr auto;
171
229
  align-items: center;
172
- gap: 0.5rem;
173
- padding: 0.375rem 0;
230
+ gap: 8px;
231
+ padding: 4px 0;
232
+ }
233
+
234
+ .vmz-ui-upload__track {
235
+ height: 6px;
236
+ border-radius: 999px;
237
+ background: var(--vmz-surface-mist);
238
+ overflow: hidden;
174
239
  }
175
240
 
176
241
  .vmz-ui-upload__bar {
177
- height: 0.45rem;
178
- border-radius: var(--vmz-radius-md, 6px);
242
+ height: 100%;
243
+ border-radius: inherit;
179
244
  background: var(--vmz-action-primary-background);
180
- min-width: 0.25rem;
245
+ min-width: 2px;
181
246
  transition: width var(--vmz-motion-control-duration) var(--vmz-motion-control-easing);
182
247
  }
183
248
 
184
249
  .vmz-ui-upload__pct {
185
- font-family: var(--vmz-font-mono);
186
- font-size: 0.8rem;
250
+ font-size: 12px;
251
+ line-height: 20px;
187
252
  color: var(--vmz-text-steel);
253
+ font-variant-numeric: tabular-nums;
188
254
  white-space: nowrap;
189
255
  }
190
256
 
191
257
  .vmz-ui-upload__status {
192
258
  margin: 0;
193
- font-size: 0.9em;
259
+ font-size: 14px;
260
+ line-height: 22px;
194
261
  color: var(--vmz-text-steel);
195
262
  }
196
263
 
@@ -206,25 +273,44 @@
206
273
  .vmz-ui-upload__result {
207
274
  margin: 0;
208
275
  font-family: var(--vmz-font-mono);
209
- font-size: 0.88rem;
276
+ font-size: 12px;
277
+ line-height: 20px;
210
278
  color: var(--vmz-text-ink);
211
279
  }
212
280
 
213
281
  .vmz-ui-upload__actions {
214
282
  display: flex;
215
- gap: 0.5rem;
283
+ flex-wrap: wrap;
284
+ gap: 8px;
216
285
  }
217
286
 
218
287
  .vmz-ui-upload__cancel,
219
288
  .vmz-ui-upload__resume {
289
+ display: inline-flex;
290
+ align-items: center;
291
+ justify-content: center;
292
+ gap: 6px;
293
+ min-height: var(--vmz-control-height, 32px);
294
+ box-sizing: border-box;
220
295
  font: inherit;
221
- padding: var(--vmz-density-compact-padding-y) var(--vmz-density-compact-padding-x);
296
+ font-size: 14px;
297
+ line-height: 22px;
298
+ padding: var(--vmz-density-control-padding-y) var(--vmz-density-control-padding-x);
222
299
  border: 1px solid var(--vmz-line-default);
223
- border-radius: var(--vmz-radius-md, 6px);
300
+ border-radius: var(--vmz-radius-md);
224
301
  background: var(--vmz-surface-paper);
225
302
  color: var(--vmz-text-ink);
226
303
  cursor: pointer;
227
304
  outline: none;
305
+ transition:
306
+ color var(--vmz-motion-control-duration) var(--vmz-motion-control-easing),
307
+ border-color var(--vmz-motion-control-duration) var(--vmz-motion-control-easing);
308
+ }
309
+
310
+ .vmz-ui-upload__cancel:hover,
311
+ .vmz-ui-upload__resume:hover {
312
+ border-color: var(--vmz-action-primary-background);
313
+ color: var(--vmz-action-primary-background);
228
314
  }
229
315
 
230
316
  .vmz-ui-upload__cancel:focus-visible,
@@ -239,19 +325,24 @@
239
325
 
240
326
  .vmz-ui-upload__description {
241
327
  margin: 0;
242
- opacity: 0.75;
243
- font-size: 0.9em;
328
+ font-size: 14px;
329
+ line-height: 22px;
330
+ color: var(--vmz-text-tertiary);
244
331
  }
245
332
 
246
333
  .vmz-ui-upload__error {
247
334
  margin: 0;
335
+ font-size: 14px;
336
+ line-height: 22px;
248
337
  color: var(--vmz-status-danger-foreground);
249
- font-size: 0.9em;
250
338
  }
251
339
 
252
340
  @media (prefers-reduced-motion: reduce) {
253
341
  .vmz-ui-upload__bar,
254
- .vmz-ui-upload__item-bar {
342
+ .vmz-ui-upload__item-bar,
343
+ .vmz-ui-upload__zone,
344
+ .vmz-ui-upload__cancel,
345
+ .vmz-ui-upload__resume {
255
346
  transition: none;
256
347
  }
257
348
  }
@@ -272,7 +363,7 @@ export default class Upload {
272
363
  public description: string = "";
273
364
  /** Form-level validation message from parent. */
274
365
  public error: string = "";
275
- public emptyText: string = "No file selected";
366
+ public emptyText: string = "";
276
367
  public accept: string = "";
277
368
  public disabled: boolean = false;
278
369
  public invalidAttr: string = "false";
@@ -310,10 +401,14 @@ export default class Upload {
310
401
  public onValue: ((items: UploadValueItem[]) => void) | null = null;
311
402
  /** Parent mirrors overall status for form fixture / submit gates. */
312
403
  public onStatus: ((status: string) => void) | null = null;
313
- public cancelLabel: string = "Cancel upload";
314
- public resumeLabel: string = "Resume upload";
315
- /** Caller-supplied drop-zone hint; empty keeps the mode-aware default. */
404
+ public cancelLabel: string = "";
405
+ public resumeLabel: string = "";
406
+ /** Caller-supplied drop-zone hint; empty hides the hint line. */
316
407
  public hint: string = "";
408
+ /** Localized status / diagnostic copy. Empty keys hide the status line. */
409
+ public messages: Record<string, string> = {};
410
+ /** Suffix for multi-file success (`N <readyText>`); part of the stable result text. */
411
+ public readyText: string = "file(s) ready";
317
412
  // presentation (component-owned — NOT public props)
318
413
  status: string = "idle";
319
414
  progress: string = "0";
@@ -333,7 +428,6 @@ export default class Upload {
333
428
  directoryAttr: string = "false";
334
429
  chunkAttr: string = "0";
335
430
  sessionAttr: string = "";
336
- zoneHint: string = "Choose a file or drop it here";
337
431
  /** @type {{ key: string, name: string, meta: string, status: string, showBar: boolean, barStyle: string }[]} */
338
432
  queue: {
339
433
  key: string;
@@ -374,6 +468,11 @@ export default class Upload {
374
468
  this.applyDirectoryAttr();
375
469
  this.syncChunkAttr();
376
470
  }
471
+ msg(key: string) {
472
+ const messages = this.messages || {};
473
+ const value = messages[key];
474
+ return typeof value === "string" ? value : "";
475
+ }
377
476
  onPick(ev) {
378
477
  this.syncClearToken();
379
478
  this.applyDirectoryAttr();
@@ -432,11 +531,6 @@ export default class Upload {
432
531
  applyDirectoryAttr() {
433
532
  this.directoryAttr = this.directory ? "true" : "false";
434
533
  this.selectionMode = this.directory ? "directory" : this.multiple ? "multiple" : "single";
435
- this.zoneHint = this.directory
436
- ? "Choose a folder or drop files here"
437
- : this.multiple
438
- ? "Choose files or drop them here"
439
- : "Choose a file or drop it here";
440
534
  // Native directory picker (UA-specific).
441
535
  const el = typeof document !== "undefined" ? document.getElementById(this.controlId) : null;
442
536
  if (el && "webkitdirectory" in el) {
@@ -464,24 +558,24 @@ export default class Upload {
464
558
  const capped =
465
559
  this.multiple || this.directory ? files.slice(0, maxCount) : files.slice(0, 1);
466
560
  if ((this.multiple || this.directory) && files.length > maxCount) {
467
- this.failAll(`At most ${maxCount} file(s)`);
561
+ this.failAll(this.msg("tooMany"));
468
562
  return;
469
563
  }
470
564
  const maxBytes = Number(this.maxBytes) || 0;
471
565
  for (const f of capped) {
472
566
  const acceptErr = acceptError(f, this.accept);
473
567
  if (acceptErr) {
474
- this.failAll(acceptErr);
568
+ this.failAll(this.msg("acceptType"));
475
569
  return;
476
570
  }
477
571
  if (maxBytes > 0 && Number(f.size) > maxBytes) {
478
- this.failAll(`File too large (max ${maxBytes} bytes)`);
572
+ this.failAll(this.msg("fileTooLarge"));
479
573
  return;
480
574
  }
481
575
  }
482
576
  const chunkSize = Math.max(0, Number(this.chunkSize) || 0);
483
577
  if (chunkSize > 0 && capped.length !== 1) {
484
- this.failAll("Resumable chunk upload supports one file per session");
578
+ this.failAll(this.msg("singleFileChunk"));
485
579
  return;
486
580
  }
487
581
  this.cancelActive(false);
@@ -530,7 +624,7 @@ export default class Upload {
530
624
  */
531
625
  async runClient(gen) {
532
626
  if (gen !== this._gen) return;
533
- this.setOverall("uploading", "Preparing…", true, false);
627
+ this.setOverall("uploading", this.msg("preparing"), true, false);
534
628
  const values = [];
535
629
  for (let i = 0; i < this._files.length; i++) {
536
630
  if (gen !== this._gen) return;
@@ -540,7 +634,7 @@ export default class Upload {
540
634
  status: "done",
541
635
  showBar: true,
542
636
  barStyle: `width:${pct}%`,
543
- meta: `${formatBytes(file.size)} · local`,
637
+ meta: formatBytes(file.size),
544
638
  });
545
639
  this.setProgress(pct);
546
640
  values.push({
@@ -565,11 +659,11 @@ export default class Upload {
565
659
  if (gen !== this._gen) return;
566
660
  const action = String(this.action || "").trim();
567
661
  if (!action) {
568
- this.failAll("Upload action URL required");
662
+ this.failAll(this.msg("actionRequired"));
569
663
  return;
570
664
  }
571
665
  // Sequential single-XHR for thin gate; multi-file appends all parts then one request.
572
- this.setOverall("uploading", "Uploading…", true, true);
666
+ this.setOverall("uploading", this.msg("uploading"), true, true);
573
667
  this.setProgress(0);
574
668
  for (let i = 0; i < this.queue.length; i++) {
575
669
  this.patchItem(i, {
@@ -610,7 +704,7 @@ export default class Upload {
610
704
  }
611
705
  const body = result.body;
612
706
  if (!(result.status >= 200 && result.status < 300) || !body || !body.ok) {
613
- this.failAll((body && body.error) || `Upload failed (${result.status || "network"})`);
707
+ this.failAll((body && body.error) || this.msg("failed"));
614
708
  return;
615
709
  }
616
710
  // Single-file shape { ok, id, bytes, name } or multi { ok, items: [...] }
@@ -648,7 +742,7 @@ export default class Upload {
648
742
  status: "done",
649
743
  showBar: true,
650
744
  barStyle: "width:100%",
651
- meta: `${formatBytes(values[i] && values[i].bytes)} · uploaded`,
745
+ meta: formatBytes(values[i] && values[i].bytes),
652
746
  });
653
747
  }
654
748
  this._values = values;
@@ -666,16 +760,16 @@ export default class Upload {
666
760
  .trim()
667
761
  .replace(/\/+$/, "");
668
762
  if (!base) {
669
- this.failAll("Upload action URL required");
763
+ this.failAll(this.msg("actionRequired"));
670
764
  return;
671
765
  }
672
766
  const file = this._files[0];
673
767
  if (!file) {
674
- this.failAll("No file for resumable upload");
768
+ this.failAll(this.msg("noFile"));
675
769
  return;
676
770
  }
677
771
  this._activeChunkSize = chunkSize;
678
- this.setOverall("uploading", this._uploadId ? "Resuming…" : "Uploading…", true, true);
772
+ this.setOverall("uploading", this._uploadId ? this.msg("resuming") : this.msg("uploading"), true, true);
679
773
  this.canResume = false;
680
774
  this.showActions = true;
681
775
  this.patchItem(0, {
@@ -707,7 +801,7 @@ export default class Upload {
707
801
  this.failAll(
708
802
  (initResult.body && initResult.body.error) ||
709
803
  initResult.error ||
710
- `Init failed (${initResult.status || "network"})`,
804
+ this.msg("initFailed"),
711
805
  );
712
806
  return;
713
807
  }
@@ -719,7 +813,7 @@ export default class Upload {
719
813
  this._received = [];
720
814
  this.syncChunkAttr();
721
815
  if (!this._uploadId) {
722
- this.failAll("Init returned no uploadId");
816
+ this.failAll(this.msg("uploadIdMissing"));
723
817
  return;
724
818
  }
725
819
  } else {
@@ -743,7 +837,7 @@ export default class Upload {
743
837
  this.failAll(
744
838
  (statusResult.body && statusResult.body.error) ||
745
839
  statusResult.error ||
746
- `Status failed (${statusResult.status || "network"})`,
840
+ this.msg("statusFailed"),
747
841
  );
748
842
  return;
749
843
  }
@@ -762,7 +856,7 @@ export default class Upload {
762
856
  this.setProgress(Math.round((receivedSet.size / total) * 100));
763
857
  this.patchItem(0, {
764
858
  barStyle: `width:${Math.round((receivedSet.size / total) * 100)}%`,
765
- meta: `${receivedSet.size}/${total} chunks`,
859
+ meta: `${receivedSet.size}/${total}`,
766
860
  });
767
861
  for (let index = 0; index < total; index++) {
768
862
  if (gen !== this._gen) return;
@@ -805,7 +899,7 @@ export default class Upload {
805
899
  this.failAll(
806
900
  (result.body && result.body.error) ||
807
901
  result.error ||
808
- `Chunk ${index} failed (${result.status || "network"})`,
902
+ this.msg("chunkFailed"),
809
903
  );
810
904
  return;
811
905
  }
@@ -819,7 +913,7 @@ export default class Upload {
819
913
  this.setProgress(pct);
820
914
  this.patchItem(0, {
821
915
  barStyle: `width:${pct}%`,
822
- meta: `${receivedSet.size}/${total} chunks`,
916
+ meta: `${receivedSet.size}/${total}`,
823
917
  });
824
918
  }
825
919
  if (gen !== this._gen) return;
@@ -843,7 +937,7 @@ export default class Upload {
843
937
  this.failAll(
844
938
  (completeResult.body && completeResult.body.error) ||
845
939
  completeResult.error ||
846
- `Complete failed (${completeResult.status || "network"})`,
940
+ this.msg("completeFailed"),
847
941
  );
848
942
  return;
849
943
  }
@@ -865,7 +959,7 @@ export default class Upload {
865
959
  status: "done",
866
960
  showBar: true,
867
961
  barStyle: "width:100%",
868
- meta: `${formatBytes(values[0].bytes)} · ${total} chunks`,
962
+ meta: `${formatBytes(values[0].bytes)} (${total})`,
869
963
  });
870
964
  this.resetSession();
871
965
  this._values = values;
@@ -877,10 +971,10 @@ export default class Upload {
877
971
  async runObject(gen) {
878
972
  if (gen !== this._gen) return;
879
973
  if (typeof this.onPresign !== "function") {
880
- this.failAll("onPresign callback required for object destination");
974
+ this.failAll(this.msg("presignRequired"));
881
975
  return;
882
976
  }
883
- this.setOverall("uploading", "Uploading…", true, true);
977
+ this.setOverall("uploading", this.msg("uploading"), true, true);
884
978
  const values = [];
885
979
  for (let i = 0; i < this._files.length; i++) {
886
980
  if (gen !== this._gen) return;
@@ -894,13 +988,13 @@ export default class Upload {
894
988
  try {
895
989
  signed = await this.onPresign(file);
896
990
  } catch (err) {
897
- this.failAll(err instanceof Error ? err.message : String(err || "presign failed"));
991
+ this.failAll(err instanceof Error ? err.message : String(err || this.msg("presignFailed")));
898
992
  return;
899
993
  }
900
994
  if (gen !== this._gen) return;
901
995
  const url = signed && signed.uploadUrl ? String(signed.uploadUrl) : "";
902
996
  if (!url) {
903
- this.failAll("presign returned no uploadUrl");
997
+ this.failAll(this.msg("presignUrl"));
904
998
  return;
905
999
  }
906
1000
  const xhr = new XMLHttpRequest();
@@ -927,7 +1021,7 @@ export default class Upload {
927
1021
  }
928
1022
  if (result.error || !(result.status >= 200 && result.status < 300)) {
929
1023
  this.failAll(
930
- result.error || `Object upload failed (${result.status || "network"})`,
1024
+ result.error || this.msg("objectFailed"),
931
1025
  );
932
1026
  return;
933
1027
  }
@@ -945,7 +1039,7 @@ export default class Upload {
945
1039
  status: "done",
946
1040
  showBar: true,
947
1041
  barStyle: "width:100%",
948
- meta: `${formatBytes(file.size)} · object`,
1042
+ meta: formatBytes(file.size),
949
1043
  });
950
1044
  }
951
1045
  this._values = values;
@@ -957,7 +1051,7 @@ export default class Upload {
957
1051
  finishOk(values) {
958
1052
  this.setProgress(100);
959
1053
  this.status = "done";
960
- this.statusText = "Upload complete";
1054
+ this.statusText = this.msg("complete");
961
1055
  this.showProgress = true;
962
1056
  this.canCancel = false;
963
1057
  this.canResume = false;
@@ -975,7 +1069,7 @@ export default class Upload {
975
1069
  const chunkNote = first.chunks ? ` chunks:${first.chunks}` : "";
976
1070
  this.resultText = `${prefix}:${first.id} (${first.bytes || 0} bytes)${head}${chunkNote}`;
977
1071
  } else {
978
- this.resultText = `${values.length} file(s) ready`;
1072
+ this.resultText = `${values.length} ${this.readyText}`.trim();
979
1073
  }
980
1074
  this.emitStatus();
981
1075
  this.emitValue(values);
@@ -986,8 +1080,9 @@ export default class Upload {
986
1080
  failAll(message) {
987
1081
  this.cancelActive(false);
988
1082
  this.resetSession();
1083
+ const text = message || this.msg("failed");
989
1084
  this.status = "error";
990
- this.statusText = message || "Upload failed";
1085
+ this.statusText = text;
991
1086
  this.showProgress = false;
992
1087
  this.canCancel = false;
993
1088
  this.canResume = false;
@@ -1006,7 +1101,7 @@ export default class Upload {
1006
1101
  }
1007
1102
  markCancelled() {
1008
1103
  this.status = "cancelled";
1009
- this.statusText = "Upload cancelled";
1104
+ this.statusText = this.msg("cancelled");
1010
1105
  this.showProgress = false;
1011
1106
  this.canCancel = false;
1012
1107
  this.canResume = false;
@@ -1020,7 +1115,7 @@ export default class Upload {
1020
1115
  }
1021
1116
  markPaused() {
1022
1117
  this.status = "paused";
1023
- this.statusText = "Upload paused — resume to continue";
1118
+ this.statusText = this.msg("paused");
1024
1119
  this.showProgress = true;
1025
1120
  this.canCancel = false;
1026
1121
  this.canResume = !!this._uploadId;
@@ -1031,7 +1126,7 @@ export default class Upload {
1031
1126
  this.syncChunkAttr();
1032
1127
  this.patchItem(0, {
1033
1128
  status: "paused",
1034
- meta: `${this._received.length}/${this._chunkTotal || 0} chunks`,
1129
+ meta: `${this._received.length}/${this._chunkTotal || 0}`,
1035
1130
  });
1036
1131
  this.emitStatus();
1037
1132
  this.emitValue([]);
@@ -1183,7 +1278,7 @@ function acceptError(file, accept) {
1183
1278
  return "";
1184
1279
  }
1185
1280
  }
1186
- return `File type not allowed (${raw})`;
1281
+ return "accept";
1187
1282
  }
1188
1283
  /**
1189
1284
  * @param {number} n
@@ -1228,7 +1323,7 @@ function xhrSend(xhr, method, url, body, onProgress, headers) {
1228
1323
  aborted: false,
1229
1324
  status: 0,
1230
1325
  body: null,
1231
- error: "Network upload failed",
1326
+ error: "",
1232
1327
  });
1233
1328
  xhr.onload = () => {
1234
1329
  let parsed = null;
@@ -1274,7 +1369,7 @@ function xhrSendJson(xhr, method, url, payload) {
1274
1369
  aborted: false,
1275
1370
  status: 0,
1276
1371
  body: null,
1277
- error: "Network upload failed",
1372
+ error: "",
1278
1373
  });
1279
1374
  xhr.onload = () => {
1280
1375
  let parsed = null;