add custom tag filter

This commit is contained in:
Ivymaster
2026-06-27 12:29:28 +02:00
parent c47ac926d2
commit 4ed87b5c2b
2 changed files with 497 additions and 356 deletions
@@ -1,7 +1,7 @@
import { CoreFilterCmpt } from "../../filter/Filter.js";
import ListNew from "./List/New.js";
import CoreTag from "../../Tag/Tag.js";
import { tagHeaderFilter } from "../../../tabulator/filters/extendedHeaderFilter.js";
import { buildTagHeaderFilterExpression, buildTagOptionsFromRows, customTagFilter, setTagHeaderFilterValue, tagHeaderFilter } from "../../../tabulator/filters/extendedHeaderFilter.js";
import {
addTagInTable,
deleteTagInTable,
@@ -64,8 +64,11 @@ export default {
}
return {
initialTagData: [],
selectedTagOptions: [],
tagFilterState: {
initialOptions: [],
selectedOptions: [],
},
selectedColumnValues: [],
tabulatorOptions: {
columns: [
{ title: "UID", field: "uid", headerFilter: true },
@@ -398,7 +401,7 @@ export default {
{
event: "dataProcessed",
handler: (data) => {
console.log("dataProcessed", data);
//console.log("dataProcessed", data);
this.getAllRows();
this.autoSelectRows(data);
},
@@ -406,7 +409,7 @@ export default {
{
event: "dataLoaded",
handler: (data) => {
console.log("dataLoaded", data);
//console.log("dataLoaded", data);
if (Array.isArray(data)) {
this.count = data.length;
this.allPrestudents = data.map((item) => item.prestudent_id);
@@ -414,12 +417,13 @@ export default {
this.count = 0;
this.allPrestudents = [];
}
this.refreshTagHeaderFilterInitialOptions(Array.isArray(data) ? data : []);
},
},
{
event: "dataFiltered",
handler: (filters, rows) => {
console.log("dataFiltered", filters, rows);
//console.log("dataFiltered", filters, rows);
this.filteredcount = rows.length;
},
},
@@ -430,14 +434,14 @@ export default {
{
event: "dataTreeRowExpanded",
handler: (data) => {
console.log("dataTreeRowExpanded", data);
//console.log("dataTreeRowExpanded", data);
this.getExpandedRows();
},
},
{
event: "dataTreeRowCollapsed",
handler: (data) => {
console.log("dataTreeRowCollapsed", data);
//console.log("dataTreeRowCollapsed", data);
this.getExpandedRows();
},
},
@@ -448,7 +452,7 @@ export default {
{
event: "columnWidth",
handler: (column) => {
console.log("columnWidth", column);
//console.log("columnWidth", column);
if (column.getField() !== "tags") return;
column.getCells().forEach((cell) => {
@@ -465,7 +469,6 @@ export default {
selectedcount: 0,
//tags
expanded: [],
selectedColumnValues: [],
tagEndpoint: ApiTag,
currentEndpoint: null,
headerFilterActive: false,
@@ -561,175 +564,13 @@ export default {
title: "Tags",
field: "tags",
tooltip: false,
headerFilter: "list",
headerFilter: customTagFilter,
headerFilterParams: {
valuesLookup: () => {
console.log("headerFilterParams.valuesLookup called");
const options = new Map();
this.$refs.table.tabulator.getData("active").forEach((row) => {
let tags = row.tags;
if (typeof tags === "string") {
try {
tags = JSON.parse(tags);
} catch {
return;
}
}
if (!Array.isArray(tags)) return;
tags
.filter((tag) => tag && tag.done !== true)
.forEach((tag) => {
options.set(tag.beschreibung, {
label: tag.beschreibung,
value: tag.beschreibung,
style: tag.style,
});
});
});
return [
{
isHeader: true,
label: "test",
subOptions: [...options.values()],
},
];
},
itemFormatter: (value, label, item) => {
console.log("value, label, item", value, label, item);
if (!item.isHeader) return "<span class='m-0 p-0'></span>";
let table = document.createElement("table");
table.className = "table table-sm mb-0";
table.appendChild(document.createElement("thead"));
table
.querySelector("thead")
.appendChild(document.createElement("tr"));
let firstHeading = document.createElement("th");
firstHeading.innerHTML = "<strong>" + value + "</strong>";
table.querySelector("tr").appendChild(firstHeading);
let secondHeading = document.createElement("th");
secondHeading.innerHTML = "<strong>AND</strong>";
table.querySelector("tr").appendChild(secondHeading);
let thirdHeading = document.createElement("th");
thirdHeading.innerHTML = "<strong>OR</strong>";
table.querySelector("tr").appendChild(thirdHeading);
let fourthHeading = document.createElement("th");
fourthHeading.innerHTML = "<strong>NOT</strong>";
table.querySelector("tr").appendChild(fourthHeading);
table.appendChild(document.createElement("tbody"));
item.subOptions.forEach((option) => {
let row = document.createElement("tr");
let firstColumn = document.createElement("td");
let optionLabel = document.createElement("span");
optionLabel.className = "tag " + option.style;
optionLabel.innerText = option.label;
firstColumn.appendChild(optionLabel);
row.appendChild(firstColumn);
let secondColumn = document.createElement("td");
let firstCheckbox = document.createElement("input");
firstCheckbox.type = "checkbox";
firstCheckbox.label = "test";
firstCheckbox.style.cursor = "pointer";
firstCheckbox.checked = this.selectedTagOptions.some(
(tag) => tag.value === option.value && tag.connector === "AND",
);
firstCheckbox.addEventListener("click", (event) => {
event.stopPropagation();
if (event.target.checked) {
this.selectedTagOptions.push({
label: option.label,
value: option.value,
connector: "AND",
});
} else {
this.selectedTagOptions = this.selectedTagOptions.filter(
(tag) =>
tag.value !== option.value || tag.connector !== "AND",
);
}
console.log("selectedTagOptions", this.selectedTagOptions);
});
secondColumn.appendChild(firstCheckbox);
row.appendChild(secondColumn);
let thirdColumn = document.createElement("td");
let secondCheckbox = document.createElement("input");
secondCheckbox.type = "checkbox";
secondCheckbox.label = "test";
secondCheckbox.style.cursor = "pointer";
secondCheckbox.checked = this.selectedTagOptions.some(
(tag) => tag.value === option.value && tag.connector === "OR",
);
secondCheckbox.addEventListener("click", (event) => {
event.stopPropagation();
if (event.target.checked) {
this.selectedTagOptions.push({
label: option.label,
value: option.value,
connector: "OR",
});
} else {
this.selectedTagOptions = this.selectedTagOptions.filter(
(tag) =>
tag.value !== option.value || tag.connector !== "OR",
);
}
});
thirdColumn.appendChild(secondCheckbox);
row.appendChild(thirdColumn);
let fourthColumn = document.createElement("td");
let thirdCheckbox = document.createElement("input");
thirdCheckbox.type = "checkbox";
thirdCheckbox.label = "test";
thirdCheckbox.style.cursor = "pointer";
thirdCheckbox.checked = this.selectedTagOptions.some(
(tag) => tag.value === option.value && tag.connector === "NOT",
);
thirdCheckbox.addEventListener("click", (event) => {
event.stopPropagation();
console.log(event.target.checked);
if (event.target.checked) {
this.selectedTagOptions.push({
label: option.label,
value: option.value,
connector: "NOT",
});
} else {
this.selectedTagOptions = this.selectedTagOptions.filter(
(tag) =>
tag.value !== option.value || tag.connector !== "NOT",
);
}
});
fourthColumn.appendChild(thirdCheckbox);
row.appendChild(fourthColumn);
row.addEventListener("click", (event) => {
event.stopPropagation();
});
table.querySelector("tbody").appendChild(row);
});
return table;
},
listOnEmpty: true,
autocomplete: true,
sort: "asc",
initialOptions: this.tagFilterState.initialOptions,
selectedOptions: this.tagFilterState.selectedOptions,
},
headerFilterFunc: tagHeaderFilter,
headerFilterFuncParams: { field: "tags" },
@@ -746,33 +587,18 @@ export default {
this.translateTabulator();
}
},
selectedTagOptions: {
"tagFilterState.selectedOptions": {
handler() {
let combinedFilterStatement = null;
const selectedOptions = this.tagFilterState.selectedOptions;
const combinedFilterStatement = buildTagHeaderFilterExpression(
selectedOptions,
);
this.selectedTagOptions.forEach((tagOption) => {
const { value, connector } = tagOption;
let mappedConnector = connector === "AND" ? "&&" : connector === "OR" ? "||" : "!";
if (connector === "NOT") {
combinedFilterStatement = combinedFilterStatement
? `${combinedFilterStatement} && ${mappedConnector + value}`
: value;
}
else {
combinedFilterStatement = combinedFilterStatement
? `${combinedFilterStatement} ${mappedConnector} ${value}`
: value;
}
});
this.$refs.table.tabulator.setHeaderFilterValue(
"tags",
setTagHeaderFilterValue(
this.$refs.table.tabulator,
combinedFilterStatement,
);
this.$emit("filterActive", this.selectedTagOptions.length > 0);
console.log("selectedTagOptions changed:");
console.log(combinedFilterStatement);
this.$emit("filterActive", selectedOptions.length > 0);
},
deep: true,
},
@@ -868,7 +694,7 @@ export default {
this.$refs.new.open();
},
rowSelectionChanged(data, rows, selected, deselected) {
console.log("rowSelectionChanged", data, rows, selected, deselected);
//console.log("rowSelectionChanged", data, rows, selected, deselected);
this.selectedcount = data.length;
if (selected.length > 0 || deselected.length > 0) {
@@ -915,8 +741,8 @@ export default {
updateUrl(endpoint, first) {
this.lastSelected = first ? undefined : this.selected;
/* console.log('function param endpoint: ' + JSON.stringify(endpoint));
console.log('current endpoint: ' + JSON.stringify(this.currentEndpoint));*/
/* //console.log('function param endpoint: ' + JSON.stringify(endpoint));
//console.log('current endpoint: ' + JSON.stringify(this.currentEndpoint));*/
if (endpoint === undefined && this.currentEndpoint === null) {
endpoint = { url: "" };
@@ -1021,7 +847,7 @@ export default {
}
},
handleRowClick(e, row) {
console.log("handleRowClick", e, row);
//console.log("handleRowClick", e, row);
// TODO(chris): this should be in the filter component
if (this.focusObj) {
let el = row.getElement();
@@ -1035,12 +861,34 @@ export default {
//methods tags
addedTag(addedTag) {
addTagInTable(addedTag, this.allRows, "prestudent_id");
this.refreshTagHeaderFilterInitialOptions();
},
deletedTag(deletedTag) {
console.log("deletedTag", deletedTag);
deleteTagInTable(deletedTag, this.allRows);
this.refreshTagHeaderFilterInitialOptions();
},
updatedTag(updatedTag) {
updateTagInTable(updatedTag, this.allRows);
this.refreshTagHeaderFilterInitialOptions();
},
refreshTagHeaderFilterInitialOptions(rows) {
const data =
rows || this.$refs.table?.tabulator?.getData() || [];
const options = buildTagOptionsFromRows(data);
this.tagFilterState.initialOptions.splice(
0,
this.tagFilterState.initialOptions.length,
...options,
);
const availableValues = new Set(options.map((option) => option.value));
for (let i = this.tagFilterState.selectedOptions.length - 1; i >= 0; i--) {
if (!availableValues.has(this.tagFilterState.selectedOptions[i].value)) {
this.tagFilterState.selectedOptions.splice(i, 1);
}
}
},
getAllRows() {
this.allRows = this.$refs.table.tabulator.getRows();
@@ -1053,7 +901,7 @@ export default {
this.headerFilterActive = filterActive;
},
handleMouseDown(e, row) {
console.log("handleMouseDown", e, row);
//console.log("handleMouseDown", e, row);
let data = row.getData();
let id = data.uid ?? data.prestudent_id ?? data.person_id;
@@ -1065,12 +913,12 @@ export default {
isAlreadySelected && this.selected?.length ? this.selected : [data];
},
handleDataLoading() {
console.log("handleDataLoading");
//console.log("handleDataLoading");
this.oldScrollLeft = this.$refs.table.tabulator.rowManager.scrollLeft;
this.oldScrollTop = this.$refs.table.tabulator.rowManager.scrollTop;
},
handleRenderComplete() {
console.log("handleRenderComplete");
//console.log("handleRenderComplete");
const table = this.$refs.table.tabulator.element.querySelector(
".tabulator-tableholder",
);
@@ -1,163 +1,456 @@
function parseFilterExpression(expression)
{
const collections = [];
let activeTagDropdownCleanup = null;
const TAG_FILTER_CONNECTORS = {
AND: {
key: "AND",
label: "AND",
operator: "&&",
},
OR: {
key: "OR",
label: "OR",
operator: "||",
},
NOT: {
key: "NOT",
label: "NOT",
operator: "!",
},
};
const TAG_FILTER_CONNECTOR_ORDER = [
TAG_FILTER_CONNECTORS.AND,
TAG_FILTER_CONNECTORS.OR,
TAG_FILTER_CONNECTORS.NOT,
];
try {
const orParts = expression.split('||').map(part => part.trim());
export function setTagHeaderFilterValue(tabulator, value) {
tabulator?.setHeaderFilterValue("tags", value);
orParts.forEach(part => {
const input = tabulator
?.getColumn("tags")
?.getElement()
?.querySelector(".tabulator-header-filter input");
const andParts = part.split('&&').map(p => p.trim());
const collection = { positives: [], negatives: [] };
andParts.forEach(term => {
const comparisonMatch = term.match(/^(<=|>=|<|>|=|!=)\s*(-?\d+(?:[.,]\d+)?)$/);
if (comparisonMatch)
{
const operator = comparisonMatch[1];
const numberStr = comparisonMatch[2].replace(',', '.');
const number = parseFloat(numberStr);
collection.positives.push({ type: 'comparison', operator, number });
}
else if (term.startsWith('!'))
{
const excludeTerm = term.substring(1).trim().replace(/\*/g, '.*');
collection.negatives.push({ type: 'regex', regex: new RegExp(excludeTerm, 'i') });
}
else
{
const includeTerm = term.replace(/\*/g, '.*');
collection.positives.push({ type: 'regex', regex: new RegExp(includeTerm, 'i') });
}
});
collections.push(collection);
});
} catch (e) {}
return collections;
if (input) {
input.value = value ?? "";
}
}
export function extendedHeaderFilter(headerValue, rowValue, rowData, filterParams)
{
const fields = Array.isArray(filterParams?.field)
? filterParams.field
: [filterParams?.field];
export function buildTagHeaderFilterExpression(selectedTagOptions) {
let expression = null;
if (fields.length > 1 && rowData)
{
rowValue = fields
.map(f => rowData[f] ?? '')
.filter(Boolean)
.join(' ');
}
if (typeof headerValue === 'boolean')
{
return rowValue === headerValue;
}
selectedTagOptions.forEach(({ value, connector }) => {
const connectorDef = TAG_FILTER_CONNECTORS[connector];
if (!connectorDef) return;
const collections = parseFilterExpression(headerValue);
if (connectorDef.key === TAG_FILTER_CONNECTORS.NOT.key) {
expression = expression
? `${expression} ${TAG_FILTER_CONNECTORS.AND.operator} ${connectorDef.operator}${value}`
: `${connectorDef.operator}${value}`;
} else {
expression = expression
? `${expression} ${connectorDef.operator} ${value}`
: value;
}
});
function matchValue(value)
{
try {
return collections.some(collection => {
let positives = collection.positives.length === 0 || collection.positives.every(condition => {
if (condition.type === 'comparison')
{
let value = parseFloat(rowValue);
if (isNaN(value)) return false;
switch (condition.operator) {
case '<':
return value < condition.number;
case '>':
return value > condition.number;
case '<=':
return value <= condition.number;
case '>=':
return value >= condition.number;
case '=':
return value === condition.number;
case '!=':
return value !== condition.number;
default:
return false;
}
}
else if (condition.type === 'regex')
{
return condition.regex.test(rowValue);
}
return false;
});
let negatives = collection.negatives.every(condition => {
return !condition.regex.test(rowValue);
});
return positives && negatives;
});
} catch (e) {
}
}
if (matchValue(rowValue))
return true;
if (rowData && filterParams)
{
const childrenField = filterParams?.children || '_children';
const field = filterParams?.field;
const children = rowData[childrenField];
if (Array.isArray(children))
{
for (let child of children)
{
let childValue = child[field];
if (extendedHeaderFilter(headerValue, childValue, child, filterParams))
{
return true;
}
}
}
}
return false;
return expression;
}
export function tagHeaderFilter(headerValue, rowValue, rowData, filterParams)
{
console.log('tagHeaderFilter', headerValue, rowValue, rowData, filterParams);
let data;
console.log('rowValue', rowValue);
try {
data = typeof rowValue === 'string' ? JSON.parse(rowValue) : rowValue;
} catch (error) {
return false;
}
let combinedText;
export function buildTagOptionsFromRows(rows = []) {
const options = new Map();
if (Array.isArray(data))
{
combinedText = data
.filter(item => item?.done !== true)
.map(item => `${item?.beschreibung} ${item?.notiz}`)
.join(' ');
}
else if (typeof data === 'object' && data !== null)
{
combinedText = data?.erledigt === false ? `${data?.beschreibung} ${data?.notiz}` : '';
}
else
{
combinedText = String(data);
}
rows.forEach((row) => {
let tags = row.tags;
return extendedHeaderFilter(headerValue, combinedText, rowData, filterParams)
}
if (typeof tags === "string") {
try {
tags = JSON.parse(tags);
} catch {
return;
}
}
if (!Array.isArray(tags)) return;
tags
.filter((tag) => tag && tag.done !== true)
.forEach((tag) => {
options.set(tag.beschreibung, {
label: tag.beschreibung,
value: tag.beschreibung,
style: tag.style,
});
});
});
return [...options.values()];
}
export function customTagFilter(cell, onRendered, success, cancel, params) {
const container = document.createElement("div");
container.style.width = "100%";
const input = document.createElement("input");
input.style.width = "100%";
input.type = "text";
let dropdownMenu = null;
Object.defineProperty(container, "value", {
get: () => input.value,
set: (value) => {
input.value = value ?? "";
},
});
const positionDropdown = () => {
if (!dropdownMenu) return;
const rect = input.getBoundingClientRect();
const width = Math.min(Math.max(rect.width, 360), window.innerWidth - 16);
const left = Math.min(
Math.max(rect.left, 8),
window.innerWidth - width - 8,
);
dropdownMenu.style.position = "fixed";
dropdownMenu.style.left = `${left}px`;
dropdownMenu.style.top = `${rect.bottom + 2}px`;
dropdownMenu.style.width = `${width}px`;
dropdownMenu.style.maxHeight = "50vh";
dropdownMenu.style.overflowY = "auto";
dropdownMenu.style.zIndex = "2000";
};
const closeDropdown = () => {
if (dropdownMenu) {
dropdownMenu.remove();
dropdownMenu = null;
}
if (activeTagDropdownCleanup === closeDropdown) {
activeTagDropdownCleanup = null;
}
window.removeEventListener("scroll", positionDropdown, true);
window.removeEventListener("resize", positionDropdown);
document.removeEventListener("mousedown", handleOutsideClick, true);
};
const handleOutsideClick = (event) => {
if (
dropdownMenu?.contains(event.target) ||
container.contains(event.target)
) {
return;
}
closeDropdown();
};
const clearFilter = () => {
input.value = "";
if (Array.isArray(params.selectedOptions)) {
params.selectedOptions.splice(
0,
params.selectedOptions.length,
);
}
success("");
closeDropdown();
};
input.addEventListener("input", () => {
success(input.value);
});
input.addEventListener("change", () => {
success(input.value);
});
input.addEventListener("keydown", (event) => {
if (event.key !== "Escape") return;
event.preventDefault();
event.stopPropagation();
clearFilter();
});
input.addEventListener("focus", (e) => {
e.stopPropagation();
if (activeTagDropdownCleanup) {
activeTagDropdownCleanup();
}
dropdownMenu = getRebuiltDropdown(
params.initialOptions,
params.selectedOptions,
);
dropdownMenu.addEventListener("mousedown", (event) => {
event.stopPropagation();
});
document.body.appendChild(dropdownMenu);
activeTagDropdownCleanup = closeDropdown;
positionDropdown();
window.addEventListener("scroll", positionDropdown, true);
window.addEventListener("resize", positionDropdown);
document.addEventListener("mousedown", handleOutsideClick, true);
});
input.addEventListener("blur", (e) => {
});
container.appendChild(input);
return container;
}
function getRebuiltDropdown(initialOptions, selectedOptions) {
let dropdownTable = generateDropdownTable(
initialOptions || [],
selectedOptions || [],
);
const menu = document.createElement("div");
menu.className = "dropdown-menu show";
menu.appendChild(dropdownTable);
return menu;
}
function generateDropdownTable(options, selectedTagOptions) {
const table = document.createElement("table");
table.className = "table table-sm mb-0";
// HEADER
const thead = document.createElement("thead");
const headRow = document.createElement("tr");
const headers = [
"Tag",
...TAG_FILTER_CONNECTOR_ORDER.map((connector) => connector.label),
];
headers.forEach((h) => {
const th = document.createElement("th");
th.innerHTML = `<strong>${h}</strong>`;
headRow.appendChild(th);
});
thead.appendChild(headRow);
table.appendChild(thead);
// BODY
const tbody = document.createElement("tbody");
options.forEach((option) => {
const row = document.createElement("tr");
// LABEL
const labelTd = document.createElement("td");
const label = document.createElement("span");
label.className = "tag " + option.style;
label.innerText = option.label;
labelTd.appendChild(label);
row.appendChild(labelTd);
function makeCheckbox(connector) {
const td = document.createElement("td");
const cb = document.createElement("input");
cb.type = "checkbox";
cb.style.cursor = "pointer";
cb.checked = selectedTagOptions.some(
(t) => t.value === option.value && t.connector === connector.key,
);
cb.addEventListener("click", (event) => {
event.stopPropagation();
if (event.target.checked) {
selectedTagOptions.push({
label: option.label,
value: option.value,
connector: connector.key,
});
} else {
const idx = selectedTagOptions.findIndex(
(t) => t.value === option.value && t.connector === connector.key,
);
if (idx !== -1) {
selectedTagOptions.splice(idx, 1);
}
}
});
td.appendChild(cb);
return td;
}
TAG_FILTER_CONNECTOR_ORDER.forEach((connector) => {
row.appendChild(makeCheckbox(connector));
});
tbody.appendChild(row);
});
table.appendChild(tbody);
return table;
}
function parseFilterExpression(expression) {
const collections = [];
try {
const orParts = expression.split("||").map((part) => part.trim());
orParts.forEach((part) => {
const andParts = part.split("&&").map((p) => p.trim());
const collection = { positives: [], negatives: [] };
andParts.forEach((term) => {
const comparisonMatch = term.match(
/^(<=|>=|<|>|=|!=)\s*(-?\d+(?:[.,]\d+)?)$/,
);
if (comparisonMatch) {
const operator = comparisonMatch[1];
const numberStr = comparisonMatch[2].replace(",", ".");
const number = parseFloat(numberStr);
collection.positives.push({ type: "comparison", operator, number });
} else if (term.startsWith("!")) {
const excludeTerm = term.substring(1).trim().replace(/\*/g, ".*");
collection.negatives.push({
type: "regex",
regex: new RegExp(excludeTerm, "i"),
});
} else {
const includeTerm = term.replace(/\*/g, ".*");
collection.positives.push({
type: "regex",
regex: new RegExp(includeTerm, "i"),
});
}
});
collections.push(collection);
});
} catch (e) {}
return collections;
}
export function extendedHeaderFilter(
headerValue,
rowValue,
rowData,
filterParams,
) {
const fields = Array.isArray(filterParams?.field)
? filterParams.field
: [filterParams?.field];
if (fields.length > 1 && rowData) {
rowValue = fields
.map((f) => rowData[f] ?? "")
.filter(Boolean)
.join(" ");
}
if (typeof headerValue === "boolean") {
return rowValue === headerValue;
}
const collections = parseFilterExpression(headerValue);
function matchValue(value) {
try {
return collections.some((collection) => {
let positives =
collection.positives.length === 0 ||
collection.positives.every((condition) => {
if (condition.type === "comparison") {
let value = parseFloat(rowValue);
if (isNaN(value)) return false;
switch (condition.operator) {
case "<":
return value < condition.number;
case ">":
return value > condition.number;
case "<=":
return value <= condition.number;
case ">=":
return value >= condition.number;
case "=":
return value === condition.number;
case "!=":
return value !== condition.number;
default:
return false;
}
} else if (condition.type === "regex") {
return condition.regex.test(rowValue);
}
return false;
});
let negatives = collection.negatives.every((condition) => {
return !condition.regex.test(rowValue);
});
return positives && negatives;
});
} catch (e) {}
}
if (matchValue(rowValue)) return true;
if (rowData && filterParams) {
const childrenField = filterParams?.children || "_children";
const field = filterParams?.field;
const children = rowData[childrenField];
if (Array.isArray(children)) {
for (let child of children) {
let childValue = child[field];
if (
extendedHeaderFilter(headerValue, childValue, child, filterParams)
) {
return true;
}
}
}
}
return false;
}
export function tagHeaderFilter(headerValue, rowValue, rowData, filterParams) {
let data;
try {
data = typeof rowValue === "string" ? JSON.parse(rowValue) : rowValue;
} catch (error) {
return false;
}
let combinedText;
if (Array.isArray(data)) {
combinedText = data
.filter((item) => item?.done !== true)
.map((item) => `${item?.beschreibung} ${item?.notiz}`)
.join(" ");
} else if (typeof data === "object" && data !== null) {
combinedText =
data?.erledigt === false ? `${data?.beschreibung} ${data?.notiz}` : "";
} else {
combinedText = String(data);
}
return extendedHeaderFilter(headerValue, combinedText, rowData, filterParams);
}