@arcfusionz/arc-primitive-ui 0.2.15 → 0.2.18

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.
@@ -6,7 +6,7 @@ import { Button } from "@base-ui/react/button";
6
6
  const baseClasses = "relative inline-flex items-center justify-center whitespace-nowrap select-none rounded-md font-sans font-semibold transition-colors duration-150 cursor-pointer focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 disabled:pointer-events-none disabled:opacity-50 data-disabled:pointer-events-none data-disabled:opacity-50";
7
7
  const variantClasses = {
8
8
  primary: "bg-primary text-primary-foreground hover:bg-primary-700 active:bg-primary-800",
9
- secondary: "bg-secondary text-secondary-foreground hover:bg-primary-300 active:bg-primary-400",
9
+ secondary: "bg-secondary text-secondary-foreground hover:bg-primary-50 active:bg-primary-200",
10
10
  outline: "border border-border bg-transparent text-foreground hover:bg-muted active:bg-slate-200",
11
11
  ghost: "bg-transparent text-primary hover:bg-primary-50 active:bg-primary-100",
12
12
  destructive: "bg-destructive text-destructive-foreground hover:bg-error-700 active:bg-error-800",
@@ -37,20 +37,12 @@ declare function formatFileSize(bytes: number): string;
37
37
  interface FileUploadHandle {
38
38
  /** The currently selected files. */
39
39
  files: File[];
40
- /** Number of selected files. */
41
- fileCount: number;
42
40
  /** A file is being dragged over the dropzone. */
43
41
  isDragging: boolean;
44
42
  /** The upload is disabled. */
45
43
  disabled: boolean;
46
44
  /** The upload is marked invalid. */
47
45
  invalid: boolean;
48
- /** Effective file cap (multiple mode only); `undefined` when unlimited or single. */
49
- maxFiles?: number;
50
- /** Slots left before `maxFiles`; `undefined` when there is no cap. */
51
- remainingFiles?: number;
52
- /** The selection has reached `maxFiles`. */
53
- isAtMaxFiles: boolean;
54
46
  /** Open the native file picker. */
55
47
  open: () => void;
56
48
  /** Remove one file from the selection. */
@@ -105,7 +97,7 @@ interface FileUploadProps extends Omit<ComponentPropsWithoutRef<"div">, "classNa
105
97
  name?: string;
106
98
  /** Accessible name for the hidden file input. */
107
99
  inputLabel?: string;
108
- /** Replace the rendered root `div` or inspect `{ dragging, disabled, invalid, empty }`. */
100
+ /** Replace the rendered root `div` or inspect `{ dragging, disabled, invalid, empty, atMax }`. */
109
101
  render?: useRender.RenderProp<FileUploadRenderState>;
110
102
  /** Compose the pieces: `FileUploadDropzone`, `FileUploadList`, `FileUploadTrigger`. */
111
103
  children?: ReactNode;
@@ -198,7 +190,7 @@ interface FileUploadItemProps extends Omit<ComponentPropsWithoutRef<"li">, "clas
198
190
  progress?: number;
199
191
  /** Detailed message shown while `status="error"`, in place of the size. */
200
192
  error?: ReactNode;
201
- /** Short visible + announced status label. Defaults per `status` ("Uploading", "Uploaded", "Failed"); pass to override the copy. */
193
+ /** Short visible, accessible status label. Defaults per `status` ("Uploading", "Uploaded", "Failed"); pass to override the copy. This is not a live-region announcement. */
202
194
  statusLabel?: ReactNode;
203
195
  /** Show the remove button. Defaults to `true`. */
204
196
  removable?: boolean;
@@ -206,6 +198,12 @@ interface FileUploadItemProps extends Omit<ComponentPropsWithoutRef<"li">, "clas
206
198
  onRemove?: () => void;
207
199
  /** Accessible name for the remove button. Defaults to `Remove {file name}`. */
208
200
  removeLabel?: string;
201
+ /**
202
+ * Replace the leading visual completely. This compatibility escape hatch
203
+ * takes precedence over `previewSrc`; the consumer owns its presentation and
204
+ * accessibility. Prefer `previewSrc` when a standard thumbnail is sufficient.
205
+ */
206
+ preview?: ReactNode;
209
207
  /** URL of a preview image (e.g. a server-side thumbnail). The primitive owns the dimensions, radius, `object-fit`, decorative attributes, and drag prevention — you supply only the URL. Falls back to a file icon when omitted, non-image, or the image fails to load. */
210
208
  previewSrc?: string;
211
209
  /** Replace the entire row body. */
@@ -58,13 +58,9 @@ function useFileUpload() {
58
58
  const context = useFileUploadContext("useFileUpload");
59
59
  return {
60
60
  files: context.files,
61
- fileCount: context.fileCount,
62
61
  isDragging: context.isDragging,
63
62
  disabled: context.disabled,
64
63
  invalid: context.invalid,
65
- maxFiles: context.maxFiles,
66
- remainingFiles: context.remainingFiles,
67
- isAtMaxFiles: context.isAtMaxFiles,
68
64
  open: context.openPicker,
69
65
  remove: context.removeFile,
70
66
  clear: context.clear
@@ -430,7 +426,7 @@ const itemStatusColors = {
430
426
  * consumer-driven so the row reflects real upload state; the component shows
431
427
  * failures but does not itself retry.
432
428
  */
433
- const FileUploadItem = forwardRef(({ file, status = "idle", progress, error, statusLabel, removable = true, onRemove, removeLabel, previewSrc, children, className, ...rest }, ref) => {
429
+ const FileUploadItem = forwardRef(({ file, status = "idle", progress, error, statusLabel, removable = true, onRemove, removeLabel, preview, previewSrc, children, className, ...rest }, ref) => {
434
430
  const context = useFileUploadContext("FileUploadItem");
435
431
  const handleRemove = onRemove ?? (() => context.removeFile(file));
436
432
  const resolvedStatusLabel = statusLabel !== void 0 ? statusLabel : itemStatusLabels[status] || null;
@@ -443,7 +439,7 @@ const FileUploadItem = forwardRef(({ file, status = "idle", progress, error, sta
443
439
  children: children ?? /* @__PURE__ */ jsxs(Fragment$1, { children: [
444
440
  /* @__PURE__ */ jsx("span", {
445
441
  className: "shrink-0",
446
- children: /* @__PURE__ */ jsx(FileUploadThumbnail, {
442
+ children: preview ?? /* @__PURE__ */ jsx(FileUploadThumbnail, {
447
443
  file,
448
444
  src: previewSrc
449
445
  })
@@ -500,9 +496,8 @@ const thumbnailImageClasses = "size-10 rounded-md object-cover";
500
496
  const thumbnailFallbackClasses = "flex size-10 items-center justify-center rounded-md bg-muted text-muted-foreground";
501
497
  function FileUploadThumbnail({ file, src }) {
502
498
  const [autoUrl, setAutoUrl] = useState(null);
503
- const [failed, setFailed] = useState(false);
499
+ const [failedSrc, setFailedSrc] = useState(null);
504
500
  useEffect(() => {
505
- setFailed(false);
506
501
  if (src != null || !file.type.startsWith("image/") || !canUseObjectUrl()) {
507
502
  setAutoUrl(null);
508
503
  return;
@@ -512,13 +507,13 @@ function FileUploadThumbnail({ file, src }) {
512
507
  return () => URL.revokeObjectURL(objectUrl);
513
508
  }, [file, src]);
514
509
  const resolvedSrc = src ?? autoUrl;
515
- if (resolvedSrc != null && !failed) return /* @__PURE__ */ jsx("img", {
510
+ if (resolvedSrc != null && failedSrc !== resolvedSrc) return /* @__PURE__ */ jsx("img", {
516
511
  src: resolvedSrc,
517
512
  alt: "",
518
513
  "aria-hidden": "true",
519
514
  className: thumbnailImageClasses,
520
515
  draggable: false,
521
- onError: () => setFailed(true)
516
+ onError: () => setFailedSrc(resolvedSrc)
522
517
  });
523
518
  return /* @__PURE__ */ jsx("span", {
524
519
  className: thumbnailFallbackClasses,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@arcfusionz/arc-primitive-ui",
3
- "version": "0.2.15",
3
+ "version": "0.2.18",
4
4
  "description": "ArcFusion primitive UI components",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -342,7 +342,7 @@
342
342
  --color-primary: var(--color-primary-600);
343
343
  --color-primary-foreground: #ffffff;
344
344
 
345
- --color-secondary: var(--color-primary-200);
345
+ --color-secondary: var(--color-primary-100);
346
346
  --color-secondary-foreground: var(--color-navy-950);
347
347
 
348
348
  --color-hero: var(--color-navy-950);