/**
* 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 `
${items.length === 0 ? ComponentHelpers.renderEmpty(`No ${title.toLowerCase()} found`, '📦') : `
${columns.map(col => `
|
${ComponentHelpers.escapeHtml(col.label)}
|
`).join('')}
${items.map((item, itemIdx) => `
${columns.map(col => `
|
${col.render ? col.render(item) : ComponentHelpers.escapeHtml(String(item[col.key] || ''))}
|
`).join('')}
`).join('')}
`}
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) => `
${item.src ? `
})
` : '
📄
'}
${ComponentHelpers.escapeHtml(item.title || 'Untitled')}
${item.subtitle ? `
${ComponentHelpers.escapeHtml(item.subtitle)}
` : ''}
${onDelete ? `
` : ''}
`).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)}
`;
}
/**
* 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();
});
}
}