mirror of
https://github.com/spacedriveapp/spacedrive.git
synced 2026-10-23 01:24:36 -04:00
[ENG-1537] Search UI improvements (#1940)
* wip * Improve search ui * Update index.tsx * Update Filters.tsx
This commit is contained in:
1 parent
b7872421f4
commit
df887cd057
4 files changed
+72
-43
No files matched your search
@@ -1,10 +1,11 @@
|
||||
import { OperatingSystem, Platform } from '@sd/interface';
|
||||
import { dialog, invoke, os, shell } from '@tauri-apps/api';
|
||||
import { confirm } from '@tauri-apps/api/dialog';
|
||||
import { homeDir } from '@tauri-apps/api/path';
|
||||
import { open } from '@tauri-apps/api/shell';
|
||||
|
||||
// @ts-expect-error: Doesn't have a types package.
|
||||
import ConsistentHash from 'consistent-hash';
|
||||
import { OperatingSystem, Platform } from '@sd/interface';
|
||||
|
||||
import { commands, events } from './commands';
|
||||
import { env } from './env';
|
||||
@@ -35,7 +36,7 @@ function constructServerUrl(urlSuffix: string) {
|
||||
if (!customUriServerUrl)
|
||||
throw new Error("'window.__SD_CUSTOM_URI_SERVER__' was not injected correctly!");
|
||||
|
||||
console.log(ConsistentHash);
|
||||
|
||||
hr = new ConsistentHash();
|
||||
customUriServerUrl.forEach((url) => hr.add(url));
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@ import {
|
||||
Icon,
|
||||
Key,
|
||||
MonitorPlay,
|
||||
Planet,
|
||||
Rows,
|
||||
SidebarSimple,
|
||||
SlidersHorizontal,
|
||||
@@ -50,7 +49,7 @@ export const useExplorerTopBarOptions = () => {
|
||||
|
||||
const option = {
|
||||
layout,
|
||||
toolTipLabel: t(`${layout}_view`),
|
||||
toolTipLabel: t(`${layout} View`),
|
||||
icon: <Icon className={TOP_BAR_ICON_STYLE} />,
|
||||
keybinds: [controlIcon, (i + 1).toString()],
|
||||
topBarActive:
|
||||
|
||||
@@ -10,6 +10,7 @@ import {
|
||||
useNodes
|
||||
} from '@sd/client';
|
||||
import { Button, Input } from '@sd/ui';
|
||||
import { Icon as SDIcon } from '~/components';
|
||||
|
||||
import { SearchOptionItem, SearchOptionSubMenu } from '.';
|
||||
import { AllKeys, FilterOption, getKey } from './store';
|
||||
@@ -91,11 +92,13 @@ export function useToggleOptionSelected({ search }: { search: UseSearch }) {
|
||||
const FilterOptionList = ({
|
||||
filter,
|
||||
options,
|
||||
search
|
||||
search,
|
||||
empty
|
||||
}: {
|
||||
filter: SearchFilterCRUD;
|
||||
options: FilterOption[];
|
||||
search: UseSearch;
|
||||
empty?: () => JSX.Element;
|
||||
}) => {
|
||||
const { allFiltersKeys } = search;
|
||||
|
||||
@@ -103,29 +106,31 @@ const FilterOptionList = ({
|
||||
|
||||
return (
|
||||
<SearchOptionSubMenu name={filter.name} icon={filter.icon}>
|
||||
{options?.map((option) => {
|
||||
const optionKey = getKey({
|
||||
...option,
|
||||
type: filter.name
|
||||
});
|
||||
{empty?.() && options.length === 0
|
||||
? empty()
|
||||
: options?.map((option) => {
|
||||
const optionKey = getKey({
|
||||
...option,
|
||||
type: filter.name
|
||||
});
|
||||
|
||||
return (
|
||||
<SearchOptionItem
|
||||
selected={allFiltersKeys.has(optionKey)}
|
||||
setSelected={(value) => {
|
||||
toggleOptionSelected({
|
||||
filter,
|
||||
option,
|
||||
select: value
|
||||
});
|
||||
}}
|
||||
key={option.value}
|
||||
icon={option.icon}
|
||||
>
|
||||
{option.name}
|
||||
</SearchOptionItem>
|
||||
);
|
||||
})}
|
||||
return (
|
||||
<SearchOptionItem
|
||||
selected={allFiltersKeys.has(optionKey)}
|
||||
setSelected={(value) => {
|
||||
toggleOptionSelected({
|
||||
filter,
|
||||
option,
|
||||
select: value
|
||||
});
|
||||
}}
|
||||
key={option.value}
|
||||
icon={option.icon}
|
||||
>
|
||||
{option.name}
|
||||
</SearchOptionItem>
|
||||
);
|
||||
})}
|
||||
</SearchOptionSubMenu>
|
||||
);
|
||||
};
|
||||
@@ -462,19 +467,32 @@ export const filterRegistry = [
|
||||
.filter(Boolean) as any;
|
||||
},
|
||||
useOptions: () => {
|
||||
const query = useLibraryQuery(['tags.list'], { keepPreviousData: true });
|
||||
const query = useLibraryQuery(['tags.list']);
|
||||
useNodes(query.data?.nodes);
|
||||
const tags = useCache(query.data?.items);
|
||||
|
||||
return (tags ?? []).map((tag) => ({
|
||||
name: tag.name!,
|
||||
value: tag.id,
|
||||
icon: tag.color || 'CircleDashed'
|
||||
}));
|
||||
},
|
||||
Render: ({ filter, options, search }) => (
|
||||
<FilterOptionList filter={filter} options={options} search={search} />
|
||||
)
|
||||
Render: ({ filter, options, search }) => {
|
||||
return (
|
||||
<FilterOptionList
|
||||
empty={() => (
|
||||
<div className="flex flex-col items-center justify-center gap-2 p-2">
|
||||
<SDIcon name="Tags" size={32} />
|
||||
<p className="w-[80%] text-center text-xs text-ink-dull">
|
||||
You have not created any tags
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
filter={filter}
|
||||
options={options}
|
||||
search={search}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}),
|
||||
createInOrNotInFilter({
|
||||
name: 'Kind',
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { CaretRight, FunnelSimple, Icon, Plus } from '@phosphor-icons/react';
|
||||
import { FunnelSimple, Icon, Plus } from '@phosphor-icons/react';
|
||||
import { IconTypes } from '@sd/assets/util';
|
||||
import clsx from 'clsx';
|
||||
import { memo, PropsWithChildren, useDeferredValue, useState } from 'react';
|
||||
@@ -27,8 +27,8 @@ import {
|
||||
import { UseSearch } from './useSearch';
|
||||
import { RenderIcon } from './util';
|
||||
|
||||
export * from './useSearch';
|
||||
export * from './context';
|
||||
export * from './useSearch';
|
||||
|
||||
// const Label = tw.span`text-ink-dull mr-2 text-xs`;
|
||||
export const OptionContainer = tw.div`flex flex-row items-center`;
|
||||
@@ -44,9 +44,9 @@ const MENU_STYLES = `!rounded-md border !border-app-line !bg-app-box`;
|
||||
const SearchOptionItemInternals = (props: SearchOptionItemProps) => {
|
||||
return (
|
||||
<div className="flex w-full items-center justify-between gap-1.5">
|
||||
<div className="flex items-center gap-1.5">
|
||||
<div className="flex w-[165px] items-center gap-1.5 overflow-hidden">
|
||||
<RenderIcon icon={props.icon} />
|
||||
<span className="w-fit max-w-[150px] truncate">{props.children}</span>
|
||||
<span className="truncate">{props.children}</span>
|
||||
</div>
|
||||
{props.selected !== undefined && <RadixCheckbox checked={props.selected} />}
|
||||
</div>
|
||||
@@ -159,12 +159,20 @@ const SearchResults = memo(
|
||||
}
|
||||
key={option.key}
|
||||
>
|
||||
<div className="mr-4 flex flex-row items-center gap-1.5">
|
||||
<RenderIcon icon={filter.icon} />
|
||||
<span className="text-ink-dull">{filter.name}</span>
|
||||
<CaretRight weight="bold" className="text-ink-dull/70" />
|
||||
<RenderIcon icon={option.icon} />
|
||||
{option.name}
|
||||
<div className="mr-4 flex flex-row items-center gap-2.5">
|
||||
<div className="flex items-center gap-1">
|
||||
<RenderIcon
|
||||
className="h-[13px] w-[13px] opacity-80"
|
||||
icon={filter.icon}
|
||||
/>
|
||||
<span className="text-xs text-ink-dull opacity-80">
|
||||
{filter.name}
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-1 overflow-hidden">
|
||||
<RenderIcon icon={option.icon} />
|
||||
<span className="truncate">{option.name}</span>
|
||||
</div>
|
||||
</div>
|
||||
</SearchOptionItem>
|
||||
);
|
||||
@@ -195,7 +203,10 @@ function AddFilterButton() {
|
||||
<OptionContainer className="shrink-0">
|
||||
<DropdownMenu.Root
|
||||
onKeyDown={(e) => e.stopPropagation()}
|
||||
className={MENU_STYLES}
|
||||
className={clsx(
|
||||
MENU_STYLES,
|
||||
'explorer-scroll max-h-[80vh] min-h-[100px] min-w-[200px] max-w-fit'
|
||||
)}
|
||||
trigger={
|
||||
<Button className="flex flex-row gap-1" size="xs" variant="dotted">
|
||||
<FunnelSimple />
|
||||
|
||||
Reference in new issue
Block a user