metis-desk/packages/rdx-ui/src/lib/entity-column-configuration.ts
2026-10-25 01:19:17 +02:00

59 lines
1.6 KiB
TypeScript

import { createTextMatcher } from "./text-search";
export { normalizeSearchText as normalizeColumnSearch } from "./text-search";
export type EntityColumnMetadata = {
groupId?: string;
keywords?: readonly string[];
};
export type EntityColumnGroup = {
id: string;
label: string;
description?: string;
};
export type EntityColumnOption = EntityColumnMetadata & {
id: string;
label: string;
visible: boolean;
hideable: boolean;
};
/** Los IDs conservan identidad; la primera declaración de un grupo u opción prevalece. */
export function groupColumnOptions(
options: readonly EntityColumnOption[],
groups: readonly EntityColumnGroup[],
search: string
) {
const matchesSearch = createTextMatcher(search);
const uniqueOptions = new Map<string, EntityColumnOption>();
const uniqueGroups = new Map<string, EntityColumnGroup>();
for (const group of groups) {
if (!uniqueGroups.has(group.id)) {
uniqueGroups.set(group.id, group);
}
}
for (const option of options) {
if (!uniqueOptions.has(option.id)) {
uniqueOptions.set(option.id, option);
}
}
const matches = [...uniqueOptions.values()].filter((option) =>
[option.label, ...(option.keywords ?? [])].some(matchesSearch)
);
const grouped = [...uniqueGroups.values()].map((group) => ({
group,
options: matches.filter((option) => option.groupId === group.id),
}));
const ungrouped = matches.filter(
(option) => !(option.groupId && uniqueGroups.has(option.groupId))
);
return [
...grouped.filter((group) => group.options.length > 0),
...(ungrouped.length ? [{ group: undefined, options: ungrouped }] : []),
];
}