From e75c94d072ca6066672ae12c0f3016cb9e4911ac Mon Sep 17 00:00:00 2001 From: Chiyu Liang <532117255@qq.com> Date: Tue, 31 Oct 2023 21:10:11 +0800 Subject: [PATCH] Filtering dates (#691) --- src/settings/base/settings.ts | 28 ++++++ src/ui/app/filterFunctions.ts | 92 ++++++++++++------- .../viewOptions/color/ColorOptions.svelte | 8 ++ .../app/toolbar/viewOptions/color/helpers.ts | 10 ++ .../viewOptions/filter/FilterOptions.svelte | 9 ++ .../app/toolbar/viewOptions/filter/helpers.ts | 10 ++ 6 files changed, 122 insertions(+), 35 deletions(-) diff --git a/src/settings/base/settings.ts b/src/settings/base/settings.ts index 73589c3f..f9d5cc78 100644 --- a/src/settings/base/settings.ts +++ b/src/settings/base/settings.ts @@ -68,6 +68,27 @@ export function isBooleanFilterOperator( return ["is-checked", "is-not-checked"].includes(op); } +export type DateFilterOperator = + | "is-on" + | "is-not-on" + | "is-before" + | "is-after" + | "is-on-and-before" + | "is-on-and-after"; + +export function isDateFilterOperator( + op: FilterOperator +): op is DateFilterOperator { + return [ + "is-on", + "is-not-on", + "is-before", + "is-after", + "is-on-and-before", + "is-on-and-after", + ].includes(op); +} + export type ListFilterOperator = "has-any-of" | "has-all-of" | "has-none-of"; export function isListFilterOperator( @@ -81,6 +102,7 @@ export type FilterOperator = | StringFilterOperator | NumberFilterOperator | BooleanFilterOperator + | DateFilterOperator | ListFilterOperator; export type FilterOperatorType = "unary" | "binary"; @@ -100,6 +122,12 @@ export const filterOperatorTypes: Record = { gte: "binary", "is-checked": "unary", "is-not-checked": "unary", + "is-on": "binary", + "is-not-on": "binary", + "is-before": "binary", + "is-after": "binary", + "is-on-and-before": "binary", + "is-on-and-after": "binary", "has-any-of": "binary", "has-all-of": "binary", "has-none-of": "binary", diff --git a/src/ui/app/filterFunctions.ts b/src/ui/app/filterFunctions.ts index c69cea1a..19cf4a47 100644 --- a/src/ui/app/filterFunctions.ts +++ b/src/ui/app/filterFunctions.ts @@ -1,15 +1,21 @@ import produce from "immer"; -import type { - DataFrame, - DataRecord, - DataValue, - Optional, +import dayjs from "dayjs"; +import { + type DataFrame, + type DataRecord, + type DataValue, + type Optional, + isOptionalString, + isOptionalNumber, + isOptionalBoolean, + isOptionalDate, + isOptionalList, } from "src/lib/dataframe/dataframe"; -import { isOptionalList } from "src/lib/dataframe/dataframe"; import { isBooleanFilterOperator, isNumberFilterOperator, isStringFilterOperator, + isDateFilterOperator, isListFilterOperator, type BaseFilterOperator, type BooleanFilterOperator, @@ -17,6 +23,7 @@ import { type FilterDefinition, type NumberFilterOperator, type StringFilterOperator, + type DateFilterOperator, type ListFilterOperator, } from "src/settings/settings"; @@ -41,24 +48,20 @@ export function matchesCondition( } } - switch (typeof value) { - case "string": - if (isStringFilterOperator(operator)) { - return stringFns[operator](value, cond.value); - } - break; - case "number": - if (isNumberFilterOperator(operator)) { - return numberFns[operator]( - value, - cond.value ? parseFloat(cond.value) : undefined - ); - } - break; - case "boolean": - if (isBooleanFilterOperator(operator)) { - return booleanFns[operator](value); - } + if (isOptionalString(value) && isStringFilterOperator(operator)) { + return stringFns[operator](value, cond.value); + } else if (isOptionalNumber(value) && isNumberFilterOperator(operator)) { + return numberFns[operator]( + value, + cond.value ? parseFloat(cond.value) : undefined + ); + } else if (isOptionalBoolean(value) && isBooleanFilterOperator(operator)) { + return booleanFns[operator](value); + } else if (isOptionalDate(value) && isDateFilterOperator(operator)) { + return dateFns[operator]( + value, + cond.value ? dayjs(cond.value ?? "").toDate() : undefined + ); } return false; @@ -96,34 +99,53 @@ export const baseFns: Record< export const stringFns: Record< StringFilterOperator, - (left: string, right?: string) => boolean + (left: Optional, right?: string) => boolean > = { - is: (left, right) => left === right, - "is-not": (left, right) => left !== right, - contains: (left, right) => left.contains(right ?? ""), - "not-contains": (left, right) => !left.contains(right ?? ""), + is: (left, right) => (left ? left == right : false), + "is-not": (left, right) => (left ? left != right : true), + contains: (left, right) => (left ? left.contains(right ?? "") : false), + "not-contains": (left, right) => (left ? !left.contains(right ?? "") : true), }; export const numberFns: Record< NumberFilterOperator, - (left: number, right?: number) => boolean + (left: Optional, right?: number) => boolean > = { eq: (left, right) => left === right, neq: (left, right) => left !== right, - lt: (left, right) => (right ? left < right : false), - gt: (left, right) => (right ? left > right : false), - lte: (left, right) => (right ? left <= right : false), - gte: (left, right) => (right ? left >= right : false), + lt: (left, right) => (left && right ? left < right : false), + gt: (left, right) => (left && right ? left > right : false), + lte: (left, right) => (left && right ? left <= right : false), + gte: (left, right) => (left && right ? left >= right : false), }; export const booleanFns: Record< BooleanFilterOperator, - (value: boolean) => boolean + (value: Optional) => boolean > = { "is-checked": (value) => value === true, "is-not-checked": (value) => value === false, }; +export const dateFns: Record< + DateFilterOperator, + (left: Optional, right?: Optional) => boolean +> = { + "is-on": (left, right) => { + return left && right ? left.getTime() == right.getTime() : false; + }, + "is-not-on": (left, right) => + left && right ? left.getTime() != right.getTime() : true, + "is-before": (left, right) => + left && right ? left.getTime() < right.getTime() : false, + "is-after": (left, right) => + left && right ? left.getTime() > right.getTime() : false, + "is-on-and-before": (left, right) => + left && right ? left.getTime() <= right.getTime() : false, + "is-on-and-after": (left, right) => + left && right ? left.getTime() >= right.getTime() : false, +}; + export const listFns: Record< ListFilterOperator, (left: Optional[], right?: Optional[]) => boolean diff --git a/src/ui/app/toolbar/viewOptions/color/ColorOptions.svelte b/src/ui/app/toolbar/viewOptions/color/ColorOptions.svelte index 264fa133..bf9c6615 100644 --- a/src/ui/app/toolbar/viewOptions/color/ColorOptions.svelte +++ b/src/ui/app/toolbar/viewOptions/color/ColorOptions.svelte @@ -1,5 +1,6 @@