Skip to content

Commit

Permalink
Filtering dates (#691)
Browse files Browse the repository at this point in the history
  • Loading branch information
Acylation authored Oct 31, 2023
1 parent 6f03e76 commit e75c94d
Show file tree
Hide file tree
Showing 6 changed files with 122 additions and 35 deletions.
28 changes: 28 additions & 0 deletions src/settings/base/settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand All @@ -81,6 +102,7 @@ export type FilterOperator =
| StringFilterOperator
| NumberFilterOperator
| BooleanFilterOperator
| DateFilterOperator
| ListFilterOperator;

export type FilterOperatorType = "unary" | "binary";
Expand All @@ -100,6 +122,12 @@ export const filterOperatorTypes: Record<FilterOperator, FilterOperatorType> = {
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",
Expand Down
92 changes: 57 additions & 35 deletions src/ui/app/filterFunctions.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,29 @@
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,
type FilterCondition,
type FilterDefinition,
type NumberFilterOperator,
type StringFilterOperator,
type DateFilterOperator,
type ListFilterOperator,
} from "src/settings/settings";

Expand All @@ -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;
Expand Down Expand Up @@ -96,34 +99,53 @@ export const baseFns: Record<

export const stringFns: Record<
StringFilterOperator,
(left: string, right?: string) => boolean
(left: Optional<string>, 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<number>, 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>) => boolean
> = {
"is-checked": (value) => value === true,
"is-not-checked": (value) => value === false,
};

export const dateFns: Record<
DateFilterOperator,
(left: Optional<Date>, right?: Optional<Date>) => 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<DataValue>[], right?: Optional<DataValue>[]) => boolean
Expand Down
8 changes: 8 additions & 0 deletions src/ui/app/toolbar/viewOptions/color/ColorOptions.svelte
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
<script lang="ts">
import produce from "immer";
import dayjs from "dayjs";
import { dndzone } from "svelte-dnd-action";
import {
Button,
Expand All @@ -10,6 +11,7 @@
NumberInput,
ColorInput,
Checkbox,
DateInput,
} from "obsidian-svelte";
import { TagsInput } from "src/ui/components/TagsInput";
import HorizontalGroup from "src/ui/components/HorizontalGroup/HorizontalGroup.svelte";
Expand All @@ -18,6 +20,7 @@
filterOperatorTypes,
isNumberFilterOperator,
isStringFilterOperator,
isDateFilterOperator,
isListFilterOperator,
type ColorFilterDefinition,
type FilterOperator,
Expand Down Expand Up @@ -154,6 +157,11 @@
value={parseFloat(rule.condition.value ?? "")}
on:blur={handleValueChange(i)}
/>
{:else if isDateFilterOperator(rule.condition.operator)}
<DateInput
value={dayjs(rule.condition.value ?? "").toDate()}
on:blur={handleValueChange(i)}
/>
{:else if isListFilterOperator(rule.condition.operator)}
<TagsInput
strict={true}
Expand Down
10 changes: 10 additions & 0 deletions src/ui/app/toolbar/viewOptions/color/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,16 @@ export function getOperatorsByField(field: DataField): Array<{
{ label: "≤", value: "lte" },
{ label: "≥", value: "gte" },
];
case DataFieldType.Date:
return [
...baseOperators,
{ label: "is on", value: "is-on" },
{ label: "is not on", value: "is-not-on" },
{ label: "is before", value: "is-before" },
{ label: "is after", value: "is-after" },
{ label: "is on and before", value: "is-on-and-before" },
{ label: "is on and after", value: "is-on-and-after" },
];
}

return baseOperators;
Expand Down
9 changes: 9 additions & 0 deletions src/ui/app/toolbar/viewOptions/filter/FilterOptions.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
TextInput,
NumberInput,
Checkbox,
// DateInput, //use native date input temporarily,
} from "obsidian-svelte";
import { TagsInput } from "src/ui/components/TagsInput";
import HorizontalGroup from "src/ui/components/HorizontalGroup/HorizontalGroup.svelte";
Expand All @@ -16,6 +17,7 @@
filterOperatorTypes,
isNumberFilterOperator,
isStringFilterOperator,
isDateFilterOperator,
isListFilterOperator,
type FilterDefinition,
type FilterOperator,
Expand Down Expand Up @@ -112,6 +114,13 @@
value={parseFloat(condition.value ?? "")}
on:blur={handleValueChange(i)}
/>
{:else if isDateFilterOperator(condition.operator)}
<input
type="date"
value={condition.value ?? ""}
on:blur={handleValueChange(i)}
max="2999-12-31"
/>
{:else if isListFilterOperator(condition.operator)}
<TagsInput
strict={true}
Expand Down
10 changes: 10 additions & 0 deletions src/ui/app/toolbar/viewOptions/filter/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,16 @@ export function getOperatorsByField(field: DataField): Array<{
{ label: "≤", value: "lte" },
{ label: "≥", value: "gte" },
];
case DataFieldType.Date:
return [
...baseOperators,
{ label: "is on", value: "is-on" },
{ label: "is not on", value: "is-not-on" },
{ label: "is before", value: "is-before" },
{ label: "is after", value: "is-after" },
{ label: "is on and before", value: "is-on-and-before" },
{ label: "is on and after", value: "is-on-and-after" },
];
}

return baseOperators;
Expand Down

0 comments on commit e75c94d

Please sign in to comment.