From 734a4e82e42fdf81e7f37e3570a35fa4073141a7 Mon Sep 17 00:00:00 2001 From: ameer2468 <33054370+ameer2468@users.noreply.github.com> Date: Sat, 8 Apr 2023 02:45:16 +0300 Subject: [PATCH] ENG-449 - Keyboard support for Search & Component Extraction (#683) * Extracted components within TopBar to their own files as components. Added support for for cmd/ctrl + f and Esc for keyboard searchbar. * Fixed misplaced event prevent default --- .../app/$libraryId/Explorer/SearchBar.tsx | 75 ++++++++ interface/app/$libraryId/Explorer/TopBar.tsx | 168 +----------------- .../app/$libraryId/Explorer/TopBarButton.tsx | 43 +++++ interface/app/$libraryId/spacedrop.tsx | 16 +- packages/ui/src/Input.tsx | 2 +- 5 files changed, 131 insertions(+), 173 deletions(-) create mode 100644 interface/app/$libraryId/Explorer/SearchBar.tsx create mode 100644 interface/app/$libraryId/Explorer/TopBarButton.tsx diff --git a/interface/app/$libraryId/Explorer/SearchBar.tsx b/interface/app/$libraryId/Explorer/SearchBar.tsx new file mode 100644 index 000000000..2138a8532 --- /dev/null +++ b/interface/app/$libraryId/Explorer/SearchBar.tsx @@ -0,0 +1,75 @@ +import clsx from 'clsx'; +import { ComponentPropsWithRef, forwardRef, useEffect } from 'react'; +import { useForm } from 'react-hook-form'; +import { Input, Shortcut } from '@sd/ui'; +import { useOperatingSystem } from '~/hooks/useOperatingSystem'; + +export default forwardRef>( + (props, forwardedRef) => { + const { + register, + handleSubmit, + reset, + formState: { dirtyFields } + } = useForm(); + + const { ref, ...searchField } = register('searchField', { + onBlur: () => { + // if there's no text in the search bar, don't mark it as dirty so the key hint shows + if (!dirtyFields.searchField) reset(); + } + }); + + const platform = useOperatingSystem(false); + const os = useOperatingSystem(true); + + useEffect(() => { + const keyboardSearchFocus = (event: KeyboardEvent) => { + if (typeof forwardedRef !== 'function') { + if ((event.key === 'f' && event.metaKey) || event.ctrlKey) { + event.preventDefault(); + forwardedRef?.current?.focus(); + } else if (forwardedRef?.current === document.activeElement && event.key === 'Escape') { + forwardedRef.current?.blur(); + } + } + }; + document.addEventListener('keydown', keyboardSearchFocus); + return () => { + document.removeEventListener('keydown', keyboardSearchFocus); + }; + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + + return ( +
null)} className="relative flex h-7"> + { + ref(el); + if (typeof forwardedRef === 'function') forwardedRef(el); + else if (forwardedRef) forwardedRef.current = el; + }} + placeholder="Search" + className={clsx('w-32 transition-all focus-within:w-52', props.className)} + size="sm" + {...searchField} + right={ +
+ {platform === 'browser' ? ( + + ) : os === 'macOS' ? ( + + ) : ( + + )} +
+ } + /> +
+ ); + } +); diff --git a/interface/app/$libraryId/Explorer/TopBar.tsx b/interface/app/$libraryId/Explorer/TopBar.tsx index b410520da..f0fc60290 100644 --- a/interface/app/$libraryId/Explorer/TopBar.tsx +++ b/interface/app/$libraryId/Explorer/TopBar.tsx @@ -5,7 +5,6 @@ import { CaretRight, Columns, Key, - List, MonitorPlay, Rows, SidebarSimple, @@ -13,128 +12,26 @@ import { SquaresFour, Tag } from 'phosphor-react'; -import { ComponentProps, forwardRef, useEffect, useRef } from 'react'; -import { useForm } from 'react-hook-form'; +import { useEffect, useRef } from 'react'; import { useNavigate } from 'react-router-dom'; -import { Button, Input, Popover, Shortcut, Tooltip, cva } from '@sd/ui'; +import { Popover, Tooltip } from '@sd/ui'; import { getExplorerStore, useExplorerStore } from '~/hooks/useExplorerStore'; import { useOperatingSystem } from '~/hooks/useOperatingSystem'; import { KeybindEvent } from '~/util/keybind'; import { KeyManager } from '../KeyManager'; import OptionsPanel from './OptionsPanel'; - -export interface TopBarButtonProps { - children: React.ReactNode; - rounding?: 'none' | 'left' | 'right' | 'both'; - active?: boolean; - className?: string; - onClick?: () => void; -} - -// export const TopBarIcon = (icon: any) => tw(icon)`m-0.5 w-5 h-5 text-ink-dull`; - -const topBarButtonStyle = cva( - 'text-ink hover:text-ink text-md hover:bg-app-selected radix-state-open:bg-app-selected mr-[1px] flex border-none !p-0.5 font-medium outline-none transition-colors duration-100', - { - variants: { - active: { - true: 'bg-app-selected', - false: 'bg-transparent' - }, - rounding: { - none: 'rounded-none', - left: 'rounded-l-md rounded-r-none', - right: 'rounded-r-md rounded-l-none', - both: 'rounded-md' - } - }, - defaultVariants: { - active: false, - rounding: 'both' - } - } -); - -const TOP_BAR_ICON_STYLE = 'm-0.5 w-5 h-5 text-ink-dull'; - -const TopBarButton = forwardRef( - ({ active, rounding, className, ...props }, ref) => { - return ( - - ); - } -); - -export const SearchBar = forwardRef>( - (props, forwardedRef) => { - const { - register, - handleSubmit, - reset, - formState: { dirtyFields } - } = useForm(); - - const { ref, ...searchField } = register('searchField', { - onBlur: () => { - // if there's no text in the search bar, don't mark it as dirty so the key hint shows - if (!dirtyFields.searchField) reset(); - } - }); - - const platform = useOperatingSystem(false); - const os = useOperatingSystem(true); - - return ( -
null)} className="relative flex h-7"> - { - ref(el); - - if (typeof forwardedRef === 'function') forwardedRef(el); - else if (forwardedRef) forwardedRef.current = el; - }} - placeholder="Search" - className={clsx('w-32 transition-all focus-within:w-52', props.className)} - size="sm" - {...searchField} - right={ -
- {platform === 'browser' ? ( - - ) : os === 'macOS' ? ( - - ) : ( - - )} -
- } - /> -
- ); - } -); +import SearchBar from './SearchBar'; +import TopBarButton from './TopBarButton'; export type TopBarProps = { showSeparator?: boolean; }; export default (props: TopBarProps) => { + const TOP_BAR_ICON_STYLE = 'm-0.5 w-5 h-5 text-ink-dull'; const platform = useOperatingSystem(false); const os = useOperatingSystem(true); - const store = useExplorerStore(); - const navigate = useNavigate(); //create function to focus on search box when cmd+k is pressed @@ -216,12 +113,6 @@ export default (props: TopBarProps) => { - {/*
- - - -
*/} -
@@ -251,16 +142,6 @@ export default (props: TopBarProps) => { - {/* - (getExplorerStore().layoutMode = 'timeline')} - > - - - */} - { - // } >
- +
@@ -303,12 +182,7 @@ export default (props: TopBarProps) => { - - // store.locationId && - // generateThumbsForLocation.mutate({ id: store.locationId, path: '' }) - // } - > + @@ -345,34 +219,6 @@ export default (props: TopBarProps) => { /> - {/* - store.locationId && - generateThumbsForLocation({ id: store.locationId, path: '' }) - }, - { - name: 'Identify Unique', - icon: ArrowsClockwise, - onPress: () => - store.locationId && identifyUniqueFiles({ id: store.locationId, path: '' }) - }, - { - name: 'Validate Objects', - icon: ArrowsClockwise, - onPress: () => - store.locationId && objectValidator({ id: store.locationId, path: '' }) - } - ] - ]} - buttonComponent={} - /> */}
diff --git a/interface/app/$libraryId/Explorer/TopBarButton.tsx b/interface/app/$libraryId/Explorer/TopBarButton.tsx new file mode 100644 index 000000000..11c0c58ed --- /dev/null +++ b/interface/app/$libraryId/Explorer/TopBarButton.tsx @@ -0,0 +1,43 @@ +import { cva } from 'class-variance-authority'; +import { forwardRef } from 'react'; +import { Button } from '@sd/ui'; + +export interface TopBarButtonProps { + children: React.ReactNode; + rounding?: 'none' | 'left' | 'right' | 'both'; + active?: boolean; + className?: string; + onClick?: () => void; +} + +const topBarButtonStyle = cva( + 'text-ink hover:text-ink text-md hover:bg-app-selected radix-state-open:bg-app-selected mr-[1px] flex border-none !p-0.5 font-medium outline-none transition-colors duration-100', + { + variants: { + active: { + true: 'bg-app-selected', + false: 'bg-transparent' + }, + rounding: { + none: 'rounded-none', + left: 'rounded-l-md rounded-r-none', + right: 'rounded-r-md rounded-l-none', + both: 'rounded-md' + } + }, + defaultVariants: { + active: false, + rounding: 'both' + } + } +); + +export default forwardRef( + ({ active, rounding, className, ...props }, ref) => { + return ( + + ); + } +); diff --git a/interface/app/$libraryId/spacedrop.tsx b/interface/app/$libraryId/spacedrop.tsx index 5c67793b1..6c141c7cd 100644 --- a/interface/app/$libraryId/spacedrop.tsx +++ b/interface/app/$libraryId/spacedrop.tsx @@ -2,11 +2,11 @@ import { GoogleDrive, Mega, iCloud } from '@sd/assets/images'; import clsx from 'clsx'; import { DeviceMobile, HardDrives, Icon, Laptop, User } from 'phosphor-react'; import { useRef, useState } from 'react'; -import { Button, Label, Select, SelectOption, forms, tw } from '@sd/ui'; +import { Button, Select, SelectOption, forms, tw } from '@sd/ui'; import { PeerMetadata, useBridgeMutation, useBridgeSubscription } from '~/../packages/client/src'; import { SubtleButton, SubtleButtonContainer } from '~/components/SubtleButton'; import { OperatingSystem } from '~/util/Platform'; -import { SearchBar } from './Explorer/TopBar'; +import SearchBar from './Explorer/SearchBar'; import * as PageLayout from './PageLayout'; import classes from './spacedrop.module.scss'; @@ -107,8 +107,6 @@ function TemporarySpacedropDemo() { } }); - console.log({ discoveredPeers }); - const onSubmit = form.handleSubmit((data) => { doSpacedrop.mutate({ peer_id: data.target_peer, @@ -160,16 +158,12 @@ function TemporarySpacedropDemo() { export const Component = () => { const searchRef = useRef(null); - return ( <> +
+ +
- -
- - {/* */} -
-