UNPKG

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
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