mirror of
https://github.com/spacedriveapp/spacedrive.git
synced 2026-04-25 08:57:33 -04:00
[ENG-1430] Improve keymatcher hook (#1835)
* improve keymatcher hook * fix delete context menu icon
This commit is contained in:
@@ -1,15 +1,8 @@
|
||||
import { Image, Package, Trash, TrashSimple } from '@phosphor-icons/react';
|
||||
import { libraryClient, useLibraryMutation } from '@sd/client';
|
||||
import {
|
||||
ContextMenu,
|
||||
dialogManager,
|
||||
keySymbols,
|
||||
ModifierKeys,
|
||||
modifierSymbols,
|
||||
toast
|
||||
} from '@sd/ui';
|
||||
import { ContextMenu, dialogManager, ModifierKeys, toast } from '@sd/ui';
|
||||
import { Menu } from '~/components/Menu';
|
||||
import { useOperatingSystem } from '~/hooks';
|
||||
import { useKeysMatcher, useOperatingSystem } from '~/hooks';
|
||||
import { useKeybindFactory } from '~/hooks/useKeybindFactory';
|
||||
import { useQuickRescan } from '~/hooks/useQuickRescan';
|
||||
import { isNonEmpty } from '~/util';
|
||||
@@ -33,7 +26,7 @@ export const Delete = new ConditionalItem({
|
||||
},
|
||||
Component: ({ selectedFilePaths, selectedEphemeralPaths }) => {
|
||||
const rescan = useQuickRescan();
|
||||
|
||||
const os = useOperatingSystem();
|
||||
const dirCount =
|
||||
selectedFilePaths.filter((p) => p.is_dir).length +
|
||||
selectedEphemeralPaths.filter((p) => p.is_dir).length;
|
||||
@@ -55,12 +48,18 @@ export const Delete = new ConditionalItem({
|
||||
paths: selectedEphemeralPaths.map((p) => p.path)
|
||||
}
|
||||
: undefined;
|
||||
const deleteKeybind = useKeysMatcher(['Meta', 'Backspace']);
|
||||
|
||||
return (
|
||||
<Menu.Item
|
||||
icon={Trash}
|
||||
label="Delete"
|
||||
variant="danger"
|
||||
keybind={
|
||||
os === 'windows'
|
||||
? 'Del'
|
||||
: `${deleteKeybind.Meta.icon}${deleteKeybind.Backspace.icon}`
|
||||
}
|
||||
onClick={() =>
|
||||
dialogManager.create((dp) => (
|
||||
<DeleteDialog
|
||||
|
||||
@@ -1,59 +1,84 @@
|
||||
import { ModifierKeys, modifierSymbols } from '@sd/ui';
|
||||
|
||||
import { OperatingSystem } from '..';
|
||||
import { EditingKeys, ModifierKeys, modifierSymbols, UIKeys, NavigationKeys } from '@sd/ui';
|
||||
import { useOperatingSystem } from './useOperatingSystem';
|
||||
import { OperatingSystem } from '..';
|
||||
|
||||
type keysToMatch = 'Meta' | 'Alt' | 'Shift';
|
||||
type keysOsMap = Record<keysToMatch, osKeys>;
|
||||
type osKeys = Record<OperatingSystem, { key: Partial<keyof typeof ModifierKeys>; icon: string }>;
|
||||
|
||||
type keyTypes =
|
||||
keyof typeof ModifierKeys | keyof typeof EditingKeys | keyof typeof UIKeys | keyof typeof NavigationKeys;
|
||||
|
||||
//This is a helper function to handle the possibility of a modifier key being undefined due to OS initial check
|
||||
const modifierKey = (key: keyof typeof ModifierKeys, os: 'Windows' | 'macOS' | 'Other') => {
|
||||
const modifierKey = (key: keyTypes, os: 'Windows' | 'macOS' | 'Other') => {
|
||||
return modifierSymbols[key][os] ?? modifierSymbols[key]['Other'];
|
||||
};
|
||||
|
||||
|
||||
//Match macOS keys to Windows keys and others
|
||||
const keysOsMap = {
|
||||
const keysOsMap: {
|
||||
[T in keyTypes]?: {
|
||||
[T in os]?: { key: string; icon: string };
|
||||
};
|
||||
} = {
|
||||
Meta: {
|
||||
macOS: { key: 'Meta', icon: modifierKey(ModifierKeys.Meta, 'macOS') },
|
||||
windows: { key: 'Control', icon: modifierKey(ModifierKeys.Control, 'Windows') },
|
||||
browser: { key: 'Control', icon: modifierKey(ModifierKeys.Control, 'Windows') },
|
||||
linux: { key: 'Control', icon: modifierKey(ModifierKeys.Control, 'Windows') },
|
||||
unknown: { key: 'Control', icon: modifierKey(ModifierKeys.Control, 'Windows') }
|
||||
},
|
||||
all: { key: 'Control', icon: modifierKey(ModifierKeys.Control, 'Windows') }, },
|
||||
Shift: {
|
||||
macOS: { key: 'Shift', icon: modifierKey(ModifierKeys.Shift, 'macOS') },
|
||||
windows: { key: 'Shift', icon: modifierKey(ModifierKeys.Shift, 'Other') },
|
||||
browser: { key: 'Shift', icon: modifierKey(ModifierKeys.Shift, 'Other') },
|
||||
linux: { key: 'Shift', icon: modifierKey(ModifierKeys.Shift, 'Other') },
|
||||
unknown: { key: 'Shift', icon: modifierKey(ModifierKeys.Shift, 'Other') }
|
||||
all: { key: 'Shift', icon: modifierKey(ModifierKeys.Shift, 'Other') },
|
||||
},
|
||||
Alt: {
|
||||
macOS: { key: 'Alt', icon: modifierKey(ModifierKeys.Alt, 'macOS') },
|
||||
windows: { key: 'Alt', icon: modifierKey(ModifierKeys.Alt, 'Other') },
|
||||
browser: { key: 'Alt', icon: modifierKey(ModifierKeys.Alt, 'Other') },
|
||||
linux: { key: 'Alt', icon: modifierKey(ModifierKeys.Alt, 'Other') },
|
||||
unknown: { key: 'Alt', icon: modifierKey(ModifierKeys.Alt, 'Other') }
|
||||
all: { key: 'Alt', icon: modifierKey(ModifierKeys.Alt, 'Other') },
|
||||
},
|
||||
Escape: {
|
||||
macOS: { key: 'Escape', icon: modifierKey(UIKeys.Escape, 'macOS') },
|
||||
all: { key: 'Escape', icon: modifierKey(UIKeys.Escape, 'Other') },
|
||||
},
|
||||
Delete: {
|
||||
macOS: { key: 'Delete', icon: modifierKey(EditingKeys.Delete, 'macOS') },
|
||||
all: { key: 'Delete', icon: modifierKey(EditingKeys.Delete, 'Other') },
|
||||
},
|
||||
Backspace: {
|
||||
macOS: { key: 'Backspace', icon: modifierKey(EditingKeys.Backspace, 'macOS') },
|
||||
all: { key: 'Backspace', icon: modifierKey(EditingKeys.Backspace, 'Other') },
|
||||
},
|
||||
ArrowUp: {
|
||||
all: { key: 'ArrowUp', icon: modifierKey(NavigationKeys.ArrowUp, 'Other') },
|
||||
},
|
||||
ArrowDown: {
|
||||
all: { key: 'ArrowDown', icon: modifierKey(NavigationKeys.ArrowDown, 'Other') },
|
||||
},
|
||||
ArrowLeft: {
|
||||
all: { key: 'ArrowLeft', icon: modifierKey(NavigationKeys.ArrowLeft, 'Other') },
|
||||
},
|
||||
ArrowRight: {
|
||||
all: { key: 'ArrowRight', icon: modifierKey(NavigationKeys.ArrowRight, 'Other') },
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export function useKeyMatcher<T extends keysToMatch>(arg: T): { key: string; icon: string } {
|
||||
const os = useOperatingSystem();
|
||||
const key = keysOsMap[arg][os];
|
||||
type keysOfOsMap = keyof typeof keysOsMap;
|
||||
type os = Exclude<OperatingSystem, "linux" | "browser" | "unknown"> | "all"
|
||||
|
||||
export function useKeyMatcher<T extends keysOfOsMap>(arg: T):
|
||||
{ key: string; icon: string } {
|
||||
const os = useOperatingSystem() as os;
|
||||
const key = keysOsMap[arg]?.[os] ?? keysOsMap[arg]?.['all']
|
||||
if (!key) {
|
||||
throw new Error(`No key found for ${arg} on ${os}`);
|
||||
}
|
||||
return key;
|
||||
}
|
||||
|
||||
//This is another hook to pass an array for multiple keys rather than one at a time
|
||||
export function useKeysMatcher<T extends keysToMatch>(
|
||||
export function useKeysMatcher<T extends keysOfOsMap>(
|
||||
arg: T[]
|
||||
): Record<T, { key: string; icon: string }> {
|
||||
const os = useOperatingSystem();
|
||||
const os = useOperatingSystem() as os;
|
||||
const object = {} as Record<T, { key: string; icon: string }>;
|
||||
for (const key of arg) {
|
||||
object[key] = {
|
||||
key: keysOsMap[key][os].key,
|
||||
icon: keysOsMap[key][os].icon
|
||||
};
|
||||
object[key] = {
|
||||
key: keysOsMap[key]?.[os]?.key as string ?? keysOsMap[key]?.['all']?.key,
|
||||
icon: keysOsMap[key]?.[os]?.icon as string ?? keysOsMap[key]?.['all']?.icon,
|
||||
};
|
||||
}
|
||||
return object;
|
||||
}
|
||||
|
||||
@@ -11,13 +11,29 @@ export enum ModifierKeys {
|
||||
NumLock = 'NumLock',
|
||||
ScrollLock = 'ScrollLock',
|
||||
Symbol = 'Symbol',
|
||||
SymbolLock = 'SymbolLock'
|
||||
SymbolLock = 'SymbolLock',
|
||||
}
|
||||
|
||||
export enum EditingKeys {
|
||||
Backspace = 'Backspace',
|
||||
Delete = 'Delete'
|
||||
}
|
||||
|
||||
export enum UIKeys {
|
||||
Escape = 'Escape',
|
||||
}
|
||||
|
||||
export enum NavigationKeys {
|
||||
ArrowUp = 'ArrowUp',
|
||||
ArrowDown = 'ArrowDown',
|
||||
ArrowLeft = 'ArrowLeft',
|
||||
ArrowRight = 'ArrowRight',
|
||||
}
|
||||
|
||||
export type OSforKeys = 'macOS' | 'Windows' | 'Other';
|
||||
|
||||
export const modifierSymbols: Record<
|
||||
ModifierKeys,
|
||||
ModifierKeys | EditingKeys | UIKeys | NavigationKeys,
|
||||
{ macOS?: string; Windows?: string; Other: string }
|
||||
> = {
|
||||
Alt: { macOS: '⌥', Other: 'Alt' },
|
||||
@@ -31,7 +47,14 @@ export const modifierSymbols: Record<
|
||||
ScrollLock: { macOS: '⤓', Other: 'ScrLk' },
|
||||
Shift: { Other: 'Shift', macOS: '⇧' },
|
||||
Symbol: { macOS: '⎄', Other: 'Sym' },
|
||||
SymbolLock: { macOS: '⎄', Other: 'Sym' }
|
||||
SymbolLock: { macOS: '⎄', Other: 'Sym' },
|
||||
Escape: { macOS: '⎋', Other: 'Esc' },
|
||||
Delete: { macOS: '⌦', Other: 'Del' },
|
||||
Backspace: { macOS: '⌫', Other: '⟵' },
|
||||
ArrowUp: { Other: '↑' },
|
||||
ArrowDown: { Other: '↓' },
|
||||
ArrowLeft: { Other: '←' },
|
||||
ArrowRight: { Other: '→' },
|
||||
};
|
||||
|
||||
export const keySymbols: Record<string, { macOS?: string; Windows?: string; Other: string }> = {
|
||||
|
||||
Reference in New Issue
Block a user