import { addFiles, onFilesChange } from './table/on_files_change.js'; import { onFolderChange } from './table/on_folder_change.js'; import { findNode, flattenFiles, render } from './table/render.js'; import { esc } from './table/esc.js'; const DEFAULTS = { allowedExt: [], labels: { pickFiles: 'Выбрать файлы', pickFolder: 'Выбрать папку', clear: 'Очистить', empty: 'Нет выбранных файлов', statusReady: 'готов', remove: 'Удалить', columns: { path: 'Путь', size: 'Размер', status: 'Статус' }, count: (count, bytes) => `${count} файлов · ${bytes} B`, }, layout: { columns: ['path', 'size', 'status'], controls: ['files', 'folder', 'clear'] }, limits: { maxEntries: 1000, maxTotalBytes: 100 * 1024 * 1024, maxEntryBytes: 50 * 1024 * 1024, maxDepth: 20, }, }; function resolveMount(mount) { const element = typeof mount === 'string' ? document.querySelector(mount) : mount; if (!element) throw new Error('File picker mount element не найден'); return element; } function normalizeConfig(config) { return { ...config, allowedExt: config.allowedExt || DEFAULTS.allowedExt, labels: { ...DEFAULTS.labels, ...(config.labels || {}), columns: { ...DEFAULTS.labels.columns, ...(config.labels?.columns || {}) }, }, layout: { ...DEFAULTS.layout, ...(config.layout || {}) }, limits: { ...DEFAULTS.limits, ...(config.limits || {}) }, }; } function injectStyles() { if (document.querySelector('style[data-file-picker]')) return; const style = document.createElement('style'); style.dataset.filePicker = ''; style.textContent = ` .file-picker { color: #17211b; font-family: Georgia, 'Times New Roman', serif; } .file-picker .fp-toolbar { display: flex; flex-wrap: wrap; gap: 10px; margin: 16px 0; } .file-picker .fp-toolbar button { border: 0; border-radius: 4px; padding: 12px 17px; color: #f7faf4; background: #1f5a3b; font: 700 13px sans-serif; cursor: pointer; } .file-picker .fp-toolbar button.secondary { color: #1f5a3b; background: #cbdcc9; } .file-picker .fp-toolbar button.quiet { color: #5d6a61; background: transparent; } .file-picker .fp-table-wrap { overflow-x: auto; border-top: 1px solid #b9c7ba; } .file-picker .fp-table { width: 100%; border-collapse: collapse; font: 14px/1.4 sans-serif; } .file-picker .fp-table th, .file-picker .fp-table td { padding: 14px 10px; border-bottom: 1px solid #cbd5cc; text-align: left; } .file-picker .fp-table th { color: #56745f; font-size: 11px; text-transform: uppercase; letter-spacing: 1px; } .file-picker .fp-status, .file-picker .fp-count { color: #5d6a61; font: 13px sans-serif; } .file-picker .fp-count { min-height: 22px; } .file-picker .tree-name { display: inline-flex; align-items: center; gap: 8px; min-width: 240px; padding-top: 0; padding-bottom: 0; border: 0; background: transparent; color: #17211b; font: inherit; text-align: left; } .file-picker .tree-group { padding-left: 0; cursor: pointer; } .file-picker .tree-chevron { display: inline-block; width: 12px; color: #78917d; font-size: 10px; } .file-picker .remove-btn { padding: 2px 7px; color: #8c5148; background: transparent; font: 20px/1 sans-serif; } .file-picker .tree-row td:first-child { white-space: nowrap; } @media (max-width: 600px) { .file-picker .fp-toolbar button { flex: 1 1 42%; } } `; document.head.append(style); } function buildMarkup(root, cfg) { const controls = cfg.layout.controls || []; const control = (name, id, label, className = '') => controls.includes(name) ? `` : ''; const columns = cfg.layout.columns?.length ? cfg.layout.columns : Object.keys(cfg.labels.columns); const headings = columns.map((column) => `