Files
AtreidesAssetsFree/client/ui/table-markup.js
T

112 lines
6.9 KiB
JavaScript
Raw Normal View History

2026-10-29 11:57:29 +01:00
// Generated, do not edit. sha256:a4f2c112e439e3cff424dfd351eedcec45c341af567633d67ac86c751e1e4999
import { esc } from './escape.js';
export function cellClasses(col) {
const classes = [];
if (col.align === 'num') classes.push('num');
else if (col.align === 'center') classes.push('center');
if (col.text) classes.push('text');
return classes;
}
export function cellClassAttr(col) {
const classes = cellClasses(col);
return classes.length ? ` class="${classes.join(' ')}"` : '';
}
export function cellHtml(col, row) {
const raw = col.render ? col.render(row) : esc(row[col.key] ?? '');
const content = col.text ? `<div class="cell-clamp">${raw}</div>` : raw;
return `<td${cellClassAttr(col)} data-label="${esc(col.label || '')}">${content}</td>`;
}
export function expCellHtml(hasExpansion, isOpen, chevron) {
if (!hasExpansion) return '';
return `<td class="exp-cell"><button type="button" class="chev-btn${isOpen ? ' is-open is-accent' : ''}" tabindex="-1" aria-hidden="true">${chevron}</button></td>`;
}
export function headRowHtml({ columns, sort, hasExpansion, hasActions }) {
const ths = columns.map(col => {
const classes = cellClasses(col);
const label = esc(col.label || '');
if (!col.sortable) return `<th${cellClassAttr(col)}>${label}</th>`;
const active = sort && sort.key === col.key;
const arrow = active ? `<span class="sort-arrow" aria-hidden="true">${sort.dir === 'asc' ? '▲' : '▼'}</span>` : '';
const ariaSort = active ? ` aria-sort="${sort.dir === 'asc' ? 'ascending' : 'descending'}"` : '';
return `<th class="${[...classes, 'th-sort'].join(' ')}" data-sort="${esc(col.key)}" tabindex="0"${ariaSort}>${label}${arrow}</th>`;
}).join('');
const expTh = hasExpansion ? '<th class="exp-cell"></th>' : '';
return `<tr>${expTh}${ths}${hasActions ? '<th class="actions-cell"></th>' : ''}</tr>`;
}
const PLUS = '<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round"><line x1="12" y1="5" x2="12" y2="19"/><line x1="5" y1="12" x2="19" y2="12"/></svg>';
export function controlsHtml({ cfg, labels, canCreate, groupCols, groupKey, groupKey2, groupKey3, search, editing }) {
const dis = editing ? ' disabled' : '';
const newLabel = esc(cfg.newLabel || labels.new);
const newBtn = canCreate
? `<button class="btn btn-new" data-tbl-new title="${newLabel}" aria-label="${newLabel}"${dis}>${PLUS}</button>`
: '';
const noteText = typeof cfg.toolbarNote === 'function' ? cfg.toolbarNote() : cfg.toolbarNote;
const note = noteText ? `<span class="tbl-note">${esc(noteText)}</span>` : '';
const toolbarBtns = (cfg.toolbarActions || []).map(a => a.icon
? `<button class="btn btn-new" data-tbl-act="${esc(a.id)}" title="${esc(a.title || a.label)}" aria-label="${esc(a.title || a.label)}"${dis}>${a.icon}</button>`
: `<button class="btn btn-ghost" data-tbl-act="${esc(a.id)}" title="${esc(a.title || a.label)}"${dis}>${esc(a.label)}</button>`).join('');
let group = '';
if (groupCols.length && cfg.groupChoice !== false) {
const noneText = esc(cfg.noneLabel || labels.none);
const aria = ` aria-label="${esc(cfg.groupLabel || cfg.noneLabel || labels.none)}"`;
const none = `<option value="">${noneText}</option>`;
const opt = (c, chosen) => `<option value="${esc(c.key)}"${c.key === chosen ? ' selected' : ''}>${esc(c.label)}</option>`;
const select = (level, cols, chosen) =>
`<select class="input tbl-group-select" data-tbl-group="${level}"${aria}${dis}>${none}${cols.map(c => opt(c, chosen)).join('')}</select>`;
group = select(1, groupCols, groupKey);
if (groupKey) {
group += select(2, groupCols.filter(c => c.key !== groupKey), groupKey2);
if (groupKey2) {
const cols3 = groupCols.filter(c => c.key !== groupKey && c.key !== groupKey2);
if (cols3.length) group += select(3, cols3, groupKey3);
}
}
}
const searchText = esc(cfg.searchLabel || labels.search);
const searchBox = `<input class="input tbl-search" data-tbl-search type="search" placeholder="${searchText}" aria-label="${searchText}" value="${esc(search)}"${dis}>`;
const filterSlot = groupKey && cfg.groupChoice !== false && cfg.columns.some(c => c.key === groupKey)
? '<div class="tbl-filter" data-gfilter></div>' : '';
const countBox = `<div class="tbl-count" title="${esc(cfg.countLabel || labels.count)}">${cfg.rows.length}</div>`;
return `<div class="table-controls">${newBtn}${note}${toolbarBtns}${group}${searchBox}${filterSlot}${countBox}</div>`;
}
const sameId = (a, b) => a != null && b != null && String(a) === String(b);
export function editActionsHtml({ cfg, labels }) {
return `<div class="row-actions edit-actions">
<button class="btn btn-primary btn-xs" data-save="1">${esc(cfg.saveLabel || labels.save)}</button>
<button class="btn btn-ghost btn-xs" data-cancel="1">${esc(cfg.cancelLabel || labels.cancel)}</button>
</div>`;
}
export function actionsCellHtml({ cfg, labels, row, idKey, isEditRow, editing, selectedId }) {
if (isEditRow || editing) return '<td class="actions-cell"></td>';
const id = row[idKey];
if (!sameId(id, selectedId)) return '<td class="actions-cell"></td>';
const eid = esc(id);
const extra = (cfg.extraActions || []).map(a =>
`<button class="btn btn-secondary btn-xs" data-action="${esc(a.key)}" data-id="${eid}">${esc(a.label)}</button>`).join('');
const edit = (cfg.onSave && (!cfg.rowEditable || cfg.rowEditable(row))) ? `<button class="btn btn-secondary btn-xs" data-edit="${eid}" title="${esc(labels.edit)}">✎</button>` : '';
const del = (cfg.onDelete && (!cfg.rowDeletable || cfg.rowDeletable(row))) ? `<button class="btn btn-danger btn-xs" data-del="${eid}">✕</button>` : '';
return `<td class="actions-cell"><div class="row-actions">${extra}${edit}${del}</div></td>`;
}
export function groupHeaderRowHtml({ token, label, count, summaryHtml = '', isCollapsed = false, collapsible, colCount, chevron }) {
const chev = collapsible ? `<button type="button" class="chev-btn is-accent${isCollapsed ? '' : ' is-open'}" tabindex="-1" aria-hidden="true">${chevron}</button>` : '';
const attrs = collapsible ? ` data-gcollapse="${esc(token)}" role="button" tabindex="0"` : '';
const sum = summaryHtml ? `<span class="group-summary">${summaryHtml}</span>` : '';
return `<tr class="group-row${isCollapsed ? ' is-collapsed' : ''}"><td colspan="${colCount}"><div class="group-header"${attrs}>${chev}<span>${esc(label)}</span><span class="group-count">${count}</span>${sum}</div></td></tr>`;
}
export function frameHtml({ controls, head, body, hasExpansion, title = '' }) {
const wrapClasses = hasExpansion ? ' sticky-sel has-exp' : '';
const titleHtml = title ? `<div class="tbl-title t-label">${esc(title)}</div>` : '';
return `<div class="table-frame"><div class="table-controls-bar">${titleHtml}${controls}</div><div class="table-wrap${wrapClasses}"><table><thead>${head}</thead><tbody>${body}</tbody></table></div></div>`;
}