@adaptabletools/adaptable
Version:
Powerful data-agnostic HTML5 AG Grid extension which provides advanced, cutting-edge functionality to meet all DataGrid requirements
605 lines (604 loc) • 24.8 kB
JavaScript
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';
/**
* Array of Predicate Defs which are shipped by AdapTable
*/
export const SystemPredicateDefs = [
{
id: 'In',
label: 'In',
icon: { text: 'IN' },
columnScope: { DataTypes: ['text', 'number', 'date'] },
moduleScope: ['columnFilter', 'flashingcell', 'formatColumn', 'alert', 'badgeStyle'],
handler: (context) => {
const { inputs = [], column, value, adaptableApi } = context;
const predicateInputs = (context.inputs || []).reduce((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') {
return inputs.some((input) => {
if (adaptableApi.optionsApi.getPredicateOptions().evaluateValuesPredicateUsingTime) {
return isEqual(input, value);
}
else {
return isSameDay(parseDateValue(input), parseDateValue(value));
}
});
}
if (column.dataType === 'number') {
return inputs.includes(value);
}
if (column.dataType === 'text') {
return adaptableApi.predicateApi.useCaseSensitivity()
? inputs
.map((i) => {
return i?.toLocaleLowerCase();
})
.includes(value?.toLocaleLowerCase())
: inputs.includes(value);
}
return true;
},
toString: ({ inputs }) => `IN (${inputs.join(', ')})`,
shortcuts: ['#', '['],
},
{
id: 'NotIn',
label: 'Not In',
icon: { text: '!IN' },
columnScope: { DataTypes: ['text', 'number', 'date'] },
moduleScope: ['columnFilter', 'flashingcell', 'formatColumn', 'alert', 'badgeStyle'],
handler: (context) => {
const { inputs, column, value, adaptableApi } = context;
const predicateInputs = context.inputs.reduce((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.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.optionsApi.getPredicateOptions().evaluateValuesPredicateUsingTime) {
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 === 'text') {
return adaptableApi.predicateApi.useCaseSensitivity()
? !inputs
.map((i) => {
return i?.toLocaleLowerCase();
})
.includes(value?.toLocaleLowerCase())
: !inputs.includes(value);
}
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 }) => Helper.isInputNullOrEmpty(value),
},
{
id: 'NonBlanks',
label: 'Non Blanks',
icon: { name: 'filled-circle' },
columnScope: { All: true },
moduleScope: ['columnFilter', 'alert', 'flashingcell', 'formatColumn', 'badgeStyle'],
handler: ({ value }) => 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 }) => {
if (!value) {
return false;
}
const ignoreCase = !adaptableApi.predicateApi.useCaseSensitivity();
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 }) => {
if (!value) {
return true;
}
const ignoreCase = !adaptableApi.predicateApi.useCaseSensitivity();
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 }) => {
if (!value) {
return false;
}
const ignoreCase = !adaptableApi.predicateApi.useCaseSensitivity();
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 }) => {
if (!value) {
return true;
}
const ignoreCase = !adaptableApi.predicateApi.useCaseSensitivity();
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 }) => {
if (!value) {
return false;
}
const ignoreCase = !adaptableApi.predicateApi.useCaseSensitivity();
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 }) => {
if (!value) {
return false;
}
const ignoreCase = !adaptableApi.predicateApi.useCaseSensitivity();
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);