ZDDC/tables/js/filters.js
2026-06-11 13:32:31 -05:00

68 lines
2.2 KiB
JavaScript

(function (app) {
'use strict';
// A filter is per-column and has one of two shapes:
// - free-text: { kind: 'contains', value: '<string>' }
// - enum: { kind: 'enum', value: ['<choice>', ...] }
// An empty value (empty string or empty array) matches everything.
//
// The render layer only emits the free-text shape; enum is kept here
// for back-compat with any inline-context test fixtures that seed
// filter state directly. defaultFilterFor always returns text.
function isEnumColumn(col) {
return Array.isArray(col.enum) && col.enum.length > 0;
}
function defaultFilterFor(_col) {
return { kind: 'contains', value: '' };
}
function rowMatches(filter, cellValue) {
if (filter.kind === 'enum') {
if (!Array.isArray(filter.value) || filter.value.length === 0) {
return true;
}
const s = cellValue == null ? '' : String(cellValue);
return filter.value.indexOf(s) !== -1;
}
// contains
if (!filter.value) {
return true;
}
const needle = String(filter.value).toLowerCase();
const hay = cellValue == null ? '' : String(cellValue).toLowerCase();
return hay.indexOf(needle) !== -1;
}
function isEmpty(filter) {
if (filter.kind === 'enum') {
return !Array.isArray(filter.value) || filter.value.length === 0;
}
return !filter.value;
}
function apply(rows, columns, filterMap, resolveField) {
return rows.filter(function (row) {
for (let i = 0; i < columns.length; i++) {
const col = columns[i];
const filter = filterMap[col.field];
if (!filter || isEmpty(filter)) {
continue;
}
const cellValue = resolveField(row.data, col.field);
if (!rowMatches(filter, cellValue)) {
return false;
}
}
return true;
});
}
app.modules.filters = {
defaultFilterFor: defaultFilterFor,
isEnumColumn: isEnumColumn,
isEmpty: isEmpty,
apply: apply
};
})(window.tablesApp);