Merge pull request #29 from trey-wallis/dev

1.0.0
This commit is contained in:
Trey Wallis 2024-05-25 14:25:53 -06:00 committed by GitHub
commit ace38fa69b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
24 changed files with 585 additions and 88 deletions

BIN
bun.lockb

Binary file not shown.

View file

@ -1,7 +1,7 @@
{
"id": "vault-explorer",
"name": "Vault Explorer",
"version": "0.5.5",
"version": "1.0.0",
"minAppVersion": "1.4.13",
"description": "Explore your vault in visual format",
"author": "Trey Wallis",

View file

@ -1,34 +1,35 @@
{
"name": "obsidian-vault-explorer",
"version": "0.5.5",
"description": "Explore your vault in visual format",
"main": "main.js",
"scripts": {
"dev": "node esbuild.config.mjs",
"build": "tsc -noEmit -skipLibCheck && node esbuild.config.mjs production",
"version": "node version-bump.mjs && git add manifest.json versions.json"
},
"keywords": [],
"author": "Trey Wallis",
"license": "MIT",
"devDependencies": {
"@tsconfig/svelte": "^5.0.4",
"@types/bun": "latest",
"@types/lodash": "^4.17.0",
"@types/node": "^16.11.6",
"@typescript-eslint/eslint-plugin": "5.29.0",
"@typescript-eslint/parser": "5.29.0",
"builtin-modules": "3.3.0",
"esbuild": "0.17.3",
"esbuild-svelte": "^0.8.0",
"obsidian": "latest",
"svelte-preprocess": "^5.1.4",
"tslib": "2.4.0",
"typescript": "4.7.4"
},
"dependencies": {
"lodash": "^4.17.21",
"nanoid": "^5.0.7",
"svelte": "^4.2.15"
}
"name": "obsidian-vault-explorer",
"version": "1.0.0",
"description": "Explore your vault in visual format",
"main": "main.js",
"scripts": {
"dev": "node esbuild.config.mjs",
"build": "tsc -noEmit -skipLibCheck && node esbuild.config.mjs production",
"version": "node version-bump.mjs && git add manifest.json versions.json"
},
"keywords": [],
"author": "Trey Wallis",
"license": "MIT",
"devDependencies": {
"@tsconfig/svelte": "^5.0.4",
"@types/bun": "latest",
"@types/lodash": "^4.17.0",
"@types/node": "^16.11.6",
"@typescript-eslint/eslint-plugin": "5.29.0",
"@typescript-eslint/parser": "5.29.0",
"builtin-modules": "3.3.0",
"esbuild": "0.17.3",
"esbuild-svelte": "^0.8.0",
"obsidian": "latest",
"svelte-preprocess": "^5.1.4",
"tslib": "2.4.0",
"typescript": "4.7.4"
},
"dependencies": {
"js-logger": "^1.6.1",
"lodash": "^4.17.21",
"nanoid": "^5.0.7",
"svelte": "^4.2.15"
}
}

View file

@ -1,10 +1,13 @@
import { LOG_LEVEL_WARN } from "./logger/constants";
import { generateUUID } from "./svelte/shared/services/uuid";
import { VaultExplorerPluginSettings } from "./types";
export const VAULT_EXPLORER_VIEW = "vault-explorer";
const uuid = generateUUID();
const groupUUID = generateUUID();
export const DEFAULT_SETTINGS: VaultExplorerPluginSettings = {
logLevel: LOG_LEVEL_WARN,
properties: {
favorite: "",
url: "",
@ -19,14 +22,13 @@ export const DEFAULT_SETTINGS: VaultExplorerPluginSettings = {
timestamp: "all",
sort: "file-name-asc",
properties: {
selectedGroupId: uuid,
selectedGroupId: groupUUID,
groups:
[
{
id: uuid,
id: groupUUID,
name: "Group 1",
filters: [],
position: 0,
isEnabled: true
}
]

6
src/logger/constants.ts Normal file
View file

@ -0,0 +1,6 @@
export const LOG_LEVEL_OFF = "off";
export const LOG_LEVEL_ERROR = "error";
export const LOG_LEVEL_WARN = "warn";
export const LOG_LEVEL_INFO = "info";
export const LOG_LEVEL_DEBUG = "debug";
export const LOG_LEVEL_TRACE = "trace";

54
src/logger/index.ts Normal file
View file

@ -0,0 +1,54 @@
import Logger, { ILogLevel } from "js-logger";
import { LOG_LEVEL_OFF, LOG_LEVEL_ERROR, LOG_LEVEL_WARN, LOG_LEVEL_INFO, LOG_LEVEL_DEBUG, LOG_LEVEL_TRACE } from "./constants";
import { FormattedLogMessage, LogMessageHeader } from "./types";
export const logLevelToString = (level: ILogLevel) => {
switch (level) {
case Logger.OFF:
return LOG_LEVEL_OFF;
case Logger.ERROR:
return LOG_LEVEL_ERROR;
case Logger.WARN:
return LOG_LEVEL_WARN;
case Logger.INFO:
return LOG_LEVEL_INFO;
case Logger.DEBUG:
return LOG_LEVEL_DEBUG;
case Logger.TRACE:
return LOG_LEVEL_TRACE;
default:
throw new Error("Unhandled log level");
}
}
export const stringToLogLevel = (value: string) => {
switch (value) {
case LOG_LEVEL_OFF:
return Logger.OFF;
case LOG_LEVEL_ERROR:
return Logger.ERROR;
case LOG_LEVEL_WARN:
return Logger.WARN;
case LOG_LEVEL_INFO:
return Logger.INFO;
case LOG_LEVEL_DEBUG:
return Logger.DEBUG;
case LOG_LEVEL_TRACE:
return Logger.TRACE;
default:
throw new Error(`Unhandled log level: ${value}`);
}
}
export const formatMessageForLogger = (...args: unknown[]): FormattedLogMessage => {
const head: unknown = args[0];
const body = args[1] as unknown as Record<string, unknown>;
if (typeof args[0] == "object") {
const headers = head as LogMessageHeader;
const { fileName, functionName, message } = headers;
return { message: `[${fileName}:${functionName}] ${message}`, data: body };
} else {
return { message: String(head), data: body };
}
}

10
src/logger/types.ts Normal file
View file

@ -0,0 +1,10 @@
export interface FormattedLogMessage {
message: string;
data: Record<string, unknown> | null;
}
export interface LogMessageHeader {
fileName: string;
functionName: string;
message: string;
}

View file

@ -8,7 +8,11 @@ import { DEFAULT_SETTINGS, VAULT_EXPLORER_VIEW } from './constants';
import _ from 'lodash';
import EventManager from './event/event-manager';
import { isVersionLessThan } from './utils';
import { VaultExplorerPluginSettings_0_3_3 } from './types-0.3.0';
import { VaultExplorerPluginSettings_0_3_3 } from './types/types-0.3.0';
import { VaultExplorerPluginSettings_0_5_5 } from './types/types-0.5.5';
import Logger from 'js-logger';
import { formatMessageForLogger, stringToLogLevel } from './logger';
import { LOG_LEVEL_WARN } from './logger/constants';
export default class VaultExplorerPlugin extends Plugin {
settings: VaultExplorerPluginSettings = DEFAULT_SETTINGS;
@ -16,6 +20,20 @@ export default class VaultExplorerPlugin extends Plugin {
async onload() {
await this.loadSettings();
//Setup logger
Logger.useDefaults();
Logger.setHandler(function (messages) {
const { message, data } = formatMessageForLogger(...messages);
console.log(message);
if (data) {
console.log(data);
}
});
const logLevel = stringToLogLevel(this.settings.logLevel);
Logger.setLevel(logLevel);
this.registerView(
VAULT_EXPLORER_VIEW,
(leaf) => new VaultExplorerView(leaf, this)
@ -105,7 +123,7 @@ export default class VaultExplorerPlugin extends Plugin {
if (isVersionLessThan(settingsVersion, "0.4.0")) {
console.log("Upgrading settings from version 0.3.3 to 0.4.0");
const typedData = (data as unknown) as VaultExplorerPluginSettings_0_3_3;
const newData: VaultExplorerPluginSettings = {
const newData: VaultExplorerPluginSettings_0_5_5 = {
...typedData,
filters: {
...typedData.filters,
@ -117,6 +135,31 @@ export default class VaultExplorerPlugin extends Plugin {
}
data = newData as unknown as Record<string, unknown>;
}
if (isVersionLessThan(settingsVersion, "1.0.0")) {
console.log("Upgrading settings from version 0.5.5 to 1.0.0");
const typedData = (data as unknown) as VaultExplorerPluginSettings_0_5_5;
const newData: VaultExplorerPluginSettings = {
...typedData,
logLevel: LOG_LEVEL_WARN,
filters: {
...typedData.filters,
properties: {
...typedData.filters.properties,
groups: typedData.filters.properties.groups.map(group => {
const { id, name, filters, isEnabled } = group;
return {
id,
name,
filters,
isEnabled
}
})
}
}
}
data = newData as unknown as Record<string, unknown>;
}
}
}
@ -128,7 +171,8 @@ export default class VaultExplorerPlugin extends Plugin {
}
async saveSettings() {
Logger.trace({ fileName: "main.ts", functionName: "saveSettings", message: "called" });
Logger.debug({ fileName: "main.ts", functionName: "saveSettings", message: "Saving settings" }, this.settings);
await this.saveData(this.settings);
// console.log("Settings saved");
}
}

View file

@ -1,6 +1,9 @@
import { App, PluginSettingTab, Setting } from "obsidian";
import VaultExplorerPlugin from "src/main";
import { getAllObsidianProperties, getDropdownOptionsForProperties, getObsidianPropertiesByType } from "./utils";
import { getDropdownOptionsForProperties, getObsidianPropertiesByType } from "./utils";
import { LOG_LEVEL_DEBUG, LOG_LEVEL_ERROR, LOG_LEVEL_INFO, LOG_LEVEL_OFF, LOG_LEVEL_TRACE, LOG_LEVEL_WARN } from "src/logger/constants";
import Logger from "js-logger";
import { stringToLogLevel } from "src/logger";
export default class VaultExplorerSettingsTab extends PluginSettingTab {
plugin: VaultExplorerPlugin;
@ -89,5 +92,29 @@ export default class VaultExplorerSettingsTab extends PluginSettingTab {
await this.plugin.saveSettings();
}));
new Setting(containerEl).setName("Debugging").setHeading();
new Setting(containerEl)
.setName("Log level")
.setDesc(
"Sets the log level. Please use trace to see all log messages."
)
.addDropdown((cb) => {
cb.addOptions({
[LOG_LEVEL_OFF]: "Off",
[LOG_LEVEL_ERROR]: "Error",
[LOG_LEVEL_WARN]: "Warn",
[LOG_LEVEL_INFO]: "Info",
[LOG_LEVEL_DEBUG]: "Debug",
[LOG_LEVEL_TRACE]: "Trace"
})
cb.setValue(this.plugin.settings.logLevel).onChange(
async (value) => {
this.plugin.settings.logLevel = value;
await this.plugin.saveSettings();
Logger.setLevel(stringToLogLevel(value));
}
);
});
}
}

View file

@ -14,6 +14,9 @@
name={group.name}
isSelected={group.isEnabled}
on:groupClick
on:groupDrop
on:groupDragOver
on:groupDragStart
/>
{/each}
</Stack>

View file

@ -13,15 +13,36 @@
function handleClick() {
dispatch("groupClick", { id });
}
function handleDragStart(event: Event) {
dispatch("groupDragStart", { nativeEvent: event, id });
}
function handleDragOver(event: Event) {
dispatch("groupDragOver", { nativeEvent: event, id });
}
function handleDrop(event: Event) {
dispatch("groupDrop", { nativeEvent: event, id });
}
</script>
<button class={className} on:click={handleClick}>
<div
tabindex="0"
role="button"
draggable="true"
class={className}
on:dragstart={handleDragStart}
on:dragover={handleDragOver}
on:drop={handleDrop}
on:click={handleClick}
on:keydown={(e) => (e.key === "Enter" || e.key === " ") && handleClick()}
>
{name}
</button>
</div>
<style>
.vault-explorer-group-tag {
all: unset;
white-space: nowrap;
font-size: var(--tag-size);
font-weight: var(--tag-weight);

View file

@ -358,6 +358,63 @@
propertyFilterGroups = newGroups;
}
function handleGroupDrop(e: CustomEvent) {
const { id, nativeEvent } = e.detail;
const dragId = nativeEvent.dataTransfer.getData("text");
nativeEvent.dataTransfer.dropEffect = "move";
const draggedIndex = propertyFilterGroups.findIndex(
(group) => group.id === dragId,
);
const dragged = propertyFilterGroups.find(
(group) => group.id === dragId,
);
const droppedIndex = propertyFilterGroups.findIndex(
(group) => group.id === id,
);
const dropped = propertyFilterGroups.find((group) => group.id === id);
if (!dragged || !dropped || draggedIndex === -1 || droppedIndex === -1)
return;
let newGroups = [...propertyFilterGroups];
newGroups[draggedIndex] = dropped;
newGroups[droppedIndex] = dragged;
propertyFilterGroups = newGroups;
}
function handleGroupDragOver(e: CustomEvent) {
const { nativeEvent } = e.detail;
nativeEvent.preventDefault();
}
function handleGroupDragStart(e: CustomEvent) {
const { id, nativeEvent } = e.detail;
nativeEvent.dataTransfer.setData("text", id);
nativeEvent.dataTransfer.effectAllowed = "move";
//Set drag image
//The drag image by default will be square
//We can create a custom drag image by cloning the target element
const dragImage = nativeEvent.target.cloneNode(true);
const rect = nativeEvent.target.getBoundingClientRect();
dragImage.style.position = "absolute";
dragImage.style.top = "-9999px"; // Hide the element
dragImage.style.left = "-9999px"; // Hide the element
document.body.appendChild(dragImage);
nativeEvent.dataTransfer.setDragImage(
dragImage,
rect.width / 2,
rect.height / 2,
);
nativeEvent.target.addEventListener("dragend", () => {
dragImage.remove();
});
}
function openPropertiesFilterModal() {
new PropertiesFilterModal(plugin).open();
}
@ -532,6 +589,9 @@
<GroupTagList
groups={propertyFilterGroups}
on:groupClick={handleGroupClick}
on:groupDrop={handleGroupDrop}
on:groupDragOver={handleGroupDragOver}
on:groupDragStart={handleGroupDragStart}
/>
{/if}
{#if propertyFilterGroups.length === 0}

View file

@ -2,6 +2,7 @@ import { FrontMatterCache } from "obsidian";
import { CheckboxFilterCondition, DateFilterCondition, ListFilterCondition, NumberFilterCondition, PropertyFilterGroup } from "src/types";
import { FilterCondition, TextFilterCondition } from "src/types";
import { getBeforeMidnightMillis, getMidnightMillis, getMillis } from "../time-utils";
import Logger from "js-logger";
//Tests
//Group is enabled/disabled
@ -30,14 +31,14 @@ export const filterByProperty = (frontmatter: FrontMatterCache | undefined, grou
if (type === "text") {
//If the value is not a string, skip the filter
if (propertyValue !== null && typeof propertyValue !== "string") {
console.error(`Property value is not a string: ${propertyValue}`);
Logger.warn(`Property value is not a string: ${propertyValue}`);
return true;
}
const doesMatch = doesTextMatchFilter(condition, propertyValue, value);
return doesMatch;
} else if (type === "list") {
if (propertyValue !== null && !Array.isArray(propertyValue)) {
console.error(`Property value is not an array: ${propertyValue}`);
Logger.warn(`Property value is not an array: ${propertyValue}`);
return true;
}
const compare = value.split(",").map((v) => v.trim());
@ -45,7 +46,7 @@ export const filterByProperty = (frontmatter: FrontMatterCache | undefined, grou
return doesMatch;
} else if (type === "number") {
if (propertyValue !== null && typeof propertyValue !== "number") {
console.error(`Property value is not a number: ${propertyValue}`);
Logger.warn(`Property value is not a number: ${propertyValue}`);
return true;
}
const compare = parseFloat(value);
@ -53,7 +54,7 @@ export const filterByProperty = (frontmatter: FrontMatterCache | undefined, grou
return doesMatch;
} else if (type === "checkbox") {
if (propertyValue !== null && typeof propertyValue !== "boolean") {
console.error(`Property value is not a boolean: ${propertyValue}`);
Logger.warn(`Property value is not a boolean: ${propertyValue}`);
return true;
}
@ -63,7 +64,7 @@ export const filterByProperty = (frontmatter: FrontMatterCache | undefined, grou
} else if (type === "date" || type === "datetime") {
if (propertyValue !== null && typeof propertyValue !== "string") {
console.error(`Property value is not a string: ${propertyValue}`);
Logger.warn(`Property value is not a string: ${propertyValue}`);
return true;
}

View file

@ -1,26 +0,0 @@
<script lang="ts">
export let name: string;
export let isSelected: boolean;
$: className =
"vault-explorer-group-button" +
(isSelected ? " vault-explorer-group-button--active" : "");
</script>
<button class={className} on:click>{name}</button>
<style>
.vault-explorer-group-button {
all: unset;
width: calc(100% - 12px);
padding: 4px 6px;
}
.vault-explorer-group-button:hover {
background-color: var(--background-modifier-hover);
}
.vault-explorer-group-button--active {
background-color: var(--background-modifier-hover);
}
</style>

View file

@ -37,7 +37,6 @@
{#if selectedGroup.filters.length > 0}
<PropertyFilterList
filters={selectedGroup.filters}
on:groupClick
on:filterConditionChange
on:filterTypeChange
on:filterNameChange

View file

@ -0,0 +1,95 @@
<script lang="ts">
import Flex from "src/svelte/shared/components/flex.svelte";
import Icon from "src/svelte/shared/components/icon.svelte";
export let id: string;
export let name: string;
export let isSelected: boolean;
let ref: HTMLElement;
import { createEventDispatcher } from "svelte";
const dispatch = createEventDispatcher();
function handleClick() {
dispatch("itemClick", { id });
}
function handleMouseDown(event: Event) {
ref.setAttribute("draggable", "true");
Object.defineProperty(event, "target", {
value: ref,
});
ref.dispatchEvent(new Event(event.type, event));
}
function handleDragStart(event: Event) {
dispatch("itemDragStart", { nativeEvent: event, id });
}
function handleDragOver(event: Event) {
dispatch("itemDragOver", { nativeEvent: event, id });
}
function handleDrop(event: Event) {
dispatch("itemDrop", { nativeEvent: event, id });
}
function handleDragEnd(event: Event) {
dispatch("itemDragEnd", { nativeEvent: event, id });
}
$: className =
"vault-explorer-group-item" +
(isSelected ? " vault-explorer-group-item--active" : "");
</script>
<div
role="listitem"
bind:this={ref}
on:dragstart={handleDragStart}
on:dragover={handleDragOver}
on:drop={handleDrop}
on:dragend={handleDragEnd}
>
<Flex align="center">
<div
tabindex="0"
role="button"
class="vault-explorer-group-item__icon"
on:mousedown={handleMouseDown}
>
<Icon iconId="grip-vertical" />
</div>
<div
tabindex="0"
role="button"
class={className}
on:click={handleClick}
on:keydown={(e) =>
(e.key === "Enter" || e.key === " ") && handleClick()}
>
{name}
</div>
</Flex>
</div>
<style>
.vault-explorer-group-item__icon {
margin-top: 4px;
}
.vault-explorer-group-item {
display: flex;
width: calc(100% - 12px);
padding: 4px 6px;
}
.vault-explorer-group-item:hover {
background-color: var(--background-modifier-hover);
}
.vault-explorer-group-button--active {
background-color: var(--background-modifier-hover);
}
</style>

View file

@ -7,8 +7,9 @@
export let selectedGroup: PropertyFilterGroup | undefined;
import { createEventDispatcher } from "svelte";
import GroupButton from "./group-button.svelte";
import Flex from "src/svelte/shared/components/flex.svelte";
import GroupListItem from "./group-list-item.svelte";
const dispatch = createEventDispatcher();
function handleAddGroupClick() {
@ -18,10 +19,6 @@
function handleDeleteGroupClick() {
dispatch("deleteGroupClick");
}
function handleGroupClick(id: string) {
dispatch("groupClick", { id });
}
</script>
<div class="vault-explorer-group-list-container">
@ -41,10 +38,15 @@
<div class="vault-explorer-group-list">
<Flex direction="column" width="100px">
{#each groups as group (group.id)}
<GroupButton
<GroupListItem
id={group.id}
name={group.name}
isSelected={selectedGroup?.id === group.id}
on:click={() => handleGroupClick(group.id)}
on:itemClick
on:itemDragStart
on:itemDragOver
on:itemDrop
on:itemDragEnd
/>
{/each}
</Flex>

View file

@ -34,6 +34,7 @@
import VaultExplorerPlugin from "src/main";
import { ObsidianProperty } from "src/obsidian/types";
import Wrap from "src/svelte/shared/components/wrap.svelte";
const dispatch = createEventDispatcher();
function handleDeleteClick() {

View file

@ -56,7 +56,6 @@
id: generateUUID(),
name: `Group ${groups.length + 1}`,
filters: [createPropertyFilter()],
position: groups.length,
isEnabled: groups.length === 0,
};
@ -135,6 +134,44 @@
groups = newGroups;
}
function handleGroupDragStart(e: CustomEvent) {
const { nativeEvent, id } = e.detail;
nativeEvent.dataTransfer.setData("text", id);
nativeEvent.dataTransfer.effectAllowed = "move";
}
function handleGroupDragOver(e: CustomEvent) {
const { nativeEvent } = e.detail;
nativeEvent.preventDefault();
}
function handleGroupDragEnd(e: CustomEvent) {
const { nativeEvent } = e.detail;
nativeEvent.target.draggable = false;
}
function handleGroupDrop(e: CustomEvent) {
const { id, nativeEvent } = e.detail;
const dragId = nativeEvent.dataTransfer.getData("text");
nativeEvent.dataTransfer.dropEffect = "move";
const draggedIndex = groups.findIndex((group) => group.id === dragId);
const dragged = groups.find((group) => group.id === dragId);
const droppedIndex = groups.findIndex((group) => group.id === id);
const dropped = groups.find((group) => group.id === id);
if (!dragged || !dropped || draggedIndex === -1 || droppedIndex === -1)
return;
let newGroups = [...groups];
newGroups[draggedIndex] = dropped;
newGroups[droppedIndex] = dragged;
groups = newGroups;
}
function handleFilterDeleteClick(e: CustomEvent) {
const { id } = e.detail;
@ -253,7 +290,11 @@
<GroupList
{groups}
{selectedGroup}
on:groupClick={handleGroupClick}
on:itemClick={handleGroupClick}
on:itemDragStart={handleGroupDragStart}
on:itemDragOver={handleGroupDragOver}
on:itemDrop={handleGroupDrop}
on:itemDragEnd={handleGroupDragEnd}
on:addGroupClick={handleAddGroupClick}
on:deleteGroupClick={handleDeleteGroupClick}
/>
@ -263,7 +304,6 @@
{selectedGroup}
on:groupNameChange={handleGroupNameChange}
on:filterAddClick={handleFilterAddClick}
on:groupClick={handleGroupClick}
on:filterTypeChange={handleFilterTypeChange}
on:filterConditionChange={handleFilterConditionChange}
on:filterDeleteClick={handleFilterDeleteClick}

View file

@ -0,0 +1,25 @@
<script lang="ts">
import { setIcon } from "obsidian";
import { onMount } from "svelte";
export let ariaLabel = "";
export let iconId = "";
let ref: HTMLElement;
// Use onMount to ensure the element is available in the DOM
onMount(() => {
if (iconId === "ellipsis-vertical") return;
setIcon(ref, iconId);
});
</script>
<div class="vault-explorer-icon" aria-label={ariaLabel} bind:this={ref}></div>
<style>
.vault-explorer-icon {
cursor: var(--cursor);
color: var(--icon-color);
opacity: var(--icon-opacity);
}
</style>

View file

@ -1,4 +1,5 @@
export interface VaultExplorerPluginSettings {
logLevel: string;
properties: {
favorite: string;
url: string;
@ -120,7 +121,6 @@ export interface PropertyFilterGroup {
id: string;
name: string;
filters: PropertyFilter[];
position: number;
isEnabled: boolean;
}

131
src/types/types-0.5.5.ts Normal file
View file

@ -0,0 +1,131 @@
export interface VaultExplorerPluginSettings_0_5_5 {
properties: {
favorite: string;
url: string;
custom1: string;
custom2: string;
custom3: string;
},
filters: {
folder: string;
search: string;
onlyFavorites: boolean;
sort: SortFilter;
timestamp: TimestampFilter;
properties: {
selectedGroupId: string;
groups: PropertyFilterGroup[];
}
},
currentView: CurrentView;
pageSize: number;
pluginVersion: string | null;
}
export type FilterOperator = "and" | "or";
export 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",
}
export enum ListFilterCondition {
CONTAINS = "contains",
DOES_NOT_CONTAIN = "does-not-contain",
EXISTS = "exists",
DOES_NOT_EXIST = "does-not-exist",
}
export 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",
}
export enum CheckboxFilterCondition {
IS = "is",
IS_NOT = "is-not",
EXISTS = "exists",
DOES_NOT_EXIST = "does-not-exist",
}
//TODO: add more types
export enum DateFilterCondition {
IS = "is",
IS_BEFORE = "is-before",
IS_AFTER = "is-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: PropertyFilterType;
isEnabled: boolean;
value: string;
}
export enum PropertyFilterType {
TEXT = "text",
NUMBER = "number",
LIST = "list",
CHECKBOX = "checkbox",
DATE = "date",
DATETIME = "datetime",
}
export interface TextPropertyFilter extends BasePropertyFilter {
type: PropertyFilterType.TEXT;
condition: TextFilterCondition;
}
export interface NumberPropertyFilter extends BasePropertyFilter {
type: PropertyFilterType.NUMBER;
condition: NumberFilterCondition;
}
export interface ListPropertyFilter extends BasePropertyFilter {
type: PropertyFilterType.LIST
condition: ListFilterCondition;
}
export interface CheckboxPropertyFilter extends BasePropertyFilter {
type: PropertyFilterType.CHECKBOX
condition: CheckboxFilterCondition;
}
export interface DatePropertyFilter extends BasePropertyFilter {
type: PropertyFilterType.DATE | PropertyFilterType.DATETIME;
condition: DateFilterCondition;
}
export type PropertyFilter = TextPropertyFilter | NumberPropertyFilter | ListPropertyFilter | CheckboxPropertyFilter | DatePropertyFilter;
export interface PropertyFilterGroup {
id: string;
name: string;
filters: PropertyFilter[];
position: number;
isEnabled: boolean;
}
export type CurrentView = "grid" | "list";
export type SortFilter = "file-name-asc" | "file-name-desc" | "modified-asc" | "modified-desc";
export type TimestampFilter = "created-today" | "modified-today" | "created-this-week" | "modified-this-week" | "created-2-weeks" | "modified-2-weeks" | "all";

View file

@ -42,5 +42,6 @@
"0.5.2": "1.4.13",
"0.5.3": "1.4.13",
"0.5.4": "1.4.13",
"0.5.5": "1.4.13"
"0.5.5": "1.4.13",
"1.0.0": "1.4.13"
}