107 lines
4.6 KiB
JavaScript
107 lines
4.6 KiB
JavaScript
(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 = '';
|
|
|
|
// Required fields come from the row schema (form.yaml schema.required).
|
|
const ctx = app.context || {};
|
|
const reqList = (ctx.rowSchema && Array.isArray(ctx.rowSchema.required)) ? ctx.rowSchema.required : [];
|
|
const requiredSet = {};
|
|
for (let r = 0; r < reqList.length; r++) requiredSet[reqList[r]] = true;
|
|
|
|
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 isReq = !!requiredSet[col.field];
|
|
const th = util.h('th', {
|
|
className: 'zddc-table__th' + (isReq ? ' zddc-table__th--required' : ''),
|
|
'data-field': col.field,
|
|
title: isReq ? 'Required' : null,
|
|
style: col.width ? 'width:' + col.width : null,
|
|
onClick: function (ev) { onHeaderClick(col.field, ev.shiftKey); }
|
|
}, col.title || col.field, indicator);
|
|
// Mandatory marker — a red asterisk after the column title.
|
|
if (isReq) th.insertBefore(util.h('span', { className: 'zddc-table__req', 'aria-hidden': 'true' }, ' *'), th.childNodes[1] || null);
|
|
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);
|