Merge pull request #322 from decaf-dev/update-ui

Update UI
This commit is contained in:
DecafDev 2024-09-09 23:42:13 -06:00 committed by GitHub
commit ecaf51a5dc
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
32 changed files with 564 additions and 598 deletions

View file

@ -1,7 +1,7 @@
{
"id": "vault-explorer",
"name": "Vault Explorer",
"version": "1.41.1",
"version": "1.42.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.41.1",
"version": "1.42.0",
"description": "Explore your vault in visual format",
"main": "main.js",
"scripts": {

View file

@ -22,14 +22,6 @@ export const DEFAULT_SETTINGS: VaultExplorerPluginSettings = {
isEnabled: true,
value: "",
},
favorites: {
isEnabled: true,
value: false,
},
timestamp: {
isEnabled: true,
value: "all",
},
sort: {
isEnabled: true,
value: "file-name-asc",
@ -94,8 +86,6 @@ export const DEFAULT_SETTINGS: VaultExplorerPluginSettings = {
enableClockUpdates: true,
enableFileIcons: false,
loadBodyTags: true,
filterGroupsWidth: "300px",
shouldWrapFilterGroups: false,
shouldCollapseFilters: false,
pageSize: 25,
viewOrder: [

View file

@ -21,7 +21,6 @@ export enum PluginEvent {
VIEW_TOGGLE_SETTING_CHANGE = "view-toggle-setting-change",
FILE_ICONS_SETTING_CHANGE = "file-icons-setting-change",
LOAD_BODY_TAGS_SETTING_CHANGE = "load-body-tags-setting-change",
WRAP_FILTER_GROUPS_SETTING_CHANGE = "wrap-filter-groups-setting-change",
LOAD_SOCIAL_MEDIA_IMAGE_SETTING_CHANGE = "load-social-media-image-setting-change",
SHOW_TAGS_SETTING_CHANGE = "show-tags-setting-change",
}

View file

@ -20,6 +20,7 @@ import { isVersionLessThan } from "./utils";
import License from "./svelte/shared/services/license";
import { clearSocialMediaImageCache } from "./svelte/app/services/social-media-image-cache";
import store from "./svelte/shared/services/store";
import "./styles.css";
export default class VaultExplorerPlugin extends Plugin {
settings: VaultExplorerPluginSettings = DEFAULT_SETTINGS;
@ -63,6 +64,22 @@ export default class VaultExplorerPlugin extends Plugin {
}
private registerEvents() {
this.registerEvent(
this.app.workspace.on("active-leaf-change", () => {
const explorerLeaf =
this.app.workspace.getActiveViewOfType(VaultExplorerView);
const statusBar = document.querySelector(
".status-bar"
) as HTMLElement | null;
if (!statusBar) return;
if (explorerLeaf) {
statusBar.classList.add("vault-explorer-status-bar");
} else {
statusBar.classList.remove("vault-explorer-status-bar");
}
})
);
//Callback if the file is renamed or moved
//This callback is already debounced by Obsidian
this.registerEvent(

View file

@ -108,20 +108,6 @@ export default class VaultExplorerSettingsTab extends PluginSettingTab {
})
);
new Setting(containerEl)
.setName("Wrap filter groups")
.addToggle((toggle) =>
toggle
.setValue(this.plugin.settings.shouldWrapFilterGroups)
.onChange(async (value) => {
this.plugin.settings.shouldWrapFilterGroups = value;
await this.plugin.saveSettings();
EventManager.getInstance().emit(
PluginEvent.WRAP_FILTER_GROUPS_SETTING_CHANGE
);
})
);
new Setting(containerEl).setName("Filters").setHeading();
new Setting(containerEl).setName("Search filter").addToggle((toggle) =>
@ -136,36 +122,6 @@ export default class VaultExplorerSettingsTab extends PluginSettingTab {
})
);
new Setting(containerEl)
.setName("Favorites filter")
.addToggle((toggle) =>
toggle
.setValue(this.plugin.settings.filters.favorites.isEnabled)
.onChange(async (value) => {
this.plugin.settings.filters.favorites.isEnabled =
value;
await this.plugin.saveSettings();
EventManager.getInstance().emit(
PluginEvent.FILTER_TOGGLE_SETTING_CHANGE
);
})
);
new Setting(containerEl)
.setName("Timestamp filter")
.addToggle((toggle) =>
toggle
.setValue(this.plugin.settings.filters.timestamp.isEnabled)
.onChange(async (value) => {
this.plugin.settings.filters.timestamp.isEnabled =
value;
await this.plugin.saveSettings();
EventManager.getInstance().emit(
PluginEvent.FILTER_TOGGLE_SETTING_CHANGE
);
})
);
new Setting(containerEl).setName("Sort filter").addToggle((toggle) =>
toggle
.setValue(this.plugin.settings.filters.sort.isEnabled)

3
src/styles.css Normal file
View file

@ -0,0 +1,3 @@
.vault-explorer-status-bar {
display: none;
}

View file

@ -1,7 +0,0 @@
<script lang="ts">
import Checkbox from "src/svelte/shared/components/checkbox.svelte";
export let value: boolean;
</script>
<Checkbox id="favorites" label="Favorites" {value} on:change />

View file

@ -35,7 +35,6 @@
export let path: string;
export let createdMillis: number;
export let content: string | null;
export let isFavorite: boolean | null;
let ref: HTMLElement | null = null;
let enableFileIcons = false;
@ -146,16 +145,9 @@
openInCurrentTab(plugin, path);
}
function handleFavoriteChange(filePath: string, value: boolean) {
dispatch("favoritePropertyChange", { filePath, value });
}
function handleCardContextMenu(e: Event) {
const nativeEvent = e as MouseEvent;
openContextMenu(plugin, path, nativeEvent, {
isFavorite,
onFavoriteChange: handleFavoriteChange,
});
openContextMenu(plugin, path, nativeEvent, {});
}
function handleCardMouseOver(e: MouseEvent) {

View file

@ -46,7 +46,6 @@
path={fileRenderData.path}
content={fileRenderData.content}
createdMillis={fileRenderData.createdMillis}
isFavorite={fileRenderData.isFavorite}
on:favoritePropertyChange
/>
{/each}

View file

@ -3,115 +3,50 @@
import _ from "lodash";
import VaultExplorerPlugin from "src/main";
import store from "src/svelte/shared/services/store";
import { onMount } from "svelte";
import Wrap from "src/svelte/shared/components/wrap.svelte";
import EventManager from "src/event/event-manager";
import { PluginEvent } from "src/event/types";
import FilterGroup from "./filter-group.svelte";
import Stack from "src/svelte/shared/components/stack.svelte";
export let groups: TFilterGroup[] = [];
let containerRef: HTMLDivElement | null = null;
let shouldWrapFilterGroups: boolean = false;
const debounceSaveContainerWidth = _.debounce(saveContainerWidth, 200);
let plugin: VaultExplorerPlugin;
store.plugin.subscribe((p) => {
plugin = p;
shouldWrapFilterGroups = plugin.settings.shouldWrapFilterGroups;
});
onMount(() => {
function handleWrapFilterGroupsSettingChange() {
shouldWrapFilterGroups = plugin.settings.shouldWrapFilterGroups;
}
EventManager.getInstance().on(
PluginEvent.WRAP_FILTER_GROUPS_SETTING_CHANGE,
handleWrapFilterGroupsSettingChange,
);
return () => {
EventManager.getInstance().off(
PluginEvent.WRAP_FILTER_GROUPS_SETTING_CHANGE,
handleWrapFilterGroupsSettingChange,
);
};
});
onMount(() => {
let resizeObserver: ResizeObserver;
if (containerRef) {
containerRef.style.width = plugin.settings.filterGroupsWidth;
resizeObserver = new ResizeObserver(() => {
debounceSaveContainerWidth();
});
resizeObserver.observe(containerRef);
}
return () => {
resizeObserver?.disconnect();
};
});
async function saveContainerWidth() {
if (containerRef) {
plugin.settings.filterGroupsWidth = containerRef.style.width;
await plugin.saveSettings();
}
}
</script>
<div class="vault-explorer-filter-group-list">
<div
class="vault-explorer-filter-group-list__container"
bind:this={containerRef}
>
<Stack spacing="md" width="fit-content">
{#if groups.length > 0}
<Wrap
spacingX="sm"
spacingY="sm"
wrap={shouldWrapFilterGroups ? "wrap" : "nowrap"}
>
{#each groups as group (group.id)}
<FilterGroup
id={group.id}
name={group.name}
isSelected={group.isEnabled}
isSticky={group.isSticky}
on:groupClick
on:groupDrop
on:groupDragOver
on:groupDragStart
on:groupContextMenu
/>
{/each}
</Wrap>
{#each groups as group (group.id)}
<FilterGroup
id={group.id}
name={group.name}
isSelected={group.isEnabled}
isSticky={group.isSticky}
on:groupClick
on:groupDrop
on:groupDragOver
on:groupDragStart
on:groupContextMenu
/>
{/each}
{/if}
{#if groups.length === 0}
<span class="vault-explorer-empty-label">No groups</span>
<span class="vault-explorer-empty-label">No groups to display</span>
{/if}
</div>
</Stack>
</div>
<style>
.vault-explorer-filter-group-list {
width: 100%;
position: relative;
}
.vault-explorer-filter-group-list__container {
padding-bottom: 8px;
resize: horizontal;
width: min(100%, 700px);
overflow-x: auto;
max-width: 100%;
}
.vault-explorer-empty-label {
color: var(--text-faint);
font-size: var(--font-smaller);
font-size: var(--font-md);
margin-left: 4px;
}
</style>

View file

@ -66,18 +66,16 @@
<style>
.vault-explorer-filter-group {
display: flex;
align-items: center;
width: 150px;
height: 75px;
white-space: nowrap;
font-size: var(--tag-size);
font-weight: var(--tag-weight);
text-decoration: var(--tag-decoration);
padding: var(--tag-padding-y) var(--tag-padding-x);
line-height: 1;
border-radius: var(--tag-radius);
padding: 4px 8px;
border-radius: var(--radius-m);
color: var(--text-faint);
border: 1px solid var(--background-modifier-border);
background-color: var(--background-primary);
pointer-events: auto;
}
.vault-explorer-filter-group:focus-visible {

View file

@ -35,7 +35,6 @@
export let custom1: string | null;
export let custom2: string | null;
export let custom3: string | null;
export let isFavorite: boolean | null;
export let coverImageFit: CoverImageFit;
let plugin: VaultExplorerPlugin;
@ -147,9 +146,7 @@
const showCoverImageOptions = path.endsWith(".md");
openContextMenu(plugin, path, nativeEvent, {
isFavorite,
coverImageFit: showCoverImageOptions ? coverImageFit : undefined,
onFavoriteChange: handleFavoriteChange,
onCoverImageFitChange: showCoverImageOptions
? handleCoverImageFitChange
: undefined,
@ -229,14 +226,9 @@
{/if}
{/each}
{#if imageUrl === null}
<div class="vault-explorer-grid-card__cover-icon">
<Icon
iconId={getIconIdForFile(baseName, extension)}
size="lg"
/>
</div>
<div class="vault-explorer-grid-card__image"></div>
{/if}
{#if isFavorite === true}
<!-- {#if isFavorite === true}
<div class="vault-explorer-grid-card__favorite">
<Flex
justify="center"
@ -247,7 +239,7 @@
<Icon iconId="star" ariaLabel="Favorite" />
</Flex>
</div>
{/if}
{/if} -->
</div>
<div class="vault-explorer-grid-card__body">
<div
@ -353,14 +345,7 @@
border-top-right-radius: var(--radius-m);
}
.vault-explorer-grid-card__cover-icon {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
.vault-explorer-grid-card__favorite {
/* .vault-explorer-grid-card__favorite {
position: absolute;
top: 8px;
right: 8px;
@ -368,7 +353,7 @@
height: 20px;
background-color: var(--background-primary);
border-radius: 50%;
}
} */
.vault-explorer-grid-card__body {
padding: 8px 16px;

View file

@ -35,7 +35,6 @@
custom1={fileRenderData.custom1}
custom2={fileRenderData.custom2}
custom3={fileRenderData.custom3}
isFavorite={fileRenderData.isFavorite}
on:favoritePropertyChange
on:coverImageFitChange
/>

View file

@ -18,7 +18,6 @@
export let extension: string;
export let path: string;
export let tags: string[] | null;
export let isFavorite: boolean | null;
export let showTags: boolean;
export let isSmallScreenSize: boolean;
@ -82,10 +81,7 @@
function handleItemContextMenu(e: Event) {
const nativeEvent = e as MouseEvent;
openContextMenu(plugin, path, nativeEvent, {
isFavorite,
onFavoriteChange: handleFavoriteChange,
});
openContextMenu(plugin, path, nativeEvent, {});
}
function handleItemMouseOver(e: MouseEvent) {

View file

@ -33,7 +33,6 @@
{isSmallScreenSize}
path={fileRenderData.path}
tags={fileRenderData.tags}
isFavorite={fileRenderData.isFavorite}
on:favoritePropertyChange
/>
{/each}

View file

@ -53,50 +53,67 @@
</script>
<div class="vault-explorer-pagination-indicator" bind:this={ref}>
<Stack
align="center"
direction={isWrapped ? "row-reverse" : "row"}
justify="flex-end"
spacing="md"
>
<Stack spacing="xs">
<div class="vault-explorer-pagination-indicator__container">
<Stack
align="center"
direction={isWrapped ? "row-reverse" : "row"}
justify="flex-end"
spacing="md"
>
<Flex>
<IconButton
iconId="chevrons-left"
ariaLabel="First page"
on:click={() => handlePageChange(1)}
/>
<IconButton
iconId="chevron-left"
ariaLabel="Previous page"
disabled={currentPage === 1}
on:click={() => handlePageChange(currentPage - 1)}
/>
<IconButton
iconId="chevron-right"
ariaLabel="Next page"
disabled={currentPage === totalPages}
on:click={() => handlePageChange(currentPage + 1)}
/>
<IconButton
iconId="chevrons-right"
ariaLabel="Last page"
on:click={() => handlePageChange(totalPages)}
/>
</Flex>
<Stack spacing="xs">
<span>{startIndex + 1}</span>
<span>-</span>
<span>{endIndex}</span>
<Stack spacing="xs">
<span>{startIndex + 1}</span>
<span>-</span>
<span>{endIndex}</span>
</Stack>
<span>of</span>
<span>{totalItems}</span>
</Stack>
<span>of</span>
<span>{totalItems}</span>
</Stack>
<Flex>
<IconButton
iconId="chevrons-left"
ariaLabel="First page"
on:click={() => handlePageChange(1)}
/>
<IconButton
iconId="chevron-left"
ariaLabel="Previous page"
disabled={currentPage === 1}
on:click={() => handlePageChange(currentPage - 1)}
/>
<IconButton
iconId="chevron-right"
ariaLabel="Next page"
disabled={currentPage === totalPages}
on:click={() => handlePageChange(currentPage + 1)}
/>
<IconButton
iconId="chevrons-right"
ariaLabel="Last page"
on:click={() => handlePageChange(totalPages)}
/>
</Flex>
</Stack>
</div>
</div>
<style>
.vault-explorer-pagination-indicator {
.vault-explorer-pagination-indicator__container {
flex: 1;
position: fixed;
bottom: 0px;
right: 0px;
display: flex;
border-style: solid;
border-width: var(--status-bar-border-width);
border-color: var(--status-bar-border-color);
background-color: var(--status-bar-background);
color: var(--status-bar-text-color);
font-size: var(--status-bar-font-size);
padding: var(--size-4-1);
}
.vault-explorer-pagination-indicator {
display: relative;
}
</style>

View file

@ -1,36 +1,67 @@
<script lang="ts">
import IconButton from "src/svelte/shared/components/icon-button.svelte";
import Stack from "src/svelte/shared/components/stack.svelte";
import { createEventDispatcher } from "svelte";
export let value: string;
let inputRef: HTMLInputElement | null;
let hidden: boolean = true;
const dispatch = createEventDispatcher();
function handleClearClick() {
dispatch("clear", { value: "" });
}
function handleIconClick() {
hidden = !hidden;
if (!hidden) {
requestAnimationFrame(() => {
inputRef?.focus();
});
}
}
</script>
<div class="vault-explorer-search-filter">
<input type="text" placeholder="Search..." {value} on:input />
{#if value.length > 0}
<div
tabindex="0"
role="button"
aria-label="Clear search"
class="search-input-clear-button"
on:click={() => handleClearClick()}
on:keydown={(e) => {
if (e.key === "Enter") {
handleClearClick();
}
}}
<Stack spacing="sm">
<div
class="vault-explorer-search-filter"
style="display: {hidden ? 'none' : 'block'};"
>
<input
bind:this={inputRef}
type="text"
placeholder="Search..."
{value}
on:input
/>
{/if}
</div>
{#if value.length > 0}
<div
tabindex="0"
role="button"
aria-label="Clear search"
class="search-input-clear-button"
on:click={() => handleClearClick()}
on:keydown={(e) => {
if (e.key === "Enter") {
handleClearClick();
}
}}
/>
{/if}
</div>
<IconButton
iconId="search"
ariaLabel="Search"
on:click={() => handleIconClick()}
/>
</Stack>
<style>
.vault-explorer-search-filter input[type="text"] {
width: 100%;
height: 24px;
}
.vault-explorer-search-filter {

View file

@ -42,11 +42,6 @@
key: "tags",
label: "Tags",
},
{
key: "isFavorite",
label: "Favorite",
format: (value: unknown) => (value === true ? "Yes" : "No"),
},
{
key: "createdMillis",
label: "Created",
@ -85,10 +80,6 @@
};
});
function handleFavoriteChange(filePath: string, value: boolean) {
dispatch("favoritePropertyChange", { filePath, value });
}
function handleRowClick(path: string) {
if (plugin === null) return;
@ -108,18 +99,11 @@
});
}
function handleRowContextMenu(
e: Event,
path: string,
isFavorite: boolean | null,
) {
function handleRowContextMenu(e: Event, path: string) {
if (plugin === null) return;
const nativeEvent = e as MouseEvent;
openContextMenu(plugin, path, nativeEvent, {
isFavorite,
onFavoriteChange: handleFavoriteChange,
});
openContextMenu(plugin, path, nativeEvent, {});
}
function getValue(item: FileRenderData, column: TColumn): unknown {
@ -171,11 +155,7 @@
}}
on:focus={() => {}}
on:contextmenu={(e) =>
handleRowContextMenu(
e,
filteredItem.path,
filteredItem.isFavorite,
)}
handleRowContextMenu(e, filteredItem.path)}
on:mouseover={(e) =>
handleRowMouseOver(e, filteredItem.path)}
>

View file

@ -1,67 +0,0 @@
<script lang="ts">
import { Menu } from "obsidian";
import IconButton from "src/svelte/shared/components/icon-button.svelte";
import { TimestampFilterOption } from "src/types";
import { createEventDispatcher } from "svelte";
export let value: TimestampFilterOption;
const dispatch = createEventDispatcher();
function handleValueChange(value: TimestampFilterOption) {
dispatch("change", { value });
}
function handleButtonClick(e: CustomEvent) {
const nativeEvent = e.detail.nativeEvent as MouseEvent;
const menu = new Menu();
menu.setUseNativeMenu(true);
menu.addItem((item) => {
item.setTitle("All");
item.setChecked(value === "all");
item.onClick(() => handleValueChange("all"));
});
menu.addSeparator();
menu.addItem((item) => {
item.setTitle("Modified today");
item.setChecked(value === "modified-today");
item.onClick(() => handleValueChange("modified-today"));
});
menu.addItem((item) => {
item.setTitle("Created today");
item.setChecked(value === "created-today");
item.onClick(() => handleValueChange("created-today"));
});
menu.addSeparator();
menu.addItem((item) => {
item.setTitle("Modifed this week");
item.setChecked(value === "modified-this-week");
item.onClick(() => handleValueChange("modified-this-week"));
});
menu.addItem((item) => {
item.setTitle("Created this week");
item.setChecked(value === "created-this-week");
item.onClick(() => handleValueChange("created-this-week"));
});
menu.addSeparator();
menu.addItem((item) => {
item.setTitle("Modifed 2 weeks");
item.setChecked(value === "modified-2-weeks");
item.onClick(() => handleValueChange("modified-2-weeks"));
});
menu.addItem((item) => {
item.setTitle("Created 2 weeks");
item.setChecked(value === "created-2-weeks");
item.onClick(() => handleValueChange("created-2-weeks"));
});
menu.showAtMouseEvent(nativeEvent);
}
</script>
<IconButton
ariaLabel="Change timestamp filter"
iconId="clock"
on:click={handleButtonClick}
/>

View file

@ -4,16 +4,13 @@
// ============================================
import Stack from "../shared/components/stack.svelte";
import Flex from "../shared/components/flex.svelte";
import FavoritesFilter from "./components/favorites-filter.svelte";
import TabList from "../shared/components/tab-list.svelte";
import Tab from "../shared/components/tab.svelte";
import { Notice, TFile } from "obsidian";
import {
TCustomFilter,
TFavoritesFilter,
TSearchFilter,
TSortFilter,
TTimestampFilter,
TExplorerView,
CoverImageFit,
} from "src/types";
@ -21,9 +18,7 @@
import VaultExplorerPlugin from "src/main";
import GridView from "./components/grid-view.svelte";
import ListView from "./components/list-view.svelte";
import { filterByFavorites } from "./services/filters/favorite-filter";
import { filterBySearch } from "./services/filters/search-filter";
import { filterByTimestamp } from "./services/filters/timestamp-filter";
import { filterByGroups } from "./services/filters/custom/filter-by-groups";
import { formatFileDataForRender } from "./services/render-data";
import _ from "lodash";
@ -38,7 +33,6 @@
import { FileRenderData } from "./types";
import Logger from "js-logger";
import SearchFilter from "./components/search-filter.svelte";
import TimestampFilter from "./components/timestamp-filter.svelte";
import SortFilter from "./components/sort-filter.svelte";
import { DEBOUNCE_INPUT_TIME, SCREEN_SIZE_MD } from "./constants";
import FeedView from "./components/feed-view.svelte";
@ -82,14 +76,6 @@
isEnabled: true,
value: "",
};
let favoritesFilter: TFavoritesFilter = {
isEnabled: false,
value: false,
};
let timestampFilter: TTimestampFilter = {
isEnabled: true,
value: "all",
};
let sortFilter: TSortFilter = {
isEnabled: true,
value: "file-name-asc",
@ -146,9 +132,7 @@
shouldCollapseFilters = settings.shouldCollapseFilters;
pageSize = settings.pageSize;
searchFilter = settings.filters.search;
favoritesFilter = settings.filters.favorites;
sortFilter = settings.filters.sort;
timestampFilter = settings.filters.timestamp;
currentView = settings.currentView;
customFilter = settings.filters.custom;
viewOrder = settings.viewOrder;
@ -171,9 +155,7 @@
});
searchFilter = plugin.settings.filters.search;
favoritesFilter = plugin.settings.filters.favorites;
sortFilter = plugin.settings.filters.sort;
timestampFilter = plugin.settings.filters.timestamp;
customFilter = plugin.settings.filters.custom;
}
@ -579,10 +561,6 @@
searchFilter.value = e.target.value;
}, DEBOUNCE_INPUT_TIME);
const debounceFavoriteFilterChange = _.debounce((value) => {
favoritesFilter.value = value;
}, DEBOUNCE_INPUT_TIME);
function handleReshuffleClick() {
randomFileSortStore.load(plugin.app);
}
@ -619,8 +597,6 @@
async function saveSettings() {
plugin.settings.filters.search = searchFilter;
plugin.settings.filters.sort = sortFilter;
plugin.settings.filters.timestamp = timestampFilter;
plugin.settings.filters.favorites = favoritesFilter;
plugin.settings.currentView = currentView;
plugin.settings.filters.custom = customFilter;
plugin.settings.viewOrder = viewOrder;
@ -672,20 +648,11 @@
customFilter.groups = newGroups;
}
function handleListViewTagsToggle() {
showListViewTags = !showListViewTags;
}
function handleViewDragOver(e: CustomEvent) {
const { nativeEvent } = e.detail;
nativeEvent.preventDefault();
}
function handleTimestampFilterChange(e: CustomEvent) {
const { value } = e.detail;
timestampFilter.value = value;
}
function handleViewDragStart(e: CustomEvent, id: string) {
const { nativeEvent } = e.detail;
nativeEvent.dataTransfer.setData("text", id);
@ -778,12 +745,6 @@
sortFilter.value = value;
}
function handleFavoritesChange(e: CustomEvent) {
const nativeEvent = e.detail.nativeEvent;
const value = (nativeEvent.target as HTMLInputElement).checked;
debounceFavoriteFilterChange(value);
}
function handleCoverImageFitChange(e: CustomEvent) {
const { filePath, value } = e.detail as {
filePath: string;
@ -895,7 +856,6 @@
) {
formatted = filteredCustom.map((loadedFile) => {
const { id, file } = loadedFile;
const isFavorite = favoritesCache.get(file.path) ?? null;
const content = contentCache.get(file.path) ?? null;
return formatFileDataForRender({
@ -904,7 +864,6 @@
fileId: id,
file,
fileContent: content,
fileFavorite: isFavorite,
});
});
}
@ -917,27 +876,7 @@
return true;
});
$: filteredFavorites = filteredSearch.filter((file) => {
const { isEnabled, value } = favoritesFilter;
if (isEnabled) {
return filterByFavorites(file, value);
}
return true;
});
$: filteredTimestamp = filteredFavorites.filter((file) => {
const { modifiedMillis, createdMillis } = file;
return filterByTimestamp({
value: timestampFilter.value,
createdMillis,
modifiedMillis,
startOfTodayMillis,
startOfThisWeekMillis,
startOfLastWeekMillis,
});
});
$: renderData = [...filteredTimestamp].sort((a, b) => {
$: renderData = [...filteredSearch].sort((a, b) => {
const { value } = sortFilter;
if (value === "file-name-asc") {
return a.displayName
@ -966,8 +905,6 @@
//and save the settings again
$: searchFilter,
sortFilter,
timestampFilter,
favoritesFilter,
currentView,
customFilter,
viewOrder,
@ -992,64 +929,25 @@
<div class="vault-explorer" bind:this={ref}>
{#if shouldCollapseFilters === false}
<div class="vault-explorer-filters">
<Stack spacing="md" direction="column">
{#if searchFilter.isEnabled}
<SearchFilter
value={searchFilter.value}
on:input={debounceSearchFilterChange}
on:clear={() => (searchFilter.value = "")}
/>
{/if}
<Stack direction="column" spacing="sm">
<Flex justify="space-between">
<Stack spacing="sm">
{#if favoritesFilter.isEnabled}
<FavoritesFilter
value={favoritesFilter.value}
on:change={handleFavoritesChange}
/>
{/if}
<Flex>
{#if timestampFilter.isEnabled}
<TimestampFilter
value={timestampFilter.value}
on:change={handleTimestampFilterChange}
/>
{/if}
{#if sortFilter.isEnabled}
<SortFilter
value={sortFilter.value}
on:change={handleSortChange}
/>
{/if}
{#if sortFilter.value == "random"}
<IconButton
iconId="shuffle"
ariaLabel="Reshuffle files"
on:click={handleReshuffleClick}
/>
{/if}
<IconButton
ariaLabel="Change custom filter"
iconId="list-filter"
on:click={handleCustomFilterClick}
/>
</Flex>
</Stack>
</Flex>
<Flex justify="space-between">
{#if customFilter.isEnabled}
<FilterGroupList
groups={customFilter.groups}
on:groupClick={handleGroupClick}
on:groupContextMenu={handleGroupContextMenu}
on:groupDrop={handleGroupDrop}
on:groupDragOver={handleGroupDragOver}
on:groupDragStart={handleGroupDragStart}
/>
{/if}
</Flex>
</Stack>
<Stack spacing="sm" direction="column">
<Flex justify="space-between">
{#if customFilter.isEnabled}
<FilterGroupList
groups={customFilter.groups}
on:groupClick={handleGroupClick}
on:groupContextMenu={handleGroupContextMenu}
on:groupDrop={handleGroupDrop}
on:groupDragOver={handleGroupDragOver}
on:groupDragStart={handleGroupDragStart}
/>
{/if}
</Flex>
<div>
<button
class="vault-explorer-button"
on:click={handleCustomFilterClick}>Configure</button
>
</div>
</Stack>
<Spacer size="md" />
</div>
@ -1081,24 +979,28 @@
/> -->
<!-- </Stack> -->
</div>
<Stack spacing="sm">
{#if currentView === "list"}
<IconButton
iconId="tags"
ariaLabel="Toggle tags"
on:click={handleListViewTagsToggle}
<Flex>
{#if searchFilter.isEnabled}
<SearchFilter
value={searchFilter.value}
on:input={debounceSearchFilterChange}
on:clear={() => (searchFilter.value = "")}
/>
<Divider direction="vertical" />
{/if}
<PaginationIndicator
{startIndex}
{endIndex}
{currentPage}
{totalPages}
{totalItems}
on:change={handlePageChange}
/>
</Stack>
{#if sortFilter.isEnabled}
<SortFilter
value={sortFilter.value}
on:change={handleSortChange}
/>
{/if}
{#if sortFilter.value == "random"}
<IconButton
iconId="shuffle"
ariaLabel="Reshuffle files"
on:click={handleReshuffleClick}
/>
{/if}
</Flex>
</Wrap>
<Spacer size="md" />
{#if currentView === "grid"}
@ -1133,6 +1035,14 @@
on:favoritePropertyChange={handleFavoritePropertyChange}
/>
{/if}
<PaginationIndicator
{startIndex}
{endIndex}
{currentPage}
{totalPages}
{totalItems}
on:change={handlePageChange}
/>
</div>
<style>
@ -1144,4 +1054,14 @@
.vault-explorer-view-select {
flex: 1;
}
.vault-explorer-button {
background: none;
text-decoration: underline;
color: var(--text-faint);
font-size: var(--font-small);
border: none;
box-shadow: none;
padding: 0;
}
</style>

View file

@ -7,14 +7,10 @@ export const openContextMenu = (
filePath: string,
e: MouseEvent,
{
isFavorite,
coverImageFit,
onFavoriteChange,
onCoverImageFitChange,
}: {
isFavorite: boolean | null;
coverImageFit?: CoverImageFit;
onFavoriteChange: (filePath: string, value: boolean) => void;
onCoverImageFitChange?: (
filePath: string,
value: CoverImageFit
@ -35,25 +31,15 @@ export const openContextMenu = (
item.setTitle("Open in new window");
item.onClick(() => openInNewWindow(plugin, filePath));
});
menu.addSeparator();
menu.addItem((item) => {
item.setTitle(
isFavorite ? "Remove from favorites" : "Add to favorites"
);
item.onClick(() => {
const newValue = isFavorite !== null ? !isFavorite : true;
onFavoriteChange(filePath, newValue);
});
});
if (coverImageFit !== undefined && onCoverImageFitChange !== undefined) {
menu.addSeparator();
menu.addItem((item) => {
item.setTitle("Cover image fit: cover");
item.setTitle("Cover");
item.setChecked(coverImageFit === "cover");
item.onClick(() => onCoverImageFitChange(filePath, "cover"));
});
menu.addItem((item) => {
item.setTitle("Cover image fit: contain");
item.setTitle("Contain");
item.setChecked(coverImageFit === "contain");
item.onClick(() => onCoverImageFitChange(filePath, "contain"));
});

View file

@ -1,11 +0,0 @@
import { FileRenderData } from "../../types";
export const filterByFavorites = (
file: FileRenderData,
onlyFavorites: boolean
) => {
if (onlyFavorites) {
return file.isFavorite;
}
return true;
};

View file

@ -1,34 +0,0 @@
import { TimestampFilterOption } from "src/types";
interface FilterByTimestampParams {
value: TimestampFilterOption;
createdMillis: number;
modifiedMillis: number;
startOfTodayMillis: number;
startOfThisWeekMillis: number;
startOfLastWeekMillis: number;
}
export const filterByTimestamp = ({
value,
createdMillis,
modifiedMillis,
startOfTodayMillis,
startOfThisWeekMillis,
startOfLastWeekMillis,
}: FilterByTimestampParams) => {
if (value === "modified-this-week") {
return modifiedMillis > startOfThisWeekMillis;
} else if (value === "created-this-week") {
return createdMillis > startOfThisWeekMillis;
} else if (value === "modified-2-weeks") {
return modifiedMillis > startOfLastWeekMillis;
} else if (value === "created-2-weeks") {
return createdMillis > startOfLastWeekMillis;
} else if (value === "modified-today") {
return modifiedMillis > startOfTodayMillis;
} else if (value === "created-today") {
return createdMillis > startOfTodayMillis;
}
return true;
}

View file

@ -46,14 +46,12 @@ export const formatFileDataForRender = ({
file,
fileId,
fileContent,
fileFavorite,
}: {
app: App;
settings: VaultExplorerPluginSettings;
file: TFile;
fileId: string;
fileContent: string | null;
fileFavorite: boolean | null;
}): FileRenderData => {
const { name, basename, extension, path } = file;
@ -96,17 +94,6 @@ export const formatFileDataForRender = ({
PropertyType.TEXT
);
let isFavorite: boolean | null;
if (fileFavorite === null) {
isFavorite = loadPropertyValue<boolean>(
fileFrontmatter,
favoriteProp,
PropertyType.CHECKBOX
);
} else {
isFavorite = fileFavorite;
}
let coverImageFit: CoverImageFit | null = loadPropertyValue<string>(
fileFrontmatter,
coverImageFitProp,
@ -250,7 +237,6 @@ export const formatFileDataForRender = ({
content: fileContent,
tags,
imageUrl,
isFavorite,
createdMillis,
modifiedMillis,
custom1,

View file

@ -12,7 +12,6 @@ export interface FileRenderData {
url: string | null;
imageUrl: string | null;
tags: string[] | null;
isFavorite: boolean | null;
createdMillis: number;
modifiedMillis: number;
custom1: string | null;

View file

@ -26,6 +26,7 @@
}
$: className = getClassName(noPadding);
$: hasSlotContent = !!$$slots.default;
</script>
<button
@ -34,9 +35,14 @@
{disabled}
aria-label={ariaLabel}
on:click={handleClick}
><Icon {iconId} {size} />
<div class="vault-explorer-icon-button__text"><slot /></div></button
>
<Icon {iconId} {size} />
{#if hasSlotContent}
<div class="vault-explorer-icon-button__text">
<slot />
</div>
{/if}
</button>
<style>
.vault-explorer-icon-button--no-padding {

View file

@ -30,11 +30,6 @@ export function isVaultExplorerPluginSettings(obj: unknown): obj is VaultExplore
typeof typedObj["filters"]["search"] === "function") &&
typeof typedObj["filters"]["search"]["isEnabled"] === "boolean" &&
typeof typedObj["filters"]["search"]["value"] === "string" &&
(typedObj["filters"]["favorites"] !== null &&
typeof typedObj["filters"]["favorites"] === "object" ||
typeof typedObj["filters"]["favorites"] === "function") &&
typeof typedObj["filters"]["favorites"]["isEnabled"] === "boolean" &&
typeof typedObj["filters"]["favorites"]["value"] === "boolean" &&
(typedObj["filters"]["sort"] !== null &&
typeof typedObj["filters"]["sort"] === "object" ||
typeof typedObj["filters"]["sort"] === "function") &&
@ -44,17 +39,6 @@ export function isVaultExplorerPluginSettings(obj: unknown): obj is VaultExplore
typedObj["filters"]["sort"]["value"] === "modified-asc" ||
typedObj["filters"]["sort"]["value"] === "modified-desc" ||
typedObj["filters"]["sort"]["value"] === "random") &&
(typedObj["filters"]["timestamp"] !== null &&
typeof typedObj["filters"]["timestamp"] === "object" ||
typeof typedObj["filters"]["timestamp"] === "function") &&
typeof typedObj["filters"]["timestamp"]["isEnabled"] === "boolean" &&
(typedObj["filters"]["timestamp"]["value"] === "created-today" ||
typedObj["filters"]["timestamp"]["value"] === "modified-today" ||
typedObj["filters"]["timestamp"]["value"] === "created-this-week" ||
typedObj["filters"]["timestamp"]["value"] === "modified-this-week" ||
typedObj["filters"]["timestamp"]["value"] === "created-2-weeks" ||
typedObj["filters"]["timestamp"]["value"] === "modified-2-weeks" ||
typedObj["filters"]["timestamp"]["value"] === "all") &&
(typedObj["filters"]["custom"] !== null &&
typeof typedObj["filters"]["custom"] === "object" ||
typeof typedObj["filters"]["custom"] === "function") &&
@ -608,9 +592,7 @@ export function isVaultExplorerPluginSettings(obj: unknown): obj is VaultExplore
typeof e["isEnabled"] === "boolean"
) &&
(typedObj["views"]["grid"]["coverImageFit"] === "cover" ||
typedObj["views"]["grid"]["coverImageFit"] === "contain" ||
typedObj["views"]["grid"]["coverImageFit"] === "scale-down" ||
typedObj["views"]["grid"]["coverImageFit"] === "none") &&
typedObj["views"]["grid"]["coverImageFit"] === "contain") &&
typeof typedObj["views"]["grid"]["loadSocialMediaImage"] === "boolean" &&
(typedObj["views"]["list"] !== null &&
typeof typedObj["views"]["list"] === "object" ||
@ -653,8 +635,6 @@ export function isVaultExplorerPluginSettings(obj: unknown): obj is VaultExplore
typedObj["currentView"] === TExplorerView.RECOMMENDED ||
typedObj["currentView"] === TExplorerView.RELATED) &&
typeof typedObj["pageSize"] === "number" &&
typeof typedObj["filterGroupsWidth"] === "string" &&
typeof typedObj["shouldWrapFilterGroups"] === "boolean" &&
typeof typedObj["shouldCollapseFilters"] === "boolean" &&
Array.isArray(typedObj["viewOrder"]) &&
typedObj["viewOrder"].every((e: any) =>

View file

@ -13,9 +13,7 @@ export interface VaultExplorerPluginSettings {
};
filters: {
search: TSearchFilter;
favorites: TFavoritesFilter;
sort: TSortFilter;
timestamp: TTimestampFilter;
custom: TCustomFilter;
};
views: {
@ -33,8 +31,6 @@ export interface VaultExplorerPluginSettings {
loadBodyTags: boolean;
currentView: TExplorerView | null;
pageSize: number;
filterGroupsWidth: string;
shouldWrapFilterGroups: boolean;
shouldCollapseFilters: boolean;
viewOrder: TExplorerView[];
configDir: string;
@ -97,32 +93,15 @@ export interface TSearchFilter extends BaseFilter {
value: string;
}
export interface TFavoritesFilter extends BaseFilter {
value: boolean;
}
export interface TSortFilter extends BaseFilter {
value: SortFilterOption;
}
export interface TTimestampFilter extends BaseFilter {
value: TimestampFilterOption;
}
export interface TCustomFilter extends BaseFilter {
selectedGroupId: string;
groups: TFilterGroup[];
}
export type TimestampFilterOption =
| "created-today"
| "modified-today"
| "created-this-week"
| "modified-this-week"
| "created-2-weeks"
| "modified-2-weeks"
| "all";
export type SortFilterOption =
| "file-name-asc"
| "file-name-desc"

332
src/types/types-1.41.1.ts Normal file
View file

@ -0,0 +1,332 @@
export interface VaultExplorerPluginSettings_1_41_1 {
properties: {
favorite: string;
url: string;
image: string;
coverImageFit: string;
createdDate: string;
modifiedDate: string;
custom1: string;
custom2: string;
custom3: string;
};
filters: {
search: TSearchFilter;
favorites: TFavoritesFilter;
sort: TSortFilter;
timestamp: TTimestampFilter;
custom: TCustomFilter;
};
views: {
dashboard: TDashboardView;
grid: TGridView;
list: TListView;
table: TTableView;
feed: TFeedView;
recommended: TRecommendedView;
related: TRelatedView;
};
titleWrapping: WordBreak;
enableClockUpdates: boolean;
enableFileIcons: boolean;
loadBodyTags: boolean;
currentView: TExplorerView | null;
pageSize: number;
filterGroupsWidth: string;
shouldWrapFilterGroups: boolean;
shouldCollapseFilters: boolean;
viewOrder: TExplorerView[];
configDir: string;
pluginVersion: string | null;
logLevel: string;
}
type FileInteractionStyle = "title" | "content";
interface BaseView {
isEnabled: boolean;
}
interface TTableView extends BaseView {}
interface TListView extends BaseView {
showTags: boolean;
}
interface TGridView extends BaseView {
coverImageSources: CoverImageSource[];
coverImageFit: CoverImageFit;
loadSocialMediaImage: boolean;
}
interface CoverImageSource {
type: CoverImageSourceType;
isEnabled: boolean;
}
type CoverImageFit = "cover" | "contain";
type CoverImageSourceType =
| "image-property"
| "url-property"
| "frontmatter"
| "body";
interface TDashboardView extends BaseView {}
type CollapseStyle = "no-new-lines" | "no-extra-new-lines";
interface TFeedView extends BaseView {
collapseStyle: CollapseStyle;
removeH1: boolean;
lineClampSmall: number;
lineClampMedium: number;
lineClampLarge: number;
}
interface TRecommendedView extends BaseView {}
interface TRelatedView extends BaseView {}
interface BaseFilter {
isEnabled: boolean;
}
interface TSearchFilter extends BaseFilter {
value: string;
}
interface TFavoritesFilter extends BaseFilter {
value: boolean;
}
interface TSortFilter extends BaseFilter {
value: SortFilterOption;
}
interface TTimestampFilter extends BaseFilter {
value: TimestampFilterOption;
}
interface TCustomFilter extends BaseFilter {
selectedGroupId: string;
groups: TFilterGroup[];
}
type TimestampFilterOption =
| "created-today"
| "modified-today"
| "created-this-week"
| "modified-this-week"
| "created-2-weeks"
| "modified-2-weeks"
| "all";
type SortFilterOption =
| "file-name-asc"
| "file-name-desc"
| "modified-asc"
| "modified-desc"
| "random";
type WordBreak = "normal" | "break-word";
enum TExplorerView {
DASHBOARD = "dashboard",
GRID = "grid",
LIST = "list",
FEED = "feed",
TABLE = "table",
RECOMMENDED = "recommended",
RELATED = "related",
}
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",
}
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",
}
enum ContentFilterCondition {
CONTAINS = "contains",
DOES_NOT_CONTAIN = "does-not-contain",
IS_EMPTY = "is-empty",
IS_NOT_EMPTY = "is-not-empty",
}
enum FolderFilterCondition {
IS = "is",
IS_NOT = "is-not",
}
enum FileNameFilterCondition {
IS = "is",
IS_NOT = "is-not",
CONTAINS = "contains",
DOES_NOT_CONTAIN = "does-not-contain",
STARTS_WITH = "starts-with",
ENDS_WITH = "ends-with",
}
type FilterCondition =
| TextFilterCondition
| NumberFilterCondition
| DateFilterCondition
| CheckboxFilterCondition
| ListFilterCondition
| ContentFilterCondition
| FolderFilterCondition
| FileNameFilterCondition;
//This matches the Obsidian property types
enum PropertyType {
TEXT = "text",
NUMBER = "number",
LIST = "list",
CHECKBOX = "checkbox",
DATE = "date",
DATETIME = "datetime",
}
enum FilterRuleType {
PROPERTY = "property",
FOLDER = "folder",
FILE_NAME = "file-name",
CONTENT = "content",
}
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;
condition: FilterCondition;
isEnabled: boolean;
value: string;
matchWhenPropertyDNE: boolean;
}
interface TextPropertyFilterRule extends BaseFilterRule {
type: FilterRuleType.PROPERTY;
propertyType: PropertyType.TEXT;
propertyName: string;
condition: TextFilterCondition;
}
interface NumberPropertyFilterRule extends BaseFilterRule {
type: FilterRuleType.PROPERTY;
propertyType: PropertyType.NUMBER;
propertyName: string;
condition: NumberFilterCondition;
}
interface ListPropertyFilterRule extends BaseFilterRule {
type: FilterRuleType.PROPERTY;
propertyType: PropertyType.LIST;
propertyName: string;
condition: ListFilterCondition;
}
interface CheckboxPropertyFilterRule extends BaseFilterRule {
type: FilterRuleType.PROPERTY;
propertyType: PropertyType.CHECKBOX;
propertyName: string;
condition: CheckboxFilterCondition;
}
interface DatePropertyFilterRule extends BaseFilterRule {
type: FilterRuleType.PROPERTY;
propertyType: PropertyType.DATE | PropertyType.DATETIME;
propertyName: string;
condition: DateFilterCondition;
valueData: string;
}
interface FolderFilterRule extends BaseFilterRule {
type: FilterRuleType.FOLDER;
condition: FolderFilterCondition;
includeSubfolders: boolean;
}
interface FileNameFilterRule extends BaseFilterRule {
type: FilterRuleType.FILE_NAME;
condition: FileNameFilterCondition;
}
interface ContentFilterRule extends BaseFilterRule {
type: FilterRuleType.CONTENT;
condition: ContentFilterCondition;
}
type TFilterRule =
| PropertyFilterRule
| FolderFilterRule
| FileNameFilterRule
| ContentFilterRule;
type PropertyFilterRule =
| TextPropertyFilterRule
| NumberPropertyFilterRule
| ListPropertyFilterRule
| CheckboxPropertyFilterRule
| DatePropertyFilterRule;
interface TFilterGroup {
id: string;
name: string;
rules: TFilterRule[];
isEnabled: boolean;
isSticky: boolean;
}

View file

@ -105,6 +105,6 @@
"feed"
],
"configDir": ".vaultexplorer",
"pluginVersion": "1.41.0",
"pluginVersion": "1.41.1",
"logLevel": "trace"
}

View file

@ -136,5 +136,6 @@
"1.40.1": "1.4.13",
"1.40.2": "1.4.13",
"1.41.0": "1.4.13",
"1.41.1": "1.4.13"
"1.41.1": "1.4.13",
"1.42.0": "1.4.13"
}