@spark-ui/components 11.4.2 → 11.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -17,8 +17,9 @@ import {
17
17
  } from "../chunk-6QCEPQ3U.mjs";
18
18
 
19
19
  // src/file-upload/FileUpload.tsx
20
+ import { useFormFieldControl } from "@spark-ui/components/form-field";
20
21
  import { useCombinedState } from "@spark-ui/hooks/use-combined-state";
21
- import { createContext, useContext, useRef, useState } from "react";
22
+ import { createContext, useContext, useId, useRef, useState } from "react";
22
23
 
23
24
  // src/file-upload/utils.ts
24
25
  import { CvOutline } from "@spark-ui/icons/CvOutline";
@@ -122,33 +123,45 @@ function getFileIcon(file) {
122
123
  // src/file-upload/FileUpload.tsx
123
124
  import { jsx, jsxs } from "react/jsx-runtime";
124
125
  var FileUploadContext = createContext(null);
126
+ var ID_PREFIX = ":file-upload";
125
127
  var FileUpload = ({
126
128
  asChild: _asChild = false,
127
129
  children,
128
130
  defaultValue = [],
129
131
  value: controlledValue,
130
- onFilesChange,
132
+ onFileAccept,
133
+ onFileReject,
134
+ onFileChange,
131
135
  multiple = true,
132
136
  accept,
133
137
  maxFiles,
134
- onMaxFilesReached,
135
138
  maxFileSize,
136
139
  minFileSize,
137
- onFileSizeError,
138
- disabled = false,
139
- readOnly = false,
140
+ disabled: disabledProp = false,
141
+ readOnly: readOnlyProp = false,
140
142
  locale
141
143
  }) => {
144
+ const field = useFormFieldControl();
145
+ const {
146
+ id: fieldId,
147
+ name: fieldName,
148
+ isInvalid,
149
+ isRequired,
150
+ description,
151
+ disabled: fieldDisabled,
152
+ readOnly: fieldReadOnly
153
+ } = field;
142
154
  const defaultLocale = locale || (typeof navigator !== "undefined" && navigator.language ? navigator.language : "en");
155
+ const internalId = useId();
156
+ const inputId = fieldId || `${ID_PREFIX}-${internalId}`;
157
+ const inputName = fieldName;
143
158
  const inputRef = useRef(null);
144
159
  const triggerRef = useRef(null);
145
160
  const dropzoneRef = useRef(null);
146
161
  const deleteButtonRefs = useRef([]);
147
- const [filesState, setFilesState, ,] = useCombinedState(
148
- controlledValue,
149
- defaultValue,
150
- onFilesChange
151
- );
162
+ const disabled = fieldDisabled ?? disabledProp;
163
+ const readOnly = fieldReadOnly ?? readOnlyProp;
164
+ const [filesState, setFilesState, ,] = useCombinedState(controlledValue, defaultValue);
152
165
  const files = filesState ?? [];
153
166
  const setFiles = setFilesState;
154
167
  const [rejectedFiles, setRejectedFiles] = useState([]);
@@ -177,9 +190,6 @@ var FileUpload = ({
177
190
  errors: [error]
178
191
  });
179
192
  }
180
- if (onFileSizeError) {
181
- onFileSizeError(file, error);
182
- }
183
193
  };
184
194
  setFiles((prev) => {
185
195
  const currentFiles = prev ?? [];
@@ -243,19 +253,29 @@ var FileUpload = ({
243
253
  filesToAdd.forEach((file) => {
244
254
  addRejectedFile(file, "TOO_MANY_FILES");
245
255
  });
246
- onMaxFilesReached?.(maxFiles, filesToAdd.length);
247
256
  filesToAdd = [];
248
257
  } else if (filesToAdd.length > remainingSlots) {
249
258
  filesToAdd.forEach((file) => {
250
259
  addRejectedFile(file, "TOO_MANY_FILES");
251
260
  });
252
- onMaxFilesReached?.(maxFiles, filesToAdd.length);
253
261
  filesToAdd = [];
254
262
  }
255
263
  }
256
264
  const updated = multiple ? [...currentFiles, ...filesToAdd] : filesToAdd;
257
265
  const rejectedFilesToAdd = [...newRejectedFiles];
258
266
  setRejectedFiles(rejectedFilesToAdd);
267
+ if (filesToAdd.length > 0 && onFileAccept) {
268
+ onFileAccept({ files: filesToAdd });
269
+ }
270
+ if (rejectedFilesToAdd.length > 0 && onFileReject) {
271
+ onFileReject({ files: rejectedFilesToAdd });
272
+ }
273
+ if (onFileChange) {
274
+ onFileChange({
275
+ acceptedFiles: updated,
276
+ rejectedFiles: rejectedFilesToAdd
277
+ });
278
+ }
259
279
  return updated;
260
280
  });
261
281
  };
@@ -266,10 +286,18 @@ var FileUpload = ({
266
286
  setFiles((prev) => {
267
287
  const currentFiles = prev ?? [];
268
288
  const updated = currentFiles.filter((_, i) => i !== index);
289
+ let updatedRejectedFiles = rejectedFiles;
269
290
  if (maxFiles !== void 0 && updated.length < maxFiles) {
270
- setRejectedFiles(
271
- (prevRejected) => prevRejected.filter((rejected) => !rejected.errors.includes("TOO_MANY_FILES"))
291
+ updatedRejectedFiles = rejectedFiles.filter(
292
+ (rejected) => !rejected.errors.includes("TOO_MANY_FILES")
272
293
  );
294
+ setRejectedFiles(updatedRejectedFiles);
295
+ }
296
+ if (onFileChange) {
297
+ onFileChange({
298
+ acceptedFiles: updated,
299
+ rejectedFiles: updatedRejectedFiles
300
+ });
273
301
  }
274
302
  return updated;
275
303
  });
@@ -281,6 +309,12 @@ var FileUpload = ({
281
309
  setFiles([]);
282
310
  setRejectedFiles([]);
283
311
  deleteButtonRefs.current = [];
312
+ if (onFileChange) {
313
+ onFileChange({
314
+ acceptedFiles: [],
315
+ rejectedFiles: []
316
+ });
317
+ }
284
318
  };
285
319
  const removeRejectedFile = (index) => {
286
320
  if (disabled || readOnly) {
@@ -312,7 +346,10 @@ var FileUpload = ({
312
346
  maxFilesReached,
313
347
  disabled,
314
348
  readOnly,
315
- locale: defaultLocale
349
+ locale: defaultLocale,
350
+ description,
351
+ isInvalid,
352
+ isRequired
316
353
  },
317
354
  children: /* @__PURE__ */ jsxs("div", { className: "relative", children: [
318
355
  children,
@@ -322,12 +359,15 @@ var FileUpload = ({
322
359
  ref: inputRef,
323
360
  type: "file",
324
361
  tabIndex: -1,
325
- id: "image_uploads",
362
+ id: inputId,
326
363
  multiple,
327
- name: "image_uploads",
364
+ name: inputName,
328
365
  accept,
329
366
  disabled,
330
367
  readOnly: readOnly && !disabled,
368
+ required: isRequired,
369
+ "aria-invalid": isInvalid,
370
+ "aria-describedby": description,
331
371
  className: "sr-only",
332
372
  onChange: (e) => {
333
373
  if (e.target.files && !disabled && !readOnly) {
@@ -353,45 +393,23 @@ var useFileUploadContext = () => {
353
393
  return context;
354
394
  };
355
395
 
356
- // src/file-upload/FileUploadItem.tsx
357
- import { cx } from "class-variance-authority";
358
- import { jsx as jsx2 } from "react/jsx-runtime";
359
- var Item = ({
360
- asChild: _asChild = false,
361
- className,
362
- children,
363
- ...props
364
- }) => {
365
- return /* @__PURE__ */ jsx2(
366
- "li",
367
- {
368
- "data-spark-component": "file-upload-item",
369
- className: cx(
370
- "relative",
371
- "default:bg-surface default:border-sm default:border-outline default:p-md default:rounded-md",
372
- "gap-md flex items-center justify-between default:w-full",
373
- className
374
- ),
375
- ...props,
376
- children
377
- }
378
- );
379
- };
380
- Item.displayName = "FileUpload.Item";
396
+ // src/file-upload/FileUploadAcceptedFile.tsx
397
+ import { cx as cx2 } from "class-variance-authority";
381
398
 
382
399
  // src/file-upload/FileUploadItemDeleteTrigger.tsx
383
400
  import { Close } from "@spark-ui/icons/Close";
384
- import { cx as cx2 } from "class-variance-authority";
401
+ import { cx } from "class-variance-authority";
385
402
  import { useRef as useRef2 } from "react";
386
- import { jsx as jsx3 } from "react/jsx-runtime";
403
+ import { jsx as jsx2 } from "react/jsx-runtime";
387
404
  var ItemDeleteTrigger = ({
388
405
  className,
389
- fileIndex,
406
+ file,
390
407
  onClick,
391
408
  ...props
392
409
  }) => {
393
- const { removeFile, triggerRef, dropzoneRef, deleteButtonRefs, disabled, readOnly } = useFileUploadContext();
410
+ const { removeFile, triggerRef, dropzoneRef, deleteButtonRefs, disabled, readOnly, files } = useFileUploadContext();
394
411
  const buttonRef = useRef2(null);
412
+ const fileIndex = files.findIndex((f) => f.name === file.name && f.size === file.size);
395
413
  const handleClick = (e) => {
396
414
  if (disabled || readOnly) {
397
415
  return;
@@ -427,98 +445,68 @@ var ItemDeleteTrigger = ({
427
445
  }
428
446
  }
429
447
  };
430
- return /* @__PURE__ */ jsx3(
448
+ return /* @__PURE__ */ jsx2(
431
449
  IconButton,
432
450
  {
433
451
  ref: setRef,
434
452
  "data-spark-component": "file-upload-item-delete-trigger",
435
- className: cx2(className),
453
+ className: cx(className),
436
454
  onClick: handleClick,
437
455
  disabled: disabled || readOnly,
438
456
  size: "sm",
439
457
  design: "contrast",
440
458
  intent: "surface",
441
459
  ...props,
442
- children: /* @__PURE__ */ jsx3(Icon, { size: "sm", children: /* @__PURE__ */ jsx3(Close, {}) })
460
+ children: /* @__PURE__ */ jsx2(Icon, { size: "sm", children: /* @__PURE__ */ jsx2(Close, {}) })
443
461
  }
444
462
  );
445
463
  };
446
464
  ItemDeleteTrigger.displayName = "FileUpload.ItemDeleteTrigger";
447
465
 
448
- // src/file-upload/FileUploadItemFileName.tsx
449
- import { cx as cx3 } from "class-variance-authority";
450
- import { jsx as jsx4 } from "react/jsx-runtime";
451
- var ItemFileName = ({
452
- asChild: _asChild = false,
453
- className,
454
- children,
455
- ...props
456
- }) => {
457
- return /* @__PURE__ */ jsx4(
458
- "p",
459
- {
460
- "data-spark-component": "file-upload-item-file-name",
461
- className: cx3("text-body-2 truncate font-medium", className),
462
- ...props,
463
- children
464
- }
465
- );
466
- };
467
- ItemFileName.displayName = "FileUpload.ItemFileName";
468
-
469
- // src/file-upload/FileUploadItemSizeText.tsx
470
- import { cx as cx4 } from "class-variance-authority";
471
- import { jsx as jsx5 } from "react/jsx-runtime";
472
- var ItemSizeText = ({
473
- asChild: _asChild = false,
474
- className,
475
- children,
476
- ...props
477
- }) => {
478
- return /* @__PURE__ */ jsx5(
479
- "p",
480
- {
481
- "data-spark-component": "file-upload-item-size-text",
482
- className: cx4("text-caption", className),
483
- ...props,
484
- children
485
- }
486
- );
487
- };
488
- ItemSizeText.displayName = "FileUpload.ItemSizeText";
489
-
490
466
  // src/file-upload/FileUploadAcceptedFile.tsx
491
- import { jsx as jsx6, jsxs as jsxs2 } from "react/jsx-runtime";
467
+ import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
492
468
  var AcceptedFile = ({
493
469
  asChild: _asChild = false,
494
470
  className,
495
471
  file,
496
- fileIndex,
497
472
  uploadProgress,
498
473
  deleteButtonAriaLabel,
499
474
  progressAriaLabel,
500
475
  ...props
501
476
  }) => {
502
477
  const { locale } = useFileUploadContext();
503
- return /* @__PURE__ */ jsxs2(Item, { className, ...props, children: [
504
- /* @__PURE__ */ jsx6("div", { className: "size-sz-40 bg-support-container flex items-center justify-center rounded-md", children: /* @__PURE__ */ jsx6(Icon, { size: "md", children: getFileIcon(file) }) }),
505
- /* @__PURE__ */ jsxs2("div", { className: "min-w-0 flex-1", children: [
506
- /* @__PURE__ */ jsxs2("div", { className: "gap-md flex flex-row items-center justify-between", children: [
507
- /* @__PURE__ */ jsx6(ItemFileName, { children: file.name }),
508
- /* @__PURE__ */ jsx6(ItemSizeText, { className: "opacity-dim-1", children: formatFileSize(file.size, locale) })
509
- ] }),
510
- uploadProgress !== void 0 && /* @__PURE__ */ jsx6("div", { className: "mt-md", children: /* @__PURE__ */ jsx6(Progress, { value: uploadProgress, max: 100, "aria-label": progressAriaLabel }) })
511
- ] }),
512
- /* @__PURE__ */ jsx6(ItemDeleteTrigger, { "aria-label": deleteButtonAriaLabel, fileIndex })
513
- ] });
478
+ return /* @__PURE__ */ jsxs2(
479
+ "li",
480
+ {
481
+ "data-spark-component": "file-upload-accepted-file",
482
+ className: cx2(
483
+ "relative",
484
+ "default:bg-surface default:border-sm default:border-outline default:p-md default:rounded-md",
485
+ "gap-md flex items-center justify-between default:w-full",
486
+ className
487
+ ),
488
+ ...props,
489
+ children: [
490
+ /* @__PURE__ */ jsx3("div", { className: "size-sz-40 bg-support-container flex items-center justify-center rounded-md", children: /* @__PURE__ */ jsx3(Icon, { size: "md", children: getFileIcon(file) }) }),
491
+ /* @__PURE__ */ jsxs2("div", { className: "min-w-0 flex-1", children: [
492
+ /* @__PURE__ */ jsxs2("div", { className: "gap-md flex flex-row items-center justify-between", children: [
493
+ /* @__PURE__ */ jsx3("p", { className: "text-body-2 truncate font-medium", children: file.name }),
494
+ /* @__PURE__ */ jsx3("p", { className: "text-caption opacity-dim-1", children: formatFileSize(file.size, locale) })
495
+ ] }),
496
+ uploadProgress !== void 0 && /* @__PURE__ */ jsx3("div", { className: "mt-md", children: /* @__PURE__ */ jsx3(Progress, { value: uploadProgress, max: 100, "aria-label": progressAriaLabel }) })
497
+ ] }),
498
+ /* @__PURE__ */ jsx3(ItemDeleteTrigger, { "aria-label": deleteButtonAriaLabel, file })
499
+ ]
500
+ }
501
+ );
514
502
  };
515
503
  AcceptedFile.displayName = "FileUpload.AcceptedFile";
516
504
 
517
505
  // src/file-upload/FileUploadContext.tsx
518
- import { Fragment, jsx as jsx7 } from "react/jsx-runtime";
506
+ import { Fragment, jsx as jsx4 } from "react/jsx-runtime";
519
507
  var Context = ({ children }) => {
520
508
  const { files = [], rejectedFiles = [], locale } = useFileUploadContext();
521
- return /* @__PURE__ */ jsx7(Fragment, { children: children({
509
+ return /* @__PURE__ */ jsx4(Fragment, { children: children({
522
510
  acceptedFiles: files,
523
511
  rejectedFiles,
524
512
  formatFileSize,
@@ -528,12 +516,11 @@ var Context = ({ children }) => {
528
516
  Context.displayName = "FileUpload.Context";
529
517
 
530
518
  // src/file-upload/FileUploadDropzone.tsx
531
- import { cx as cx5 } from "class-variance-authority";
519
+ import { cx as cx3 } from "class-variance-authority";
532
520
  import { useRef as useRef3 } from "react";
533
- import { jsx as jsx8 } from "react/jsx-runtime";
521
+ import { jsx as jsx5 } from "react/jsx-runtime";
534
522
  function Dropzone({
535
523
  children,
536
- onFiles,
537
524
  className,
538
525
  unstyled = false
539
526
  }) {
@@ -548,7 +535,6 @@ function Dropzone({
548
535
  return;
549
536
  }
550
537
  const files = e.dataTransfer.files;
551
- onFiles?.(files);
552
538
  let filesArray = [];
553
539
  if (files) {
554
540
  filesArray = Array.isArray(files) ? [...files] : Array.from(files);
@@ -571,7 +557,7 @@ function Dropzone({
571
557
  }
572
558
  };
573
559
  const isDisabled = ctx.disabled || ctx.readOnly;
574
- return /* @__PURE__ */ jsx8(
560
+ return /* @__PURE__ */ jsx5(
575
561
  "div",
576
562
  {
577
563
  ref: (node) => {
@@ -583,18 +569,21 @@ function Dropzone({
583
569
  role: "button",
584
570
  tabIndex: isDisabled ? -1 : 0,
585
571
  "aria-disabled": ctx.disabled ? true : void 0,
572
+ "aria-describedby": ctx.description,
573
+ "aria-invalid": ctx.isInvalid,
574
+ "aria-required": ctx.isRequired,
586
575
  onClick: handleClick,
587
576
  onKeyDown: handleKeyDown,
588
577
  onDrop: handleDrop,
589
578
  onDragOver: (e) => {
590
579
  e.preventDefault();
591
580
  },
592
- className: unstyled ? className : cx5(
593
- "default:bg-surface default:border-sm default:border-outline default:rounded-lg default:border-dashed",
581
+ className: unstyled ? className : cx3(
582
+ "default:bg-surface default:border-sm default:border-outline default:relative default:rounded-lg default:border-dashed",
594
583
  "gap-lg flex flex-col items-center justify-center text-center",
595
584
  "default:p-xl",
596
585
  "transition-colors duration-200",
597
- !isDisabled && "hover:bg-surface-hovered",
586
+ !isDisabled && "default:hover:bg-surface-hovered",
598
587
  "data-[drag-over=true]:border-outline-high data-[drag-over=true]:bg-surface-hovered data-[drag-over=true]:border-solid",
599
588
  // Disabled: more visually disabled (opacity + cursor)
600
589
  ctx.disabled && "cursor-not-allowed opacity-50",
@@ -617,9 +606,9 @@ function Dropzone({
617
606
  Dropzone.displayName = "FileUploadDropzone";
618
607
 
619
608
  // src/file-upload/FileUploadPreviewImage.tsx
620
- import { cx as cx6 } from "class-variance-authority";
609
+ import { cx as cx4 } from "class-variance-authority";
621
610
  import { useEffect, useState as useState2 } from "react";
622
- import { jsx as jsx9, jsxs as jsxs3 } from "react/jsx-runtime";
611
+ import { jsx as jsx6, jsxs as jsxs3 } from "react/jsx-runtime";
623
612
  var PreviewImage = ({
624
613
  asChild: _asChild = false,
625
614
  className,
@@ -639,11 +628,11 @@ var PreviewImage = ({
639
628
  };
640
629
  }, [imageUrl]);
641
630
  if (!isImage || imageError) {
642
- return /* @__PURE__ */ jsx9(
631
+ return /* @__PURE__ */ jsx6(
643
632
  "div",
644
633
  {
645
634
  "data-spark-component": "file-upload-preview-image",
646
- className: cx6(
635
+ className: cx4(
647
636
  "bg-neutral-container flex items-center justify-center rounded-md",
648
637
  className
649
638
  ),
@@ -656,20 +645,20 @@ var PreviewImage = ({
656
645
  "div",
657
646
  {
658
647
  "data-spark-component": "file-upload-preview-image",
659
- className: cx6("bg-neutral-container overflow-hidden", className),
648
+ className: cx4("bg-neutral-container overflow-hidden", className),
660
649
  ...props,
661
650
  children: [
662
- /* @__PURE__ */ jsx9(
651
+ /* @__PURE__ */ jsx6(
663
652
  "img",
664
653
  {
665
654
  src: imageUrl,
666
655
  alt: file.name,
667
- className: cx6("size-full object-cover", !imageLoaded && "opacity-0"),
656
+ className: cx4("size-full object-cover", !imageLoaded && "opacity-0"),
668
657
  onLoad: () => setImageLoaded(true),
669
658
  onError: () => setImageError(true)
670
659
  }
671
660
  ),
672
- !imageLoaded && /* @__PURE__ */ jsx9("div", { className: "absolute inset-0 flex items-center justify-center", children: fallback })
661
+ !imageLoaded && /* @__PURE__ */ jsx6("div", { className: "absolute inset-0 flex items-center justify-center", children: fallback })
673
662
  ]
674
663
  }
675
664
  );
@@ -678,21 +667,24 @@ PreviewImage.displayName = "FileUpload.PreviewImage";
678
667
 
679
668
  // src/file-upload/FileUploadRejectedFile.tsx
680
669
  import { WarningOutline } from "@spark-ui/icons/WarningOutline";
681
- import { cx as cx8 } from "class-variance-authority";
670
+ import { cx as cx6 } from "class-variance-authority";
682
671
 
683
672
  // src/file-upload/FileUploadRejectedFileDeleteTrigger.tsx
684
673
  import { Close as Close2 } from "@spark-ui/icons/Close";
685
- import { cx as cx7 } from "class-variance-authority";
674
+ import { cx as cx5 } from "class-variance-authority";
686
675
  import { useRef as useRef4 } from "react";
687
- import { jsx as jsx10 } from "react/jsx-runtime";
676
+ import { jsx as jsx7 } from "react/jsx-runtime";
688
677
  var RejectedFileDeleteTrigger = ({
689
678
  className,
690
- rejectedFileIndex,
679
+ rejectedFile,
691
680
  onClick,
692
681
  ...props
693
682
  }) => {
694
- const { removeRejectedFile, triggerRef, dropzoneRef, disabled, readOnly } = useFileUploadContext();
683
+ const { removeRejectedFile, triggerRef, dropzoneRef, disabled, readOnly, rejectedFiles } = useFileUploadContext();
695
684
  const buttonRef = useRef4(null);
685
+ const rejectedFileIndex = rejectedFiles.findIndex(
686
+ (rf) => rf.file.name === rejectedFile.file.name && rf.file.size === rejectedFile.file.size
687
+ );
696
688
  const handleClick = (e) => {
697
689
  if (disabled || readOnly) {
698
690
  return;
@@ -706,59 +698,66 @@ var RejectedFileDeleteTrigger = ({
706
698
  }, 0);
707
699
  onClick?.(e);
708
700
  };
709
- return /* @__PURE__ */ jsx10(
701
+ return /* @__PURE__ */ jsx7(
710
702
  IconButton,
711
703
  {
712
704
  ref: buttonRef,
713
705
  "data-spark-component": "file-upload-rejected-file-delete-trigger",
714
- className: cx7(className),
706
+ className: cx5(className),
715
707
  onClick: handleClick,
716
708
  disabled: disabled || readOnly,
717
709
  size: "sm",
718
710
  design: "contrast",
719
711
  intent: "surface",
720
712
  ...props,
721
- children: /* @__PURE__ */ jsx10(Icon, { size: "sm", children: /* @__PURE__ */ jsx10(Close2, {}) })
713
+ children: /* @__PURE__ */ jsx7(Icon, { size: "sm", children: /* @__PURE__ */ jsx7(Close2, {}) })
722
714
  }
723
715
  );
724
716
  };
725
717
  RejectedFileDeleteTrigger.displayName = "FileUpload.RejectedFileDeleteTrigger";
726
718
 
727
719
  // src/file-upload/FileUploadRejectedFile.tsx
728
- import { jsx as jsx11, jsxs as jsxs4 } from "react/jsx-runtime";
720
+ import { jsx as jsx8, jsxs as jsxs4 } from "react/jsx-runtime";
729
721
  var RejectedFile = ({
730
722
  asChild: _asChild = false,
731
723
  className,
732
724
  rejectedFile,
733
- rejectedFileIndex,
734
725
  renderError,
735
726
  deleteButtonAriaLabel,
736
727
  ...props
737
728
  }) => {
738
729
  const { locale } = useFileUploadContext();
739
- return /* @__PURE__ */ jsxs4(Item, { className: cx8("border-error border-md", className), ...props, children: [
740
- /* @__PURE__ */ jsx11("div", { className: "size-sz-40 bg-error-container flex items-center justify-center rounded-md", children: /* @__PURE__ */ jsx11(Icon, { size: "md", className: "text-error", children: /* @__PURE__ */ jsx11(WarningOutline, {}) }) }),
741
- /* @__PURE__ */ jsx11("div", { className: "min-w-0 flex-1", children: /* @__PURE__ */ jsxs4("div", { className: "gap-md flex flex-col", children: [
742
- /* @__PURE__ */ jsxs4("div", { className: "gap-md flex flex-row items-center justify-between", children: [
743
- /* @__PURE__ */ jsx11(ItemFileName, { children: rejectedFile.file.name }),
744
- /* @__PURE__ */ jsx11(ItemSizeText, { className: "opacity-dim-1", children: formatFileSize(rejectedFile.file.size, locale) })
745
- ] }),
746
- /* @__PURE__ */ jsx11("div", { className: "gap-xs flex flex-col", children: rejectedFile.errors.map((error, errorIndex) => /* @__PURE__ */ jsx11("div", { className: "text-caption text-error", "data-error-code": error, children: renderError(error) }, errorIndex)) })
747
- ] }) }),
748
- /* @__PURE__ */ jsx11(
749
- RejectedFileDeleteTrigger,
750
- {
751
- "aria-label": deleteButtonAriaLabel,
752
- rejectedFileIndex
753
- }
754
- )
755
- ] });
730
+ return /* @__PURE__ */ jsxs4(
731
+ "li",
732
+ {
733
+ "data-spark-component": "file-upload-rejected-file",
734
+ className: cx6(
735
+ "relative",
736
+ "default:bg-surface default:border-sm default:border-outline default:p-md default:rounded-md",
737
+ "gap-md flex items-center justify-between default:w-full",
738
+ "border-error border-md",
739
+ className
740
+ ),
741
+ ...props,
742
+ children: [
743
+ /* @__PURE__ */ jsx8("div", { className: "size-sz-40 bg-error-container flex items-center justify-center rounded-md", children: /* @__PURE__ */ jsx8(Icon, { size: "md", className: "text-error", children: /* @__PURE__ */ jsx8(WarningOutline, {}) }) }),
744
+ /* @__PURE__ */ jsx8("div", { className: "min-w-0 flex-1", children: /* @__PURE__ */ jsxs4("div", { className: "gap-md flex flex-col", children: [
745
+ /* @__PURE__ */ jsxs4("div", { className: "gap-md flex flex-row items-center justify-between", children: [
746
+ /* @__PURE__ */ jsx8("p", { className: "text-body-2 truncate font-medium", children: rejectedFile.file.name }),
747
+ /* @__PURE__ */ jsx8("p", { className: "text-caption opacity-dim-1", children: formatFileSize(rejectedFile.file.size, locale) })
748
+ ] }),
749
+ /* @__PURE__ */ jsx8("div", { className: "gap-xs flex flex-col", children: rejectedFile.errors.map((error, errorIndex) => /* @__PURE__ */ jsx8("div", { className: "text-caption text-error", "data-error-code": error, children: renderError(error) }, errorIndex)) })
750
+ ] }) }),
751
+ /* @__PURE__ */ jsx8(RejectedFileDeleteTrigger, { "aria-label": deleteButtonAriaLabel, rejectedFile })
752
+ ]
753
+ }
754
+ );
756
755
  };
757
756
  RejectedFile.displayName = "FileUpload.RejectedFile";
758
757
 
759
758
  // src/file-upload/FileUploadTrigger.tsx
760
- import { cx as cx9 } from "class-variance-authority";
761
- import { jsx as jsx12 } from "react/jsx-runtime";
759
+ import { cx as cx7 } from "class-variance-authority";
760
+ import { jsx as jsx9 } from "react/jsx-runtime";
762
761
  var Trigger = ({
763
762
  className,
764
763
  children,
@@ -769,7 +768,7 @@ var Trigger = ({
769
768
  ref,
770
769
  ...props
771
770
  }) => {
772
- const { inputRef, triggerRef, disabled, readOnly } = useFileUploadContext();
771
+ const { inputRef, triggerRef, disabled, readOnly, description, isInvalid, isRequired } = useFileUploadContext();
773
772
  const handleClick = (e) => {
774
773
  e.stopPropagation();
775
774
  e.preventDefault();
@@ -779,7 +778,7 @@ var Trigger = ({
779
778
  };
780
779
  const buttonComponent = unstyled ? "button" : Button;
781
780
  const Comp = asChild ? Slot : buttonComponent;
782
- return /* @__PURE__ */ jsx12(
781
+ return /* @__PURE__ */ jsx9(
783
782
  Comp,
784
783
  {
785
784
  type: "button",
@@ -798,9 +797,12 @@ var Trigger = ({
798
797
  design,
799
798
  intent,
800
799
  "data-spark-component": "file-upload-trigger",
801
- className: cx9(className),
800
+ className: cx7(className),
802
801
  disabled: disabled || readOnly,
803
802
  onClick: handleClick,
803
+ "aria-describedby": description,
804
+ "aria-invalid": isInvalid,
805
+ "aria-required": isRequired,
804
806
  ...props,
805
807
  children
806
808
  }
@@ -810,25 +812,22 @@ Trigger.displayName = "FileUpload.Trigger";
810
812
 
811
813
  // src/file-upload/index.ts
812
814
  var FileUpload2 = Object.assign(FileUpload, {
815
+ // Main input components
813
816
  Trigger,
814
817
  Dropzone,
818
+ // Context components
815
819
  Context,
816
- Item,
817
- ItemFileName,
818
- ItemSizeText,
819
- ItemDeleteTrigger,
820
- PreviewImage,
821
820
  AcceptedFile,
822
821
  RejectedFile,
822
+ // Helpers for custom renders
823
+ PreviewImage,
824
+ ItemDeleteTrigger,
823
825
  RejectedFileDeleteTrigger
824
826
  });
825
827
  FileUpload2.displayName = "FileUpload";
826
828
  Trigger.displayName = "FileUpload.Trigger";
827
829
  Dropzone.displayName = "FileUpload.Dropzone";
828
830
  Context.displayName = "FileUpload.Context";
829
- Item.displayName = "FileUpload.Item";
830
- ItemFileName.displayName = "FileUpload.ItemFileName";
831
- ItemSizeText.displayName = "FileUpload.ItemSizeText";
832
831
  ItemDeleteTrigger.displayName = "FileUpload.ItemDeleteTrigger";
833
832
  PreviewImage.displayName = "FileUpload.PreviewImage";
834
833
  AcceptedFile.displayName = "FileUpload.AcceptedFile";