mirror of
https://github.com/spacedriveapp/spacedrive.git
synced 2026-05-18 13:26:00 -04:00
[ENG-450] TopBar revamp and restructure (#701)
* TopBar revamp and restructure * file tweaks * Update package.json * Typecheck missed * TopBar revamp and restructure file tweaks Update package.json Typecheck missed * Removed search icon and layout change * move framer to interface/package.json * Increase spacing * fixes & tweaks * Update TopBar.tsx * typecheck tweak
This commit is contained in:
@@ -4,72 +4,78 @@ import { useForm } from 'react-hook-form';
|
||||
import { Input, Shortcut } from '@sd/ui';
|
||||
import { useOperatingSystem } from '~/hooks/useOperatingSystem';
|
||||
|
||||
export default forwardRef<HTMLInputElement, ComponentPropsWithRef<'input'>>(
|
||||
(props, forwardedRef) => {
|
||||
const {
|
||||
register,
|
||||
handleSubmit,
|
||||
reset,
|
||||
formState: { dirtyFields }
|
||||
} = useForm();
|
||||
interface Props extends ComponentPropsWithRef<'input'> {
|
||||
formClassName?: string;
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
});
|
||||
export default forwardRef<HTMLInputElement, Props>((props, forwardedRef) => {
|
||||
const {
|
||||
register,
|
||||
handleSubmit,
|
||||
reset,
|
||||
formState: { dirtyFields }
|
||||
} = useForm();
|
||||
|
||||
const platform = useOperatingSystem(false);
|
||||
const os = useOperatingSystem(true);
|
||||
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();
|
||||
}
|
||||
});
|
||||
|
||||
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();
|
||||
}
|
||||
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();
|
||||
reset();
|
||||
}
|
||||
};
|
||||
document.addEventListener('keydown', keyboardSearchFocus);
|
||||
return () => {
|
||||
document.removeEventListener('keydown', keyboardSearchFocus);
|
||||
};
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
}
|
||||
};
|
||||
document.addEventListener('keydown', keyboardSearchFocus);
|
||||
return () => {
|
||||
document.removeEventListener('keydown', keyboardSearchFocus);
|
||||
};
|
||||
}, [forwardedRef, reset]);
|
||||
|
||||
return (
|
||||
<form onSubmit={handleSubmit(() => null)} className="relative flex h-7">
|
||||
<Input
|
||||
ref={(el) => {
|
||||
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={
|
||||
<div
|
||||
className={clsx(
|
||||
'pointer-events-none flex h-7 items-center space-x-1 opacity-70 group-focus-within:hidden'
|
||||
)}
|
||||
>
|
||||
{platform === 'browser' ? (
|
||||
<Shortcut chars="⌘F" aria-label={'Press Command-F to focus search bar'} />
|
||||
) : os === 'macOS' ? (
|
||||
<Shortcut chars="⌘F" aria-label={'Press Command-F to focus search bar'} />
|
||||
) : (
|
||||
<Shortcut chars="CTRL+F" aria-label={'Press CTRL-F to focus search bar'} />
|
||||
)}
|
||||
</div>
|
||||
}
|
||||
/>
|
||||
</form>
|
||||
);
|
||||
}
|
||||
);
|
||||
return (
|
||||
<form
|
||||
data-tauri-drag-region
|
||||
onSubmit={handleSubmit(() => null)}
|
||||
className={`relative flex h-7 ${props.formClassName}`}
|
||||
>
|
||||
<Input
|
||||
ref={(el) => {
|
||||
ref(el);
|
||||
if (typeof forwardedRef === 'function') forwardedRef(el);
|
||||
else if (forwardedRef) forwardedRef.current = el;
|
||||
}}
|
||||
placeholder="Search"
|
||||
className={clsx('w-52 transition-all duration-200 focus-within:w-60', props.className)}
|
||||
size="sm"
|
||||
{...searchField}
|
||||
right={
|
||||
<div
|
||||
className={clsx(
|
||||
'pointer-events-none flex h-7 items-center space-x-1 opacity-70 group-focus-within:hidden'
|
||||
)}
|
||||
>
|
||||
{platform === 'browser' ? (
|
||||
<Shortcut chars="⌘F" aria-label={'Press Command-F to focus search bar'} />
|
||||
) : os === 'macOS' ? (
|
||||
<Shortcut chars="⌘F" aria-label={'Press Command-F to focus search bar'} />
|
||||
) : (
|
||||
<Shortcut chars="CTRL+F" aria-label={'Press CTRL-F to focus search bar'} />
|
||||
)}
|
||||
</div>
|
||||
}
|
||||
/>
|
||||
</form>
|
||||
);
|
||||
});
|
||||
|
||||
@@ -1,229 +1,102 @@
|
||||
import clsx from 'clsx';
|
||||
import {
|
||||
ArrowsClockwise,
|
||||
CaretLeft,
|
||||
CaretRight,
|
||||
Columns,
|
||||
Key,
|
||||
MonitorPlay,
|
||||
Rows,
|
||||
SidebarSimple,
|
||||
SlidersHorizontal,
|
||||
SquaresFour,
|
||||
Tag
|
||||
} from 'phosphor-react';
|
||||
import { useEffect, useRef } from 'react';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { CaretLeft, CaretRight } from 'phosphor-react';
|
||||
import { useRef } from 'react';
|
||||
import { useLocation, useNavigate } from 'react-router-dom';
|
||||
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';
|
||||
import { RoutePaths, groupKeys, useToolBarRouteOptions } from '~/hooks/useToolBarOptions';
|
||||
import SearchBar from './SearchBar';
|
||||
import TopBarButton from './TopBarButton';
|
||||
|
||||
export const TOP_BAR_HEIGHT = 46;
|
||||
|
||||
export type TopBarProps = {
|
||||
showSeparator?: boolean;
|
||||
};
|
||||
|
||||
export default (props: TopBarProps) => {
|
||||
export default () => {
|
||||
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
|
||||
const searchRef = useRef<HTMLInputElement>(null);
|
||||
|
||||
const focusSearchBar = (bar: HTMLInputElement, e?: Event): boolean => {
|
||||
bar.focus();
|
||||
|
||||
e?.preventDefault();
|
||||
return false;
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const searchBar = searchRef.current;
|
||||
|
||||
if (searchBar === null || !searchBar) return;
|
||||
|
||||
const handleKeybindAction = (e: KeybindEvent) => {
|
||||
if (e.detail.action === 'open_search') {
|
||||
return focusSearchBar(searchBar, e);
|
||||
}
|
||||
};
|
||||
|
||||
const handleDOMKeydown = (e: KeyboardEvent) => {
|
||||
if (e.target === searchBar && e.key === 'Escape') {
|
||||
(e.target as HTMLInputElement).blur();
|
||||
e.preventDefault();
|
||||
return;
|
||||
}
|
||||
|
||||
const isBrowser = platform === 'browser';
|
||||
// use cmd on macOS and ctrl on Windows
|
||||
const hasModifier = os === 'macOS' ? e.metaKey : e.ctrlKey;
|
||||
|
||||
if (
|
||||
// allow slash on all platforms
|
||||
(e.key === '/' &&
|
||||
!(document.activeElement instanceof HTMLInputElement) &&
|
||||
!(document.activeElement instanceof HTMLTextAreaElement)) ||
|
||||
// only do the cmd-f keybind check on browser to allow for native keybind functionality
|
||||
// this is particularly useful for power-user niche use cases,
|
||||
// like how macOS lets you redefine keybinds for apps
|
||||
(isBrowser && hasModifier && e.key === 'f')
|
||||
) {
|
||||
document.dispatchEvent(new KeybindEvent('open_search'));
|
||||
e.preventDefault();
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
document.addEventListener('keydown', handleDOMKeydown);
|
||||
document.addEventListener('keybindexec', handleKeybindAction);
|
||||
|
||||
return () => {
|
||||
document.removeEventListener('keydown', handleDOMKeydown);
|
||||
document.removeEventListener('keybindexec', handleKeybindAction);
|
||||
};
|
||||
}, [os, platform]);
|
||||
const { pathname } = useLocation();
|
||||
const getPageName = pathname.split('/')[2] as RoutePaths;
|
||||
const { toolBarRouteOptions } = useToolBarRouteOptions();
|
||||
|
||||
return (
|
||||
<>
|
||||
<div
|
||||
data-tauri-drag-region
|
||||
className={clsx(
|
||||
'max-w duration-250 absolute left-0 z-50 flex w-full shrink-0 items-center overflow-hidden border-b border-transparent bg-app pl-3 transition-[background-color] transition-[border-color] ease-out',
|
||||
props.showSeparator && 'top-bar-blur !bg-app/90'
|
||||
)}
|
||||
style={{ height: TOP_BAR_HEIGHT }}
|
||||
>
|
||||
<div className="flex">
|
||||
<Tooltip label="Navigate back">
|
||||
<TopBarButton onClick={() => navigate(-1)}>
|
||||
<CaretLeft weight="bold" className={TOP_BAR_ICON_STYLE} />
|
||||
</TopBarButton>
|
||||
</Tooltip>
|
||||
<Tooltip label="Navigate forward">
|
||||
<TopBarButton onClick={() => navigate(1)}>
|
||||
<CaretRight weight="bold" className={TOP_BAR_ICON_STYLE} />
|
||||
</TopBarButton>
|
||||
</Tooltip>
|
||||
</div>
|
||||
<div
|
||||
data-tauri-drag-region
|
||||
className={clsx(
|
||||
'duration-250 absolute top-0 z-20 flex grid h-[46px] w-full shrink-0 grid-cols-3 items-center justify-center overflow-hidden border-b border-sidebar-divider bg-app px-5 transition-[background-color] transition-[border-color] ease-out'
|
||||
)}
|
||||
>
|
||||
<div data-tauri-drag-region className="flex ">
|
||||
<Tooltip label="Navigate back">
|
||||
<TopBarButton onClick={() => navigate(-1)}>
|
||||
<CaretLeft weight="bold" className={TOP_BAR_ICON_STYLE} />
|
||||
</TopBarButton>
|
||||
</Tooltip>
|
||||
<Tooltip label="Navigate forward">
|
||||
<TopBarButton onClick={() => navigate(1)}>
|
||||
<CaretRight weight="bold" className={TOP_BAR_ICON_STYLE} />
|
||||
</TopBarButton>
|
||||
</Tooltip>
|
||||
</div>
|
||||
|
||||
<div data-tauri-drag-region className="flex grow flex-row justify-center">
|
||||
<div className="mx-8 flex">
|
||||
<Tooltip label="Grid view">
|
||||
<TopBarButton
|
||||
rounding="left"
|
||||
active={store.layoutMode === 'grid'}
|
||||
onClick={() => (getExplorerStore().layoutMode = 'grid')}
|
||||
>
|
||||
<SquaresFour className={TOP_BAR_ICON_STYLE} />
|
||||
</TopBarButton>
|
||||
</Tooltip>
|
||||
<Tooltip label="List view">
|
||||
<TopBarButton
|
||||
rounding="none"
|
||||
active={store.layoutMode === 'rows'}
|
||||
onClick={() => (getExplorerStore().layoutMode = 'rows')}
|
||||
>
|
||||
<Rows className={TOP_BAR_ICON_STYLE} />
|
||||
</TopBarButton>
|
||||
</Tooltip>
|
||||
<Tooltip label="Columns view">
|
||||
<TopBarButton
|
||||
rounding="none"
|
||||
active={store.layoutMode === 'columns'}
|
||||
onClick={() => (getExplorerStore().layoutMode = 'columns')}
|
||||
>
|
||||
<Columns className={TOP_BAR_ICON_STYLE} />
|
||||
</TopBarButton>
|
||||
</Tooltip>
|
||||
<Tooltip label="Media view">
|
||||
<TopBarButton
|
||||
rounding="right"
|
||||
active={store.layoutMode === 'media'}
|
||||
onClick={() => (getExplorerStore().layoutMode = 'media')}
|
||||
>
|
||||
<MonitorPlay className={TOP_BAR_ICON_STYLE} />
|
||||
</TopBarButton>
|
||||
</Tooltip>
|
||||
</div>
|
||||
<SearchBar formClassName="justify-center" ref={searchRef} />
|
||||
|
||||
<SearchBar ref={searchRef} />
|
||||
|
||||
<div className="mx-8 flex space-x-2">
|
||||
<Tooltip label="Key Manager">
|
||||
<Popover
|
||||
className="focus:outline-none"
|
||||
trigger={
|
||||
<TopBarButton>
|
||||
<Key className={TOP_BAR_ICON_STYLE} />
|
||||
</TopBarButton>
|
||||
<div data-tauri-drag-region className="flex flex-row justify-end w-full">
|
||||
<div data-tauri-drag-region className="flex gap-0">
|
||||
{toolBarRouteOptions[getPageName].options.map((group) => {
|
||||
return (Object.keys(group) as groupKeys[]).map((groupKey) => {
|
||||
return group[groupKey]?.map(
|
||||
({ icon, onClick, popOverComponent, toolTipLabel, topBarActive }, index) => {
|
||||
const groupCount = Object.keys(group).length;
|
||||
const groupIndex = Object.keys(group).indexOf(groupKey);
|
||||
const roundingCondition =
|
||||
index === 0
|
||||
? 'left'
|
||||
: index === (group[groupKey]?.length as number) - 1
|
||||
? 'right'
|
||||
: 'none';
|
||||
return (
|
||||
<div data-tauri-drag-region key={toolTipLabel} className="flex items-center">
|
||||
<Tooltip label={toolTipLabel}>
|
||||
{popOverComponent ? (
|
||||
<Popover
|
||||
className="focus:outline-none"
|
||||
trigger={
|
||||
<TopBarButton
|
||||
rounding={roundingCondition}
|
||||
active={topBarActive}
|
||||
onClick={onClick}
|
||||
>
|
||||
{icon}
|
||||
</TopBarButton>
|
||||
}
|
||||
>
|
||||
<div className="block w-[250px] ">{popOverComponent}</div>
|
||||
</Popover>
|
||||
) : (
|
||||
<TopBarButton
|
||||
rounding={roundingCondition}
|
||||
active={topBarActive}
|
||||
onClick={onClick ?? undefined}
|
||||
>
|
||||
{icon}
|
||||
</TopBarButton>
|
||||
)}
|
||||
</Tooltip>
|
||||
{index === (group[groupKey]?.length as number) - 1 &&
|
||||
groupCount !== groupIndex + 1 && (
|
||||
<div
|
||||
data-tauri-drag-region
|
||||
className="mx-4 h-[15px] w-0 border-l border-zinc-600"
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
>
|
||||
<div className="block w-[350px]">
|
||||
<KeyManager />
|
||||
</div>
|
||||
</Popover>
|
||||
</Tooltip>
|
||||
<Tooltip label="Tag Assign Mode">
|
||||
<TopBarButton
|
||||
onClick={() => (getExplorerStore().tagAssignMode = !store.tagAssignMode)}
|
||||
active={store.tagAssignMode}
|
||||
>
|
||||
<Tag
|
||||
weight={store.tagAssignMode ? 'fill' : 'regular'}
|
||||
className={TOP_BAR_ICON_STYLE}
|
||||
/>
|
||||
</TopBarButton>
|
||||
</Tooltip>
|
||||
<Tooltip label="Regenerate thumbs (temp)">
|
||||
<TopBarButton>
|
||||
<ArrowsClockwise className={TOP_BAR_ICON_STYLE} />
|
||||
</TopBarButton>
|
||||
</Tooltip>
|
||||
</div>
|
||||
</div>
|
||||
<div className="mr-3 flex space-x-2">
|
||||
<Tooltip label="Explorer display options" position="left">
|
||||
<Popover
|
||||
className="focus:outline-none"
|
||||
trigger={
|
||||
<TopBarButton className="my-2">
|
||||
<SlidersHorizontal className={TOP_BAR_ICON_STYLE} />
|
||||
</TopBarButton>
|
||||
}
|
||||
>
|
||||
<div className="block w-[250px] ">
|
||||
<OptionsPanel />
|
||||
</div>
|
||||
</Popover>
|
||||
</Tooltip>
|
||||
|
||||
<Tooltip
|
||||
label={store.showInspector ? 'Hide Inspector' : 'Show Inspector'}
|
||||
position="left"
|
||||
>
|
||||
<TopBarButton
|
||||
active={store.showInspector}
|
||||
onClick={() => (getExplorerStore().showInspector = !store.showInspector)}
|
||||
className="my-2"
|
||||
>
|
||||
<SidebarSimple
|
||||
weight={store.showInspector ? 'fill' : 'regular'}
|
||||
className={clsx(TOP_BAR_ICON_STYLE, 'scale-x-[-1]')}
|
||||
/>
|
||||
</TopBarButton>
|
||||
</Tooltip>
|
||||
);
|
||||
});
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -33,8 +33,8 @@ export default function Explorer(props: Props) {
|
||||
}, [locationId]);
|
||||
|
||||
return (
|
||||
<div className="flex h-screen w-full flex-col bg-app">
|
||||
<TopBar showSeparator={separateTopBar} />
|
||||
<div className="flex flex-col w-full h-screen bg-app">
|
||||
<TopBar />
|
||||
|
||||
<div className="flex flex-1">
|
||||
<ExplorerContextMenu>
|
||||
|
||||
@@ -3,6 +3,7 @@ import { PropsWithChildren, RefObject, createContext, useContext, useRef } from
|
||||
import { createPortal } from 'react-dom';
|
||||
import { Outlet } from 'react-router';
|
||||
import DragRegion from '~/components/DragRegion';
|
||||
import TopBar from './Explorer/TopBar';
|
||||
|
||||
const PageLayoutContext = createContext<{ ref: RefObject<HTMLDivElement> } | null>(null);
|
||||
|
||||
@@ -11,11 +12,14 @@ export const Component = () => {
|
||||
|
||||
return (
|
||||
<PageLayoutContext.Provider value={{ ref }}>
|
||||
<TopBar />
|
||||
<div
|
||||
className={clsx('custom-scroll page-scroll app-background flex h-screen w-full flex-col')}
|
||||
className={clsx(
|
||||
'custom-scrol page-scroll app-background flex h-screen w-full flex-col pt-10'
|
||||
)}
|
||||
>
|
||||
<DragRegion ref={ref} />
|
||||
<div className="flex h-screen w-full flex-col p-5 pt-0">
|
||||
<div className="flex flex-col w-full h-screen p-5 pt-0">
|
||||
<Outlet />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -14,7 +14,7 @@ import {
|
||||
import Skeleton from 'react-loading-skeleton';
|
||||
import 'react-loading-skeleton/dist/skeleton.css';
|
||||
import { Statistics, useLibraryContext, useLibraryQuery } from '@sd/client';
|
||||
import { Card } from '@sd/ui';
|
||||
import { Card, ScreenHeading } from '@sd/ui';
|
||||
import useCounter from '~/hooks/useCounter';
|
||||
import { usePlatform } from '~/util/Platform';
|
||||
|
||||
@@ -96,11 +96,12 @@ export const Component = () => {
|
||||
overviewMounted = true;
|
||||
|
||||
return (
|
||||
<div className="flex h-screen w-full flex-col">
|
||||
<div className="flex flex-col w-full h-screen">
|
||||
<ScreenHeading>Overview</ScreenHeading>
|
||||
{/* STAT HEADER */}
|
||||
<div className="flex w-full">
|
||||
{/* STAT CONTAINER */}
|
||||
<div className="-mb-1 flex h-20 overflow-hidden">
|
||||
<div className="flex h-20 -mb-1 overflow-hidden">
|
||||
{Object.entries(stats?.data || []).map(([key, value]) => {
|
||||
if (!displayableStatItems.includes(key)) return null;
|
||||
return (
|
||||
@@ -115,7 +116,7 @@ export const Component = () => {
|
||||
</div>
|
||||
<div className="grow" />
|
||||
</div>
|
||||
<div className="mt-4 grid grid-cols-5 gap-3 pb-4">
|
||||
<div className="grid grid-cols-5 gap-3 pb-4 mt-4">
|
||||
<CategoryButton icon={Heart} category="Favorites" />
|
||||
<CategoryButton icon={FileText} category="Documents" />
|
||||
<CategoryButton icon={Camera} category="Movies" />
|
||||
@@ -131,7 +132,7 @@ export const Component = () => {
|
||||
<b>Note: </b> This is a pre-alpha build of Spacedrive, many features are yet to be
|
||||
functional.
|
||||
</Card>
|
||||
<div className="flex h-4 w-full shrink-0" />
|
||||
<div className="flex w-full h-4 shrink-0" />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -144,7 +145,7 @@ interface CategoryButtonProps {
|
||||
function CategoryButton({ category, icon: Icon }: CategoryButtonProps) {
|
||||
return (
|
||||
<Card className="items-center !px-3">
|
||||
<Icon weight="fill" className="mr-3 h-6 w-6 text-ink-dull opacity-20" />
|
||||
<Icon weight="fill" className="w-6 h-6 mr-3 text-ink-dull opacity-20" />
|
||||
<div>
|
||||
<h2 className="text-sm font-medium">{category}</h2>
|
||||
<p className="text-xs text-ink-faint">23,324 items</p>
|
||||
|
||||
@@ -2,7 +2,7 @@ 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, Select, SelectOption, forms, tw } from '@sd/ui';
|
||||
import { Button, ScreenHeading, 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';
|
||||
@@ -41,7 +41,7 @@ function DropItem(props: DropItemProps) {
|
||||
}
|
||||
if (brandIconSrc) {
|
||||
icon = (
|
||||
<div className="flex h-full items-center justify-center p-3">
|
||||
<div className="flex items-center justify-center h-full p-3">
|
||||
<img className="rounded-full " src={brandIconSrc} alt={props.name} />
|
||||
</div>
|
||||
);
|
||||
@@ -56,11 +56,11 @@ function DropItem(props: DropItemProps) {
|
||||
<div
|
||||
className={clsx(classes.honeycombItem, 'overflow-hidden bg-app-box/20 hover:bg-app-box/50')}
|
||||
>
|
||||
<div className="group relative flex h-full w-full flex-col items-center justify-center ">
|
||||
<div className="relative flex flex-col items-center justify-center w-full h-full group ">
|
||||
{/* <SubtleButtonContainer className="absolute left-[12px] top-[55px]">
|
||||
<SubtleButton icon={Star} />
|
||||
</SubtleButtonContainer> */}
|
||||
<div className="h-14 w-14 rounded-full bg-app-button">{icon}</div>
|
||||
<div className="rounded-full h-14 w-14 bg-app-button">{icon}</div>
|
||||
<SubtleButtonContainer className="absolute right-[12px] top-[55px] rotate-90">
|
||||
<SubtleButton />
|
||||
</SubtleButtonContainer>
|
||||
@@ -117,12 +117,12 @@ function TemporarySpacedropDemo() {
|
||||
// TODO: Input select
|
||||
return (
|
||||
<Form onSubmit={onSubmit} form={form}>
|
||||
<h1 className="mt-4 text-2xl font-bold">Spacedrop Demo</h1>
|
||||
<ScreenHeading>Spacedrop Demo</ScreenHeading>
|
||||
<p className="text-xs text-ink-dull">
|
||||
Note: Right now the file must be less than 255 bytes long and only contain UTF-8 chars.
|
||||
Create a txt file in Vscode to test (note macOS TextEdit cause that is rtf by default)
|
||||
</p>
|
||||
<div className="mt-2 flex flex-row items-center space-x-4">
|
||||
<div className="flex flex-row items-center mt-2 space-x-4">
|
||||
<Input
|
||||
size="sm"
|
||||
placeholder="/Users/oscar/Desktop/sd/demo.txt"
|
||||
@@ -131,7 +131,7 @@ function TemporarySpacedropDemo() {
|
||||
{...form.register('file_path')}
|
||||
/>
|
||||
|
||||
<Button className="block flex-shrink-0" variant="gray">
|
||||
<Button className="flex-shrink-0 block" variant="gray">
|
||||
Select File
|
||||
</Button>
|
||||
|
||||
@@ -145,7 +145,7 @@ function TemporarySpacedropDemo() {
|
||||
|
||||
<Button
|
||||
disabled={!form.getValues().target_peer}
|
||||
className="block flex-shrink-0"
|
||||
className="flex-shrink-0 block"
|
||||
variant="accent"
|
||||
type="submit"
|
||||
>
|
||||
@@ -157,12 +157,8 @@ function TemporarySpacedropDemo() {
|
||||
}
|
||||
|
||||
export const Component = () => {
|
||||
const searchRef = useRef<HTMLInputElement>(null);
|
||||
return (
|
||||
<>
|
||||
<div className="i2ems-center relative bottom-[11.5px] flex w-full flex-row justify-center">
|
||||
<SearchBar className="ml-[13px]" ref={searchRef} />
|
||||
</div>
|
||||
<TemporarySpacedropDemo />
|
||||
<div className={classes.honeycombOuter}>
|
||||
<div className={clsx(classes.honeycombContainer, 'mt-8')}>
|
||||
|
||||
149
interface/hooks/useToolBarOptions.tsx
Normal file
149
interface/hooks/useToolBarOptions.tsx
Normal file
@@ -0,0 +1,149 @@
|
||||
import clsx from 'clsx';
|
||||
import {
|
||||
ArrowClockwise,
|
||||
Columns,
|
||||
Key,
|
||||
MonitorPlay,
|
||||
Rows,
|
||||
SidebarSimple,
|
||||
SlidersHorizontal,
|
||||
SquaresFour,
|
||||
Tag
|
||||
} from 'phosphor-react';
|
||||
import { getExplorerStore, useExplorerStore } from '~/hooks/useExplorerStore';
|
||||
import OptionsPanel from '../app/$libraryId/Explorer/OptionsPanel';
|
||||
import { KeyManager } from '../app/$libraryId/KeyManager';
|
||||
|
||||
export type RoutePaths =
|
||||
| 'overview'
|
||||
| 'people'
|
||||
| 'media'
|
||||
| 'spaces'
|
||||
| 'debug'
|
||||
| 'spacedrop'
|
||||
| 'sync'
|
||||
| 'location'
|
||||
| 'tag'
|
||||
| 'settings';
|
||||
|
||||
export type groupKeys = 'groupOne' | 'groupTwo' | 'groupThree' | 'groupFour' | 'groupFive';
|
||||
|
||||
export interface ToolOptions {
|
||||
options: {
|
||||
[key in groupKeys]?: {
|
||||
icon: JSX.Element;
|
||||
onClick?: () => void;
|
||||
toolTipLabel: string;
|
||||
topBarActive?: boolean;
|
||||
popOverComponent?: JSX.Element;
|
||||
}[];
|
||||
}[];
|
||||
}
|
||||
|
||||
const TOP_BAR_ICON_STYLE = 'm-0.5 w-5 h-5 text-ink-dull';
|
||||
|
||||
export const useToolBarRouteOptions = () => {
|
||||
const store = useExplorerStore();
|
||||
|
||||
const toolBarRouteOptions: Record<RoutePaths, ToolOptions> = {
|
||||
overview: {
|
||||
options: [{}]
|
||||
},
|
||||
location: {
|
||||
options: [
|
||||
{
|
||||
groupOne: [
|
||||
{
|
||||
toolTipLabel: 'Grid view',
|
||||
icon: <SquaresFour className={TOP_BAR_ICON_STYLE} />,
|
||||
topBarActive: store.layoutMode === 'grid',
|
||||
onClick: () => (getExplorerStore().layoutMode = 'grid')
|
||||
},
|
||||
{
|
||||
toolTipLabel: 'List view',
|
||||
icon: <Rows className={TOP_BAR_ICON_STYLE} />,
|
||||
topBarActive: store.layoutMode === 'rows',
|
||||
onClick: () => (getExplorerStore().layoutMode = 'rows')
|
||||
},
|
||||
{
|
||||
toolTipLabel: 'Columns view',
|
||||
icon: <Columns className={TOP_BAR_ICON_STYLE} />,
|
||||
topBarActive: store.layoutMode === 'columns',
|
||||
onClick: () => (getExplorerStore().layoutMode = 'columns')
|
||||
},
|
||||
{
|
||||
toolTipLabel: 'Media view',
|
||||
icon: <MonitorPlay className={TOP_BAR_ICON_STYLE} />,
|
||||
topBarActive: store.layoutMode === 'media'
|
||||
}
|
||||
],
|
||||
groupTwo: [
|
||||
{
|
||||
toolTipLabel: 'Key Manager',
|
||||
icon: <Key className={TOP_BAR_ICON_STYLE} />,
|
||||
popOverComponent: <KeyManager />
|
||||
},
|
||||
{
|
||||
toolTipLabel: 'Tag Assign Mode',
|
||||
icon: (
|
||||
<Tag
|
||||
weight={store.tagAssignMode ? 'fill' : 'regular'}
|
||||
className={TOP_BAR_ICON_STYLE}
|
||||
/>
|
||||
),
|
||||
onClick: () => (getExplorerStore().tagAssignMode = !store.tagAssignMode),
|
||||
topBarActive: store.tagAssignMode
|
||||
},
|
||||
{
|
||||
toolTipLabel: 'Regenerate thumbs (temp)',
|
||||
icon: <ArrowClockwise className={TOP_BAR_ICON_STYLE} />
|
||||
}
|
||||
],
|
||||
groupThree: [
|
||||
{
|
||||
toolTipLabel: 'Explorer display',
|
||||
icon: <SlidersHorizontal className={TOP_BAR_ICON_STYLE} />,
|
||||
popOverComponent: <OptionsPanel />
|
||||
},
|
||||
{
|
||||
toolTipLabel: 'Show Inspector',
|
||||
onClick: () => (getExplorerStore().showInspector = !store.showInspector),
|
||||
icon: (
|
||||
<SidebarSimple
|
||||
weight={store.showInspector ? 'fill' : 'regular'}
|
||||
className={clsx(TOP_BAR_ICON_STYLE, 'scale-x-[-1]')}
|
||||
/>
|
||||
)
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
people: {
|
||||
options: [{}]
|
||||
},
|
||||
media: {
|
||||
options: [{}]
|
||||
},
|
||||
spaces: {
|
||||
options: [{}]
|
||||
},
|
||||
debug: {
|
||||
options: [{}]
|
||||
},
|
||||
settings: {
|
||||
options: [{}]
|
||||
},
|
||||
spacedrop: {
|
||||
options: [{}]
|
||||
},
|
||||
tag: {
|
||||
options: [{}]
|
||||
},
|
||||
sync: {
|
||||
options: [{}]
|
||||
}
|
||||
};
|
||||
|
||||
return { toolBarRouteOptions };
|
||||
};
|
||||
@@ -43,6 +43,7 @@
|
||||
"clsx": "^1.2.1",
|
||||
"crypto-random-string": "^5.0.0",
|
||||
"dayjs": "^1.11.5",
|
||||
"framer-motion": "^10.11.5",
|
||||
"phosphor-react": "^1.4.1",
|
||||
"react": "^18.2.0",
|
||||
"react-colorful": "^5.6.1",
|
||||
|
||||
@@ -2,4 +2,4 @@ import { tw } from './utils';
|
||||
|
||||
export const CategoryHeading = tw.h3`text-xs font-semibold text-ink-dull`;
|
||||
|
||||
export const ScreenHeading = tw.h3`ml-1 text-xl font-medium`;
|
||||
export const ScreenHeading = tw.h3`text-2xl font-bold`;
|
||||
|
||||
BIN
pnpm-lock.yaml
generated
BIN
pnpm-lock.yaml
generated
Binary file not shown.
Reference in New Issue
Block a user