Add date filter dropdown (#90)

* feat: change settings to support more filter rules

* feat: add data options

* chore: bump version

* feat: handle optional types

* style: move code

* feat: delete old variable names

* fix: handle valueData change

* feat: add DatePropertyFilterType enum

* feat: increase property filter select size to 160px

* feat: add custom date

* fix: use valueData for date input

* fix: clear custom date on date value switch

* refactor: rename to "select a property"

* fix: don't accept number on input

* feat: add placeholder for input

* fix: fix updating of value input

* feat: add assertion for date

* feat: add new date filtering

* feat: add other options
This commit is contained in:
DecafDev 2024-06-21 20:06:39 -06:00 committed by GitHub
parent b84bccf379
commit afdddc655c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
25 changed files with 571 additions and 200 deletions

View file

@ -27,7 +27,32 @@ export const moment = jest.fn((date: string, _formats: string[], _strict?: boole
parsedDate.setUTCHours(hour, minute, second);
return mockMoment;
}),
valueOf: jest.fn(() => parsedDate.getTime())
valueOf: jest.fn(() => parsedDate.getTime()),
isSame: jest.fn((other, unit) => {
if (unit === 'day') {
const otherDate = new Date(other);
return (
parsedDate.getUTCFullYear() === otherDate.getUTCFullYear() &&
parsedDate.getUTCMonth() === otherDate.getUTCMonth() &&
parsedDate.getUTCDate() === otherDate.getUTCDate()
);
}
return parsedDate.getTime() === new Date(other).getTime();
}),
isBefore: jest.fn((other, unit) => {
if (unit === 'day') {
const otherDate = new Date(other);
return parsedDate < otherDate && parsedDate.getUTCDate() !== otherDate.getUTCDate();
}
return parsedDate.getTime() < new Date(other).getTime();
}),
isAfter: jest.fn((other, unit) => {
if (unit === 'day') {
const otherDate = new Date(other);
return parsedDate > otherDate && parsedDate.getUTCDate() !== otherDate.getUTCDate();
}
return parsedDate.getTime() > new Date(other).getTime();
}),
};
return mockMoment;
});

View file

@ -1,7 +1,7 @@
{
"id": "vault-explorer",
"name": "Vault Explorer",
"version": "1.8.1",
"version": "1.9.0",
"minAppVersion": "1.4.13",
"description": "Explore your vault in visual format",
"author": "DecafDev",

View file

@ -1,6 +1,6 @@
{
"name": "obsidian-vault-explorer",
"version": "1.8.1",
"version": "1.9.0",
"description": "Explore your vault in visual format",
"main": "main.js",
"scripts": {

View file

@ -25,14 +25,14 @@ export const DEFAULT_SETTINGS: VaultExplorerPluginSettings = {
onlyFavorites: false,
timestamp: "all",
sort: "file-name-asc",
properties: {
custom: {
selectedGroupId: randomGroupId,
groups:
[
{
id: randomGroupId,
name: "Group 1",
filters: [],
rules: [],
isEnabled: true
}
]

View file

@ -3,7 +3,7 @@ import { Plugin, TAbstractFile, TFile, TFolder } from 'obsidian';
import VaultExplorerView from './obsidian/vault-explorer-view';
import VaultExplorerSettingsTab from './obsidian/vault-explorer-settings-tab';
import { PropertyFilter, PropertyFilterGroup, VaultExplorerPluginSettings, ViewType } from './types';
import { VaultExplorerPluginSettings, ViewType } from './types';
import { DEFAULT_SETTINGS, HOVER_LINK_SOURCE_ID, VAULT_EXPLORER_VIEW } from './constants';
import _ from 'lodash';
import EventManager from './event/event-manager';
@ -17,10 +17,11 @@ import { VaultExplorerPluginSettings_1_0_1 } from './types/types-1.0.1';
import { moveFocus } from './focus-utils';
import { VaultExplorerPluginSettings_1_2_0 } from './types/types-1.2.0';
import { VaultExplorerPluginSettings_1_2_1 } from './types/types-1.2.1';
import { VaultExplorerPluginSettings_1_5_0 } from './types/types-1.5.0';
import { PropertyFilterGroup_1_5_0, PropertyFilter_1_5_0, VaultExplorerPluginSettings_1_5_0 } from './types/types-1.5.0';
import { VaultExplorerPluginSettings_1_6_0 } from './types/types-1.6.0';
import { loadDeviceId } from './svelte/shared/services/device-id-utils';
import License from './svelte/shared/services/license';
import { VaultExplorerPluginSettings_1_8_1 } from './types/types-1.8.1';
export default class VaultExplorerPlugin extends Plugin {
settings: VaultExplorerPluginSettings = DEFAULT_SETTINGS;
@ -199,8 +200,8 @@ export default class VaultExplorerPlugin extends Plugin {
const typedData = (data as unknown) as VaultExplorerPluginSettings_1_2_1;
const groups = typedData.filters.properties.groups;
const updatedGroups: PropertyFilterGroup[] = groups.map(group => {
const updatedFilters: PropertyFilter[] = group.filters.map(filter => {
const updatedGroups: PropertyFilterGroup_1_5_0[] = groups.map(group => {
const updatedFilters: PropertyFilter_1_5_0[] = group.filters.map(filter => {
return {
...filter,
type: filter.type as any,
@ -243,7 +244,7 @@ export default class VaultExplorerPlugin extends Plugin {
if (isVersionLessThan(settingsVersion, "1.6.1")) {
console.log("Upgrading settings from version 1.6.0 to 1.6.1");
const typedData = (data as unknown) as VaultExplorerPluginSettings_1_6_0;
const newData: VaultExplorerPluginSettings = {
const newData: VaultExplorerPluginSettings_1_8_1 = {
...typedData,
properties: {
...typedData.properties,
@ -253,6 +254,38 @@ export default class VaultExplorerPlugin extends Plugin {
}
data = newData as unknown as Record<string, unknown>;
}
if (isVersionLessThan(settingsVersion, "1.9.0")) {
console.log("Upgrading settings from version 1.8.1 to 1.9.0");
const typedData = (data as unknown) as VaultExplorerPluginSettings_1_8_1;
const newData: VaultExplorerPluginSettings = {
...typedData,
filters: {
...typedData.filters,
custom: {
selectedGroupId: typedData.filters.properties.selectedGroupId,
groups: typedData.filters.properties.groups.map(group => {
const rules = group.filters.map(filter => {
return {
...filter,
valueData: "",
type: filter.type as any
}
});
return {
...group,
rules
}
})
}
}
}
delete (newData.filters as any).properties;
for (const group of newData.filters.custom.groups as any) {
delete group.filters;
}
data = newData as unknown as Record<string, unknown>;
}
}
}

View file

@ -1,9 +1,9 @@
<script lang="ts">
import { PropertyFilterGroup } from "src/types";
import { FilterGroup } from "src/types";
import GroupTag from "./group-tag.svelte";
import Stack from "src/svelte/shared/components/stack.svelte";
export let groups: PropertyFilterGroup[] = [];
export let groups: FilterGroup[] = [];
</script>
<div class="vault-explorer-group-tag-list">

View file

@ -8,7 +8,7 @@
import { FrontMatterCache, Menu, TFile, TFolder } from "obsidian";
import PropertiesFilterModal from "src/obsidian/properties-filter-modal";
import {
PropertyFilterGroup,
FilterGroup,
SortFilter,
TimestampFilter,
ViewType,
@ -65,7 +65,7 @@
let onlyFavorites: boolean = false;
let viewOrder: ViewType[] = [];
let currentView: ViewType = ViewType.GRID;
let propertyFilterGroups: PropertyFilterGroup[] = [];
let propertyFilterGroups: FilterGroup[] = [];
let selectedPropertyFilterGroupId: string = "";
let frontmatterCache: Record<string, FrontMatterCache | undefined> = {};
@ -112,16 +112,16 @@
onlyFavorites = plugin.settings.filters.onlyFavorites;
currentView = plugin.settings.views.currentView;
viewOrder = plugin.settings.views.order;
propertyFilterGroups = plugin.settings.filters.properties.groups;
propertyFilterGroups = plugin.settings.filters.custom.groups;
selectedPropertyFilterGroupId =
plugin.settings.filters.properties.selectedGroupId;
plugin.settings.filters.custom.selectedGroupId;
});
onMount(() => {
function handlePropertiesFilterUpdate() {
propertyFilterGroups = plugin.settings.filters.properties.groups;
propertyFilterGroups = plugin.settings.filters.custom.groups;
selectedPropertyFilterGroupId =
plugin.settings.filters.properties.selectedGroupId;
plugin.settings.filters.custom.selectedGroupId;
}
EventManager.getInstance().on(
@ -387,8 +387,8 @@
plugin.settings.filters.onlyFavorites = onlyFavorites;
plugin.settings.views.order = viewOrder;
plugin.settings.views.currentView = currentView;
plugin.settings.filters.properties.groups = propertyFilterGroups;
plugin.settings.filters.properties.selectedGroupId =
plugin.settings.filters.custom.groups = propertyFilterGroups;
plugin.settings.filters.custom.selectedGroupId =
selectedPropertyFilterGroupId;
await plugin.saveSettings();
}

View file

@ -1,23 +1,24 @@
import { FrontMatterCache } from "obsidian";
import { PropertyFilter, PropertyFilterGroup } from "src/types";
import { FilterRule, FilterGroup, DatePropertyFilterValue } from "src/types";
import { loadPropertyValue } from "src/svelte/shared/services/load-property-value";
import { matchTextFilter } from "./match-text-filter";
import { matchCheckboxFilter } from "./match-checkbox-filter";
import { matchListFilter } from "./match-list-filter";
import { matchDateFilter } from "./match-date-filter";
import { matchNumberFilter } from "./match-number-filter";
import { getDateDaysAgo, getDateDaysAhead } from "src/svelte/shared/services/time-utils";
export const filterByGroups = (frontmatter: FrontMatterCache | undefined, groups: PropertyFilterGroup[]) => {
export const filterByGroups = (frontmatter: FrontMatterCache | undefined, groups: FilterGroup[]) => {
return groups.every((group) => {
if (!group.isEnabled) return true;
return filterByGroup(frontmatter, group);
});
}
const filterByGroup = (frontmatter: FrontMatterCache | undefined, group: PropertyFilterGroup) => {
const filterByGroup = (frontmatter: FrontMatterCache | undefined, group: FilterGroup) => {
let result: boolean | null = null;
group.filters.forEach((filter, i) => {
group.rules.forEach((filter, i) => {
if (!filter.isEnabled) return;
const doesMatch = filterByRule(frontmatter, filter);
@ -35,11 +36,11 @@ const filterByGroup = (frontmatter: FrontMatterCache | undefined, group: Propert
return result ?? true;
}
const filterByRule = (frontmatter: FrontMatterCache | undefined, filter: PropertyFilter) => {
const { propertyName, condition, value, type, matchWhenPropertyDNE } = filter;
if (propertyName === "") return true;
const filterByRule = (frontmatter: FrontMatterCache | undefined, filter: FilterRule) => {
const { condition, value, type, matchWhenPropertyDNE } = filter;
if (type === "text") {
const { propertyName } = filter;
let propertyValue = loadPropertyValue<string>(frontmatter, propertyName, type);
if (propertyValue) {
propertyValue = propertyValue.toLowerCase().trim();
@ -49,12 +50,14 @@ const filterByRule = (frontmatter: FrontMatterCache | undefined, filter: Propert
const doesMatch = matchTextFilter(propertyValue, compare, condition, matchWhenPropertyDNE);
return doesMatch;
} else if (type === "number") {
const { propertyName } = filter;
const propertyValue = loadPropertyValue<number>(frontmatter, propertyName, type);
const compare = parseFloat(value.trim());
const doesMatch = matchNumberFilter(propertyValue, compare, condition, matchWhenPropertyDNE);
return doesMatch;
} else if (type === "checkbox") {
const { propertyName } = filter;
const propertyValue = loadPropertyValue<boolean>(frontmatter, propertyName, type);
let compare = null;
@ -67,11 +70,30 @@ const filterByRule = (frontmatter: FrontMatterCache | undefined, filter: Propert
const doesMatch = matchCheckboxFilter(propertyValue, compare, condition, matchWhenPropertyDNE);
return doesMatch;
} else if (type === "date" || type === "datetime") {
const { propertyName, valueData } = filter;
const propertyValue = loadPropertyValue<string>(frontmatter, propertyName, type);
const doesMatch = matchDateFilter(propertyValue, value, condition, matchWhenPropertyDNE);
let compare = valueData;
if (value === DatePropertyFilterValue.TODAY) {
compare = getDateDaysAgo(0);
} else if (value === DatePropertyFilterValue.YESTERDAY) {
compare = getDateDaysAgo(1);
} else if (value === DatePropertyFilterValue.TOMORROW) {
compare = getDateDaysAhead(1);
} else if (value === DatePropertyFilterValue.ONE_WEEK_AGO) {
compare = getDateDaysAgo(7);
} else if (value === DatePropertyFilterValue.ONE_WEEK_FROM_NOW) {
compare = getDateDaysAhead(7);
} else if (value === DatePropertyFilterValue.ONE_MONTH_AGO) {
compare = getDateDaysAgo(30);
} else if (value === DatePropertyFilterValue.ONE_MONTH_FROM_NOW) {
compare = getDateDaysAhead(30);
}
const doesMatch = matchDateFilter(propertyValue, compare, condition, matchWhenPropertyDNE);
return doesMatch;
} else if (type === "list") {
const { propertyName } = filter;
let propertyValue = loadPropertyValue<string[]>(frontmatter, propertyName, type);
if (propertyValue) {
propertyValue = propertyValue.map((v) => v.toLowerCase().trim());

View file

@ -1,4 +1,4 @@
import { getEndOfDayMillis, getStartOfDayMillis, getTimeMillis, isDateSupported } from "src/svelte/shared/services/time-utils";
import { getMomentDate, isDateSupported } from "src/svelte/shared/services/time-utils";
import { DateFilterCondition } from "src/types";
export const matchDateFilter = (
@ -8,40 +8,50 @@ export const matchDateFilter = (
matchIfNull: boolean
) => {
if (propertyValue) {
console.assert(isDateSupported(propertyValue), `Date filter propertyValue ${propertyValue} must be supported date format`);
console.assert(isDateSupported(propertyValue), `DateFilter propertyValue "${propertyValue}" must be supported date format`);
}
console.assert(isDateSupported(compare), `DateFilter compare "${compare}" must be supported date format`);
switch (condition) {
case DateFilterCondition.IS: {
if (propertyValue === null) return matchIfNull;
if (!isDateSupported(propertyValue)) return true;
const propertyValueTime = getTimeMillis(propertyValue);
const dayStartTime = getStartOfDayMillis(compare);
const dayEndTime = getEndOfDayMillis(compare);
return (
propertyValueTime >= dayStartTime &&
propertyValueTime <= dayEndTime
);
const propertyValueDate = getMomentDate(propertyValue);
const compareDate = getMomentDate(compare);
return propertyValueDate.isSame(compareDate, "day");
}
case DateFilterCondition.IS_AFTER: {
if (propertyValue === null) return matchIfNull;
if (!isDateSupported(propertyValue)) return true;
const propertyValueTime = getTimeMillis(propertyValue);
const dayEndTime = getEndOfDayMillis(compare);
return propertyValueTime > dayEndTime;
const propertyValueDate = getMomentDate(propertyValue);
const compareDate = getMomentDate(compare);
return propertyValueDate.isAfter(compareDate, "day");
}
case DateFilterCondition.IS_ON_OR_AFTER: {
if (propertyValue === null) return matchIfNull;
const propertyValueDate = getMomentDate(propertyValue);
const compareDate = getMomentDate(compare);
return propertyValueDate.isAfter(compareDate, "day") || propertyValueDate.isSame(compareDate, "day");
}
case DateFilterCondition.IS_BEFORE: {
if (propertyValue === null) return matchIfNull;
if (!isDateSupported(propertyValue)) return true;
const propertyValueTime = getTimeMillis(propertyValue);
const dayStartTime = getStartOfDayMillis(compare);
return propertyValueTime < dayStartTime;
const propertyValueDate = getMomentDate(propertyValue);
const compareDate = getMomentDate(compare);
return propertyValueDate.isBefore(compareDate, "day");
}
case DateFilterCondition.IS_ON_OR_BEFORE: {
if (propertyValue === null) return matchIfNull;
const propertyValueDate = getMomentDate(propertyValue);
const compareDate = getMomentDate(compare);
return propertyValueDate.isBefore(compareDate, "day") || propertyValueDate.isSame(compareDate, "day");
}
case DateFilterCondition.EXISTS:

View file

@ -1,12 +1,12 @@
import { FrontMatterCache, TFile } from "obsidian";
import { PropertyType, VaultExplorerPluginSettings } from "src/types";
import { FilterRuleType, VaultExplorerPluginSettings } from "src/types";
import { MarkdownFileRenderData } from "../types";
import { getTimeMillis, isDateSupported } from "../../shared/services/time-utils";
import Logger from "js-logger";
import { loadPropertyValue } from "src/svelte/shared/services/load-property-value";
export const formatFileDataForRender = (settings: VaultExplorerPluginSettings, file: TFile, frontmatter: FrontMatterCache | undefined,): MarkdownFileRenderData => {
const tags: string[] | null = loadPropertyValue<string[]>(frontmatter, "tags", PropertyType.LIST);
const tags: string[] | null = loadPropertyValue<string[]>(frontmatter, "tags", FilterRuleType.LIST);
const {
createdDate: createdDateProp,
@ -18,14 +18,14 @@ export const formatFileDataForRender = (settings: VaultExplorerPluginSettings, f
custom3: custom3Prop,
} = settings.properties;
const url: string | null = loadPropertyValue<string>(frontmatter, urlProp, PropertyType.TEXT);
const favorite: boolean | null = loadPropertyValue<boolean>(frontmatter, favoriteProp, PropertyType.CHECKBOX);
const creationDate: string | null = loadPropertyValue<string>(frontmatter, createdDateProp, PropertyType.DATE || PropertyType.DATETIME);
const modifiedDate: string | null = loadPropertyValue<string>(frontmatter, modifiedDateProp, PropertyType.DATE || PropertyType.DATETIME);
const url: string | null = loadPropertyValue<string>(frontmatter, urlProp, FilterRuleType.TEXT);
const favorite: boolean | null = loadPropertyValue<boolean>(frontmatter, favoriteProp, FilterRuleType.CHECKBOX);
const creationDate: string | null = loadPropertyValue<string>(frontmatter, createdDateProp, FilterRuleType.DATE || FilterRuleType.DATETIME);
const modifiedDate: string | null = loadPropertyValue<string>(frontmatter, modifiedDateProp, FilterRuleType.DATE || FilterRuleType.DATETIME);
const custom1: string | null = loadPropertyValue<string>(frontmatter, custom1Prop, PropertyType.TEXT);
const custom2: string | null = loadPropertyValue<string>(frontmatter, custom2Prop, PropertyType.TEXT);
const custom3: string | null = loadPropertyValue<string>(frontmatter, custom3Prop, PropertyType.TEXT);
const custom1: string | null = loadPropertyValue<string>(frontmatter, custom1Prop, FilterRuleType.TEXT);
const custom2: string | null = loadPropertyValue<string>(frontmatter, custom2Prop, FilterRuleType.TEXT);
const custom3: string | null = loadPropertyValue<string>(frontmatter, custom3Prop, FilterRuleType.TEXT);
let createdMillis = file.stat.ctime;
if (creationDate != null) {

View file

@ -1,4 +1,28 @@
import { CheckboxFilterCondition, DateFilterCondition, FilterCondition, ListFilterCondition, NumberFilterCondition, TextFilterCondition } from "src/types";
import { CheckboxFilterCondition, DateFilterCondition, DatePropertyFilterValue, FilterCondition, ListFilterCondition, NumberFilterCondition, TextFilterCondition } from "src/types";
export const getDisplayNameForDatePropertyFilterValue = (value: DatePropertyFilterValue) => {
switch (value) {
case DatePropertyFilterValue.TODAY:
return "today";
case DatePropertyFilterValue.TOMORROW:
return "tomorrow";
case DatePropertyFilterValue.YESTERDAY:
return "yesterday";
case DatePropertyFilterValue.ONE_WEEK_AGO:
return "one week ago";
case DatePropertyFilterValue.ONE_WEEK_FROM_NOW:
return "one week from now";
case DatePropertyFilterValue.ONE_MONTH_AGO:
return "one month ago";
case DatePropertyFilterValue.ONE_MONTH_FROM_NOW:
return "one month from now";
case DatePropertyFilterValue.CUSTOM:
return "custom";
default:
return "";
}
}
export const getDisplayNameForFilterCondition = (type: FilterCondition) => {
switch (type) {
@ -47,6 +71,10 @@ export const getDisplayNameForFilterCondition = (type: FilterCondition) => {
return "is after";
case DateFilterCondition.IS_BEFORE:
return "is before";
case DateFilterCondition.IS_ON_OR_AFTER:
return "is on or after";
case DateFilterCondition.IS_ON_OR_BEFORE:
return "is on or before";
default:
return "";
}

View file

@ -3,7 +3,7 @@
import IconButton from "src/svelte/shared/components/icon-button.svelte";
import Stack from "src/svelte/shared/components/stack.svelte";
import { PropertyFilterGroup } from "src/types";
import { FilterGroup } from "src/types";
import PropertyFilterList from "./property-filter-list.svelte";
import { createEventDispatcher } from "svelte";
@ -11,7 +11,7 @@
import Spacer from "src/svelte/shared/components/spacer.svelte";
const dispatch = createEventDispatcher();
export let selectedGroup: PropertyFilterGroup;
export let selectedGroup: FilterGroup;
function handleAddFilterClick() {
const newFilter = createPropertyFilter();
@ -34,14 +34,15 @@
</div>
<Divider borderWidth="1px" />
<div class="vault-explorer-group-edit-view__body">
{#if selectedGroup.filters.length > 0}
{#if selectedGroup.rules.length > 0}
<PropertyFilterList
filters={selectedGroup.filters}
filters={selectedGroup.rules}
on:filterConditionChange
on:filterTypeChange
on:filterPropertyNameChange
on:filterOperatorChange
on:filterValueChange
on:filterValueDataChange
on:filterToggle
on:filterDeleteClick
on:filterMatchWhenPropertyDNEChange

View file

@ -1,10 +1,10 @@
<script lang="ts">
import IconButton from "src/svelte/shared/components/icon-button.svelte";
import Stack from "src/svelte/shared/components/stack.svelte";
import { PropertyFilterGroup } from "src/types";
import { FilterGroup } from "src/types";
export let groups: PropertyFilterGroup[];
export let selectedGroup: PropertyFilterGroup | undefined;
export let groups: FilterGroup[];
export let selectedGroup: FilterGroup | undefined;
let listContainerRef: HTMLDivElement | null;
let previousLength = 0;

View file

@ -1,9 +1,9 @@
<script lang="ts">
import Stack from "src/svelte/shared/components/stack.svelte";
import PropertyFilter from "./property-filter.svelte";
import { PropertyFilter as PropertyFilterType } from "src/types";
import { FilterRule, FilterRuleType } from "src/types";
export let filters: PropertyFilterType[] = [];
export let filters: FilterRule[] = [];
</script>
<Stack direction="column" spacing="md" width="100%">
@ -11,12 +11,22 @@
<PropertyFilter
{index}
id={filter.id}
{...filter.type === FilterRuleType.TEXT ||
filter.type === FilterRuleType.NUMBER ||
filter.type === FilterRuleType.LIST ||
filter.type === FilterRuleType.CHECKBOX ||
filter.type === FilterRuleType.DATE ||
filter.type === FilterRuleType.DATETIME
? { propertyName: filter.propertyName }
: { propertyName: null }}
operator={filter.operator}
value={filter.value}
{...filter.type === FilterRuleType.DATE
? { valueData: filter.valueData }
: { valueData: null }}
condition={filter.condition}
matchWhenPropertyDNE={filter.matchWhenPropertyDNE}
type={filter.type}
propertyName={filter.propertyName}
isEnabled={filter.isEnabled}
on:groupChange
on:filterConditionChange
@ -24,6 +34,7 @@
on:filterOperatorChange
on:filterPropertyNameChange
on:filterValueChange
on:filterValueDataChange
on:filterToggle
on:filterDeleteClick
on:filterMatchWhenPropertyDNEChange

View file

@ -9,18 +9,23 @@
FilterOperator,
ListFilterCondition,
NumberFilterCondition,
PropertyType,
FilterRuleType,
TextFilterCondition,
DatePropertyFilterValue,
} from "src/types";
import { getDisplayNameForFilterCondition } from "./utils";
import {
getDisplayNameForDatePropertyFilterValue,
getDisplayNameForFilterCondition,
} from "./display-name-utils";
import { getAllObsidianProperties } from "src/obsidian/utils";
export let index: number;
export let id: string;
export let propertyName: string;
export let type: PropertyType;
export let propertyName: string | null;
export let type: FilterRuleType;
export let operator: FilterOperator;
export let value: string;
export let valueData: string | null;
export let condition: FilterCondition;
export let isEnabled: boolean;
export let matchWhenPropertyDNE: boolean;
@ -65,6 +70,11 @@
dispatch("filterValueChange", { id, value });
}
function handleValueDataChange(e: Event) {
const value = (e.target as HTMLInputElement).value;
dispatch("filterValueDataChange", { id, value });
}
function handleOperatorChange(e: Event) {
const value = (e.target as HTMLSelectElement).value;
dispatch("filterOperatorChange", { id, operator: value });
@ -95,7 +105,7 @@
return prop.type === type;
});
function findFilterConditions(type: PropertyType): FilterCondition[] {
function findFilterConditions(type: FilterRuleType): FilterCondition[] {
if (type === "text") {
return Object.values(TextFilterCondition);
} else if (type === "number") {
@ -125,16 +135,18 @@
</select>
{/if}
<select value={type} on:change={handleTypeChange}>
{#each Object.values(PropertyType) as type}
{#each Object.values(FilterRuleType) as type}
<option value={type}>{type}</option>
{/each}
</select>
<select value={propertyName} on:change={handlePropertyNameChange}>
<option value="">Select a property</option>
{#each filteredObsidianProperties as prop (prop.name)}
<option value={prop.name}>{prop.name}</option>
{/each}
</select>
{#if propertyName != null}
<select value={propertyName} on:change={handlePropertyNameChange}>
<option value="">select a property</option>
{#each filteredObsidianProperties as prop (prop.name)}
<option value={prop.name}>{prop.name}</option>
{/each}
</select>
{/if}
<select value={condition} on:change={handleConditionChange}>
{#each filterConditions as condition}
<option value={condition}>
@ -142,14 +154,37 @@
</option>
{/each}
</select>
{#if type == PropertyType.CHECKBOX && condition !== TextFilterCondition.EXISTS && condition !== TextFilterCondition.DOES_NOT_EXIST}
{#if type === FilterRuleType.CHECKBOX && condition !== CheckboxFilterCondition.EXISTS && condition !== CheckboxFilterCondition.DOES_NOT_EXIST}
<select {value} on:change={handleValueChange}>
<option value="true">true</option>
<option value="false">false</option>
</select>
{/if}
{#if type !== PropertyType.CHECKBOX && condition !== TextFilterCondition.EXISTS && condition !== TextFilterCondition.DOES_NOT_EXIST}
<input type="text" {value} on:change={handleValueChange} />
{#if (type === FilterRuleType.DATE || type === FilterRuleType.DATETIME) && condition !== DateFilterCondition.EXISTS && condition !== DateFilterCondition.DOES_NOT_EXIST}
<select {value} on:change={handleValueChange}>
{#each Object.values(DatePropertyFilterValue) as value}
<option {value}>
{getDisplayNameForDatePropertyFilterValue(value)}
</option>
{/each}
</select>
{/if}
{#if type !== FilterRuleType.CHECKBOX && type !== FilterRuleType.DATE && type !== FilterRuleType.DATETIME && condition !== TextFilterCondition.EXISTS && condition !== TextFilterCondition.DOES_NOT_EXIST}
<input
type={type === FilterRuleType.NUMBER ? "number" : "text"}
placeholder={type === FilterRuleType.LIST
? "item1,item2,item3"
: "Enter a value"}
{value}
on:change={handleValueChange}
/>
{/if}
{#if (type === FilterRuleType.DATE || type === FilterRuleType.DATETIME) && value == DatePropertyFilterValue.CUSTOM && condition !== TextFilterCondition.EXISTS && condition !== TextFilterCondition.DOES_NOT_EXIST}
<input
type="date"
value={valueData}
on:change={handleValueDataChange}
/>
{/if}
{#if condition !== TextFilterCondition.EXISTS && condition !== TextFilterCondition.DOES_NOT_EXIST}
<input
@ -180,6 +215,6 @@
}
.vault-explorer-property-filter select {
max-width: 150px;
max-width: 160px;
}
</style>

View file

@ -6,11 +6,12 @@
import {
CheckboxFilterCondition,
DateFilterCondition,
FilterCondition,
ListFilterCondition,
NumberFilterCondition,
PropertyFilterGroup,
FilterGroup,
TextFilterCondition,
FilterRuleType,
DatePropertyFilterValue,
} from "src/types";
import { generateRandomId } from "../shared/services/random";
import GroupEditView from "./components/group-edit-view.svelte";
@ -20,7 +21,7 @@
import Divider from "../shared/components/divider.svelte";
let selectedGroupId: string = "";
let groups: PropertyFilterGroup[] = [];
let groups: FilterGroup[] = [];
let plugin: VaultExplorerPlugin;
$: selectedGroup = groups.find((group) => group.id === selectedGroupId);
@ -28,16 +29,16 @@
$: groups, selectedGroupId, saveSettings();
async function saveSettings() {
plugin.settings.filters.properties.groups = groups;
plugin.settings.filters.properties.selectedGroupId = selectedGroupId;
plugin.settings.filters.custom.groups = groups;
plugin.settings.filters.custom.selectedGroupId = selectedGroupId;
await plugin.saveSettings();
}
store.plugin.subscribe((p) => {
plugin = p;
groups = plugin.settings.filters.properties.groups;
selectedGroupId = plugin.settings.filters.properties.selectedGroupId;
groups = plugin.settings.filters.custom.groups;
selectedGroupId = plugin.settings.filters.custom.selectedGroupId;
});
onMount(() => {
@ -52,10 +53,10 @@
}
function handleAddGroupClick() {
const newGroup: PropertyFilterGroup = {
const newGroup: FilterGroup = {
id: generateRandomId(),
name: `Group ${groups.length + 1}`,
filters: [createPropertyFilter()],
rules: [createPropertyFilter()],
isEnabled: groups.length === 0,
};
@ -88,7 +89,7 @@
const newGroups = groups.map((group) =>
group.id === selectedGroupId
? { ...group, filters: [...group.filters, filter] }
? { ...group, rules: [...group.rules, filter] }
: group,
);
@ -112,10 +113,8 @@
group.id === selectedGroupId
? {
...group,
filters: group.filters.map((filter) =>
filter.id === id
? { ...filter, condition }
: filter,
rules: group.rules.map((rule) =>
rule.id === id ? { ...rule, condition } : rule,
),
}
: group,
@ -167,9 +166,7 @@
group.id === selectedGroupId
? {
...group,
filters: group.filters.filter(
(filter) => filter.id !== id,
),
rules: group.rules.filter((rule) => rule.id !== id),
}
: group,
);
@ -184,10 +181,10 @@
group.id === selectedGroupId
? {
...group,
filters: group.filters.map((filter) =>
filter.id === id
? { ...filter, propertyName: name }
: filter,
rules: group.rules.map((rule) =>
rule.id === id
? { ...rule, propertyName: name }
: rule,
),
}
: group,
@ -203,10 +200,10 @@
group.id === selectedGroupId
? {
...group,
filters: group.filters.map((filter) =>
filter.id === id
? { ...filter, isEnabled: !filter.isEnabled }
: filter,
rules: group.rules.map((rule) =>
rule.id === id
? { ...rule, isEnabled: !rule.isEnabled }
: rule,
),
}
: group,
@ -222,8 +219,8 @@
group.id === selectedGroupId
? {
...group,
filters: group.filters.map((filter) =>
filter.id === id ? { ...filter, operator } : filter,
rules: group.rules.map((rule) =>
rule.id === id ? { ...rule, operator } : rule,
),
}
: group,
@ -235,7 +232,7 @@
function handleFilterTypeChange(e: CustomEvent) {
const { id, type } = e.detail;
let newCondition: FilterCondition;
let newCondition: any;
let newValue = "";
if (type === "text") {
newCondition = TextFilterCondition.IS;
@ -247,25 +244,37 @@
} else if (type === "list") {
newCondition = ListFilterCondition.CONTAINS;
} else if (type === "date" || type === "datetime") {
newValue = DatePropertyFilterValue.TODAY;
newCondition = DateFilterCondition.IS;
} else {
throw new Error(`Unhandled filter type: ${type}`);
}
const newGroups: PropertyFilterGroup[] = groups.map((group) =>
const newGroups: FilterGroup[] = groups.map((group) =>
group.id === selectedGroupId
? {
...group,
filters: group.filters.map((filter) =>
filter.id === id
rules: group.rules.map((rule) =>
rule.id === id
? {
...filter,
...rule,
type,
name: "",
...(type === FilterRuleType.TEXT ||
type === FilterRuleType.NUMBER ||
type === FilterRuleType.LIST ||
type === FilterRuleType.CHECKBOX ||
type === FilterRuleType.DATE ||
type === FilterRuleType.DATETIME
? { propertyName: "" }
: {}),
condition: newCondition,
value: newValue,
...(type === FilterRuleType.DATE ||
type === FilterRuleType.DATETIME
? { valueData: "" }
: {}),
}
: filter,
: rule,
),
}
: group,
@ -277,12 +286,44 @@
function handleFilterValueChange(e: CustomEvent) {
const { id, value } = e.detail;
const newGroups: FilterGroup[] = groups.map((group) => {
const { rules } = group;
if (group.id === selectedGroupId) {
const newRules = rules.map((rule) => {
if (rule.id === id) {
return {
...rule,
value,
...(rule.type === FilterRuleType.DATE ||
rule.type === FilterRuleType.DATETIME
? { valueData: "" }
: {}),
};
}
return rule;
});
return {
...group,
rules: newRules,
};
}
return group;
});
groups = newGroups;
}
function handleFilterValueDataChange(e: CustomEvent) {
const { id, value } = e.detail;
const newGroups = groups.map((group) =>
group.id === selectedGroupId
? {
...group,
filters: group.filters.map((filter) =>
filter.id === id ? { ...filter, value } : filter,
rules: group.rules.map((rule) =>
rule.id === id
? { ...rule, valueData: value }
: rule,
),
}
: group,
@ -294,17 +335,17 @@
function handleFilterMatchWhenPropertyDNEChange(e: CustomEvent) {
const { id, matchWhenDNE } = e.detail;
const newGroups = groups.map((group) =>
const newGroups: FilterGroup[] = groups.map((group) =>
group.id === selectedGroupId
? {
...group,
filters: group.filters.map((filter) =>
filter.id === id
rules: group.rules.map((rule) =>
rule.id === id
? {
...filter,
...rule,
matchWhenPropertyDNE: matchWhenDNE,
}
: filter,
: rule,
),
}
: group,
@ -339,6 +380,7 @@
on:filterToggle={handleFilterToggle}
on:filterOperatorChange={handleFilterOperatorChange}
on:filterValueChange={handleFilterValueChange}
on:filterValueDataChange={handleFilterValueDataChange}
on:filterMatchWhenPropertyDNEChange={handleFilterMatchWhenPropertyDNEChange}
/>
{/if}

View file

@ -1,10 +1,10 @@
import { PropertyType, TextFilterCondition, TextPropertyFilter } from "src/types";
import { FilterRuleType, TextFilterCondition, TextPropertyFilter } from "src/types";
import { generateRandomId } from "../shared/services/random";
export const createPropertyFilter = (): TextPropertyFilter => {
return {
id: generateRandomId(),
type: PropertyType.TEXT,
type: FilterRuleType.TEXT,
propertyName: "",
operator: "and",
isEnabled: true,

View file

@ -1,6 +1,6 @@
import Logger from "js-logger";
import { FrontMatterCache } from "obsidian";
import { PropertyType } from "src/types";
import { FilterRuleType } from "src/types";
import { isDateSupported } from "./time-utils";
/**
@ -10,7 +10,7 @@ import { isDateSupported } from "./time-utils";
* @param expectedType - The expected type of the property
* @returns - The property value or null if the property isn't valid
*/
export const loadPropertyValue = <T>(frontmatter: FrontMatterCache | undefined, propertyName: string, expectedType: PropertyType): T | null => {
export const loadPropertyValue = <T>(frontmatter: FrontMatterCache | undefined, propertyName: string, expectedType: FilterRuleType): T | null => {
//If the file has no frontmatter, return null
if (!frontmatter) {
return null;
@ -29,32 +29,32 @@ export const loadPropertyValue = <T>(frontmatter: FrontMatterCache | undefined,
}
//Validate the property value for the expected type
if (expectedType === PropertyType.TEXT) {
if (expectedType === FilterRuleType.TEXT) {
if (typeof propertyValue !== "string") {
Logger.warn(`Property value of type 'text' is not a string: ${propertyValue}`);
return null;
}
} else if (expectedType === PropertyType.NUMBER) {
} else if (expectedType === FilterRuleType.NUMBER) {
if (typeof propertyValue !== "number") {
Logger.warn(`Property value of type 'number' is not a number: ${propertyValue}`);
return null;
}
} else if (expectedType === PropertyType.DATE) {
} else if (expectedType === FilterRuleType.DATE) {
if (typeof propertyValue !== "string") {
Logger.warn(`Property value of type 'date' is not a string: ${propertyValue}`);
return null;
}
} else if (expectedType === PropertyType.DATETIME) {
} else if (expectedType === FilterRuleType.DATETIME) {
if (typeof propertyValue !== "string") {
Logger.warn(`Property value of type 'datetime' is not a string: ${propertyValue}`);
return null;
}
} else if (expectedType === PropertyType.CHECKBOX) {
} else if (expectedType === FilterRuleType.CHECKBOX) {
if (typeof propertyValue !== "boolean") {
Logger.warn(`Property value of type 'checkbox' is not a boolean: ${propertyValue}`);
return null;
}
} else if (expectedType === PropertyType.LIST) {
} else if (expectedType === FilterRuleType.LIST) {
if (!Array.isArray(propertyValue)) {
Logger.warn(`Property value of type 'list' is not an array: ${propertyValue}`);
//Don't return null here, because the property value can be converted to an array
@ -63,19 +63,19 @@ export const loadPropertyValue = <T>(frontmatter: FrontMatterCache | undefined,
//In older versions of Obsidian, the date can be stored in the frontmatter
//in a format other than YYYY-MM-DD or YYYY-MM-DDTHH:MM:SS
if (expectedType === PropertyType.DATE) {
if (expectedType === FilterRuleType.DATE) {
if (!isDateSupported(propertyValue)) {
Logger.warn(`Property value of type 'date' has unsupported date format: ${propertyValue}`);
return null;
}
} else if (expectedType === PropertyType.DATETIME) {
} else if (expectedType === FilterRuleType.DATETIME) {
if (!isDateSupported(propertyValue)) {
Logger.warn(`Property value of type 'datetime' has unsupported date format: ${propertyValue}`);
return null;
}
}
if (expectedType === PropertyType.LIST) {
if (expectedType === FilterRuleType.LIST) {
//If the property is not an array, return it as an array
//This is a bug in Obsidian?
if (!Array.isArray(propertyValue)) {

View file

@ -27,6 +27,18 @@ export const getStartOfThisWeekMillis = () => {
return moment().startOf("week").valueOf();
};
export const getMomentDate = (date: string) => {
return moment(date, DATE_FORMATS, true);
}
export const getDateDaysAgo = (daysAgo: number) => {
return moment().subtract(daysAgo, "days").format("YYYY-MM-DD");
}
export const getDateDaysAhead = (daysAgo: number) => {
return moment().add(daysAgo, "days").format("YYYY-MM-DD");
}
/**
* Gets 12:00 AM of the previous week in milliseconds. The week starts on Sunday.
* @returns - The start of the previous week in milliseconds

View file

@ -15,9 +15,9 @@ export interface VaultExplorerPluginSettings {
onlyFavorites: boolean;
sort: SortFilter;
timestamp: TimestampFilter;
properties: {
custom: {
selectedGroupId: string;
groups: PropertyFilterGroup[];
groups: FilterGroup[];
}
},
views: {
@ -74,29 +74,20 @@ export enum CheckboxFilterCondition {
DOES_NOT_EXIST = "does-not-exist",
}
//TODO: add more types
//TODO: add is between
export enum DateFilterCondition {
IS = "is",
IS_BEFORE = "is-before",
IS_AFTER = "is-after",
IS_ON_OR_BEFORE = "is-on-or-before",
IS_ON_OR_AFTER = "is-on-or-after",
EXISTS = "exists",
DOES_NOT_EXIST = "does-not-exist",
}
export type FilterCondition = TextFilterCondition | NumberFilterCondition | DateFilterCondition | CheckboxFilterCondition | ListFilterCondition;
interface BasePropertyFilter {
id: string;
propertyName: string;
operator: FilterOperator;
type: PropertyType;
isEnabled: boolean;
value: string;
matchWhenPropertyDNE: boolean;
}
//Matches Obsidian property types
export enum PropertyType {
export enum FilterRuleType {
TEXT = "text",
NUMBER = "number",
LIST = "list",
@ -105,37 +96,63 @@ export enum PropertyType {
DATETIME = "datetime",
}
export interface TextPropertyFilter extends BasePropertyFilter {
type: PropertyType.TEXT;
export enum DatePropertyFilterValue {
TODAY = "today",
TOMORROW = "tomorrow",
YESTERDAY = "yesterday",
ONE_WEEK_FROM_NOW = "one-week-from-now",
ONE_WEEK_AGO = "one-week-ago",
ONE_MONTH_FROM_NOW = "one-month-from-now",
ONE_MONTH_AGO = "one-month-ago",
CUSTOM = "custom"
}
interface BaseFilterRule {
id: string;
operator: FilterOperator;
type: FilterRuleType;
isEnabled: boolean;
value: string;
matchWhenPropertyDNE: boolean;
}
export interface TextPropertyFilter extends BaseFilterRule {
type: FilterRuleType.TEXT;
propertyName: string;
condition: TextFilterCondition;
}
export interface NumberPropertyFilter extends BasePropertyFilter {
type: PropertyType.NUMBER;
export interface NumberPropertyFilter extends BaseFilterRule {
type: FilterRuleType.NUMBER;
propertyName: string;
condition: NumberFilterCondition;
}
export interface ListPropertyFilter extends BasePropertyFilter {
type: PropertyType.LIST
export interface ListPropertyFilter extends BaseFilterRule {
type: FilterRuleType.LIST
propertyName: string;
condition: ListFilterCondition;
}
export interface CheckboxPropertyFilter extends BasePropertyFilter {
type: PropertyType.CHECKBOX
export interface CheckboxPropertyFilter extends BaseFilterRule {
type: FilterRuleType.CHECKBOX;
propertyName: string;
condition: CheckboxFilterCondition;
}
export interface DatePropertyFilter extends BasePropertyFilter {
type: PropertyType.DATE | PropertyType.DATETIME;
export interface DatePropertyFilter extends BaseFilterRule {
type: FilterRuleType.DATE | FilterRuleType.DATETIME;
propertyName: string;
condition: DateFilterCondition;
valueData: string;
}
export type PropertyFilter = TextPropertyFilter | NumberPropertyFilter | ListPropertyFilter | CheckboxPropertyFilter | DatePropertyFilter;
export type FilterRule = TextPropertyFilter | NumberPropertyFilter | ListPropertyFilter | CheckboxPropertyFilter | DatePropertyFilter;
export interface PropertyFilterGroup {
export interface FilterGroup {
id: string;
name: string;
filters: PropertyFilter[];
rules: FilterRule[];
isEnabled: boolean;
}

View file

@ -27,6 +27,10 @@ export interface VaultExplorerPluginSettings_1_5_0 {
pluginVersion: string | null;
}
export type PropertyFilterGroup_1_5_0 = PropertyFilterGroup;
export type PropertyFilter_1_5_0 = PropertyFilter;
type WordBreak = "normal" | "break-word";
enum ViewType {

144
src/types/types-1.8.1.ts Normal file
View file

@ -0,0 +1,144 @@
export interface VaultExplorerPluginSettings_1_8_1 {
logLevel: string;
properties: {
favorite: string;
url: string;
createdDate: string;
modifiedDate: string;
custom1: string;
custom2: string;
custom3: string;
},
filters: {
folder: string;
search: string;
onlyFavorites: boolean;
sort: SortFilter;
timestamp: TimestampFilter;
properties: {
selectedGroupId: string;
groups: PropertyFilterGroup[];
}
},
views: {
currentView: ViewType;
order: ViewType[];
titleWrapping: WordBreak;
}
pageSize: number;
pluginVersion: string | null;
}
type WordBreak = "normal" | "break-word";
enum ViewType {
GRID = "grid",
LIST = "list",
}
type FilterOperator = "and" | "or";
enum TextFilterCondition {
IS = "is",
IS_NOT = "is-not",
CONTAINS = "contains",
DOES_NOT_CONTAIN = "does-not-contain",
STARTS_WITH = "starts-with",
ENDS_WITH = "ends-with",
EXISTS = "exists",
DOES_NOT_EXIST = "does-not-exist",
}
enum ListFilterCondition {
CONTAINS = "contains",
DOES_NOT_CONTAIN = "does-not-contain",
EXISTS = "exists",
DOES_NOT_EXIST = "does-not-exist",
}
enum NumberFilterCondition {
IS_EQUAL = "is-equal",
IS_NOT_EQUAL = "is-not-equal",
IS_GREATER = "is-greater",
IS_LESS = "is-less",
IS_GREATER_OR_EQUAL = "is-greater-or-equal",
IS_LESS_OR_EQUAL = "is-less-or-equal",
EXISTS = "exists",
DOES_NOT_EXIST = "does-not-exist",
}
enum CheckboxFilterCondition {
IS = "is",
IS_NOT = "is-not",
EXISTS = "exists",
DOES_NOT_EXIST = "does-not-exist",
}
//TODO: add more types
enum DateFilterCondition {
IS = "is",
IS_BEFORE = "is-before",
IS_AFTER = "is-after",
EXISTS = "exists",
DOES_NOT_EXIST = "does-not-exist",
}
type FilterCondition = TextFilterCondition | NumberFilterCondition | DateFilterCondition | CheckboxFilterCondition | ListFilterCondition;
interface BasePropertyFilter {
id: string;
propertyName: string;
operator: FilterOperator;
type: PropertyType;
isEnabled: boolean;
value: string;
matchWhenPropertyDNE: boolean;
}
//Matches Obsidian property types
enum PropertyType {
TEXT = "text",
NUMBER = "number",
LIST = "list",
CHECKBOX = "checkbox",
DATE = "date",
DATETIME = "datetime",
}
interface TextPropertyFilter extends BasePropertyFilter {
type: PropertyType.TEXT;
condition: TextFilterCondition;
}
interface NumberPropertyFilter extends BasePropertyFilter {
type: PropertyType.NUMBER;
condition: NumberFilterCondition;
}
interface ListPropertyFilter extends BasePropertyFilter {
type: PropertyType.LIST
condition: ListFilterCondition;
}
interface CheckboxPropertyFilter extends BasePropertyFilter {
type: PropertyType.CHECKBOX
condition: CheckboxFilterCondition;
}
interface DatePropertyFilter extends BasePropertyFilter {
type: PropertyType.DATE | PropertyType.DATETIME;
condition: DateFilterCondition;
}
type PropertyFilter = TextPropertyFilter | NumberPropertyFilter | ListPropertyFilter | CheckboxPropertyFilter | DatePropertyFilter;
interface PropertyFilterGroup {
id: string;
name: string;
filters: PropertyFilter[];
isEnabled: boolean;
}
type SortFilter = "file-name-asc" | "file-name-desc" | "modified-asc" | "modified-desc";
type TimestampFilter = "created-today" | "modified-today" | "created-this-week" | "modified-this-week" | "created-2-weeks" | "modified-2-weeks" | "all";

View file

@ -1,6 +1,6 @@
import { FrontMatterCache } from "obsidian";
import { PropertyType } from "src/types";
import { FilterRuleType } from "src/types";
import { loadPropertyValue } from "src/svelte/shared/services/load-property-value";
describe("loadPropertyValue", () => {
@ -9,7 +9,7 @@ describe("loadPropertyValue", () => {
const frontmatter: FrontMatterCache | undefined = undefined;
//Act
const result = loadPropertyValue<string>(frontmatter, "test", PropertyType.TEXT);
const result = loadPropertyValue<string>(frontmatter, "test", FilterRuleType.TEXT);
//Assert
expect(result).toBeNull();
@ -21,7 +21,7 @@ describe("loadPropertyValue", () => {
const propertyName = "";
//Act
const result = loadPropertyValue<string>(frontmatter, propertyName, PropertyType.TEXT);
const result = loadPropertyValue<string>(frontmatter, propertyName, FilterRuleType.TEXT);
//Assert
expect(result).toBeNull();
@ -33,7 +33,7 @@ describe("loadPropertyValue", () => {
const propertyName = "test";
//Act
const result = loadPropertyValue<string>(frontmatter, propertyName, PropertyType.TEXT);
const result = loadPropertyValue<string>(frontmatter, propertyName, FilterRuleType.TEXT);
//Assert
expect(result).toBeNull();
@ -45,7 +45,7 @@ describe("loadPropertyValue", () => {
const propertyName = "test";
//Act
const result = loadPropertyValue<string>(frontmatter, propertyName, PropertyType.TEXT);
const result = loadPropertyValue<string>(frontmatter, propertyName, FilterRuleType.TEXT);
//Assert
expect(result).toBeNull();
@ -57,7 +57,7 @@ describe("loadPropertyValue", () => {
const propertyName = "test";
//Act
const result = loadPropertyValue<string>(frontmatter, propertyName, PropertyType.TEXT);
const result = loadPropertyValue<string>(frontmatter, propertyName, FilterRuleType.TEXT);
//Assert
expect(result).toBeNull();
@ -69,7 +69,7 @@ describe("loadPropertyValue", () => {
const propertyName = "test";
//Act
const result = loadPropertyValue<string>(frontmatter, propertyName, PropertyType.TEXT);
const result = loadPropertyValue<string>(frontmatter, propertyName, FilterRuleType.TEXT);
//Assert
expect(result).toBe("test");
@ -81,7 +81,7 @@ describe("loadPropertyValue", () => {
const propertyName = "test";
//Act
const result = loadPropertyValue<number>(frontmatter, propertyName, PropertyType.NUMBER);
const result = loadPropertyValue<number>(frontmatter, propertyName, FilterRuleType.NUMBER);
//Assert
expect(result).toBeNull();
@ -93,7 +93,7 @@ describe("loadPropertyValue", () => {
const propertyName = "test";
//Act
const result = loadPropertyValue<number>(frontmatter, propertyName, PropertyType.NUMBER);
const result = loadPropertyValue<number>(frontmatter, propertyName, FilterRuleType.NUMBER);
//Assert
expect(result).toBe(1);
@ -105,7 +105,7 @@ describe("loadPropertyValue", () => {
const propertyName = "test";
//Act
const result = loadPropertyValue<boolean>(frontmatter, propertyName, PropertyType.CHECKBOX);
const result = loadPropertyValue<boolean>(frontmatter, propertyName, FilterRuleType.CHECKBOX);
//Assert
expect(result).toBeNull();
@ -117,7 +117,7 @@ describe("loadPropertyValue", () => {
const propertyName = "test";
//Act
const result = loadPropertyValue<boolean>(frontmatter, propertyName, PropertyType.CHECKBOX);
const result = loadPropertyValue<boolean>(frontmatter, propertyName, FilterRuleType.CHECKBOX);
//Assert
expect(result).toBe(true);
@ -129,7 +129,7 @@ describe("loadPropertyValue", () => {
const propertyName = "test";
//Act
const result = loadPropertyValue<string[]>(frontmatter, propertyName, PropertyType.LIST);
const result = loadPropertyValue<string[]>(frontmatter, propertyName, FilterRuleType.LIST);
//Assert
expect(result).toEqual(["test"]);
@ -141,7 +141,7 @@ describe("loadPropertyValue", () => {
const propertyName = "test";
//Act
const result = loadPropertyValue<string[]>(frontmatter, propertyName, PropertyType.LIST);
const result = loadPropertyValue<string[]>(frontmatter, propertyName, FilterRuleType.LIST);
//Assert
expect(result).toEqual(["test"]);
@ -154,7 +154,7 @@ describe("loadPropertyValue", () => {
const propertyName = "test";
//Act
const result = loadPropertyValue<string[]>(frontmatter, propertyName, PropertyType.LIST);
const result = loadPropertyValue<string[]>(frontmatter, propertyName, FilterRuleType.LIST);
//Assert
expect(result).toEqual(["test"]);
@ -166,7 +166,7 @@ describe("loadPropertyValue", () => {
const propertyName = "test";
//Act
const result = loadPropertyValue<string>(frontmatter, propertyName, PropertyType.DATE);
const result = loadPropertyValue<string>(frontmatter, propertyName, FilterRuleType.DATE);
//Assert
expect(result).toBeNull();
@ -178,7 +178,7 @@ describe("loadPropertyValue", () => {
const propertyName = "test";
//Act
const result = loadPropertyValue<string>(frontmatter, propertyName, PropertyType.DATETIME);
const result = loadPropertyValue<string>(frontmatter, propertyName, FilterRuleType.DATETIME);
//Assert
expect(result).toBeNull();
@ -191,7 +191,7 @@ describe("loadPropertyValue", () => {
const propertyName = "test";
//Act
const result = loadPropertyValue<string>(frontmatter, propertyName, PropertyType.DATE);
const result = loadPropertyValue<string>(frontmatter, propertyName, FilterRuleType.DATE);
//Assert
expect(result).toBe("2021-01-01");
@ -203,7 +203,7 @@ describe("loadPropertyValue", () => {
const propertyName = "test";
//Act
const result = loadPropertyValue<string>(frontmatter, propertyName, PropertyType.DATETIME);
const result = loadPropertyValue<string>(frontmatter, propertyName, FilterRuleType.DATETIME);
//Assert
expect(result).toBeNull();
@ -215,7 +215,7 @@ describe("loadPropertyValue", () => {
const propertyName = "test";
//Act
const result = loadPropertyValue<string>(frontmatter, propertyName, PropertyType.DATETIME);
const result = loadPropertyValue<string>(frontmatter, propertyName, FilterRuleType.DATETIME);
//Assert
expect(result).toBeNull();
@ -227,7 +227,7 @@ describe("loadPropertyValue", () => {
const propertyName = "test";
//Act
const result = loadPropertyValue<string>(frontmatter, propertyName, PropertyType.DATETIME);
const result = loadPropertyValue<string>(frontmatter, propertyName, FilterRuleType.DATETIME);
//Assert
expect(result).toBe("2021-01-01T00:00:00");

View file

@ -189,20 +189,6 @@ describe("matchDateFilter", () => {
// Assert
expect(result).toEqual(false);
});
it("should return false for matchDateFilter(YYYY-MM-DDTHH:mm:ss, invalid-date, IS, false)", () => {
// Arrange
const propertyValue = "2020-01-01T12:00:00";
const compare = "invalid-date";
const condition = DateFilterCondition.IS;
const matchIfNull = false;
// Act
const result = matchDateFilter(propertyValue, compare, condition, matchIfNull);
// Assert
expect(result).toEqual(false);
});
});
describe("Error Cases", () => {

View file

@ -59,5 +59,6 @@
"1.7.2": "1.4.13",
"1.7.3": "1.4.13",
"1.8.0": "1.4.13",
"1.8.1": "1.4.13"
"1.8.1": "1.4.13",
"1.9.0": "1.4.13"
}