CNGX_FILTER_BUILTIN_OPERATOR_DEFS
projects/forms/filter-builder/filter-builder-operators.ts
Description#
The 14 builtin operator definitions: the 11 historical switch arms
re-expressed as data, plus the opt-in between / in / notIn
family. Default value of the CNGX_FILTER_BUILDER_CONFIG.operators
slice and the fallback registry evaluateExpression resolves against
when no options are passed - keeping the no-options evaluation path
bit-identical to the historical closed switch.
The opt-in trio is deliberately absent from DEFAULT_OPERATORS: no
field grows a picker entry its native editor cannot serve. Expose the
keys per field via FilterFieldDef.operators (or per editor type via
withDefaultOperators) together with a value editor that produces the
matching array shape - [min, max] for between, a value list for
in / notIn.
Type#
ReadonlyMapDefault value#
new Map([ // Labels for the builtins live in the default i18n bundle // (DEFAULT_I18N.operators) - def.label is the tier for // consumer-registered keys only, so the string exists once. ['eq', { evaluate: (itemValue, exprValue) => Object.is(itemValue, exprValue) }], ['neq', { evaluate: (itemValue, exprValue) => !Object.is(itemValue, exprValue) }], ['isEmpty', { valueless: true, evaluate: (itemValue) => itemValue == null || itemValue === '' }], [ 'isNotEmpty', { valueless: true, evaluate: (itemValue) => itemValue != null && itemValue !== '' }, ], ['contains', substringDef((a, b) => a.includes(b))], ['startsWith', substringDef((a, b) => a.startsWith(b))], ['endsWith', substringDef((a, b) => a.endsWith(b))], ['gt', { evaluate: (a, b) => compare(a, b) > 0 }], ['gte', { evaluate: (a, b) => compare(a, b) >= 0 }], ['lt', { evaluate: (a, b) => compare(a, b) < 0 }], ['lte', { evaluate: (a, b) => compare(a, b) <= 0 }], [ 'between', { // Value shape [min, max]. A non-array (or wrong-arity) value is a // wiring bug -> conservative false; a nullish bound means the range // editor is half-filled -> no-op true, mirroring the empty-value // guard that cannot see inside arrays. evaluate: (itemValue, exprValue) => { if (!Array.isArray(exprValue) || exprValue.length !== 2) { return false; } const [min, max] = exprValue as [unknown, unknown]; if (min == null || max == null) { return true; } return compare(itemValue, min) >= 0 && compare(itemValue, max) <= 0; }, }, ], [ 'in', { // An empty list is an unfilled editor, not "match nothing" - the // no-op guard cannot see inside arrays, so the def honours the // unfilled-row contract itself (mirrors the between bound guard). evaluate: (itemValue, exprValue) => { if (!Array.isArray(exprValue)) { return false; } if (exprValue.length === 0) { return true; } return exprValue.some((v) => Object.is(v, itemValue)); }, }, ], [ 'notIn', { evaluate: (itemValue, exprValue) => { if (!Array.isArray(exprValue)) { return false; } if (exprValue.length === 0) { return true; } return !exprValue.some((v) => Object.is(v, itemValue)); }, }, ], ])