(function (app) { 'use strict'; function renderHeader(theadEl, columns, sortState, filterMap, onHeaderClick, onFilterChange) { const util = app.modules.util; const filters = app.modules.filters; const sort = app.modules.sort; theadEl.innerHTML = ''; const titleRow = util.h('tr', { className: 'zddc-table__title-row' }); const filterRow = util.h('tr', { className: 'zddc-table__filter-row' }); for (let i = 0; i < columns.length; i++) { const col = columns[i]; const indicator = sort.indicator(sortState, col.field); const th = util.h('th', { className: 'zddc-table__th', 'data-field': col.field, style: col.width ? 'width:' + col.width : null, onClick: function (ev) { onHeaderClick(col.field, ev.shiftKey); } }, col.title || col.field, indicator); titleRow.appendChild(th); const td = util.h('td', { className: 'zddc-table__filter-cell' }); // Every column gets the same text-contains filter input, even // enum columns — keeps the filter row visually uniform and // doesn't constrain users to picking from the enum (a // case-insensitive substring match works for both free-text // and enum data). const f = filterMap[col.field] || filters.defaultFilterFor(col); const input = util.h('input', { type: 'text', className: 'zddc-table__filter-text', placeholder: 'filter…', 'aria-label': 'Filter ' + (col.title || col.field), value: typeof f.value === 'string' ? f.value : '', onInput: function (ev) { onFilterChange(col.field, { kind: 'contains', value: ev.target.value }); } }); td.appendChild(input); filterRow.appendChild(td); } theadEl.appendChild(titleRow); theadEl.appendChild(filterRow); } function renderBody(tbodyEl, rows, columns) { const util = app.modules.util; const editor = app.modules.editor; tbodyEl.innerHTML = ''; for (let i = 0; i < rows.length; i++) { const row = rows[i]; const tr = util.h('tr', { className: 'zddc-table__row' + (row.editable ? ' zddc-table__row--editable' : ' zddc-table__row--readonly'), 'data-url': row.url, 'data-editable': row.editable ? '1' : '0' }); const rowId = editor ? editor.rowKey(row) : (row.url || ''); if (editor) { editor.attachToRow(tr, rowId); } for (let c = 0; c < columns.length; c++) { const col = columns[c]; // Editor's draft buffer overrides the row's stored value // until Phase 3 commits it. Falls back to row.data when // no draft is present. const value = editor ? editor.effectiveCellValue(row, col) : util.resolveField(row.data, col.field); const text = util.formatCell(value, col.format); const td = util.h('td', { className: 'zddc-table__cell' }, text); if (editor) { editor.attachToCell(td, i, c); } tr.appendChild(td); } tbodyEl.appendChild(tr); } } function renderRowCount(el, displayed, total) { if (!el) return; if (displayed === total) { el.textContent = total + (total === 1 ? ' row' : ' rows'); } else { el.textContent = displayed + ' of ' + total + ' rows'; } } app.modules.render = { header: renderHeader, body: renderBody, rowCount: renderRowCount }; })(window.tablesApp);