From 6a7685a7bb9b3d3f15a0604195fece72764ee491 Mon Sep 17 00:00:00 2001 From: daggerok <2445604+daggerok@users.noreply.github.com> Date: Sat, 19 Sep 2026 22:49:16 +0000 Subject: [PATCH] Add shared ordered AND and OR column filters Co-authored-by: arena-agent <297053741+arena-agent@users.noreply.github.com> --- README.md | 17 ++++++++++++ filters.js | 29 ++++++++++++++++++++ index.html | 16 +++++++----- tests/filters.test.js | 61 +++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 116 insertions(+), 7 deletions(-) create mode 100644 filters.js create mode 100644 tests/filters.test.js diff --git a/README.md b/README.md index d720d41..055f938 100644 --- a/README.md +++ b/README.md @@ -5,3 +5,20 @@ ```bash jq -r '."Google YouTube Data API v3 API Key youtube"' google.json | pbcopy ``` + +## Column filters + +All desktop and mobile column filters use the same case-insensitive syntax: + +- `2022*Mentorship` or `2022&Mentorship`: match `2022` followed by `Mentorship`, with any text (or none) between them. +- `2022|Mentorship`: match either phrase. +- `2022 Mentorship|2016 Mentorship|2017 Mentorship`: match any of these exact phrases (ignoring case). +- `2022*Mentorship&Live|2016*Mentorship`: combine any number of operators. AND (`*`, `&`) binds more tightly than OR (`|`). + +Spaces are literal, including spaces next to operators; they are not trimmed or treated as AND. Do not wrap queries in quotes. AND terms must occur in order, without overlapping. Empty operands are ignored while typing; an empty or operators-only filter imposes no restriction. Operators are reserved characters, with no escaping or parentheses syntax. Filters on different columns must all match. + +### Tests + +```bash +node --test tests/filters.test.js +``` diff --git a/filters.js b/filters.js new file mode 100644 index 0000000..f5a446d --- /dev/null +++ b/filters.js @@ -0,0 +1,29 @@ +// Shared by every desktop and mobile column filter. +// OR separates alternatives; * and & join literal phrases in occurrence order. +function compileTextFilter(query) { + const alternatives = String(query ?? '').toLowerCase().split('|') + .map(alternative => alternative.split(/[*&]/).filter(term => term !== '')) + .filter(terms => terms.length > 0); + + // Ignore empty operands while typing, rather than making an empty OR match all. + if (alternatives.length === 0) return () => true; + + return value => { + const text = String(value ?? '').toLowerCase(); + return alternatives.some(terms => { + let offset = 0; + return terms.every(term => { + const index = text.indexOf(term, offset); + if (index === -1) return false; + offset = index + term.length; + return true; + }); + }); + }; +} + +const FILTER_HELP = 'Case-insensitive literal phrases; spaces are literal. Use * or & for AND in order, | for OR. AND is evaluated before OR.'; + +if (typeof module !== 'undefined' && module.exports) { + module.exports = { compileTextFilter }; +} diff --git a/index.html b/index.html index d51faa3..5eff8e3 100644 --- a/index.html +++ b/index.html @@ -102,6 +102,7 @@
+