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.

345 lines (344 loc) 10.3 kB
import { defaultConfig, validateConfig, loadConfigFromJSON, mergeConfig } from "./schema.js"; function deepMerge(target, source) { const result = { ...target }; for (const key in source) { if (source[key] !== void 0 && source[key] !== null) { if (typeof source[key] === "object" && source[key] !== null && !Array.isArray(source[key])) { result[key] = deepMerge(result[key] || {}, source[key]); } else { result[key] = source[key]; } } } return result; } function mergeConfigurations(fileConfig, propsConfig) { const sources = {}; const warnings = []; let config = { ...defaultConfig }; if (fileConfig) { try { config = deepMerge(config, fileConfig); markSources(sources, fileConfig, "file"); } catch (error) { warnings.push(`Failed to merge file configuration: ${error instanceof Error ? error.message : "Unknown error"}`); } } if (propsConfig) { try { config = deepMerge(config, propsConfig); markSources(sources, propsConfig, "props"); } catch (error) { warnings.push(`Failed to merge props configuration: ${error instanceof Error ? error.message : "Unknown error"}`); } } const validation = validateConfig(config); if (!validation.isValid) { warnings.push(`Merged configuration has validation errors: ${validation.errors.map((e) => e.message).join(", ")}`); } return { config, sources, warnings }; } function markSources(sources, config, source, prefix = "") { for (const [key, value] of Object.entries(config)) { const fullKey = prefix ? `${prefix}.${key}` : key; if (typeof value === "object" && value !== null && !Array.isArray(value)) { markSources(sources, value, source, fullKey); } else { sources[fullKey] = source; } } } async function loadConfiguration(configSource) { if (!configSource) { return { config: defaultConfig, errors: [] }; } if (typeof configSource === "string") { if (configSource.trim().startsWith("{")) { return loadConfigFromJSON(configSource); } return await loadConfigFromFile(configSource); } try { const mergedConfig = mergeConfig(configSource); const validation = validateConfig(mergedConfig); if (validation.isValid) { return { config: mergedConfig, errors: [] }; } else { return { config: defaultConfig, errors: validation.errors }; } } catch (error) { return { config: defaultConfig, errors: [{ path: "root", message: `Failed to process configuration: ${error instanceof Error ? error.message : "Unknown error"}` }] }; } } async function loadConfigFromFile(filePath) { try { if (typeof window !== "undefined") { const response = await fetch(filePath); if (!response.ok) { return { config: defaultConfig, errors: [{ path: "file", message: `Failed to load configuration file: ${response.status} ${response.statusText}` }] }; } const jsonString = await response.text(); return loadConfigFromJSON(jsonString); } else { const fs = await import("../_virtual/__vite-browser-external.js"); const path = await import("../_virtual/__vite-browser-external.js"); try { const resolvedPath = path.resolve(filePath); const jsonString = await fs.readFile(resolvedPath, "utf-8"); return loadConfigFromJSON(jsonString); } catch (fsError) { return { config: defaultConfig, errors: [{ path: "file", message: `Failed to read configuration file: ${fsError instanceof Error ? fsError.message : "Unknown file system error"}` }] }; } } } catch (error) { return { config: defaultConfig, errors: [{ path: "file", message: `Failed to load configuration file: ${error instanceof Error ? error.message : "Unknown error"}` }] }; } } function createConfigPreset(preset) { const base = { ...defaultConfig }; switch (preset) { case "minimal": return { ...base, defaults: { ...base.defaults, variant: "button" }, features: { ...base.features, dragAndDrop: false, preview: false, progress: false, multipleFiles: false, removeFiles: false, retryFailed: false, showFileSize: false, showFileType: false } }; case "full-featured": return { ...base, defaults: { ...base.defaults, variant: "multi-file", multiple: true }, features: { ...base.features, dragAndDrop: true, preview: true, progress: true, multipleFiles: true, removeFiles: true, retryFailed: true, showFileSize: true, showFileType: true, chunkedUpload: true, resumableUpload: true } }; case "image-only": return { ...base, defaults: { ...base.defaults, variant: "image-only", accept: "image/*" }, validation: { ...base.validation, allowedTypes: ["image/jpeg", "image/png", "image/gif", "image/webp"], allowedExtensions: [".jpg", ".jpeg", ".png", ".gif", ".webp"], validateDimensions: true, maxWidth: 4096, maxHeight: 4096 }, features: { ...base.features, preview: true, showFileSize: true, showFileType: false } }; case "drag-drop": return { ...base, defaults: { ...base.defaults, variant: "dropzone", multiple: true }, features: { ...base.features, dragAndDrop: true, preview: true, progress: true, multipleFiles: true, removeFiles: true } }; default: return base; } } function getConfigurationErrors(config) { const validation = validateConfig(config); return validation.errors.map((error) => { const path = error.path.replace(/\./g, " → "); return `${path}: ${error.message}`; }); } function generateTypeDefinitions(config) { return ` // Generated type definitions from configuration export interface GeneratedFileUploadConfig { defaults: { variant: '${config.defaults.variant}' size: '${config.defaults.size}' radius: '${config.defaults.radius}' theme: '${config.defaults.theme}' multiple: ${config.defaults.multiple} disabled: ${config.defaults.disabled} accept: '${config.defaults.accept}' maxSize: ${config.defaults.maxSize} maxFiles: ${config.defaults.maxFiles} } // ... additional type definitions would be generated here } `; } function configToCSSProperties(config) { const cssProps = {}; Object.entries(config.styling.colors).forEach(([key, value]) => { cssProps[`--file-upload-color-${key}`] = value; }); Object.entries(config.styling.spacing).forEach(([key, value]) => { cssProps[`--file-upload-spacing-${key}`] = value; }); Object.entries(config.styling.typography).forEach(([key, value]) => { cssProps[`--file-upload-typography-${key}`] = value; }); Object.entries(config.styling.borders).forEach(([key, value]) => { cssProps[`--file-upload-border-${key}`] = value; }); Object.entries(config.styling.shadows).forEach(([key, value]) => { cssProps[`--file-upload-shadow-${key}`] = value; }); cssProps["--file-upload-animation-duration"] = `${config.animations.duration}ms`; cssProps["--file-upload-animation-easing"] = config.animations.easing; return cssProps; } function diffConfigurations(config1, config2) { const added = []; const removed = []; const changed = []; function compareObjects(obj1, obj2, path = "") { const keys1 = Object.keys(obj1 || {}); const keys2 = Object.keys(obj2 || {}); keys1.forEach((key) => { const fullPath = path ? `${path}.${key}` : key; if (!(key in obj2)) { removed.push(fullPath); } else if (typeof obj1[key] === "object" && typeof obj2[key] === "object") { compareObjects(obj1[key], obj2[key], fullPath); } else if (obj1[key] !== obj2[key]) { changed.push(fullPath); } }); keys2.forEach((key) => { const fullPath = path ? `${path}.${key}` : key; if (!(key in obj1)) { added.push(fullPath); } }); } compareObjects(config1, config2); return { added, removed, changed }; } class ConfigurationMigrator { migrations = []; addMigration(migration) { this.migrations.push(migration); this.migrations.sort((a, b) => a.version.localeCompare(b.version)); return this; } migrate(config, fromVersion, toVersion) { const appliedMigrations = []; let currentConfig = { ...config }; for (const migration of this.migrations) { if (migration.version > fromVersion && migration.version <= toVersion) { currentConfig = migration.migrate(currentConfig); appliedMigrations.push(migration.version); } } return { config: currentConfig, appliedMigrations }; } getAvailableMigrations() { return [...this.migrations]; } } new ConfigurationMigrator().addMigration({ version: "1.1.0", description: "Add accessibility configuration", migrate: (config) => ({ ...config, accessibility: { announceFileSelection: true, announceProgress: true, announceErrors: true, keyboardNavigation: true, focusManagement: true, ...config.accessibility } }) }).addMigration({ version: "1.2.0", description: "Add animation configuration", migrate: (config) => ({ ...config, animations: { enabled: true, duration: 200, easing: "ease-in-out", ...config.animations } }) }); export { ConfigurationMigrator, configToCSSProperties, createConfigPreset, deepMerge, diffConfigurations, generateTypeDefinitions, getConfigurationErrors, loadConfigFromFile, loadConfiguration, mergeConfigurations }; //# sourceMappingURL=utils.js.map