UNPKG

@adaptabletools/adaptable

Version:

Powerful data-agnostic HTML5 AG Grid extension which provides advanced, cutting-edge functionality to meet all DataGrid requirements

778 lines (777 loc) 32.8 kB
import { isAfter } from 'date-fns'; import { isBefore } from 'date-fns'; import { isEqual } from 'date-fns'; import { isFuture } from 'date-fns'; import { isPast } from 'date-fns'; import { isSameDay } from 'date-fns'; import { isThisMonth } from 'date-fns'; import { isThisQuarter } from 'date-fns'; import { isThisWeek } from 'date-fns'; import { isThisYear } from 'date-fns'; import { isToday } from 'date-fns'; import { isTomorrow } from 'date-fns'; import { isYesterday } from 'date-fns'; import StringExtensions from '../../Utilities/Extensions/StringExtensions'; import { parseDateValue } from '../../Utilities/Helpers/DateHelper'; import Helper from '../../Utilities/Helpers/Helper'; import { AG_GRID_GROUPED_COLUMN } from '../../Utilities/Constants/GeneralConstants'; function getGroupValuesForNode(config) { const { node, value } = config; const groupValues = node.group ? [] : /*for the leaf node, also use its value*/ value != null ? [value] : []; let currentNode = node.group ? node : node.parent; while (currentNode && currentNode.groupData) { if (currentNode.treeParent && !currentNode.data) { // skip filler groups in tree mode } else { groupValues.unshift(currentNode.groupData[AG_GRID_GROUPED_COLUMN]); } currentNode = currentNode.parent; } return groupValues; } /** * Array of Predicate Defs which are shipped by AdapTable */ export const SystemPredicateDefs = [ { id: 'In', label: 'In', icon: { text: 'IN' }, columnScope: { DataTypes: ['text', 'number', 'date', 'textArray', 'numberArray'], ColumnIds: [AG_GRID_GROUPED_COLUMN], }, moduleScope: ['columnFilter', 'flashingcell', 'formatColumn', 'alert', 'badgeStyle'], handler: (context) => { const { inputs = [], column, value, adaptableApi } = context; const predicateInputs = (context.inputs || []) .map((input) => { const predicateInput = adaptableApi.predicateApi.getPredicateDefById(input); if (!predicateInput) { return; } // make sure that the input Predicate is included in the 'In' Predicate if (adaptableApi.columnScopeApi.scopeIsAll(predicateInput.columnScope) || adaptableApi.columnScopeApi.isScopeInScope(predicateInput.columnScope, // 'In' ColumnScope { DataTypes: ['text', 'number', 'date', 'textArray', 'numberArray'], })) { return predicateInput; } }) .filter(Boolean); if (predicateInputs.length) { const nestedContext = { ...context }; delete nestedContext.inputs; const predicateResult = predicateInputs.some((predicate) => { return adaptableApi.predicateApi.handlePredicate({ PredicateId: predicate.id, }, nestedContext, false); }); if (predicateResult) { // We want to use the only true values, to allow for multiple predicates to be used return predicateResult; } } if (inputs.length === 0) { return true; } if (column.columnId === AG_GRID_GROUPED_COLUMN) { const { node, treeSelectionState } = context; const groupValues = getGroupValuesForNode({ node, value }); if (!treeSelectionState) { // in case we don't have treeSelectionState // this is another possible implementation // though it won't be as performant as using the treeSelectionState const currentValues = (groupValues ?? []).join('#'); return inputs.some((values) => { const valuesString = values.join('#'); return currentValues.startsWith(valuesString); }); } const result = groupValues ? treeSelectionState.isNodeSelected(groupValues) : false; // console.log('filter', groupValues, 'pass', result, treeSelectionState.getState()); return result; } if (column.dataType === 'date') { return inputs.some((input) => { if (adaptableApi.predicateApi.internalApi.shouldEvaluateInPredicateUsingTime(column.columnId)) { return isEqual(input, value); } else { return isSameDay(parseDateValue(input), parseDateValue(value)); } }); } if (column.dataType === 'number') { return inputs.includes(value); } if (column.dataType === 'numberArray') { const arrayValue = Array.isArray(value) ? value : [value]; return inputs.some((input) => { return arrayValue.includes(input); }); } const useCaseSensitivity = adaptableApi.predicateApi.useCaseSensitivity(column.columnId); if (column.dataType === 'text') { return useCaseSensitivity ? inputs .map((i) => { return i?.toLocaleLowerCase(); }) .includes(value?.toLocaleLowerCase()) : inputs.includes(value); } if (column.dataType === 'textArray') { const arrayValue = Array.isArray(value) ? value : [value]; return inputs.some((input) => { return useCaseSensitivity ? arrayValue.map((v) => v?.toLocaleLowerCase()).includes(input?.toLocaleLowerCase()) : arrayValue.includes(input); }); } return true; }, toString: ({ inputs }) => `IN (${inputs.join(', ')})`, shortcuts: ['#', '['], }, // { // id: 'Tree', // label: 'Tree', // icon: { text: 'Tree' }, // columnScope: { DataTypes: ['date'] }, // moduleScope: ['columnFilter', 'flashingcell', 'formatColumn', 'alert'], // handler: (context) => { // const { inputs = [], column, value, adaptableApi } = context; // const predicateInputs = (context.inputs || []).reduce<AdaptablePredicateDef[]>( // (acc, input) => { // const predicate = adaptableApi.predicateApi.getPredicateDefById(input); // if (predicate) { // acc.push(predicate); // } // return acc; // }, // [] // ); // if (predicateInputs.length) { // const nestedContext = { ...context }; // delete nestedContext.inputs; // const predicateResult = predicateInputs.some((predicate) => { // return adaptableApi.predicateApi.handlePredicate( // { // PredicateId: predicate.id, // }, // nestedContext, // false // ); // }); // if (predicateResult) { // // We want to use the only true values, to allow for multiple predicates to be used // return predicateResult; // } // } // if (inputs.length === 0) { // return true; // } // if (column.dataType === 'date') { // const treeSelectionState = new TreeSelectionState( // { // defaultSelection: false, // // make sure something like // // Predicates: [{ PredicateId: 'Tree', Inputs: [['2021', '2'], ['2022']] }], // // is always converted to numbers // selectedPaths: inputs // .map((array) => { // if (!Array.isArray(array)) { // return null; // } // return array.map(Number); // }) // .filter(Boolean), // }, // () => { // return { // treeDeepMap: new DeepMap<string, any>(), // treePaths: new DeepMap<string, any>(), // }; // } // ); // const currrentDate = parseDateValue(value); // const currentYear = currrentDate.getFullYear(); // const currentMonth = currrentDate.getMonth() + 1; // const currentDay = currrentDate.getDate(); // const result = treeSelectionState.isNodeSelected([currentYear, currentMonth, currentDay]); // // console.log({ currentYear, currentMonth, currentDay, result, inputs: inputs.join('-') }); // return result; // } // return true; // }, // toString: ({ inputs }) => `IN Tree (${inputs.join(', ')})`, // shortcuts: ['#', '['], // }, { id: 'NotIn', label: 'Not In', icon: { text: '!IN' }, columnScope: { DataTypes: ['text', 'number', 'date', 'textArray', 'numberArray'] }, moduleScope: ['columnFilter', 'flashingcell', 'formatColumn', 'alert', 'badgeStyle'], handler: (context) => { const { inputs, column, value, adaptableApi } = context; const predicateInputs = (context.inputs || []) .map((input) => { const predicateInput = adaptableApi.predicateApi.getPredicateDefById(input); if (!predicateInput) { return; } // make sure that the input Predicate is included in the 'NotIn' Predicate if (adaptableApi.columnScopeApi.scopeIsAll(predicateInput.columnScope) || adaptableApi.columnScopeApi.isScopeInScope(predicateInput.columnScope, // 'NotIn' ColumnScope { DataTypes: ['text', 'number', 'date', 'textArray', 'numberArray'], })) { return predicateInput; } }) .filter(Boolean); if (predicateInputs.length) { const nestedContext = { ...context }; delete nestedContext.inputs; const predicateResult = predicateInputs.every((predicate) => { const res = adaptableApi.predicateApi.handlePredicate({ PredicateId: predicate.id, }, nestedContext, false); return res === false; }); if (!predicateResult) { // We want to use the only true values, to allow for multiple predicates to be used return false; } } // basically negation of IN if (inputs.length === 0) { return true; } if (column.dataType === 'date') { if (adaptableApi.predicateApi.useCaseSensitivity(column.columnId)) { return inputs.every((input) => { return !isEqual(input, value); }); } else { return inputs.every((input) => { return !isSameDay(parseDateValue(input), parseDateValue(value)); }); } } if (column.dataType === 'number') { return !inputs.includes(value); } if (column.dataType === 'numberArray') { const arrayValue = Array.isArray(value) ? value : [value]; return inputs.every((input) => { return !arrayValue.includes(input); }); } if (column.dataType === 'text') { return adaptableApi.predicateApi.useCaseSensitivity(column.columnId) ? !inputs .map((i) => { return i?.toLocaleLowerCase(); }) .includes(value?.toLocaleLowerCase()) : !inputs.includes(value); } if (column.dataType === 'textArray') { const arrayValue = Array.isArray(value) ? value : [value]; return inputs.every((input) => { return adaptableApi.predicateApi.useCaseSensitivity(column.columnId) ? !arrayValue.map((v) => v?.toLocaleLowerCase()).includes(input?.toLocaleLowerCase()) : !arrayValue.includes(input); }); } return true; }, toString: ({ inputs }) => `Exclude (${inputs.join(', ')})`, }, { id: 'Blanks', label: 'Blanks', icon: { name: 'unfilled-circle' }, columnScope: { All: true }, moduleScope: ['columnFilter', 'alert', 'flashingcell', 'formatColumn', 'badgeStyle'], handler: ({ value, column, adaptableApi }) => { if (adaptableApi.columnApi.internalApi.hasArrayDataType(column)) { return value == undefined || value.length === 0; } return Helper.isInputNullOrEmpty(value); }, }, { id: 'NonBlanks', label: 'Non Blanks', icon: { name: 'filled-circle' }, columnScope: { All: true }, moduleScope: ['columnFilter', 'alert', 'flashingcell', 'formatColumn', 'badgeStyle'], handler: ({ value, column, adaptableApi }) => { if (adaptableApi.columnApi.internalApi.hasArrayDataType(column)) { return value == undefined || value.length === 0; } return Helper.isInputNotNullOrEmpty(value); }, }, // Numeric System Filters { id: 'GreaterThan', label: 'Greater Than', icon: { name: 'greater-than' }, columnScope: { DataTypes: ['number'] }, moduleScope: ['columnFilter', 'alert', 'flashingcell', 'formatColumn', 'badgeStyle'], inputs: [{ type: 'number' }], handler: ({ value, inputs }) => Number(value) > Number(inputs[0]), toString: ({ inputs }) => `> ${inputs[0] ?? ''}`, shortcuts: ['>'], }, { id: 'LessThan', label: 'Less Than', icon: { name: 'less-than' }, columnScope: { DataTypes: ['number'] }, moduleScope: ['columnFilter', 'alert', 'flashingcell', 'formatColumn', 'badgeStyle'], inputs: [{ type: 'number' }], handler: ({ value, inputs }) => Number(value) < Number(inputs[0]), toString: ({ inputs }) => `< ${inputs[0] ?? ''}`, shortcuts: ['<'], }, { id: 'Positive', label: 'Positive', icon: { text: '>0' }, columnScope: { DataTypes: ['number'] }, moduleScope: ['columnFilter', 'alert', 'flashingcell', 'formatColumn', 'badgeStyle'], handler: ({ value }) => Number(value) > 0, }, { id: 'Negative', label: 'Negative', icon: { text: '<0' }, columnScope: { DataTypes: ['number'] }, moduleScope: ['columnFilter', 'alert', 'flashingcell', 'formatColumn', 'badgeStyle'], handler: ({ value }) => Number(value) < 0, }, { id: 'Zero', label: 'Zero', icon: { text: '=0' }, columnScope: { DataTypes: ['number'] }, moduleScope: ['columnFilter', 'alert', 'flashingcell', 'formatColumn', 'badgeStyle'], handler: ({ value }) => { if (typeof value === 'string' && !StringExtensions.IsNumeric(value)) { return false; } if (Helper.objectNotExists(value)) { return false; } return Number(value) === 0; }, }, { id: 'Equals', label: 'Equals', icon: { name: 'equals' }, columnScope: { DataTypes: ['number'] }, moduleScope: ['columnFilter', 'alert', 'flashingcell', 'formatColumn', 'badgeStyle'], inputs: [{ type: 'number' }], handler: ({ value, inputs }) => { const input = inputs[0]; if ((typeof value === 'string' && !StringExtensions.IsNumeric(value)) || (typeof input === 'string' && !StringExtensions.IsNumeric(input))) { return false; } return Number(value) === Number(inputs[0]); }, toString: ({ inputs }) => `= ${inputs[0] ?? ''}`, shortcuts: ['='], }, { id: 'NotEquals', label: 'Not Equals', icon: { name: 'not-equal' }, columnScope: { DataTypes: ['number'] }, moduleScope: ['columnFilter', 'alert', 'flashingcell', 'formatColumn', 'badgeStyle'], inputs: [{ type: 'number' }], handler: ({ value, inputs }) => { const input = inputs[0]; if ((typeof value === 'string' && !StringExtensions.IsNumeric(value)) || (typeof input === 'string' && !StringExtensions.IsNumeric(input))) { return false; } return Number(value) !== Number(inputs[0]); }, toString: ({ inputs }) => `!= ${inputs[0] ?? ''}`, shortcuts: ['!='], }, { id: 'Between', label: 'Between', icon: { text: 'BE' }, columnScope: { DataTypes: ['number'] }, moduleScope: ['columnFilter', 'alert', 'flashingcell', 'formatColumn', 'badgeStyle'], inputs: [{ type: 'number' }, { type: 'number' }], handler: ({ value, inputs }) => Number(value) >= Number(inputs[0]) && Number(value) <= Number(inputs[1]), toString: ({ inputs }) => `Between ${inputs[0] ?? ''}:${inputs[1]}`, shortcuts: [':'], }, { id: 'NotBetween', label: 'Not Between', icon: { text: '!BE' }, columnScope: { DataTypes: ['number'] }, moduleScope: ['columnFilter', 'alert', 'flashingcell', 'formatColumn', 'badgeStyle'], inputs: [{ type: 'number' }, { type: 'number' }], handler: ({ value, inputs }) => Number(value) < Number(inputs[0]) || Number(value) > Number(inputs[1]), toString: ({ inputs }) => `Not Between ${inputs[0] ?? ''}:${inputs[1]}`, shortcuts: ['!:'], }, // String System Filters { id: 'Is', label: 'Equals', icon: { name: 'equals' }, columnScope: { DataTypes: ['text'] }, moduleScope: ['columnFilter', 'alert', 'flashingcell', 'formatColumn', 'badgeStyle'], inputs: [{ type: 'text' }], handler: ({ value, inputs, adaptableApi, column }) => { if (!value) { return false; } const ignoreCase = !adaptableApi.predicateApi.internalApi.shouldUseCaseSensitivePredicates(column); const v = ignoreCase ? String(value).toLocaleLowerCase() : String(value); const i = ignoreCase ? String(inputs[0]).toLocaleLowerCase() : String(inputs[0]); return v == i; }, toString: ({ inputs }) => `= ${inputs[0] ?? ''}`, shortcuts: ['='], }, { id: 'IsNot', label: 'Not Equals', icon: { name: 'not-equal' }, columnScope: { DataTypes: ['text'] }, moduleScope: ['columnFilter', 'alert', 'flashingcell', 'formatColumn', 'badgeStyle'], inputs: [{ type: 'text' }], handler: ({ value, inputs, adaptableApi, column }) => { if (!value) { return true; } const ignoreCase = !adaptableApi.predicateApi.internalApi.shouldUseCaseSensitivePredicates(column); const v = ignoreCase ? String(value).toLocaleLowerCase() : String(value); const i = ignoreCase ? String(inputs[0]).toLocaleLowerCase() : String(inputs[0]); return v != i; }, toString: ({ inputs }) => `!= ${inputs[0] ?? ''}`, shortcuts: ['!='], }, { id: 'Contains', label: 'Contains', icon: { name: 'contains' }, columnScope: { DataTypes: ['text'] }, moduleScope: ['columnFilter', 'alert', 'flashingcell', 'formatColumn', 'badgeStyle'], inputs: [{ type: 'text' }], handler: ({ value, inputs, adaptableApi, column }) => { if (!value) { return false; } const ignoreCase = !adaptableApi.predicateApi.internalApi.shouldUseCaseSensitivePredicates(column); const v = ignoreCase ? String(value).toLocaleLowerCase() : String(value); const i = ignoreCase ? String(inputs[0]).toLocaleLowerCase() : String(inputs[0]); return v.indexOf(i) !== -1; }, toString: ({ inputs }) => `Contains ${inputs[0] ?? ''}`, }, { id: 'NotContains', label: 'Not Contains', icon: { name: 'not-contains' }, columnScope: { DataTypes: ['text'] }, moduleScope: ['columnFilter', 'alert', 'flashingcell', 'formatColumn', 'badgeStyle'], inputs: [{ type: 'text' }], handler: ({ value, inputs, adaptableApi, column }) => { if (!value) { return true; } const ignoreCase = !adaptableApi.predicateApi.internalApi.shouldUseCaseSensitivePredicates(column); const v = ignoreCase ? String(value).toLocaleLowerCase() : String(value); const i = ignoreCase ? String(inputs[0]).toLocaleLowerCase() : String(inputs[0]); return v.indexOf(i) === -1; }, toString: ({ inputs }) => `Not Contains ${inputs[0] ?? ''}`, }, { id: 'StartsWith', label: 'Starts With', icon: { name: 'starts-with' }, columnScope: { DataTypes: ['text'] }, moduleScope: ['columnFilter', 'alert', 'flashingcell', 'formatColumn', 'badgeStyle'], inputs: [{ type: 'text' }], handler: ({ value, inputs, adaptableApi, column }) => { if (!value) { return false; } const ignoreCase = !adaptableApi.predicateApi.internalApi.shouldUseCaseSensitivePredicates(column); const v = ignoreCase ? String(value).toLocaleLowerCase() : String(value); const i = ignoreCase ? String(inputs[0]).toLocaleLowerCase() : String(inputs[0]); return v.startsWith(i); }, toString: ({ inputs }) => `Starts With ${inputs[0] ?? ''}`, }, { id: 'EndsWith', label: 'Ends With', icon: { name: 'ends-with' }, columnScope: { DataTypes: ['text'] }, moduleScope: ['columnFilter', 'alert', 'flashingcell', 'formatColumn', 'badgeStyle'], inputs: [{ type: 'text' }], handler: ({ value, inputs, adaptableApi, column }) => { if (!value) { return false; } const ignoreCase = !adaptableApi.predicateApi.internalApi.shouldUseCaseSensitivePredicates(column); const v = ignoreCase ? String(value).toLocaleLowerCase() : String(value); const i = ignoreCase ? String(inputs[0]).toLocaleLowerCase() : String(inputs[0]); return v.endsWith(i); }, toString: ({ inputs }) => `Ends With ${inputs[0] ?? ''}`, }, { id: 'Regex', label: 'Regex', icon: { name: 'regex' }, columnScope: { DataTypes: ['text'] }, moduleScope: ['columnFilter', 'alert', 'flashingcell', 'formatColumn', 'badgeStyle'], inputs: [{ type: 'text' }], handler: ({ value, inputs }) => new RegExp(inputs[0]).test(value), toString: ({ inputs }) => `Regex ${inputs[0] ?? ''}`, }, // Date System Filters { id: 'Today', label: 'Today', icon: { name: 'calendar' }, columnScope: { DataTypes: ['date'] }, moduleScope: ['columnFilter', 'alert', 'flashingcell', 'formatColumn'], handler: ({ value }) => isToday(parseDateValue(value)), }, { id: 'Yesterday', label: 'Yesterday', icon: { name: 'calendar' }, columnScope: { DataTypes: ['date'] }, moduleScope: ['columnFilter', 'alert', 'flashingcell', 'formatColumn'], handler: ({ value }) => isYesterday(parseDateValue(value)), }, { id: 'Tomorrow', label: 'Tomorrow', icon: { name: 'calendar' }, columnScope: { DataTypes: ['date'] }, moduleScope: ['columnFilter', 'alert', 'flashingcell', 'formatColumn'], handler: ({ value }) => isTomorrow(parseDateValue(value)), }, { id: 'ThisWeek', label: 'This Week', icon: { name: 'calendar' }, columnScope: { DataTypes: ['date'] }, moduleScope: ['columnFilter', 'alert', 'flashingcell', 'formatColumn'], handler: ({ value }) => isThisWeek(parseDateValue(value)), }, { id: 'ThisMonth', label: 'This Month', icon: { name: 'calendar' }, columnScope: { DataTypes: ['date'] }, moduleScope: ['columnFilter', 'alert', 'flashingcell', 'formatColumn'], handler: ({ value }) => isThisMonth(parseDateValue(value)), }, { id: 'ThisQuarter', label: 'This Quarter', icon: { name: 'calendar' }, columnScope: { DataTypes: ['date'] }, moduleScope: ['columnFilter', 'alert', 'flashingcell', 'formatColumn'], handler: ({ value }) => isThisQuarter(parseDateValue(value)), }, { id: 'ThisYear', label: 'This Year', icon: { name: 'calendar' }, columnScope: { DataTypes: ['date'] }, moduleScope: ['columnFilter', 'alert', 'flashingcell', 'formatColumn'], handler: ({ value }) => isThisYear(parseDateValue(value)), }, { id: 'InPast', label: 'In Past', icon: { name: 'calendar' }, columnScope: { DataTypes: ['date'] }, moduleScope: ['columnFilter', 'alert', 'flashingcell', 'formatColumn'], handler: ({ value }) => isPast(parseDateValue(value)), }, { id: 'InFuture', label: 'In Future', icon: { name: 'calendar' }, columnScope: { DataTypes: ['date'] }, moduleScope: ['columnFilter', 'alert', 'flashingcell', 'formatColumn'], handler: ({ value }) => isFuture(parseDateValue(value)), }, { id: 'After', label: 'After', icon: { name: 'greater-than' }, columnScope: { DataTypes: ['date'] }, moduleScope: ['columnFilter', 'alert', 'flashingcell', 'formatColumn'], inputs: [{ type: 'date' }], handler: ({ value, inputs }) => isAfter(parseDateValue(value), parseDateValue(inputs[0])), toString: ({ inputs }) => `> ${inputs[0] ?? ''}`, }, { id: 'Before', label: 'Before', icon: { name: 'less-than' }, columnScope: { DataTypes: ['date'] }, moduleScope: ['columnFilter', 'alert', 'flashingcell', 'formatColumn'], inputs: [{ type: 'date' }], handler: ({ value, inputs }) => isBefore(parseDateValue(value), parseDateValue(inputs[0])), toString: ({ inputs }) => `< ${inputs[0] ?? ''}`, }, { id: 'On', label: 'Equals', icon: { name: 'equals' }, columnScope: { DataTypes: ['date'] }, moduleScope: ['columnFilter', 'alert', 'flashingcell', 'formatColumn'], inputs: [{ type: 'date' }], handler: ({ value, inputs }) => isSameDay(parseDateValue(value), parseDateValue(inputs[0])), toString: ({ inputs }) => `= ${inputs[0] ?? ''}`, }, { id: 'NotOn', label: 'NotEquals', icon: { name: 'not-equal' }, columnScope: { DataTypes: ['date'] }, moduleScope: ['columnFilter', 'alert', 'flashingcell', 'formatColumn'], inputs: [{ type: 'date' }], handler: ({ value, inputs }) => !isSameDay(parseDateValue(value), parseDateValue(inputs[0])), toString: ({ inputs }) => `!= ${inputs[0] ?? ''}`, }, { id: 'NextWorkDay', label: 'Next Work Day', icon: { name: 'calendar' }, columnScope: { DataTypes: ['date'] }, moduleScope: ['columnFilter', 'alert', 'flashingcell', 'formatColumn'], handler: ({ value, adaptableApi }) => isSameDay(parseDateValue(value), adaptableApi.calendarApi.getNextWorkingDay()), }, { id: 'LastWorkDay', label: 'Last Work Day', icon: { name: 'calendar' }, columnScope: { DataTypes: ['date'] }, moduleScope: ['columnFilter', 'alert', 'flashingcell', 'formatColumn'], handler: ({ value, adaptableApi }) => isSameDay(parseDateValue(value), adaptableApi.calendarApi.getPreviousWorkingDay()), }, { id: 'WorkDay', label: 'Working Day', icon: { name: 'calendar' }, columnScope: { DataTypes: ['date'] }, moduleScope: ['columnFilter', 'alert', 'flashingcell', 'formatColumn'], handler: ({ value, adaptableApi }) => adaptableApi.calendarApi.isWorkingDay(value), }, { id: 'Holiday', label: 'Holiday', icon: { name: 'calendar' }, columnScope: { DataTypes: ['date'] }, moduleScope: ['columnFilter', 'alert', 'flashingcell', 'formatColumn'], handler: ({ value, adaptableApi }) => adaptableApi.calendarApi.isHoliday(value), }, { id: 'InRange', label: 'Range', icon: { name: 'date-range' }, columnScope: { DataTypes: ['date'] }, moduleScope: ['columnFilter'], inputs: [{ type: 'date' }, { type: 'date' }], handler: ({ value, inputs }) => new Date(value) >= new Date(inputs[0]) && new Date(value) <= new Date(inputs[1]), }, // Boolean System Filters { id: 'True', label: 'True', icon: { text: 'T' }, columnScope: { DataTypes: ['boolean'] }, moduleScope: ['columnFilter', 'alert', 'flashingcell', 'formatColumn'], handler: ({ value }) => Boolean(value) === true, }, { id: 'False', label: 'False', icon: { text: 'F' }, columnScope: { DataTypes: ['boolean'] }, moduleScope: ['columnFilter', 'alert', 'flashingcell', 'formatColumn'], handler: ({ value }) => Boolean(value) === false, }, { id: 'BooleanToggle', label: 'BooleanToggle', icon: { name: 'boolean-list' }, columnScope: { DataTypes: ['boolean'] }, moduleScope: ['columnFilter'], inputs: [{ type: 'boolean' }], // working with string aliases instead of booleans because the ColumnFilterAPI cannot handle falsy filter values (it ignores them) handler: ({ value, inputs }) => { if (inputs[0] == 'all') { // if it's indeterminate, all values (true&false) are matched return true; } const booleanInputValue = inputs[0] === 'checked' ? true : false; return Boolean(value) === Boolean(booleanInputValue); }, }, // Other System Filters { id: 'AnyChange', label: 'Any Change', columnScope: { All: true }, moduleScope: ['alert', 'flashingcell'], handler: ({ value, oldValue }) => value !== oldValue, }, { id: 'PercentChange', label: 'Percent Change', columnScope: { DataTypes: ['number'] }, moduleScope: ['alert', 'flashingcell'], inputs: [{ type: 'number' }], handler: ({ value, oldValue, inputs }) => { const change = Math.abs(Number(value) - Number(oldValue)); const base = Math.min(Number(value), Number(oldValue)); const threshold = Number(inputs[0]); return (change / base) * 100 > threshold; }, }, ]; export const SystemFilterPredicateIds = SystemPredicateDefs.filter((p) => p.moduleScope.includes('columnFilter')).map((p) => p.id); export const SystemAlertPredicateIds = SystemPredicateDefs.filter((p) => p.moduleScope.includes('alert')).map((p) => p.id); export const SystemFormatColumnPredicateIds = SystemPredicateDefs.filter((p) => p.moduleScope.includes('formatColumn')).map((p) => p.id); export const SystemFlashingCellPredicateIds = SystemPredicateDefs.filter((p) => p.moduleScope.includes('flashingcell')).map((p) => p.id); export const SystemBadgeStylePredicateIds = SystemPredicateDefs.filter((p) => p.moduleScope.includes('badgeStyle')).map((p) => p.id);