Files
spacedrive/interface/app/$libraryId/KeyManager/Key.tsx
Brendan Allan 9c27f5d2cf fix landing deploys (#816)
* fix react-spring issue

* tsconfck

* revert tsconfck

* use href not onClick
2023-05-13 09:22:03 +00:00

252 lines
6.8 KiB
TypeScript

import * as DropdownMenu from '@radix-ui/react-dropdown-menu';
import { animated, useTransition } from '@react-spring/web';
import clsx from 'clsx';
import { DotsThree, Eye, Key as KeyIcon } from 'phosphor-react';
import { PropsWithChildren, useState } from 'react';
import { useLibraryMutation } from '@sd/client';
import { Button, Tooltip } from '@sd/ui';
// TODO: Replace this with Prisma type when integrating with backend
export interface Key {
id: string;
name: string;
queue: Set<string>;
mounted?: boolean;
locked?: boolean;
stats?: {
objectCount?: number;
containerCount?: number;
};
default?: boolean;
memoryOnly?: boolean;
automount?: boolean;
// Nodes this key is mounted on
nodes?: string[]; // will be node object
}
interface Props extends DropdownMenu.MenuContentProps {
trigger: React.ReactNode;
transformOrigin?: string;
disabled?: boolean;
}
export const KeyDropdown = ({
trigger,
children,
transformOrigin,
className
}: PropsWithChildren<Props>) => {
const [open, setOpen] = useState(false);
const transitions = useTransition(open, {
from: {
opacity: 0,
transform: `scale(0.9)`,
transformOrigin: transformOrigin || 'top'
},
enter: { opacity: 1, transform: 'scale(1)' },
leave: { opacity: -0.5, transform: 'scale(0.95)' },
config: { mass: 0.4, tension: 200, friction: 10 }
});
return (
<DropdownMenu.Root open={open} onOpenChange={setOpen}>
<DropdownMenu.Trigger>{trigger}</DropdownMenu.Trigger>
{transitions(
(styles, show) =>
show && (
<DropdownMenu.Portal forceMount>
<DropdownMenu.Content forceMount asChild>
<animated.div
// most of this is copied over from the `OverlayPanel`
className={clsx(
'flex flex-col',
'z-50 m-2 space-y-1 px-4 py-2',
'cursor-default select-none rounded-lg',
'text-left text-sm text-ink',
'bg-app-overlay/80 backdrop-blur',
// 'border border-app-overlay',
'shadow-2xl shadow-black/60 ',
className
)}
style={styles}
>
{children}
</animated.div>
</DropdownMenu.Content>
</DropdownMenu.Portal>
)
)}
</DropdownMenu.Root>
);
};
export const Key = ({ data }: { data: Key }) => {
const mountKey = useLibraryMutation('keys.mount');
const unmountKey = useLibraryMutation('keys.unmount');
const deleteKey = useLibraryMutation('keys.deleteFromLibrary');
const setDefaultKey = useLibraryMutation('keys.setDefault');
const changeAutomountStatus = useLibraryMutation('keys.updateAutomountStatus');
const syncToLibrary = useLibraryMutation('keys.syncKeyToLibrary');
if (data.mounted && data.queue.has(data.id)) {
data.queue.delete(data.id);
}
return (
<div
className={clsx(
'flex items-center justify-between rounded-lg bg-app-box px-2 py-1.5 text-sm shadow-lg shadow-app-shade/10'
)}
>
<div className="flex items-center">
<KeyIcon
className={clsx(
'ml-1 mr-3 h-5 w-5',
data.mounted
? data.locked
? 'text-accent'
: 'text-accent'
: 'text-gray-400/80'
)}
/>
<div className="flex flex-col ">
<div className="flex flex-row items-center">
<div className="font-semibold">{data.name}</div>
{data.mounted && (
<div className="ml-2 inline rounded bg-gray-500 px-1 text-[8pt] font-medium text-gray-300">
{data.nodes?.length || 0 > 0
? `${data.nodes?.length || 0} nodes`
: 'This node'}
</div>
)}
{data.default && (
<div className="ml-2 inline rounded bg-gray-500 px-1 text-[8pt] font-medium text-gray-300">
Default
</div>
)}
</div>
{/* <div className="text-xs text-gray-300 opacity-30">#{data.id}</div> */}
{data.stats ? (
<div className="mt-[1px] flex flex-row space-x-3">
{data.stats.objectCount && (
<div className="text-[8pt] font-medium text-ink-dull opacity-30">
{data.stats.objectCount} Objects
</div>
)}
{data.stats.containerCount && (
<div className="text-[8pt] font-medium text-ink-dull opacity-30">
{data.stats.containerCount} Containers
</div>
)}
</div>
) : (
!data.mounted && (
<div className="text-[8pt] font-medium text-ink-dull opacity-30">
{data.queue.has(data.id) ? 'Key mounting...' : 'Key not mounted'}
</div>
)
)}
</div>
</div>
<div className="space-x-1">
{data.mounted && (
<Tooltip label="Browse files">
<Button size="icon">
<Eye className="h-4 w-4 text-ink-faint" />
</Button>
</Tooltip>
)}
<KeyDropdown
trigger={
<Button size="icon">
<DotsThree className="h-4 w-4 text-ink-faint" />
</Button>
}
>
<KeyDropdownItem
onClick={() => {
unmountKey.mutate(data.id);
}}
hidden={!data.mounted}
value="Unmount"
/>
<KeyDropdownItem
onClick={() => {
syncToLibrary.mutate(data.id);
}}
hidden={!data.memoryOnly}
value="Sync to library"
/>
<KeyDropdownItem
onClick={() => {
data.queue.add(data.id);
mountKey.mutate(data.id);
}}
hidden={data.mounted || data.queue.has(data.id)}
value="Mount"
/>
<KeyDropdownItem
onClick={() => {
deleteKey.mutate(data.id);
}}
value="Delete from Library"
/>
<KeyDropdownItem
onClick={() => {
setDefaultKey.mutate(data.id);
}}
hidden={data.default}
value="Set as Default"
/>
<KeyDropdownItem
onClick={() => {
changeAutomountStatus.mutate({ uuid: data.id, status: false });
}}
hidden={!data.automount || data.memoryOnly}
value="Disable Automount"
/>
<KeyDropdownItem
onClick={() => {
changeAutomountStatus.mutate({ uuid: data.id, status: true });
}}
hidden={data.automount || data.memoryOnly}
value="Enable Automount"
/>
</KeyDropdown>
</div>
</div>
);
};
export const KeyDropdownItem = (props: {
value: string;
hidden?: boolean | undefined;
onClick: () => void;
}) => {
return (
<DropdownMenu.DropdownMenuItem
className="!cursor-default select-none py-0.5 text-menu-ink focus:outline-none active:opacity-80"
onClick={props.onClick}
hidden={props.hidden}
>
{props.value}
</DropdownMenu.DropdownMenuItem>
);
};
export const DummyKey = (props: { text: string }) => {
return (
<div className="flex items-center justify-between rounded-lg bg-app-box p-2 py-1.5 text-sm shadow-lg shadow-app-shade/10">
<div className="flex items-center">
<KeyIcon className="ml-1 mr-3 h-5 w-5 text-gray-400/80" />
<div className="flex flex-col ">
<div className="flex flex-row items-center">
<div className="font-medium">{props.text}</div>
</div>
</div>
</div>
</div>
);
};