/** * tool-templates.js * Reusable template functions for building team-specific tool components * Follows DRY principles to avoid code duplication across 14 team tools */ import { ComponentHelpers } from './component-helpers.js'; import toolBridge from '../services/tool-bridge.js'; /** * Create a side-by-side comparison view * Used for: Storybook/Figma, Storybook/Live, Figma/Live comparisons * * @param {Object} config * @param {string} config.leftTitle - Title for left panel * @param {string} config.rightTitle - Title for right panel * @param {string} config.leftSrc - URL or content for left panel * @param {string} config.rightSrc - URL or content for right panel * @param {Function} config.onSync - Optional sync scroll callback * @returns {string} HTML template */ export function createComparisonView(config) { const { leftTitle = 'Left', rightTitle = 'Right', leftSrc = '', rightSrc = '', onSync = null } = config; return `
Use mouse wheel to zoom, drag to pan
${ComponentHelpers.escapeHtml(leftTitle)}
${leftSrc ? `` : ComponentHelpers.renderEmpty('Select content to display', '📄')}
${ComponentHelpers.escapeHtml(rightTitle)}
${rightSrc ? `` : ComponentHelpers.renderEmpty('Select content to display', '📄')}
`; } /** * Create a list view with search, filter, and actions * Used for: Token list, Asset list, Component list * * @param {Object} config * @param {string} config.title - List title * @param {Array} config.items - Array of items to display * @param {Array} config.columns - Column definitions [{ key, label, render }] * @param {Array} config.actions - Action buttons [{ label, icon, onClick }] * @param {Function} config.onSearch - Search callback * @param {Function} config.onFilter - Filter callback * @returns {string} HTML template */ export function createListView(config) { const { title = 'Items', items = [], columns = [], actions = [], onSearch = null, onFilter = null } = config; return `

${ComponentHelpers.escapeHtml(title)}

${actions.map((action, idx) => ` `).join('')}
${items.length === 0 ? ComponentHelpers.renderEmpty(`No ${title.toLowerCase()} found`, '📦') : ` ${columns.map(col => ` `).join('')} ${items.map((item, itemIdx) => ` ${columns.map(col => ` `).join('')} `).join('')}
${ComponentHelpers.escapeHtml(col.label)}
${col.render ? col.render(item) : ComponentHelpers.escapeHtml(String(item[col.key] || ''))}
`}
Showing ${items.length} ${title.toLowerCase()}
`; } /** * Create an editor view with save/export functionality * Used for: ESRE editor, configuration editors * * @param {Object} config * @param {string} config.title - Editor title * @param {string} config.content - Initial content * @param {string} config.language - Syntax highlighting language (text, json, yaml, etc.) * @param {Function} config.onSave - Save callback * @param {Function} config.onExport - Export callback * @returns {string} HTML template */ export function createEditorView(config) { const { title = 'Editor', content = '', language = 'text', onSave = null, onExport = null } = config; return `

${ComponentHelpers.escapeHtml(title)}

0 lines, 0 characters Language: ${language}
`; } /** * Create a gallery/grid view for visual content * Used for: Screenshot gallery, navigation demos * * @param {Object} config * @param {string} config.title - Gallery title * @param {Array} config.items - Array of items with { id, src, title, subtitle } * @param {Function} config.onItemClick - Item click callback * @param {Function} config.onDelete - Delete callback * @returns {string} HTML template */ export function createGalleryView(config) { const { title = 'Gallery', items = [], onItemClick = null, onDelete = null } = config; return `

${ComponentHelpers.escapeHtml(title)}

${items.length} ${items.length === 1 ? 'item' : 'items'}
${items.length === 0 ? ComponentHelpers.renderEmpty(`No ${title.toLowerCase()} available`, '🖼️') : `
${items.map((item, idx) => ` `).join('')}
`}
`; } /** * Create a form view with validation * Used for: Project analysis configuration, quick wins settings * * @param {Object} config * @param {string} config.title - Form title * @param {Array} config.fields - Field definitions [{ name, label, type, placeholder, required }] * @param {Function} config.onSubmit - Submit callback * @returns {string} HTML template */ export function createFormView(config) { const { title = 'Configuration', fields = [], onSubmit = null } = config; return `

${ComponentHelpers.escapeHtml(title)}

${fields.map(field => `
${field.type === 'textarea' ? ` ` : field.type === 'select' ? ` ` : ` `} ${field.description ? `
${ComponentHelpers.escapeHtml(field.description)}
` : ''}
`).join('')}
`; } /** * Setup event handlers for comparison view */ export function setupComparisonHandlers(container, config) { const syncBtn = container.querySelector('#sync-scroll-btn'); const resetBtn = container.querySelector('#reset-zoom-btn'); const leftPanel = container.querySelector('#left-panel-content'); const rightPanel = container.querySelector('#right-panel-content'); let syncEnabled = false; if (syncBtn && leftPanel && rightPanel) { syncBtn.addEventListener('click', () => { syncEnabled = !syncEnabled; syncBtn.textContent = syncEnabled ? '🔗 Synced' : '🔗 Sync Scroll'; if (syncEnabled) { leftPanel.addEventListener('scroll', syncScroll); rightPanel.addEventListener('scroll', syncScroll); } else { leftPanel.removeEventListener('scroll', syncScroll); rightPanel.removeEventListener('scroll', syncScroll); } }); function syncScroll(e) { if (!syncEnabled) return; const source = e.target; const target = source === leftPanel ? rightPanel : leftPanel; target.scrollTop = source.scrollTop; target.scrollLeft = source.scrollLeft; } } if (resetBtn) { resetBtn.addEventListener('click', () => { const iframes = container.querySelectorAll('iframe'); iframes.forEach(iframe => { iframe.style.transform = 'scale(1)'; }); }); } } /** * Setup event handlers for list view */ export function setupListHandlers(container, config) { const searchInput = container.querySelector('#search-input'); const filterSelect = container.querySelector('#filter-select'); const actionBtns = container.querySelectorAll('.action-btn'); if (searchInput && config.onSearch) { searchInput.addEventListener('input', (e) => { config.onSearch(e.target.value); }); } if (filterSelect && config.onFilter) { filterSelect.addEventListener('change', (e) => { config.onFilter(e.target.value); }); } if (actionBtns && config.actions) { actionBtns.forEach(btn => { btn.addEventListener('click', () => { const idx = parseInt(btn.dataset.actionIdx); if (config.actions[idx] && config.actions[idx].onClick) { config.actions[idx].onClick(); } }); }); } } /** * Setup event handlers for editor view */ export function setupEditorHandlers(container, config) { const saveBtn = container.querySelector('#editor-save-btn'); const exportBtn = container.querySelector('#editor-export-btn'); const clearBtn = container.querySelector('#editor-clear-btn'); const textarea = container.querySelector('#editor-content'); const stats = container.querySelector('#editor-stats'); function updateStats() { if (textarea && stats) { const lines = textarea.value.split('\n').length; const chars = textarea.value.length; stats.textContent = `${lines} lines, ${chars} characters`; } } if (textarea) { textarea.addEventListener('input', updateStats); updateStats(); } if (saveBtn && config.onSave) { saveBtn.addEventListener('click', () => { config.onSave(textarea.value); }); } if (exportBtn && config.onExport) { exportBtn.addEventListener('click', () => { config.onExport(textarea.value); }); } if (clearBtn && textarea) { clearBtn.addEventListener('click', () => { if (confirm('Clear all content?')) { textarea.value = ''; updateStats(); } }); } } /** * Setup event handlers for gallery view */ export function setupGalleryHandlers(container, config) { const items = container.querySelectorAll('.gallery-item'); const deleteBtns = container.querySelectorAll('.gallery-delete-btn'); if (items && config.onItemClick) { items.forEach(item => { item.addEventListener('click', (e) => { // Don't trigger if delete button was clicked if (e.target.classList.contains('gallery-delete-btn')) return; const idx = parseInt(item.dataset.itemIdx); config.onItemClick(config.items[idx], idx); }); }); } if (deleteBtns && config.onDelete) { deleteBtns.forEach(btn => { btn.addEventListener('click', (e) => { e.stopPropagation(); const idx = parseInt(btn.dataset.itemIdx); if (confirm('Delete this item?')) { config.onDelete(config.items[idx], idx); } }); }); } } /** * Setup event handlers for form view */ export function setupFormHandlers(container, config) { const form = container.querySelector('#config-form'); const cancelBtn = container.querySelector('#form-cancel-btn'); if (form && config.onSubmit) { form.addEventListener('submit', (e) => { e.preventDefault(); const formData = new FormData(form); const data = Object.fromEntries(formData.entries()); config.onSubmit(data); }); } if (cancelBtn) { cancelBtn.addEventListener('click', () => { form.reset(); }); } }