@spark-ui/components 11.5.1 → 11.6.1

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.
@@ -20,15 +20,47 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
20
20
  // src/file-upload/index.ts
21
21
  var file_upload_exports = {};
22
22
  __export(file_upload_exports, {
23
+ FILE_UPLOAD_ERRORS: () => FILE_UPLOAD_ERRORS,
23
24
  FileUpload: () => FileUpload2
24
25
  });
25
26
  module.exports = __toCommonJS(file_upload_exports);
26
27
 
27
28
  // src/file-upload/FileUpload.tsx
28
29
  var import_form_field = require("@spark-ui/components/form-field");
30
+ var import_react3 = require("react");
31
+
32
+ // src/file-upload/useFileUploadState.tsx
29
33
  var import_use_combined_state = require("@spark-ui/hooks/use-combined-state");
30
34
  var import_react2 = require("react");
31
35
 
36
+ // src/file-upload/constants.ts
37
+ var FILE_UPLOAD_ERRORS = {
38
+ /**
39
+ * Exceeds the maxFiles limit
40
+ */
41
+ TOO_MANY_FILES: "TOO_MANY_FILES",
42
+ /**
43
+ * File type not in the accept list
44
+ */
45
+ FILE_INVALID_TYPE: "FILE_INVALID_TYPE",
46
+ /**
47
+ * File size exceeds maxFileSize
48
+ */
49
+ FILE_TOO_LARGE: "FILE_TOO_LARGE",
50
+ /**
51
+ * File size below minFileSize
52
+ */
53
+ FILE_TOO_SMALL: "FILE_TOO_SMALL",
54
+ /**
55
+ * Generic validation failure
56
+ */
57
+ FILE_INVALID: "FILE_INVALID",
58
+ /**
59
+ * Duplicate file detected
60
+ */
61
+ FILE_EXISTS: "FILE_EXISTS"
62
+ };
63
+
32
64
  // src/file-upload/utils.ts
33
65
  var import_CvOutline = require("@spark-ui/icons/CvOutline");
34
66
  var import_FilePdfOutline = require("@spark-ui/icons/FilePdfOutline");
@@ -127,14 +159,32 @@ function getFileIcon(file) {
127
159
  }
128
160
  return (0, import_react.createElement)(import_CvOutline.CvOutline);
129
161
  }
162
+ function isFocusable(element) {
163
+ if (!element) {
164
+ return false;
165
+ }
166
+ const tabIndex = element.tabIndex;
167
+ if (tabIndex >= 0) {
168
+ return true;
169
+ }
170
+ const isContentEditable = String(element.contentEditable) === "true";
171
+ const naturallyFocusable = element instanceof HTMLInputElement || element instanceof HTMLButtonElement || element instanceof HTMLSelectElement || element instanceof HTMLTextAreaElement || element instanceof HTMLAnchorElement && Boolean(element.href) || isContentEditable;
172
+ return naturallyFocusable;
173
+ }
174
+ function findFocusableElement(candidates, inputRef) {
175
+ for (const candidate of candidates) {
176
+ if (isFocusable(candidate)) {
177
+ return candidate;
178
+ }
179
+ }
180
+ if (inputRef.current) {
181
+ return inputRef.current;
182
+ }
183
+ return null;
184
+ }
130
185
 
131
- // src/file-upload/FileUpload.tsx
132
- var import_jsx_runtime = require("react/jsx-runtime");
133
- var FileUploadContext = (0, import_react2.createContext)(null);
134
- var ID_PREFIX = ":file-upload";
135
- var FileUpload = ({
136
- asChild: _asChild = false,
137
- children,
186
+ // src/file-upload/useFileUploadState.tsx
187
+ function useFileUploadState({
138
188
  defaultValue = [],
139
189
  value: controlledValue,
140
190
  onFileAccept,
@@ -145,32 +195,12 @@ var FileUpload = ({
145
195
  maxFiles,
146
196
  maxFileSize,
147
197
  minFileSize,
148
- disabled: disabledProp = false,
149
- readOnly: readOnlyProp = false,
198
+ disabled = false,
199
+ readOnly = false,
150
200
  locale
151
- }) => {
152
- const field = (0, import_form_field.useFormFieldControl)();
153
- const {
154
- id: fieldId,
155
- name: fieldName,
156
- isInvalid,
157
- isRequired,
158
- description,
159
- disabled: fieldDisabled,
160
- readOnly: fieldReadOnly,
161
- labelId
162
- } = field;
201
+ }) {
163
202
  const defaultLocale = locale || (typeof navigator !== "undefined" && navigator.language ? navigator.language : "en");
164
- const internalId = (0, import_react2.useId)();
165
- const inputId = fieldId || `${ID_PREFIX}-${internalId}`;
166
- const inputName = fieldName;
167
- const inputRef = (0, import_react2.useRef)(null);
168
- const triggerRef = (0, import_react2.useRef)(null);
169
- const dropzoneRef = (0, import_react2.useRef)(null);
170
- const deleteButtonRefs = (0, import_react2.useRef)([]);
171
- const disabled = fieldDisabled ?? disabledProp;
172
- const readOnly = fieldReadOnly ?? readOnlyProp;
173
- const [filesState, setFilesState, ,] = (0, import_use_combined_state.useCombinedState)(controlledValue, defaultValue);
203
+ const [filesState, setFilesState] = (0, import_use_combined_state.useCombinedState)(controlledValue, defaultValue);
174
204
  const files = filesState ?? [];
175
205
  const setFiles = setFilesState;
176
206
  const [rejectedFiles, setRejectedFiles] = (0, import_react2.useState)([]);
@@ -202,20 +232,17 @@ var FileUpload = ({
202
232
  };
203
233
  setFiles((prev) => {
204
234
  const currentFiles = prev ?? [];
205
- if (maxFiles !== void 0) {
206
- const currentCount = currentFiles.length;
207
- const remainingSlots = maxFiles - currentCount;
208
- if (remainingSlots <= 0) {
209
- newFiles.forEach((file) => {
210
- addRejectedFile(file, "TOO_MANY_FILES");
211
- });
212
- }
235
+ const remainingSlots = maxFiles !== void 0 ? maxFiles - currentFiles.length : void 0;
236
+ if (remainingSlots !== void 0 && remainingSlots <= 0) {
237
+ newFiles.forEach((file) => {
238
+ addRejectedFile(file, FILE_UPLOAD_ERRORS.TOO_MANY_FILES);
239
+ });
213
240
  }
214
241
  let filteredFiles = newFiles;
215
242
  if (accept) {
216
243
  const rejectedByAccept = newFiles.filter((file) => !validateFileAccept(file, accept));
217
244
  rejectedByAccept.forEach((file) => {
218
- addRejectedFile(file, "FILE_INVALID_TYPE");
245
+ addRejectedFile(file, FILE_UPLOAD_ERRORS.FILE_INVALID_TYPE);
219
246
  });
220
247
  filteredFiles = newFiles.filter((file) => validateFileAccept(file, accept));
221
248
  }
@@ -225,11 +252,11 @@ var FileUpload = ({
225
252
  const validation = validateFileSize(file, minFileSize, maxFileSize, defaultLocale);
226
253
  if (!validation.valid) {
227
254
  if (maxFileSize !== void 0 && file.size > maxFileSize) {
228
- addRejectedFile(file, "FILE_TOO_LARGE");
255
+ addRejectedFile(file, FILE_UPLOAD_ERRORS.FILE_TOO_LARGE);
229
256
  } else if (minFileSize !== void 0 && file.size < minFileSize) {
230
- addRejectedFile(file, "FILE_TOO_SMALL");
257
+ addRejectedFile(file, FILE_UPLOAD_ERRORS.FILE_TOO_SMALL);
231
258
  } else {
232
- addRejectedFile(file, "FILE_INVALID");
259
+ addRejectedFile(file, FILE_UPLOAD_ERRORS.FILE_INVALID);
233
260
  }
234
261
  return false;
235
262
  }
@@ -237,35 +264,27 @@ var FileUpload = ({
237
264
  });
238
265
  }
239
266
  const seenFiles = /* @__PURE__ */ new Map();
240
- const duplicateFiles = [];
241
267
  const uniqueFiles = validSizeFiles.filter((file) => {
242
268
  const fileKey = `${file.name}-${file.size}`;
243
269
  const existsInPrev = fileExists(file, currentFiles);
244
270
  if (existsInPrev) {
245
- duplicateFiles.push(file);
246
- addRejectedFile(file, "FILE_EXISTS");
271
+ addRejectedFile(file, FILE_UPLOAD_ERRORS.FILE_EXISTS);
247
272
  return false;
248
273
  }
249
274
  if (seenFiles.has(fileKey)) {
250
- duplicateFiles.push(file);
251
- addRejectedFile(file, "FILE_EXISTS");
275
+ addRejectedFile(file, FILE_UPLOAD_ERRORS.FILE_EXISTS);
252
276
  return false;
253
277
  }
254
278
  seenFiles.set(fileKey, file);
255
279
  return true;
256
280
  });
257
281
  let filesToAdd = multiple ? uniqueFiles : uniqueFiles.slice(0, 1);
258
- if (maxFiles !== void 0) {
259
- const currentCount = currentFiles.length;
260
- const remainingSlots = maxFiles - currentCount;
282
+ if (remainingSlots !== void 0) {
261
283
  if (remainingSlots <= 0) {
262
- filesToAdd.forEach((file) => {
263
- addRejectedFile(file, "TOO_MANY_FILES");
264
- });
265
284
  filesToAdd = [];
266
285
  } else if (filesToAdd.length > remainingSlots) {
267
286
  filesToAdd.forEach((file) => {
268
- addRejectedFile(file, "TOO_MANY_FILES");
287
+ addRejectedFile(file, FILE_UPLOAD_ERRORS.TOO_MANY_FILES);
269
288
  });
270
289
  filesToAdd = [];
271
290
  }
@@ -298,7 +317,7 @@ var FileUpload = ({
298
317
  let updatedRejectedFiles = rejectedFiles;
299
318
  if (maxFiles !== void 0 && updated.length < maxFiles) {
300
319
  updatedRejectedFiles = rejectedFiles.filter(
301
- (rejected) => !rejected.errors.includes("TOO_MANY_FILES")
320
+ (rejected) => !rejected.errors.includes(FILE_UPLOAD_ERRORS.TOO_MANY_FILES)
302
321
  );
303
322
  setRejectedFiles(updatedRejectedFiles);
304
323
  }
@@ -317,7 +336,6 @@ var FileUpload = ({
317
336
  }
318
337
  setFiles([]);
319
338
  setRejectedFiles([]);
320
- deleteButtonRefs.current = [];
321
339
  if (onFileChange) {
322
340
  onFileChange({
323
341
  acceptedFiles: [],
@@ -335,6 +353,82 @@ var FileUpload = ({
335
353
  setRejectedFiles([]);
336
354
  };
337
355
  const maxFilesReached = maxFiles !== void 0 && files.length >= maxFiles;
356
+ return {
357
+ files,
358
+ rejectedFiles,
359
+ addFiles,
360
+ removeFile,
361
+ removeRejectedFile,
362
+ clearFiles,
363
+ clearRejectedFiles,
364
+ maxFilesReached
365
+ };
366
+ }
367
+
368
+ // src/file-upload/FileUpload.tsx
369
+ var import_jsx_runtime = require("react/jsx-runtime");
370
+ var FileUploadContext = (0, import_react3.createContext)(null);
371
+ var ID_PREFIX = ":file-upload";
372
+ var FileUpload = ({
373
+ asChild: _asChild = false,
374
+ children,
375
+ defaultValue = [],
376
+ value: controlledValue,
377
+ onFileAccept,
378
+ onFileReject,
379
+ onFileChange,
380
+ multiple = true,
381
+ accept,
382
+ maxFiles,
383
+ maxFileSize,
384
+ minFileSize,
385
+ disabled: disabledProp = false,
386
+ readOnly: readOnlyProp = false,
387
+ locale
388
+ }) => {
389
+ const field = (0, import_form_field.useFormFieldControl)();
390
+ const internalId = (0, import_react3.useId)();
391
+ const inputId = field.id || `${ID_PREFIX}-${internalId}`;
392
+ const inputName = field.name;
393
+ const inputRef = (0, import_react3.useRef)(null);
394
+ const triggerRef = (0, import_react3.useRef)(null);
395
+ const dropzoneRef = (0, import_react3.useRef)(null);
396
+ const deleteButtonRefs = (0, import_react3.useRef)([]);
397
+ const rejectedFileDeleteButtonRefs = (0, import_react3.useRef)([]);
398
+ const disabled = field.disabled ?? disabledProp;
399
+ const readOnly = field.readOnly ?? readOnlyProp;
400
+ const {
401
+ files,
402
+ rejectedFiles,
403
+ addFiles,
404
+ removeFile,
405
+ removeRejectedFile,
406
+ clearFiles: clearFilesFromHook,
407
+ clearRejectedFiles,
408
+ maxFilesReached
409
+ } = useFileUploadState({
410
+ defaultValue,
411
+ value: controlledValue,
412
+ onFileAccept,
413
+ onFileReject,
414
+ onFileChange,
415
+ multiple,
416
+ accept,
417
+ maxFiles,
418
+ maxFileSize,
419
+ minFileSize,
420
+ disabled,
421
+ readOnly,
422
+ locale
423
+ });
424
+ const clearFiles = () => {
425
+ clearFilesFromHook();
426
+ deleteButtonRefs.current = [];
427
+ };
428
+ const clearRejectedFilesWithRefs = () => {
429
+ clearRejectedFiles();
430
+ rejectedFileDeleteButtonRefs.current = [];
431
+ };
338
432
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
339
433
  FileUploadContext.Provider,
340
434
  {
@@ -346,19 +440,20 @@ var FileUpload = ({
346
440
  removeFile,
347
441
  removeRejectedFile,
348
442
  clearFiles,
349
- clearRejectedFiles,
443
+ clearRejectedFiles: clearRejectedFilesWithRefs,
350
444
  triggerRef,
351
445
  dropzoneRef,
352
446
  deleteButtonRefs,
447
+ rejectedFileDeleteButtonRefs,
353
448
  multiple,
354
449
  maxFiles,
355
450
  maxFilesReached,
356
451
  disabled,
357
452
  readOnly,
358
- locale: defaultLocale,
359
- description,
360
- isInvalid,
361
- isRequired
453
+ locale: locale || (typeof navigator !== "undefined" && navigator.language ? navigator.language : "en"),
454
+ description: field.description,
455
+ isInvalid: field.isInvalid,
456
+ isRequired: field.isRequired
362
457
  },
363
458
  children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "relative", children: [
364
459
  children,
@@ -374,10 +469,10 @@ var FileUpload = ({
374
469
  accept,
375
470
  disabled,
376
471
  readOnly: readOnly && !disabled,
377
- required: isRequired,
378
- "aria-invalid": isInvalid,
379
- "aria-describedby": description,
380
- "aria-label": !labelId ? "Upload files test" : void 0,
472
+ required: field.isRequired,
473
+ "aria-invalid": field.isInvalid,
474
+ "aria-describedby": field.description,
475
+ "aria-label": !field.labelId ? "Upload files" : void 0,
381
476
  className: "sr-only",
382
477
  onChange: (e) => {
383
478
  if (e.target.files && !disabled && !readOnly) {
@@ -396,7 +491,7 @@ var FileUpload = ({
396
491
  };
397
492
  FileUpload.displayName = "FileUpload";
398
493
  var useFileUploadContext = () => {
399
- const context = (0, import_react2.useContext)(FileUploadContext);
494
+ const context = (0, import_react3.useContext)(FileUploadContext);
400
495
  if (!context) {
401
496
  throw Error("useFileUploadContext must be used within a FileUpload provider");
402
497
  }
@@ -405,13 +500,14 @@ var useFileUploadContext = () => {
405
500
 
406
501
  // src/file-upload/FileUploadAcceptedFile.tsx
407
502
  var import_class_variance_authority10 = require("class-variance-authority");
503
+ var import_react11 = require("react");
408
504
 
409
505
  // src/icon/Icon.tsx
410
- var import_react4 = require("react");
506
+ var import_react5 = require("react");
411
507
 
412
508
  // src/slot/Slot.tsx
413
509
  var import_radix_ui = require("radix-ui");
414
- var import_react3 = require("react");
510
+ var import_react4 = require("react");
415
511
  var import_jsx_runtime2 = require("react/jsx-runtime");
416
512
  var Slottable = import_radix_ui.Slot.Slottable;
417
513
  var Slot = ({ ref, ...props }) => {
@@ -419,7 +515,7 @@ var Slot = ({ ref, ...props }) => {
419
515
  };
420
516
  var wrapPolymorphicSlot = (asChild, children, callback) => {
421
517
  if (!asChild) return callback(children);
422
- return (0, import_react3.isValidElement)(children) ? (0, import_react3.cloneElement)(
518
+ return (0, import_react4.isValidElement)(children) ? (0, import_react4.cloneElement)(
423
519
  children,
424
520
  void 0,
425
521
  callback(children.props.children)
@@ -497,9 +593,9 @@ var Icon = ({
497
593
  children,
498
594
  ...others
499
595
  }) => {
500
- const child = import_react4.Children.only(children);
596
+ const child = import_react5.Children.only(children);
501
597
  return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_jsx_runtime4.Fragment, { children: [
502
- (0, import_react4.cloneElement)(child, {
598
+ (0, import_react5.cloneElement)(child, {
503
599
  className: iconStyles({ className, size, intent }),
504
600
  "data-spark-component": "icon",
505
601
  "aria-hidden": "true",
@@ -514,7 +610,7 @@ Icon.displayName = "Icon";
514
610
  // src/progress/Progress.tsx
515
611
  var import_class_variance_authority4 = require("class-variance-authority");
516
612
  var import_radix_ui3 = require("radix-ui");
517
- var import_react6 = require("react");
613
+ var import_react7 = require("react");
518
614
 
519
615
  // src/progress/ProgressBar.styles.ts
520
616
  var import_class_variance_authority2 = require("class-variance-authority");
@@ -531,11 +627,11 @@ var progressBarStyles = (0, import_class_variance_authority2.cva)(
531
627
  );
532
628
 
533
629
  // src/progress/ProgressContext.tsx
534
- var import_react5 = require("react");
535
- var ProgressContext = (0, import_react5.createContext)(null);
630
+ var import_react6 = require("react");
631
+ var ProgressContext = (0, import_react6.createContext)(null);
536
632
  var ID_PREFIX2 = ":progress";
537
633
  var useProgress = () => {
538
- const context = (0, import_react5.useContext)(ProgressContext);
634
+ const context = (0, import_react6.useContext)(ProgressContext);
539
635
  if (!context) {
540
636
  throw new Error("useProgress must be used within a Progress provider");
541
637
  }
@@ -586,10 +682,17 @@ var ProgressIndicator = ({
586
682
  className,
587
683
  style,
588
684
  ref,
685
+ onTransitionEnd,
589
686
  ...others
590
687
  }) => {
591
- const { value, max, intent, shape, isIndeterminate } = useProgress();
688
+ const { value, max, intent, shape, isIndeterminate, onComplete } = useProgress();
592
689
  const x = (max - value) / max * 100;
690
+ const handleTransitionEnd = (event) => {
691
+ onTransitionEnd?.(event);
692
+ if (value >= max && onComplete && !isIndeterminate) {
693
+ onComplete();
694
+ }
695
+ };
593
696
  return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
594
697
  import_radix_ui2.Progress.ProgressIndicator,
595
698
  {
@@ -597,6 +700,7 @@ var ProgressIndicator = ({
597
700
  className: progressIndicatorStyles({ className, intent, shape, isIndeterminate }),
598
701
  style: { ...style, ...!isIndeterminate && { transform: `translateX(-${x}%)` } },
599
702
  ref,
703
+ onTransitionEnd: handleTransitionEnd,
600
704
  ...others
601
705
  }
602
706
  );
@@ -634,14 +738,23 @@ var Progress = ({
634
738
  shape = "square",
635
739
  intent = "basic",
636
740
  isIndeterminate = false,
741
+ onComplete,
637
742
  children = /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(ProgressBar, {}),
638
743
  ref,
639
744
  ...others
640
745
  }) => {
641
- const [labelId, setLabelId] = (0, import_react6.useState)();
642
- const value = (0, import_react6.useMemo)(() => {
643
- return { value: valueProp ?? 0, max, intent, shape, isIndeterminate, onLabelId: setLabelId };
644
- }, [max, valueProp, intent, shape, isIndeterminate, setLabelId]);
746
+ const [labelId, setLabelId] = (0, import_react7.useState)();
747
+ const value = (0, import_react7.useMemo)(() => {
748
+ return {
749
+ value: valueProp ?? 0,
750
+ max,
751
+ intent,
752
+ shape,
753
+ isIndeterminate,
754
+ onLabelId: setLabelId,
755
+ onComplete
756
+ };
757
+ }, [max, valueProp, intent, shape, isIndeterminate, setLabelId, onComplete]);
645
758
  return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(ProgressContext.Provider, { "data-spark-component": "progress", value, children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
646
759
  import_radix_ui3.Progress.Progress,
647
760
  {
@@ -661,7 +774,7 @@ Progress.displayName = "Progress";
661
774
 
662
775
  // src/progress/ProgressLabel.tsx
663
776
  var import_use_merge_refs = require("@spark-ui/hooks/use-merge-refs");
664
- var import_react7 = require("react");
777
+ var import_react8 = require("react");
665
778
  var import_jsx_runtime8 = require("react/jsx-runtime");
666
779
  var ProgressLabel = ({
667
780
  id: idProp,
@@ -669,10 +782,10 @@ var ProgressLabel = ({
669
782
  ref: forwardedRef,
670
783
  ...others
671
784
  }) => {
672
- const internalID = `${ID_PREFIX2}-label-${(0, import_react7.useId)()}`;
785
+ const internalID = `${ID_PREFIX2}-label-${(0, import_react8.useId)()}`;
673
786
  const id = idProp || internalID;
674
787
  const { onLabelId } = useProgress();
675
- const rootRef = (0, import_react7.useCallback)(
788
+ const rootRef = (0, import_react8.useCallback)(
676
789
  (el) => {
677
790
  onLabelId(el ? id : void 0);
678
791
  },
@@ -707,11 +820,11 @@ ProgressLabel.displayName = "Progress.Label";
707
820
  // src/file-upload/FileUploadItemDeleteTrigger.tsx
708
821
  var import_Close = require("@spark-ui/icons/Close");
709
822
  var import_class_variance_authority9 = require("class-variance-authority");
710
- var import_react9 = require("react");
823
+ var import_react10 = require("react");
711
824
 
712
825
  // src/button/Button.tsx
713
826
  var import_class_variance_authority7 = require("class-variance-authority");
714
- var import_react8 = require("react");
827
+ var import_react9 = require("react");
715
828
 
716
829
  // src/spinner/Spinner.styles.tsx
717
830
  var import_internal_utils2 = require("@spark-ui/internal-utils");
@@ -1516,7 +1629,7 @@ var Button = ({
1516
1629
  }) => {
1517
1630
  const Component = asChild ? Slot : "button";
1518
1631
  const shouldNotInteract = !!disabled || isLoading;
1519
- const disabledEventHandlers = (0, import_react8.useMemo)(() => {
1632
+ const disabledEventHandlers = (0, import_react9.useMemo)(() => {
1520
1633
  const result = {};
1521
1634
  if (shouldNotInteract) {
1522
1635
  blockedEventHandlers.forEach((eventHandler) => result[eventHandler] = void 0);
@@ -1622,15 +1735,24 @@ var ItemDeleteTrigger = ({
1622
1735
  onClick,
1623
1736
  ...props
1624
1737
  }) => {
1625
- const { removeFile, triggerRef, dropzoneRef, deleteButtonRefs, disabled, readOnly, files } = useFileUploadContext();
1626
- const buttonRef = (0, import_react9.useRef)(null);
1738
+ const {
1739
+ removeFile,
1740
+ triggerRef,
1741
+ dropzoneRef,
1742
+ deleteButtonRefs,
1743
+ inputRef,
1744
+ disabled,
1745
+ readOnly,
1746
+ files
1747
+ } = useFileUploadContext();
1748
+ const buttonRef = (0, import_react10.useRef)(null);
1627
1749
  const fileIndex = files.findIndex((f) => f.name === file.name && f.size === file.size);
1628
1750
  const handleClick = (e) => {
1629
1751
  if (disabled || readOnly) {
1630
1752
  return;
1631
1753
  }
1632
1754
  removeFile(fileIndex);
1633
- setTimeout(() => {
1755
+ requestAnimationFrame(() => {
1634
1756
  const remainingButtons = deleteButtonRefs.current.filter(Boolean);
1635
1757
  if (remainingButtons.length > 0) {
1636
1758
  const targetIndex = Math.min(fileIndex, remainingButtons.length - 1);
@@ -1639,12 +1761,15 @@ var ItemDeleteTrigger = ({
1639
1761
  nextButton.focus();
1640
1762
  }
1641
1763
  } else {
1642
- const focusTarget = triggerRef.current || dropzoneRef.current;
1764
+ const focusTarget = findFocusableElement(
1765
+ [triggerRef.current, dropzoneRef.current],
1766
+ inputRef
1767
+ );
1643
1768
  if (focusTarget) {
1644
1769
  focusTarget.focus();
1645
1770
  }
1646
1771
  }
1647
- }, 0);
1772
+ });
1648
1773
  onClick?.(e);
1649
1774
  };
1650
1775
  const setRef = (node) => {
@@ -1681,7 +1806,6 @@ ItemDeleteTrigger.displayName = "FileUpload.ItemDeleteTrigger";
1681
1806
  // src/file-upload/FileUploadAcceptedFile.tsx
1682
1807
  var import_jsx_runtime13 = require("react/jsx-runtime");
1683
1808
  var AcceptedFile = ({
1684
- asChild: _asChild = false,
1685
1809
  className,
1686
1810
  file,
1687
1811
  uploadProgress,
@@ -1690,6 +1814,17 @@ var AcceptedFile = ({
1690
1814
  ...props
1691
1815
  }) => {
1692
1816
  const { locale } = useFileUploadContext();
1817
+ const [showProgress, setShowProgress] = (0, import_react11.useState)(uploadProgress !== void 0);
1818
+ (0, import_react11.useEffect)(() => {
1819
+ if (uploadProgress !== void 0) {
1820
+ setShowProgress(true);
1821
+ } else {
1822
+ setShowProgress(false);
1823
+ }
1824
+ }, [uploadProgress]);
1825
+ const handleProgressComplete = (0, import_react11.useCallback)(() => {
1826
+ setShowProgress(false);
1827
+ }, []);
1693
1828
  return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(
1694
1829
  "li",
1695
1830
  {
@@ -1702,13 +1837,19 @@ var AcceptedFile = ({
1702
1837
  ),
1703
1838
  ...props,
1704
1839
  children: [
1705
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "size-sz-40 bg-support-container flex items-center justify-center rounded-md", children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(Icon, { size: "md", children: getFileIcon(file) }) }),
1706
- /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "min-w-0 flex-1", children: [
1707
- /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "gap-md flex flex-row items-center justify-between", children: [
1708
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("p", { className: "text-body-2 truncate font-medium", children: file.name }),
1709
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("p", { className: "text-caption opacity-dim-1", children: formatFileSize(file.size, locale) })
1710
- ] }),
1711
- uploadProgress !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "mt-md", children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(Progress2, { value: uploadProgress, max: 100, "aria-label": progressAriaLabel }) })
1840
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "size-sz-36 bg-support-container flex items-center justify-center rounded-md", children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(Icon, { size: "md", children: getFileIcon(file) }) }),
1841
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "gap-md relative flex min-w-0 flex-1 flex-row items-center justify-between self-stretch", children: [
1842
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("p", { className: "text-body-2 truncate font-medium", children: file.name }),
1843
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("p", { className: "text-caption opacity-dim-1", children: formatFileSize(file.size, locale) }),
1844
+ showProgress && uploadProgress !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "absolute bottom-0 left-0 w-full", children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
1845
+ Progress2,
1846
+ {
1847
+ value: uploadProgress,
1848
+ max: 100,
1849
+ "aria-label": progressAriaLabel,
1850
+ onComplete: handleProgressComplete
1851
+ }
1852
+ ) })
1712
1853
  ] }),
1713
1854
  /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(ItemDeleteTrigger, { "aria-label": deleteButtonAriaLabel, file })
1714
1855
  ]
@@ -1732,17 +1873,17 @@ Context.displayName = "FileUpload.Context";
1732
1873
 
1733
1874
  // src/file-upload/FileUploadDropzone.tsx
1734
1875
  var import_class_variance_authority11 = require("class-variance-authority");
1735
- var import_react10 = require("react");
1876
+ var import_react12 = require("react");
1736
1877
  var import_jsx_runtime15 = require("react/jsx-runtime");
1737
- var DropzoneContext = (0, import_react10.createContext)(false);
1738
- var useDropzoneContext = () => (0, import_react10.useContext)(DropzoneContext);
1878
+ var DropzoneContext = (0, import_react12.createContext)(false);
1879
+ var useDropzoneContext = () => (0, import_react12.useContext)(DropzoneContext);
1739
1880
  function Dropzone({
1740
1881
  children,
1741
1882
  className,
1742
1883
  unstyled = false
1743
1884
  }) {
1744
1885
  const ctx = useFileUploadContext();
1745
- const dropzoneRef = (0, import_react10.useRef)(null);
1886
+ const dropzoneRef = (0, import_react12.useRef)(null);
1746
1887
  if (!ctx) throw new Error("FileUploadDropzone must be used inside <FileUpload>");
1747
1888
  const handleDrop = (e) => {
1748
1889
  e.preventDefault();
@@ -1824,20 +1965,19 @@ Dropzone.displayName = "FileUploadDropzone";
1824
1965
 
1825
1966
  // src/file-upload/FileUploadPreviewImage.tsx
1826
1967
  var import_class_variance_authority12 = require("class-variance-authority");
1827
- var import_react11 = require("react");
1968
+ var import_react13 = require("react");
1828
1969
  var import_jsx_runtime16 = require("react/jsx-runtime");
1829
1970
  var PreviewImage = ({
1830
- asChild: _asChild = false,
1831
1971
  className,
1832
1972
  file,
1833
1973
  fallback = "\u{1F4C4}",
1834
1974
  ...props
1835
1975
  }) => {
1836
- const [imageError, setImageError] = (0, import_react11.useState)(false);
1837
- const [imageLoaded, setImageLoaded] = (0, import_react11.useState)(false);
1976
+ const [imageError, setImageError] = (0, import_react13.useState)(false);
1977
+ const [imageLoaded, setImageLoaded] = (0, import_react13.useState)(false);
1838
1978
  const isImage = file.type.startsWith("image/");
1839
1979
  const imageUrl = isImage ? URL.createObjectURL(file) : null;
1840
- (0, import_react11.useEffect)(() => {
1980
+ (0, import_react13.useEffect)(() => {
1841
1981
  return () => {
1842
1982
  if (imageUrl) {
1843
1983
  URL.revokeObjectURL(imageUrl);
@@ -1889,7 +2029,7 @@ var import_class_variance_authority14 = require("class-variance-authority");
1889
2029
  // src/file-upload/FileUploadRejectedFileDeleteTrigger.tsx
1890
2030
  var import_Close2 = require("@spark-ui/icons/Close");
1891
2031
  var import_class_variance_authority13 = require("class-variance-authority");
1892
- var import_react12 = require("react");
2032
+ var import_react14 = require("react");
1893
2033
  var import_jsx_runtime17 = require("react/jsx-runtime");
1894
2034
  var RejectedFileDeleteTrigger = ({
1895
2035
  className,
@@ -1897,8 +2037,17 @@ var RejectedFileDeleteTrigger = ({
1897
2037
  onClick,
1898
2038
  ...props
1899
2039
  }) => {
1900
- const { removeRejectedFile, triggerRef, dropzoneRef, disabled, readOnly, rejectedFiles } = useFileUploadContext();
1901
- const buttonRef = (0, import_react12.useRef)(null);
2040
+ const {
2041
+ removeRejectedFile,
2042
+ triggerRef,
2043
+ dropzoneRef,
2044
+ rejectedFileDeleteButtonRefs,
2045
+ inputRef,
2046
+ disabled,
2047
+ readOnly,
2048
+ rejectedFiles
2049
+ } = useFileUploadContext();
2050
+ const buttonRef = (0, import_react14.useRef)(null);
1902
2051
  const rejectedFileIndex = rejectedFiles.findIndex(
1903
2052
  (rf) => rf.file.name === rejectedFile.file.name && rf.file.size === rejectedFile.file.size
1904
2053
  );
@@ -1907,18 +2056,43 @@ var RejectedFileDeleteTrigger = ({
1907
2056
  return;
1908
2057
  }
1909
2058
  removeRejectedFile(rejectedFileIndex);
1910
- setTimeout(() => {
1911
- const focusTarget = triggerRef.current || dropzoneRef.current;
1912
- if (focusTarget) {
1913
- focusTarget.focus();
2059
+ requestAnimationFrame(() => {
2060
+ const remainingButtons = rejectedFileDeleteButtonRefs.current.filter(Boolean);
2061
+ if (remainingButtons.length > 0) {
2062
+ const targetIndex = Math.min(rejectedFileIndex, remainingButtons.length - 1);
2063
+ const nextButton = remainingButtons[targetIndex];
2064
+ if (nextButton) {
2065
+ nextButton.focus();
2066
+ }
2067
+ } else {
2068
+ const focusTarget = findFocusableElement(
2069
+ [triggerRef.current, dropzoneRef.current],
2070
+ inputRef
2071
+ );
2072
+ if (focusTarget) {
2073
+ focusTarget.focus();
2074
+ }
1914
2075
  }
1915
- }, 0);
2076
+ });
1916
2077
  onClick?.(e);
1917
2078
  };
2079
+ const setRef = (node) => {
2080
+ buttonRef.current = node;
2081
+ if (node) {
2082
+ while (rejectedFileDeleteButtonRefs.current.length <= rejectedFileIndex) {
2083
+ rejectedFileDeleteButtonRefs.current.push(null);
2084
+ }
2085
+ rejectedFileDeleteButtonRefs.current[rejectedFileIndex] = node;
2086
+ } else {
2087
+ if (rejectedFileDeleteButtonRefs.current[rejectedFileIndex]) {
2088
+ rejectedFileDeleteButtonRefs.current[rejectedFileIndex] = null;
2089
+ }
2090
+ }
2091
+ };
1918
2092
  return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
1919
2093
  IconButton,
1920
2094
  {
1921
- ref: buttonRef,
2095
+ ref: setRef,
1922
2096
  "data-spark-component": "file-upload-rejected-file-delete-trigger",
1923
2097
  className: (0, import_class_variance_authority13.cx)(className),
1924
2098
  onClick: handleClick,
@@ -1936,7 +2110,6 @@ RejectedFileDeleteTrigger.displayName = "FileUpload.RejectedFileDeleteTrigger";
1936
2110
  // src/file-upload/FileUploadRejectedFile.tsx
1937
2111
  var import_jsx_runtime18 = require("react/jsx-runtime");
1938
2112
  var RejectedFile = ({
1939
- asChild: _asChild = false,
1940
2113
  className,
1941
2114
  rejectedFile,
1942
2115
  renderError,
@@ -1957,7 +2130,7 @@ var RejectedFile = ({
1957
2130
  ),
1958
2131
  ...props,
1959
2132
  children: [
1960
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { className: "size-sz-40 bg-error-container flex items-center justify-center rounded-md", children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(Icon, { size: "md", className: "text-error", children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_WarningOutline.WarningOutline, {}) }) }),
2133
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { className: "size-sz-36 bg-error-container flex items-center justify-center rounded-md", children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(Icon, { size: "md", className: "text-error", children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_WarningOutline.WarningOutline, {}) }) }),
1961
2134
  /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { className: "min-w-0 flex-1", children: /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { className: "gap-md flex flex-col", children: [
1962
2135
  /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { className: "gap-md flex flex-row items-center justify-between", children: [
1963
2136
  /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("p", { className: "text-body-2 truncate font-medium", children: rejectedFile.file.name }),
@@ -2076,6 +2249,7 @@ RejectedFile.displayName = "FileUpload.RejectedFile";
2076
2249
  RejectedFileDeleteTrigger.displayName = "FileUpload.RejectedFileDeleteTrigger";
2077
2250
  // Annotate the CommonJS export names for ESM import in node:
2078
2251
  0 && (module.exports = {
2252
+ FILE_UPLOAD_ERRORS,
2079
2253
  FileUpload
2080
2254
  });
2081
2255
  //# sourceMappingURL=index.js.map