zentrixui
Version:
ZentrixUI - A modern, highly customizable and accessible React file upload component library with multiple variants, JSON-based configuration, and excellent developer experience.
222 lines (221 loc) • 7.14 kB
JavaScript
import { jsx } from "react/jsx-runtime";
import { useMemo, useEffect } from "react";
import { FileUploadProvider } from "./file-upload-context.js";
import { loadConfigFromJSON, defaultConfig, mergeConfig } from "../../config/schema.js";
import { applyTheme, generateThemeClasses, cn } from "../../utils/theme.js";
import { FileUploadErrorBoundary } from "./error-boundary.js";
import { ButtonUpload } from "./variants/button-upload.js";
import { DropzoneUpload } from "./variants/dropzone-upload.js";
import { PreviewUpload } from "./variants/preview-upload.js";
import { ImageUpload } from "./variants/image-upload.js";
import { MultiFileUpload } from "./variants/multi-file-upload.js";
const FileUpload = ({
variant,
size,
radius,
theme,
disabled,
multiple,
accept,
maxSize,
maxFiles,
onUpload,
onError,
onProgress,
onFileSelect,
onFileRemove,
onDragEnter,
onDragLeave,
onDragOver,
onDrop,
config: configProp,
className,
style,
ariaLabel,
ariaDescribedBy,
children,
...props
}) => {
const mergedConfig = useMemo(() => {
let baseConfig = defaultConfig;
if (configProp) {
if (typeof configProp === "string") {
try {
const { config: loadedConfig } = loadConfigFromJSON(configProp);
if (loadedConfig) {
baseConfig = loadedConfig;
}
} catch (error) {
console.warn("Failed to load config from string:", error);
}
} else {
baseConfig = mergeConfig(configProp);
}
}
const propOverrides = {};
if (variant !== void 0) {
propOverrides.defaults = { ...propOverrides.defaults, variant };
}
if (size !== void 0) {
propOverrides.defaults = { ...propOverrides.defaults, size };
}
if (radius !== void 0) {
propOverrides.defaults = { ...propOverrides.defaults, radius };
}
if (theme !== void 0) {
propOverrides.defaults = { ...propOverrides.defaults, theme };
propOverrides.styling = { ...propOverrides.styling, theme };
}
if (disabled !== void 0) {
propOverrides.defaults = { ...propOverrides.defaults, disabled };
}
if (multiple !== void 0) {
propOverrides.defaults = { ...propOverrides.defaults, multiple };
propOverrides.features = { ...propOverrides.features, multipleFiles: multiple };
}
if (accept !== void 0) {
propOverrides.defaults = { ...propOverrides.defaults, accept };
}
if (maxSize !== void 0) {
propOverrides.defaults = { ...propOverrides.defaults, maxSize };
propOverrides.validation = { ...propOverrides.validation, maxSize };
}
if (maxFiles !== void 0) {
propOverrides.defaults = { ...propOverrides.defaults, maxFiles };
propOverrides.validation = { ...propOverrides.validation, maxFiles };
}
return Object.keys(propOverrides).length > 0 ? mergeConfig({ ...baseConfig, ...propOverrides }) : baseConfig;
}, [configProp, variant, size, radius, theme, disabled, multiple, accept, maxSize, maxFiles]);
const eventHandlers = useMemo(() => ({
onFileSelect: onFileSelect ? (event) => onFileSelect(event.files.map((f) => f.file)) : void 0,
onUploadStart: onUpload ? (event) => onUpload(event.files.map((f) => f.file)) : void 0,
onUploadProgress: onProgress ? (event) => {
const file = event.files[0];
if (file) {
onProgress(file.progress, file);
}
} : void 0,
onUploadSuccess: void 0,
// Will be handled internally
onUploadError: onError ? (event) => {
const file = event.files[0];
if (file?.error) {
onError(file.error);
}
} : void 0,
onFileRemove: onFileRemove ? (event) => {
const file = event.files[0];
if (file) {
onFileRemove(file.id);
}
} : void 0,
onUploadRetry: void 0
// Will be handled internally
}), [onUpload, onError, onProgress, onFileSelect, onFileRemove]);
useEffect(() => {
if (mergedConfig.styling?.theme) {
applyTheme(mergedConfig.styling.theme, mergedConfig);
}
}, [mergedConfig]);
const effectiveVariant = mergedConfig.defaults.variant;
const effectiveSize = mergedConfig.defaults.size;
const effectiveRadius = mergedConfig.defaults.radius;
const effectiveTheme = mergedConfig.styling.theme;
const themeClasses = generateThemeClasses(
effectiveVariant,
effectiveSize,
effectiveRadius
);
const combinedClassName = cn(
themeClasses,
disabled && "file-upload--disabled",
className
);
const cssVariables = useMemo(() => {
const variables = {};
if (mergedConfig.styling) {
const { colors, spacing, typography, borders, shadows } = mergedConfig.styling;
if (colors) {
Object.entries(colors).forEach(([key, value]) => {
variables[`--file-upload-${key}`] = value;
});
}
if (spacing) {
Object.entries(spacing).forEach(([key, value]) => {
variables[`--file-upload-spacing-${key}`] = value;
});
}
if (typography) {
Object.entries(typography).forEach(([key, value]) => {
variables[`--file-upload-${key}`] = value;
});
}
if (borders) {
Object.entries(borders).forEach(([key, value]) => {
variables[`--file-upload-border-${key}`] = value;
});
}
if (shadows) {
Object.entries(shadows).forEach(([key, value]) => {
variables[`--file-upload-shadow-${key}`] = value;
});
}
}
return variables;
}, [mergedConfig]);
const combinedStyle = {
...cssVariables,
...style
};
const renderVariant = () => {
const commonProps = {
className: combinedClassName,
style: combinedStyle,
ariaLabel,
ariaDescribedBy,
onDragEnter,
onDragLeave,
onDragOver,
onDrop,
children,
...props
};
switch (effectiveVariant) {
case "dropzone":
return /* @__PURE__ */ jsx(DropzoneUpload, { ...commonProps });
case "preview":
return /* @__PURE__ */ jsx(PreviewUpload, { ...commonProps });
case "image-only":
return /* @__PURE__ */ jsx(ImageUpload, { ...commonProps });
case "multi-file":
return /* @__PURE__ */ jsx(MultiFileUpload, { ...commonProps });
case "button":
default:
return /* @__PURE__ */ jsx(ButtonUpload, { ...commonProps });
}
};
return /* @__PURE__ */ jsx(
FileUploadErrorBoundary,
{
onError: (error, errorInfo) => {
console.error("FileUpload component crashed:", error, errorInfo);
onError?.(error.message);
},
showErrorDetails: process.env.NODE_ENV === "development",
children: /* @__PURE__ */ jsx(FileUploadProvider, { config: mergedConfig, handlers: eventHandlers, children: /* @__PURE__ */ jsx(
"div",
{
"data-theme": effectiveTheme,
className: "file-upload-wrapper",
style: cssVariables,
children: renderVariant()
}
) })
}
);
};
FileUpload.displayName = "FileUpload";
export {
FileUpload
};
//# sourceMappingURL=file-upload.js.map