Files
spacedrive/packages/ui/src/DropdownMenu.tsx
Jamie Pine ce984d44ee [ENG-1269] Search options (#1561)
* search options start

* small progress

* more

* bunch of stuff

* semi functioning filters

* cleanup setup api

* progress

* remove filters

* hooked up to query epic moment

* fix

* move db stuff to specific modules

* in/notIn for some fields

* generate ts

* big gains

* working filter options for locations, tags and kind

* working search query

* perfect fixed filters

* saved searches lol

* merge error

* saved searches via api

* better routing

* [ENG-1338] Fix fresh Spacedrive install failing to start due to attempting to query a nonexistent Library (#1649)

Fix Spacedrive failing to start due to attempting to query a nonexistent Library
 - Rename useShoudRedirect to useRedirectToNewLocations
 - Improve behaviour for the immedite redirection after adding a new location

* Show hidden files false by default (#1652)

bool

* fix remove filter in list

* tweaks

* fix nav buttons

* unify MediaData search handling

* cleanup saved search writing

* Add left top bar portals for tags and search + fixed media view on tags

* added search to filter dropdown

* render cycle improvements

* hotfix

* wip

* Refactor with Brendan, but this is a WIP and the search query no longer works

Co-authored-by: Brendan Allan <Brendonovich@users.noreply.github.com>

* progress

* fix location/$id page

* fix tags too

Co-authored-by: Brendan Allan <Brendonovich@users.noreply.github.com>

* 3rd refactor lol

epic style

* half-done with enum-ification of SearchFilterArgs

* broken fixed filters but working inNotIn filters

* search name + extension kinda working

* hidden filter

* fixed filters working??

* deferred search value

* extensions works

* filtered search items mostly working

* tweaks

* stacked approach working for non-search filters

* move to Explorer/Search

* actually use filterArgs in queries

things actually work properly now

* added new icons from Mint

* goof

* cleanup types, filters and mutation logic

* actually use search value

* remove overview from sidebar

* don't shrink LibrariesDropdown ga

* remove overview from sidebar and default to /network

---------

Co-authored-by: Brendan Allan <brendonovich@outlook.com>
Co-authored-by: Vítor Vasconcellos <vasconcellos.dev@gmail.com>
Co-authored-by: Brendan Allan <Brendonovich@users.noreply.github.com>
2023-11-17 06:58:44 +00:00

171 lines
3.7 KiB
TypeScript

'use client';
import * as RadixDM from '@radix-ui/react-dropdown-menu';
import clsx from 'clsx';
import React, {
ContextType,
createContext,
PropsWithChildren,
ReactNode,
Suspense,
useCallback,
useContext,
useRef,
useState
} from 'react';
import { Link } from 'react-router-dom';
import {
contextMenuClassNames,
ContextMenuDivItem,
contextMenuItemClassNames,
ContextMenuItemProps,
contextMenuSeparatorClassNames
} from './ContextMenu';
interface DropdownMenuProps
extends RadixDM.MenuContentProps,
Pick<RadixDM.DropdownMenuProps, 'onOpenChange'> {
trigger: React.ReactNode;
triggerClassName?: string;
alignToTrigger?: boolean;
}
const DropdownMenuContext = createContext<boolean | null>(null);
export const useDropdownMenuContext = <T extends boolean>({ suspense }: { suspense?: T } = {}) => {
const ctx = useContext(DropdownMenuContext);
if (suspense && ctx === null) throw new Error('DropdownMenuContext.Provider not found!');
return ctx as T extends true
? NonNullable<ContextType<typeof DropdownMenuContext>>
: NonNullable<ContextType<typeof DropdownMenuContext>> | undefined;
};
const Root = (props: PropsWithChildren<DropdownMenuProps>) => {
const {
alignToTrigger,
onOpenChange,
trigger,
triggerClassName,
asChild = true,
className,
children,
...contentProps
} = props;
const [width, setWidth] = useState<number>();
const measureRef = useCallback(
(ref: HTMLButtonElement | null) => {
alignToTrigger && ref && setWidth(ref.getBoundingClientRect().width);
},
[alignToTrigger]
);
return (
<RadixDM.Root onOpenChange={onOpenChange}>
<RadixDM.Trigger ref={measureRef} className={triggerClassName} asChild={asChild}>
{trigger}
</RadixDM.Trigger>
<RadixDM.Portal>
<Suspense fallback={null}>
<RadixDM.Content
className={clsx(contextMenuClassNames, width && '!min-w-0', className)}
align="start"
style={{ width }}
{...contentProps}
>
<DropdownMenuContext.Provider value={true}>
{children}
</DropdownMenuContext.Provider>
</RadixDM.Content>
</Suspense>
</RadixDM.Portal>
</RadixDM.Root>
);
};
const Separator = (props: { className?: string }) => (
<RadixDM.Separator className={clsx(contextMenuSeparatorClassNames, props.className)} />
);
const SubMenu = ({
label,
icon,
iconProps,
keybind,
variant,
className,
...props
}: RadixDM.MenuSubContentProps & ContextMenuItemProps & { trigger?: ReactNode }) => {
return (
<RadixDM.Sub>
<RadixDM.SubTrigger className={contextMenuItemClassNames}>
{props.trigger || (
<ContextMenuDivItem
rightArrow
{...{ label, icon, iconProps, keybind, variant }}
/>
)}
</RadixDM.SubTrigger>
<RadixDM.Portal>
<Suspense fallback={null}>
<RadixDM.SubContent
className={clsx(contextMenuClassNames, className)}
{...props}
/>
</Suspense>
</RadixDM.Portal>
</RadixDM.Sub>
);
};
interface DropdownItemProps extends ContextMenuItemProps {
to?: string;
selected?: boolean;
}
const Item = ({
icon,
iconProps,
label,
children,
keybind,
variant,
className,
selected,
to,
...props
}: DropdownItemProps & RadixDM.MenuItemProps) => {
const ref = useRef<HTMLDivElement>(null);
const renderInner = (
// to style this, pass in variant
<ContextMenuDivItem
// className={clsx(selected && 'bg-accent text-white')}
{...{ icon, iconProps, label, keybind, variant, children }}
/>
);
return (
<RadixDM.Item ref={ref} className={clsx(contextMenuItemClassNames, className)} {...props}>
{to ? (
<Link to={to} onClick={() => ref.current?.click()}>
{renderInner}
</Link>
) : (
renderInner
)}
</RadixDM.Item>
);
};
export const DropdownMenu = {
Root,
Item,
Separator,
SubMenu
};