From d768104aad2beae97aa8bc1bc15515d34e82d84e Mon Sep 17 00:00:00 2001 From: aria Date: Sun, 15 Dec 2024 11:42:34 -0600 Subject: [PATCH] feat: add thumbnail zoom setting --- src/ImagePickerSettings.tsx | 1 + .../ImagePickerView/ImagePickerView.tsx | 73 +++++++++++++------ src/client/ImagePickerView/Pagination.tsx | 16 +++- src/constants.ts | 10 +++ 4 files changed, 73 insertions(+), 27 deletions(-) diff --git a/src/ImagePickerSettings.tsx b/src/ImagePickerSettings.tsx index 262031b..c6a6482 100644 --- a/src/ImagePickerSettings.tsx +++ b/src/ImagePickerSettings.tsx @@ -6,6 +6,7 @@ export interface ImagePickerSettings { imageFolder: string animateGifs: boolean debugMode: boolean + zoom: number } export class ImagePickerSettingTab extends PluginSettingTab { diff --git a/src/client/ImagePickerView/ImagePickerView.tsx b/src/client/ImagePickerView/ImagePickerView.tsx index 580776e..6bbcfaa 100644 --- a/src/client/ImagePickerView/ImagePickerView.tsx +++ b/src/client/ImagePickerView/ImagePickerView.tsx @@ -1,4 +1,4 @@ -import { debounce, isEqual, truncate } from 'lodash' +import { debounce, isEqual, throttle, truncate } from 'lodash' import { useEffect, useState, useRef, useCallback, useMemo } from 'react' import { Notice, TFile } from 'obsidian' @@ -7,8 +7,15 @@ import { MOBILE_MAX_FILE_SIZE, DESKTOP_MAX_FILE_SIZE, ROW_HEIGHT, + DEFAULT_SETTINGS, } from '../../constants' -import { copyToClipboard, getSizeInKb, nodeToEmbed } from '../../utils' +import { + calculateGrid, + copyToClipboard, + getSizeInKb, + nodeToEmbed, + setGridHeight, +} from '../../utils' import { AbstractIndexerNode, IndexerNode } from '../../backend/Indexer' import { useApp, useFiles, usePlugin } from '../ImagePickerContext' @@ -49,7 +56,6 @@ export const ImagePickerView = () => { const app = useApp() const images = useFiles() const cachedImages = useRef([]) - const [searchInput, setSearchInput] = useState('') const [searchQuery, setSearchQuery] = useState< ReturnType >({ @@ -66,6 +72,42 @@ export const ImagePickerView = () => { const [loadedImages, setLoadedImages] = useState>({}) const [nextImageIndex, setNextImageIndex] = useState(0) + const [rowHeight, setRowHeight] = useState( + (plugin.settings.zoom || DEFAULT_SETTINGS.zoom) * ROW_HEIGHT + ) + + const [zoom, setZoom] = useState(1) + + const updateZoomSetting = useMemo( + () => + debounce((zoom: number) => { + plugin.settings.zoom = zoom + plugin.backgrounder.enqueue({ + type: 'saveSettings', + disableDoubleQueue: true, + action: plugin.saveSettings, + }) + }, 500), + [plugin.backgrounder, plugin.saveSettings, plugin.settings] + ) + + const updateVisualZoom = useCallback( + throttle((zoom: number) => { + setGridHeight(zoom) + setRowHeight(zoom * ROW_HEIGHT) + }, 50), + [] + ) + + const onZoom = useCallback( + (zoom: number) => { + setZoom(zoom) + updateZoomSetting(zoom) + updateVisualZoom(zoom) + }, + [updateVisualZoom, updateZoomSetting] + ) + useEffect(() => { if (columns !== prevColumns.current) { setLoadedImages({}) @@ -136,37 +178,18 @@ export const ImagePickerView = () => { .sort((a, b) => b.stat.ctime - a.stat.ctime) }, [images, app.vault, searchQuery]) - const calculateGrid = useCallback( - (containerSize: number, assetSize: number) => { - if (gridRef.current) { - const computedStyle = window.getComputedStyle(gridRef.current) - const gap = parseInt(computedStyle.getPropertyValue('gap'), 10) || 0 - const totalGapsWidth = - containerSize < assetSize * 2 + gap - ? 0 - : gap * (Math.floor(containerSize / assetSize) - 1) - const newColumns = Math.floor( - (containerSize - totalGapsWidth) / assetSize - ) - return newColumns - } - return 0 - }, - [] - ) - const updateCalculations = useCallback( (container: HTMLDivElement) => { const height = container.clientHeight const width = container.clientWidth const newRows = Math.floor(height / ROW_HEIGHT) - const newColumns = calculateGrid(width, 100) + const newColumns = calculateGrid(gridRef, width, rowHeight) setColumns(newColumns) setItemsPerPage(newRows * newColumns) }, - [calculateGrid] + [rowHeight] ) useEffect(() => { @@ -354,6 +377,8 @@ export const ImagePickerView = () => { current={currentPage} onNext={handleNextPage} onPrev={handlePrevPage} + zoom={zoom} + onZoom={onZoom} /> ) diff --git a/src/client/ImagePickerView/Pagination.tsx b/src/client/ImagePickerView/Pagination.tsx index 279447b..21976de 100644 --- a/src/client/ImagePickerView/Pagination.tsx +++ b/src/client/ImagePickerView/Pagination.tsx @@ -1,10 +1,13 @@ import React, { FC } from 'react' +import { MAX_THUMBNAIL_ZOOM, MIN_THUMBNAIL_ZOOM } from 'src/constants' interface PaginationProps { total: number current: number + zoom: number onNext: () => void onPrev: () => void + onZoom: (zoom: number) => void } export const Pagination: FC = ({ @@ -12,15 +15,22 @@ export const Pagination: FC = ({ current, onNext, onPrev, + zoom, + onZoom, }) => { return (
- - Page {current} of {total || 1} - + onZoom(parseFloat(e.target.value))} + /> diff --git a/src/constants.ts b/src/constants.ts index 30eaa1b..1ca306a 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -46,4 +46,14 @@ export const DEFAULT_SETTINGS: ImagePickerSettings = { imageFolder: '', animateGifs: false, debugMode: false, + zoom: 1, } + +/** + * The min/max thumbnail zoom for the image picker + * + * The zoom is applied to the baseline ROW_HEIGHT + * to determine the thumbnail size. + */ +export const MIN_THUMBNAIL_ZOOM = 0.8 +export const MAX_THUMBNAIL_ZOOM = 2