diff --git a/interface/app/$libraryId/Explorer/OptionsPanel/ListView/IconSize.tsx b/interface/app/$libraryId/Explorer/OptionsPanel/ListView/IconSize.tsx
index 829e1ebe1..551ee2ede 100644
--- a/interface/app/$libraryId/Explorer/OptionsPanel/ListView/IconSize.tsx
+++ b/interface/app/$libraryId/Explorer/OptionsPanel/ListView/IconSize.tsx
@@ -15,8 +15,8 @@ export const IconSize = () => {
const explorer = useExplorerContext();
const settings = explorer.useSettingsSnapshot();
- const defaultValue = useMemo(
- () => sizes.findIndex((size) => size[0] === settings.listViewIconSize),
+ const value = useMemo(
+ () => sizes.indexMap.get(settings.listViewIconSize),
[settings.listViewIconSize]
);
@@ -25,11 +25,11 @@ export const IconSize = () => {
{t('icon_size')}
{
- const size = value !== undefined && sizes[value];
- if (size) explorer.settingsStore.listViewIconSize = size[0];
+ const size = value !== undefined && sizes.sizeMap.get(value);
+ if (size) explorer.settingsStore.listViewIconSize = size;
}}
/>
diff --git a/interface/app/$libraryId/Explorer/OptionsPanel/ListView/TextSize.tsx b/interface/app/$libraryId/Explorer/OptionsPanel/ListView/TextSize.tsx
index 4268bf6fa..d7cb992cd 100644
--- a/interface/app/$libraryId/Explorer/OptionsPanel/ListView/TextSize.tsx
+++ b/interface/app/$libraryId/Explorer/OptionsPanel/ListView/TextSize.tsx
@@ -16,7 +16,7 @@ export const TextSize = () => {
const settings = explorer.useSettingsSnapshot();
const defaultValue = useMemo(
- () => sizes.findIndex((size) => size[0] === settings.listViewTextSize),
+ () => sizes.indexMap.get(settings.listViewTextSize),
[settings.listViewTextSize]
);
@@ -25,11 +25,11 @@ export const TextSize = () => {
{t('text_size')}
{
- const size = value !== undefined && sizes[value];
- if (size) explorer.settingsStore.listViewTextSize = size[0];
+ const size = value !== undefined && sizes.sizeMap.get(value);
+ if (size) explorer.settingsStore.listViewTextSize = size;
}}
/>
diff --git a/interface/app/$libraryId/Explorer/OptionsPanel/ListView/util.ts b/interface/app/$libraryId/Explorer/OptionsPanel/ListView/util.ts
index 2dd8dd5b2..8ee118a32 100644
--- a/interface/app/$libraryId/Explorer/OptionsPanel/ListView/util.ts
+++ b/interface/app/$libraryId/Explorer/OptionsPanel/ListView/util.ts
@@ -1,3 +1,18 @@
export function getSizes(sizes: T) {
- return (Object.entries(sizes) as [keyof T, T[keyof T]][]).sort((a, b) => a[1] - b[1]);
+ const sizesArr = (Object.entries(sizes) as [keyof T, T[keyof T]][]).sort((a, b) => a[1] - b[1]);
+
+ // Map fo size to index
+ const indexMap = new Map();
+
+ // Map of index to size
+ const sizeMap = new Map();
+
+ for (let i = 0; i < sizesArr.length; i++) {
+ const size = sizesArr[i];
+ if (!size) continue;
+ indexMap.set(size[0], i);
+ sizeMap.set(i, size[0]);
+ }
+
+ return { indexMap, sizeMap };
}
diff --git a/interface/app/$libraryId/Explorer/OptionsPanel/index.tsx b/interface/app/$libraryId/Explorer/OptionsPanel/index.tsx
index c697384c4..a764c016f 100644
--- a/interface/app/$libraryId/Explorer/OptionsPanel/index.tsx
+++ b/interface/app/$libraryId/Explorer/OptionsPanel/index.tsx
@@ -108,7 +108,7 @@ export default () => {
onValueChange={(value) => {
explorer.settingsStore.gridItemSize = value[0] || 100;
}}
- defaultValue={[settings.gridItemSize]}
+ value={[settings.gridItemSize]}
max={200}
step={10}
min={60}
diff --git a/interface/app/$libraryId/Explorer/View/index.tsx b/interface/app/$libraryId/Explorer/View/index.tsx
index 1a423093d..fa324d627 100644
--- a/interface/app/$libraryId/Explorer/View/index.tsx
+++ b/interface/app/$libraryId/Explorer/View/index.tsx
@@ -10,7 +10,7 @@ import {
} from '@sd/client';
import { dialogManager } from '@sd/ui';
import { Loader } from '~/components';
-import { useKeyMatcher, useShortcut } from '~/hooks';
+import { useKeyMatcher, useMouseItemResize, useShortcut } from '~/hooks';
import { useRoutingContext } from '~/RoutingContext';
import { isNonEmpty } from '~/util';
@@ -139,6 +139,9 @@ export const View = ({ emptyNotice, ...contextProps }: ExplorerViewProps) => {
return () => element.removeEventListener('wheel', handleWheel);
}, [explorer.scrollRef, drag?.type]);
+ // Handle resizing of items in the Explorer grid and list view using the mouse wheel
+ useMouseItemResize();
+
if (!explorer.layouts[layoutMode]) return null;
return (
diff --git a/interface/hooks/index.ts b/interface/hooks/index.ts
index 91834b80b..6f2fbdf72 100644
--- a/interface/hooks/index.ts
+++ b/interface/hooks/index.ts
@@ -18,6 +18,9 @@ export * from './useIsLocationIndexing';
export * from './useIsTextTruncated';
export * from './useKeyMatcher';
export * from './useLocale';
+export * from './useMouseItemResize';
+export * from './usePrefersReducedMotion';
+export * from './useRandomInterval';
export * from './useRedirectToNewLocation';
export * from './useRouteTitle';
export * from './useShortcut';
@@ -25,8 +28,6 @@ export * from './useShowControls';
export * from './useTheme';
export * from './useWindowSize';
export * from './useWindowState';
+export * from './useZodParams';
export * from './useZodRouteParams';
export * from './useZodSearchParams';
-export * from './usePrefersReducedMotion';
-export * from './useRandomInterval';
-export * from './useZodParams';
diff --git a/interface/hooks/useMouseItemResize.ts b/interface/hooks/useMouseItemResize.ts
new file mode 100644
index 000000000..1fce52e14
--- /dev/null
+++ b/interface/hooks/useMouseItemResize.ts
@@ -0,0 +1,65 @@
+import { useCallback, useEffect } from 'react';
+import { useExplorerContext } from '~/app/$libraryId/Explorer/Context';
+import { LIST_VIEW_ICON_SIZES } from '~/app/$libraryId/Explorer/View/ListView/useTable';
+
+import { useOperatingSystem } from './useOperatingSystem';
+import { getSizes } from '~/app/$libraryId/Explorer/OptionsPanel/ListView/util';
+
+/**
+ * Hook that allows resizing of items in the Explorer views for GRID and LIST only - using the mouse wheel.
+ */
+
+export const useMouseItemResize = () => {
+ const os = useOperatingSystem();
+ const explorer = useExplorerContext();
+ const { layoutMode } = explorer.useSettingsSnapshot();
+
+ const handleWheel = useCallback(
+ (e: WheelEvent) => {
+ const isList = layoutMode === 'list';
+ const deltaYModifier = isList ? Math.sign(e.deltaY) : e.deltaY / 10; // Sensitivity adjustment
+ const newSize =
+ Number(
+ isList
+ ? explorer.settingsStore.listViewIconSize
+ : explorer.settingsStore.gridItemSize
+ ) + deltaYModifier;
+
+ const minSize = isList ? 0 : 60;
+ const maxSize = isList ? 2 : 200;
+ const clampedSize = Math.max(minSize, Math.min(maxSize, newSize));
+
+ if (isList) {
+ const listSizes = getSizes(LIST_VIEW_ICON_SIZES);
+ explorer.settingsStore.listViewIconSize = listSizes.sizeMap.get(clampedSize) ?? "0"
+ } else if (layoutMode === 'grid') {
+ explorer.settingsStore.gridItemSize = Number(clampedSize.toFixed(0));
+ }
+ },
+ [explorer.settingsStore, layoutMode]
+ );
+
+ useEffect(() => {
+ if (os !== 'windows') return;
+
+ const handleKeyDown = (e: KeyboardEvent) => {
+ if (e.key === 'Control') {
+ document.addEventListener('wheel', handleWheel);
+ }
+ };
+
+ const handleKeyUp = (e: KeyboardEvent) => {
+ if (e.key === 'Control') {
+ document.removeEventListener('wheel', handleWheel);
+ }
+ };
+
+ document.addEventListener('keydown', handleKeyDown);
+ document.addEventListener('keyup', handleKeyUp);
+
+ return () => {
+ document.removeEventListener('keydown', handleKeyDown);
+ document.removeEventListener('keyup', handleKeyUp);
+ };
+ }, [os, handleWheel]);
+};