Skip to content

Commit

Permalink
feat(ProjectsTable): Added better text filtering functionality (#45)
Browse files Browse the repository at this point in the history
  • Loading branch information
pruizlezcano authored Mar 18, 2024
1 parent fa3ffe8 commit b7f0faa
Show file tree
Hide file tree
Showing 2 changed files with 69 additions and 40 deletions.
8 changes: 5 additions & 3 deletions docs/src/content/docs/search-filters.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,18 @@ Lean more about the properties [here](/legato/managing-projects#project-properti
## Text

It filters projects based on the value of a specific column and a filter value provided by the user.
If the filter value contains a space, it must be enclosed in double quotes (`"`).

Examples:
```text
scale:C
scale:"C minor"
genre:House
tags:master
progress:inProgress
progress:in-progress
```


You can use the same field multiple times to filter multiple values.
For example, `genre:House genre:Techno` will filter for projects that have both `House` and `Techno` as genres.

## Number

Expand Down
101 changes: 64 additions & 37 deletions src/renderer/Components/ProjectsTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -75,10 +75,11 @@ import EditableSelectCell from './EditableSelectCell';

declare module '@tanstack/table-core' {
interface FilterFns {
textFilter: FilterFn<unknown>;
numberFilter: FilterFn<unknown>;
arrayFilter: FilterFn<unknown>;
booleanFn: FilterFn<unknown>;
notNullFn: FilterFn<unknown>;
booleanFilter: FilterFn<unknown>;
notNullFilter: FilterFn<unknown>;
}
}

Expand Down Expand Up @@ -121,6 +122,7 @@ const ProjectsTable = () => {
);
},
size: 160,
filterFn: 'textFilter',
}) as ColumnDef<Project>,
columnHelper.accessor('bpm', {
header: ({ column }) => (
Expand Down Expand Up @@ -161,6 +163,7 @@ const ProjectsTable = () => {
},
enableGlobalFilter: false,
size: 105,
filterFn: 'textFilter',
}) as ColumnDef<Project>,
columnHelper.accessor('genre', {
header: ({ column }) => (
Expand All @@ -180,8 +183,10 @@ const ProjectsTable = () => {
},
size: 110,
enableGlobalFilter: false,
filterFn: 'textFilter',
}) as ColumnDef<Project>,
columnHelper.accessor('tagNames', {
id: 'tags',
header: ({ column }) => (
<DataTableColumnHeader column={column} title="Tags" />
),
Expand Down Expand Up @@ -239,6 +244,7 @@ const ProjectsTable = () => {
enableSorting: true,
enableGlobalFilter: false,
size: 50,
filterFn: 'textFilter',
}) as ColumnDef<Project>,
columnHelper.accessor('modifiedAt', {
header: ({ column }) => (
Expand Down Expand Up @@ -272,6 +278,7 @@ const ProjectsTable = () => {
return <p>{project.daw}</p>;
},
enableGlobalFilter: false,
filterFn: 'textFilter',
}) as ColumnDef<Project>,
columnHelper.accessor('favorite', {
header: ({ column }) => (
Expand Down Expand Up @@ -303,7 +310,7 @@ const ProjectsTable = () => {
);
},
enableGlobalFilter: false,
filterFn: 'booleanFn',
filterFn: 'booleanFilter',
size: 0,
}) as ColumnDef<Project>,
columnHelper.accessor('hidden', {
Expand Down Expand Up @@ -332,7 +339,7 @@ const ProjectsTable = () => {
);
},
enableGlobalFilter: false,
filterFn: 'booleanFn',
filterFn: 'booleanFilter',
size: 0,
}) as ColumnDef<Project>,
columnHelper.accessor('audioFile', {
Expand All @@ -359,7 +366,7 @@ const ProjectsTable = () => {
);
},
enableGlobalFilter: false,
filterFn: 'notNullFn',
filterFn: 'notNullFilter',
size: 0,
}) as ColumnDef<Project>,
columnHelper.accessor('controls', {
Expand Down Expand Up @@ -467,41 +474,61 @@ const ProjectsTable = () => {
},
},
filterFns: {
numberFilter: (row, columnId, filterValue) => {
textFilter: (row, columnId, filterValue: string[]) => {
const value: string = row.getValue(columnId);
if (value === undefined || value === null) return false;
return filterValue.some((filter) =>
value.toLowerCase().includes(filter.toLowerCase()),
);
},
numberFilter: (row, columnId, filterValue: string[]) => {
const value: number = row.getValue(columnId);
if (value === undefined) return true;
if (/\d+-\d+/.test(filterValue)) {
// Number interval: number:120-150
const [min, max] = filterValue.split('-');
return value >= Number(min) && value <= Number(max);
}
if (/>\d+/.test(filterValue)) {
// Greater than: number:>120
const threshold = Number(filterValue.slice(1)); // Remove '>'
return value > threshold;
if (value === undefined) return false;
for (const filter of filterValue) {
if (/\d+-\d+/.test(filter)) {
// Number interval: number:120-150
const [min, max] = filter.split('-');
if (value >= Number(min) && value <= Number(max)) {
return true;
}
}
if (/>\d+/.test(filter)) {
// Greater than: number:>120
const threshold = Number(filter.slice(1)); // Remove '>'
if (value > threshold) {
return true;
}
}
if (/<\d+/.test(filter)) {
// Less than: number:<120
const threshold = Number(filter.slice(1)); // Remove '<'
if (value < threshold) {
return true;
}
}
// Exact number: number:123
if (value === Number(filter)) {
return true;
}
}
if (/<\d+/.test(filterValue)) {
// Less than: number:<120
const threshold = Number(filterValue.slice(1)); // Remove '<'
return value < threshold;
}
// Exact number: number:123
return value === Number(filterValue);
return false;
},
arrayFilter: (row, columnId, filterValue) => {
arrayFilter: (row, columnId, filterValue: string[]) => {
const value: string[] = row.getValue(columnId);
if (value === undefined) return true;
const tags = value.map((tag) => tag.toLowerCase()).join(' ');
return tags.includes(filterValue.toLowerCase());
if (value === undefined) return false;
const arrayValues = value.map((tag) => tag.toLowerCase()).join(' ');
return filterValue.some((filter) =>
arrayValues.includes(filter.toLowerCase()),
);
},
booleanFn: (row, columnId, filterValue) => {
booleanFilter: (row, columnId, filterValue: string[]) => {
const value: boolean = row.getValue(columnId);
if (value === undefined) return true;
return value === (filterValue === 'true');
if (value === undefined) return false;
return value === (filterValue[0] === 'true');
},
notNullFn: (row, columnId, filterValue) => {
notNullFilter: (row, columnId, filterValue: string[]) => {
const value = row.getValue(columnId);
if (filterValue === 'true')
if (filterValue[0] === 'true')
return value !== undefined && value !== null;
return value === undefined || value === null;
},
Expand All @@ -527,12 +554,12 @@ const ProjectsTable = () => {

setFilterQuery(query);
// Parse the general search term
const generalSearch = query.replace(/(\w+):([^" ]+|"[^"]*")/g, '').trim();
const regex = /(\w+):(\w+|"[^"]*")/g;
const generalSearch = query.replace(regex, '').trim();

// Parse additional filters
const regex = /(\w+):([^" ]+|"[^"]*")/g;
let match;
const filters: { [key: string]: string } = {};
const filters: { [key: string]: string[] } = {};

// Reset all filters
for (const column of table.getAllColumns()) {
Expand All @@ -545,8 +572,8 @@ const ProjectsTable = () => {
while ((match = regex.exec(query)) !== null) {
// eslint-disable-next-line prefer-const
let [, field, value] = match;
if (field === 'tags') field = 'tagNames';
filters[field] = value;
if (!filters[field]) filters[field] = [];
filters[field] = [...filters[field], value.replace(/^"|"$/g, '').trim()];
const column = table.getColumn(field);
if (column) column.setFilterValue(filters[field]);
}
Expand Down

0 comments on commit b7f0faa

Please sign in to comment.