mirror of
https://github.com/spacedriveapp/spacedrive.git
synced 2026-05-18 13:26:00 -04:00
[ENG-1119] - Open folder shortcut + select item with arrow down (#1355)
* Open folder shortcut + select item with arrow down * tweaks * symbol tweaks * Update index.tsx
This commit is contained in:
@@ -11,9 +11,8 @@ import {
|
||||
import clsx from 'clsx';
|
||||
import { useEffect, useRef } from 'react';
|
||||
import { useRspcLibraryContext } from '@sd/client';
|
||||
import { ModifierKeys } from '~/../packages/ui/src';
|
||||
import { ModifierKeys, modifierSymbols } from '@sd/ui';
|
||||
import { useOperatingSystem } from '~/hooks';
|
||||
import { keybindForOs } from '~/util/keybinds';
|
||||
|
||||
import { KeyManager } from '../KeyManager';
|
||||
import TopBarOptions, { ToolOption, TOP_BAR_ICON_STYLE } from '../TopBar/TopBarOptions';
|
||||
@@ -26,7 +25,14 @@ export const useExplorerTopBarOptions = () => {
|
||||
const explorerStore = useExplorerStore();
|
||||
const explorer = useExplorerContext();
|
||||
const os = useOperatingSystem();
|
||||
const keybind = keybindForOs(os);
|
||||
const controlSymbol = (letter: string) => {
|
||||
return [
|
||||
os === 'macOS'
|
||||
? modifierSymbols[ModifierKeys.Meta][os]
|
||||
: modifierSymbols[ModifierKeys.Control]['Other'],
|
||||
letter
|
||||
] as string[];
|
||||
};
|
||||
|
||||
const settings = explorer.useSettingsSnapshot();
|
||||
|
||||
@@ -34,7 +40,7 @@ export const useExplorerTopBarOptions = () => {
|
||||
{
|
||||
toolTipLabel: 'Grid view',
|
||||
icon: <SquaresFour className={TOP_BAR_ICON_STYLE} />,
|
||||
keybinds: [keybind([ModifierKeys.Meta], ['V'])],
|
||||
keybinds: controlSymbol('V'),
|
||||
topBarActive: settings.layoutMode === 'grid',
|
||||
onClick: () => (explorer.settingsStore.layoutMode = 'grid'),
|
||||
showAtResolution: 'sm:flex'
|
||||
@@ -42,7 +48,7 @@ export const useExplorerTopBarOptions = () => {
|
||||
{
|
||||
toolTipLabel: 'List view',
|
||||
icon: <Rows className={TOP_BAR_ICON_STYLE} />,
|
||||
keybinds: [keybind([ModifierKeys.Meta], ['V'])],
|
||||
keybinds: controlSymbol('V'),
|
||||
topBarActive: settings.layoutMode === 'list',
|
||||
onClick: () => (explorer.settingsStore.layoutMode = 'list'),
|
||||
showAtResolution: 'sm:flex'
|
||||
@@ -57,7 +63,7 @@ export const useExplorerTopBarOptions = () => {
|
||||
{
|
||||
toolTipLabel: 'Media view',
|
||||
icon: <MonitorPlay className={TOP_BAR_ICON_STYLE} />,
|
||||
keybinds: [keybind([ModifierKeys.Meta], ['V'])],
|
||||
keybinds: controlSymbol('V'),
|
||||
topBarActive: settings.layoutMode === 'media',
|
||||
onClick: () => (explorer.settingsStore.layoutMode = 'media'),
|
||||
showAtResolution: 'sm:flex'
|
||||
@@ -74,7 +80,7 @@ export const useExplorerTopBarOptions = () => {
|
||||
},
|
||||
{
|
||||
toolTipLabel: 'Show Inspector',
|
||||
keybinds: [keybind([ModifierKeys.Meta], ['I'])],
|
||||
keybinds: controlSymbol('I'),
|
||||
onClick: () => (getExplorerStore().showInspector = !explorerStore.showInspector),
|
||||
icon: (
|
||||
<SidebarSimple
|
||||
|
||||
@@ -84,7 +84,7 @@ const GridListItem = (props: {
|
||||
|
||||
return (
|
||||
<div
|
||||
className="h-full w-full"
|
||||
className="w-full h-full"
|
||||
data-selectable=""
|
||||
data-selectable-index={props.index}
|
||||
data-selectable-id={itemId}
|
||||
@@ -248,6 +248,20 @@ export default ({ children }: { children: RenderItem }) => {
|
||||
return;
|
||||
}
|
||||
|
||||
if (e.key === 'ArrowDown' && explorer.selectedItems.size === 0) {
|
||||
const item = grid.getItem(0);
|
||||
if (!item?.data) return;
|
||||
const selectedItemDom = document.querySelector(
|
||||
`[data-selectable-id="${uniqueId(item.data)}"]`
|
||||
);
|
||||
if (selectedItemDom) {
|
||||
explorer.resetSelectedItems([item.data]);
|
||||
selecto.current?.setSelectedTargets([selectedItemDom as HTMLElement]);
|
||||
activeItem.current = item.data;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (explorer.selectedItems.size > 0) e.preventDefault();
|
||||
|
||||
const lastItem = activeItem.current;
|
||||
|
||||
@@ -59,7 +59,7 @@ interface ListViewItemProps {
|
||||
const ListViewItem = memo((props: ListViewItemProps) => {
|
||||
return (
|
||||
<ViewItem data={props.row.original} className="w-full">
|
||||
<div role="row" className="flex h-full items-center">
|
||||
<div role="row" className="flex items-center h-full">
|
||||
{props.row.getVisibleCells().map((cell) => (
|
||||
<div
|
||||
role="cell"
|
||||
@@ -819,6 +819,15 @@ export default () => {
|
||||
|
||||
const range = getRangeByIndex(ranges.length - 1);
|
||||
|
||||
if (e.key === 'ArrowDown' && explorer.selectedItems.size === 0) {
|
||||
const item = rows[0]?.original;
|
||||
if (item) {
|
||||
explorer.addSelectedItem(item);
|
||||
setRanges([[uniqueId(item), uniqueId(item)]]);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (!range) return;
|
||||
|
||||
if (e.key === 'Escape') {
|
||||
@@ -1019,7 +1028,7 @@ export default () => {
|
||||
useLayoutEffect(() => setListOffset(tableRef.current?.offsetTop ?? 0), []);
|
||||
|
||||
return (
|
||||
<div className="flex w-full flex-col" ref={tableRef}>
|
||||
<div className="flex flex-col w-full" ref={tableRef}>
|
||||
{sized && (
|
||||
<ScrollSync>
|
||||
<>
|
||||
@@ -1041,7 +1050,7 @@ export default () => {
|
||||
<div
|
||||
ref={tableHeaderRef}
|
||||
key={headerGroup.id}
|
||||
className="flex grow border-b border-app-line/50"
|
||||
className="flex border-b grow border-app-line/50"
|
||||
onMouseDown={(e) => e.stopPropagation()}
|
||||
>
|
||||
{headerGroup.headers.map((header, i) => {
|
||||
@@ -1064,7 +1073,7 @@ export default () => {
|
||||
return (
|
||||
<div
|
||||
key={header.id}
|
||||
className="relative shrink-0 px-4 py-2 text-xs first:pl-24"
|
||||
className="relative px-4 py-2 text-xs shrink-0 first:pl-24"
|
||||
style={{
|
||||
width:
|
||||
i === 0
|
||||
@@ -1181,7 +1190,7 @@ export default () => {
|
||||
</ScrollSyncPane>
|
||||
|
||||
<ScrollSyncPane>
|
||||
<div className="no-scrollbar overflow-x-auto overscroll-x-none">
|
||||
<div className="overflow-x-auto no-scrollbar overscroll-x-none">
|
||||
<div
|
||||
ref={tableBodyRef}
|
||||
className="relative"
|
||||
@@ -1209,7 +1218,7 @@ export default () => {
|
||||
return (
|
||||
<div
|
||||
key={row.id}
|
||||
className="absolute left-0 top-0 flex w-full"
|
||||
className="absolute top-0 left-0 flex w-full"
|
||||
style={{
|
||||
height: virtualRow.size,
|
||||
transform: `translateY(${
|
||||
@@ -1242,7 +1251,7 @@ export default () => {
|
||||
)}
|
||||
>
|
||||
{selectedPrior && (
|
||||
<div className="absolute inset-x-3 top-0 h-px bg-accent/10" />
|
||||
<div className="absolute top-0 h-px inset-x-3 bg-accent/10" />
|
||||
)}
|
||||
|
||||
<ListViewItem
|
||||
|
||||
@@ -13,6 +13,7 @@ import {
|
||||
} from 'react';
|
||||
import { createPortal } from 'react-dom';
|
||||
import { createSearchParams, useNavigate } from 'react-router-dom';
|
||||
import { useKeys } from 'rooks';
|
||||
import {
|
||||
getItemObject,
|
||||
isPath,
|
||||
@@ -53,8 +54,15 @@ export const ViewItem = ({ data, children, ...props }: ViewItemProps) => {
|
||||
const navigate = useNavigate();
|
||||
const { library } = useLibraryContext();
|
||||
const { openFilePaths } = usePlatform();
|
||||
const os = useOperatingSystem();
|
||||
|
||||
const updateAccessTime = useLibraryMutation('files.updateAccessTime');
|
||||
const metaCtrlKey = os === 'macOS' ? ModifierKeys.Meta : ModifierKeys.Control;
|
||||
|
||||
useKeys([metaCtrlKey, 'ArrowUp'], async (e) => {
|
||||
e.stopPropagation();
|
||||
await onDoubleClick();
|
||||
});
|
||||
|
||||
const onDoubleClick = async () => {
|
||||
const selectedItems = [...explorer.selectedItems];
|
||||
@@ -279,7 +287,7 @@ export const EmptyNotice = (props: { icon?: Icon | ReactNode; message?: ReactNod
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="flex h-full flex-col items-center justify-center text-ink-faint">
|
||||
<div className="flex flex-col items-center justify-center h-full text-ink-faint">
|
||||
{props.icon
|
||||
? isValidElement(props.icon)
|
||||
? props.icon
|
||||
|
||||
@@ -1,9 +1,16 @@
|
||||
import { Gear } from '@phosphor-icons/react';
|
||||
import { useNavigate } from 'react-router';
|
||||
import { JobManagerContextProvider, useClientContext, useDebugState } from '@sd/client';
|
||||
import { Button, ButtonLink, dialogManager, ModifierKeys, Popover, Tooltip } from '@sd/ui';
|
||||
import {
|
||||
Button,
|
||||
ButtonLink,
|
||||
dialogManager,
|
||||
ModifierKeys,
|
||||
modifierSymbols,
|
||||
Popover,
|
||||
Tooltip
|
||||
} from '@sd/ui';
|
||||
import { useKeyBind, useOperatingSystem } from '~/hooks';
|
||||
import { keybindForOs } from '~/util/keybinds';
|
||||
|
||||
import DebugPopover from './DebugPopover';
|
||||
import FeedbackDialog from './FeedbackDialog';
|
||||
@@ -13,8 +20,12 @@ export default () => {
|
||||
const { library } = useClientContext();
|
||||
const debugState = useDebugState();
|
||||
const os = useOperatingSystem();
|
||||
const keybind = keybindForOs(os);
|
||||
const navigate = useNavigate();
|
||||
const jobManagerKeys = [os === 'macOS' ? ModifierKeys.Meta : ModifierKeys.Control, 'j'];
|
||||
const recentJobsSymbol =
|
||||
os === 'macOS'
|
||||
? modifierSymbols[ModifierKeys.Meta][os]
|
||||
: modifierSymbols[ModifierKeys.Control]['Other'];
|
||||
|
||||
useKeyBind(['g', 's'], (e) => {
|
||||
e.stopPropagation();
|
||||
@@ -23,7 +34,7 @@ export default () => {
|
||||
|
||||
return (
|
||||
<div className="space-y-2">
|
||||
<div className="flex w-full items-center justify-between">
|
||||
<div className="flex items-center justify-between w-full">
|
||||
<div className="flex">
|
||||
<ButtonLink
|
||||
to="settings/client/general"
|
||||
@@ -32,12 +43,12 @@ export default () => {
|
||||
className="text-sidebar-inkFaint ring-offset-sidebar"
|
||||
>
|
||||
<Tooltip label="Settings" keybinds={['G', 'S']}>
|
||||
<Gear className="h-5 w-5" />
|
||||
<Gear className="w-5 h-5" />
|
||||
</Tooltip>
|
||||
</ButtonLink>
|
||||
<JobManagerContextProvider>
|
||||
<Popover
|
||||
keybind={['Meta', 'j']}
|
||||
keybind={jobManagerKeys}
|
||||
trigger={
|
||||
<Button
|
||||
size="icon"
|
||||
@@ -48,7 +59,7 @@ export default () => {
|
||||
{library && (
|
||||
<Tooltip
|
||||
label="Recent Jobs"
|
||||
keybinds={[keybind([ModifierKeys.Meta], ['J'])]}
|
||||
keybinds={[recentJobsSymbol as string, 'J']}
|
||||
>
|
||||
<IsRunningJob />
|
||||
</Tooltip>
|
||||
|
||||
@@ -3,6 +3,7 @@ import { useLayoutEffect, useState } from 'react';
|
||||
import { useKeys } from 'rooks';
|
||||
import { ModifierKeys, Popover, Tooltip } from '@sd/ui';
|
||||
import { ExplorerLayout } from '~/../packages/client/src';
|
||||
import { useKeyBind, useOperatingSystem } from '~/hooks';
|
||||
|
||||
import { useExplorerContext } from '../Explorer/Context';
|
||||
import TopBarButton from './TopBarButton';
|
||||
@@ -33,8 +34,10 @@ export default ({ options }: TopBarChildrenProps) => {
|
||||
const toolsNotSmFlex = options
|
||||
?.flatMap((group) => group)
|
||||
.filter((t) => t.showAtResolution !== 'sm:flex');
|
||||
const os = useOperatingSystem();
|
||||
const keys = [os === 'macOS' ? ModifierKeys.Meta : ModifierKeys.Control, 'v'];
|
||||
|
||||
useKeys(['Meta', 'v'], (e) => {
|
||||
useKeyBind(keys, (e) => {
|
||||
e.stopPropagation();
|
||||
const explorerLayouts: ExplorerLayout[] = ['grid', 'list', 'media']; //based on the order of the icons
|
||||
const currentLayout = explorerLayouts.indexOf(
|
||||
@@ -56,7 +59,7 @@ export default ({ options }: TopBarChildrenProps) => {
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div data-tauri-drag-region className="flex flex-1 justify-end">
|
||||
<div data-tauri-drag-region className="flex justify-end flex-1">
|
||||
<div data-tauri-drag-region className={`flex gap-0`}>
|
||||
{options?.map((group, groupIndex) => {
|
||||
return group.map(
|
||||
|
||||
@@ -2,8 +2,8 @@ import clsx from 'clsx';
|
||||
import { forwardRef } from 'react';
|
||||
import { useFormContext } from 'react-hook-form';
|
||||
import { InputField, InputFieldProps, toast } from '@sd/ui';
|
||||
|
||||
import { usePlatform } from '~/util/Platform';
|
||||
|
||||
import { openDirectoryPickerDialog } from './openDirectoryPickerDialog';
|
||||
|
||||
export const LocationPathInputField = forwardRef<
|
||||
@@ -12,7 +12,6 @@ export const LocationPathInputField = forwardRef<
|
||||
>((props, ref) => {
|
||||
const platform = usePlatform();
|
||||
const form = useFormContext();
|
||||
console.log(form.formState.isDirty);
|
||||
|
||||
return (
|
||||
<InputField
|
||||
|
||||
Reference in New Issue
Block a user