mirror of
https://github.com/spacedriveapp/spacedrive
synced 2024-07-18 15:29:11 +00:00
UI refactor (#418)
button refactor Co-authored-by: Jamie Pine <32987599+jamiepine@users.noreply.github.com>
This commit is contained in:
parent
cf39b6fcc9
commit
21e156c517
|
@ -9,9 +9,11 @@ import { Discord, Github } from '@icons-pack/react-simple-icons';
|
|||
import AppLogo from '@sd/assets/images/logo.png';
|
||||
import { Dropdown, DropdownItem } from '@sd/ui';
|
||||
import clsx from 'clsx';
|
||||
import { DotsThreeVertical, List } from 'phosphor-react';
|
||||
|
||||
import { DotsThreeVertical } from 'phosphor-react';
|
||||
import { PropsWithChildren, useEffect, useState } from 'react';
|
||||
|
||||
|
||||
import { positions } from '../pages/careers.page';
|
||||
import { getWindow } from '../utils';
|
||||
|
||||
|
|
|
@ -72,12 +72,12 @@ export const Inspector = (props: Props) => {
|
|||
<FavoriteButton data={objectData} />
|
||||
</Tooltip>
|
||||
<Tooltip label="Share">
|
||||
<Button size="sm" noPadding>
|
||||
<Button size="sm" padding="sm">
|
||||
<ShareIcon className="w-[18px] h-[18px]" />
|
||||
</Button>
|
||||
</Tooltip>
|
||||
<Tooltip label="Link">
|
||||
<Button size="sm" noPadding>
|
||||
<Button size="sm" padding="sm">
|
||||
<Link className="w-[18px] h-[18px]" />
|
||||
</Button>
|
||||
</Tooltip>
|
||||
|
|
|
@ -30,7 +30,7 @@ export default function FavoriteButton(props: Props) {
|
|||
};
|
||||
|
||||
return (
|
||||
<Button onClick={toggleFavorite} size="sm" noPadding>
|
||||
<Button onClick={toggleFavorite} size="sm" padding="sm">
|
||||
<Heart weight={favorite ? 'fill' : 'regular'} className="w-[18px] h-[18px]" />
|
||||
</Button>
|
||||
);
|
||||
|
|
|
@ -48,7 +48,7 @@ export function JobsManager() {
|
|||
{/* <div className="z-10 flex flex-row w-full h-10 bg-gray-500 border-b border-gray-700 bg-opacity-30"></div> */}
|
||||
<div className="h-full mr-1 overflow-x-hidden custom-scroll inspector-scroll">
|
||||
<div className="py-1 pl-2">
|
||||
<div className="fixed flex items-center h-10 ">
|
||||
<div className="fixed flex items-center h-10">
|
||||
<h3 className="mt-1.5 ml-2 text-md font-medium opacity-40">Recent Jobs</h3>
|
||||
</div>
|
||||
<div className="h-10"></div>
|
||||
|
@ -64,31 +64,28 @@ export function JobsManager() {
|
|||
<Tooltip label={job.status}>
|
||||
<niceData.icon className={clsx('w-5 mr-3', color)} />
|
||||
</Tooltip>
|
||||
<div className="flex flex-col">
|
||||
<span className="flex mt-0.5 items-center font-semibold">{niceData.name}</span>
|
||||
<div className="flex items-center">
|
||||
<span className="text-xs opacity-60">
|
||||
<div className="flex flex-1 flex-col">
|
||||
<span className="mt-0.5 font-semibold">{niceData.name}</span>
|
||||
<div className="flex items-center opacity-60">
|
||||
<span className="text-xs">
|
||||
{job.status === 'Failed' ? 'Failed after' : 'Took'}{' '}
|
||||
{job.seconds_elapsed
|
||||
? dayjs.duration({ seconds: job.seconds_elapsed }).humanize()
|
||||
: 'less than a second'}
|
||||
</span>
|
||||
<span className="mx-1 opacity-30">•</span>
|
||||
<span className="text-xs opacity-60">
|
||||
{dayjs(job.date_created).toNow(true)} ago
|
||||
</span>
|
||||
<span className="mx-1 opacity-50">•</span>
|
||||
<span className="text-xs">{dayjs(job.date_created).toNow(true)} ago</span>
|
||||
</div>
|
||||
<span className="text-xs opacity-60">{job.data}</span>
|
||||
<span className="text-xs">{job.data}</span>
|
||||
</div>
|
||||
<div className="flex-grow" />
|
||||
<div className="flex space-x-2">
|
||||
<div className="space-x-2">
|
||||
{job.status === 'Failed' && (
|
||||
<Button className="!p-0 w-7 h-7 flex items-center" variant="gray">
|
||||
<ArrowsClockwise className="w-4" />
|
||||
<Button padding="thin" variant="gray">
|
||||
<ArrowsClockwise className="w-4 h-4" />
|
||||
</Button>
|
||||
)}
|
||||
<Button className="!p-0 w-7 h-7 flex items-center" variant="gray">
|
||||
<XMarkIcon className="w-4" />
|
||||
<Button padding="thin" variant="gray">
|
||||
<XMarkIcon className="w-4 h-4" />
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -11,10 +11,10 @@ export function KeyManager(props: KeyManagerProps) {
|
|||
<div>
|
||||
<Tabs.Root defaultValue="mount">
|
||||
<Tabs.List>
|
||||
<Tabs.Trigger className="text-sm font-medium text-gray-300" value="mount">
|
||||
<Tabs.Trigger className="text-sm font-medium " value="mount">
|
||||
Mount
|
||||
</Tabs.Trigger>
|
||||
<Tabs.Trigger className="text-sm font-medium text-gray-300" value="keys">
|
||||
<Tabs.Trigger className="text-sm font-medium " value="keys">
|
||||
Keys
|
||||
</Tabs.Trigger>
|
||||
</Tabs.List>
|
||||
|
|
|
@ -168,7 +168,7 @@ export function Sidebar() {
|
|||
|
||||
<Dropdown
|
||||
buttonProps={{
|
||||
justifyLeft: true,
|
||||
justify: 'left',
|
||||
className: clsx(
|
||||
`flex w-full text-left max-w-full mb-1 mt-1 -mr-0.5 shadow-xs rounded !bg-gray-50 border-gray-150 hover:!bg-gray-1000 dark:!bg-gray-500 dark:hover:!bg-gray-500 dark:!border-gray-550 dark:hover:!border-gray-500`,
|
||||
macOnly(
|
||||
|
@ -233,14 +233,10 @@ export function Sidebar() {
|
|||
|
||||
{library && <RunningJobsWidget />}
|
||||
|
||||
<div className="mt-2 mb-2">
|
||||
<div className="mt-2 mb-3">
|
||||
<NavLink to="/settings/general">
|
||||
{({ isActive }) => (
|
||||
<Button
|
||||
noPadding
|
||||
variant={'default'}
|
||||
className={clsx('px-[4px] hover:!bg-opacity-20 mb-1')}
|
||||
>
|
||||
<Button padding="sm" variant="default" className={clsx('hover:!bg-opacity-20')}>
|
||||
<CogIcon className="w-5 h-5" />
|
||||
</Button>
|
||||
)}
|
||||
|
@ -251,9 +247,9 @@ export function Sidebar() {
|
|||
disabled={!library}
|
||||
trigger={
|
||||
<Button
|
||||
noPadding
|
||||
padding="sm"
|
||||
className={clsx(
|
||||
'px-[4px] !outline-none hover:!bg-opacity-20 disabled:opacity-50 disabled:cursor-not-allowed'
|
||||
'!outline-none hover:!bg-opacity-20 disabled:opacity-50 disabled:cursor-not-allowed'
|
||||
)}
|
||||
>
|
||||
<CheckCircle className="w-5 h-5" />
|
||||
|
|
|
@ -14,7 +14,7 @@ import { SettingsHeader } from '../../../components/settings/SettingsHeader';
|
|||
function LibraryListItem(props: { library: LibraryConfigWrapped }) {
|
||||
const [openDeleteModal, setOpenDeleteModal] = useState(false);
|
||||
|
||||
const { mutate: deleteLib, isLoading: libDeletePending } = useBridgeMutation('library.delete', {
|
||||
const deleteLibrary = useBridgeMutation('library.delete', {
|
||||
onSuccess: () => {
|
||||
setOpenDeleteModal(false);
|
||||
}
|
||||
|
@ -23,16 +23,16 @@ function LibraryListItem(props: { library: LibraryConfigWrapped }) {
|
|||
return (
|
||||
<Card>
|
||||
<DotsSixVertical weight="bold" className="mt-[15px] mr-3 opacity-30" />
|
||||
<div className="flex-grow my-0.5">
|
||||
<div className="flex-1 my-0.5">
|
||||
<h3 className="font-semibold">{props.library.config.name}</h3>
|
||||
<p className="mt-0.5 text-xs text-gray-200">{props.library.uuid}</p>
|
||||
</div>
|
||||
<div className="mt-2 space-x-2">
|
||||
<ButtonLink to="/settings/library" variant="gray" className="!p-1.5">
|
||||
<div className="flex flex-row space-x-2 items-center">
|
||||
<ButtonLink to="/settings/library" variant="gray" padding="sm">
|
||||
<PencilIcon className="w-4 h-4" />
|
||||
</ButtonLink>
|
||||
<DeleteLibraryDialog libraryUuid={props.library.uuid}>
|
||||
<Button variant="gray" className="!p-1.5">
|
||||
<Button variant="gray" padding="sm">
|
||||
<TrashIcon className="w-4 h-4" />
|
||||
</Button>
|
||||
</DeleteLibraryDialog>
|
||||
|
|
|
@ -1,95 +1,10 @@
|
|||
import { VariantProps, cva } from 'class-variance-authority';
|
||||
import clsx from 'clsx';
|
||||
import { forwardRef } from 'react';
|
||||
import { Link, LinkProps } from 'react-router-dom';
|
||||
|
||||
const sizes = {
|
||||
default: 'py-1 px-3 text-md font-medium',
|
||||
sm: 'py-1 px-2 text-xs font-medium'
|
||||
};
|
||||
|
||||
const variants = {
|
||||
default: `
|
||||
bg-gray-50
|
||||
shadow-sm
|
||||
hover:bg-gray-100
|
||||
active:bg-gray-50
|
||||
dark:bg-transparent
|
||||
dark:active:bg-gray-600
|
||||
dark:hover:bg-gray-550
|
||||
dark:active:opacity-80
|
||||
|
||||
border-gray-100
|
||||
hover:border-gray-200
|
||||
active:border-gray-200
|
||||
dark:border-transparent
|
||||
dark:active:border-gray-600
|
||||
dark:hover:border-gray-500
|
||||
|
||||
text-gray-700
|
||||
hover:text-gray-900
|
||||
active:text-gray-600
|
||||
dark:text-gray-200
|
||||
dark:active:text-white
|
||||
dark:hover:text-white
|
||||
`,
|
||||
gray: `
|
||||
bg-gray-100
|
||||
shadow-sm
|
||||
hover:bg-gray-200
|
||||
active:bg-gray-100
|
||||
dark:bg-gray-500
|
||||
dark:hover:bg-gray-500
|
||||
dark:bg-opacity-80
|
||||
dark:hover:bg-opacity-100
|
||||
dark:active:opacity-80
|
||||
|
||||
border-gray-200
|
||||
hover:border-gray-300
|
||||
active:border-gray-200
|
||||
dark:border-gray-500
|
||||
dark:hover:border-gray-500
|
||||
|
||||
text-gray-700
|
||||
hover:text-gray-900
|
||||
active:text-gray-600
|
||||
dark:text-gray-200
|
||||
dark:active:text-white
|
||||
dark:hover:text-white
|
||||
|
||||
`,
|
||||
primary: `
|
||||
bg-primary-600
|
||||
text-white
|
||||
shadow-sm
|
||||
active:bg-primary-600
|
||||
hover:bg-primary
|
||||
border-primary-500
|
||||
hover:border-primary-500
|
||||
active:border-primary-700
|
||||
`,
|
||||
colored: `
|
||||
text-white
|
||||
shadow-sm
|
||||
hover:bg-opacity-90
|
||||
active:bg-opacity-100
|
||||
`,
|
||||
selected: `bg-gray-100 dark:bg-gray-500
|
||||
text-black hover:text-black active:text-black dark:hover:text-white dark:text-white
|
||||
`
|
||||
};
|
||||
|
||||
export type ButtonVariant = keyof typeof variants;
|
||||
export type ButtonSize = keyof typeof sizes;
|
||||
|
||||
export interface ButtonBaseProps {
|
||||
variant?: ButtonVariant;
|
||||
size?: ButtonSize;
|
||||
loading?: boolean;
|
||||
export interface ButtonBaseProps extends VariantProps<typeof styles> {
|
||||
icon?: React.ReactNode;
|
||||
noPadding?: boolean;
|
||||
noBorder?: boolean;
|
||||
pressEffect?: boolean;
|
||||
justifyLeft?: boolean;
|
||||
}
|
||||
|
||||
export type ButtonProps = ButtonBaseProps &
|
||||
|
@ -109,73 +24,102 @@ type Button = {
|
|||
|
||||
const hasHref = (props: ButtonProps | LinkButtonProps): props is LinkButtonProps => 'href' in props;
|
||||
|
||||
const styles = cva(
|
||||
'border rounded-md items-center transition-colors duration-100 cursor-default disabled:opacity-50 disabled:cursor-not-allowed',
|
||||
{
|
||||
variants: {
|
||||
pressEffect: {
|
||||
true: 'active:translate-y-[1px]'
|
||||
},
|
||||
loading: {
|
||||
true: 'opacity-70'
|
||||
},
|
||||
padding: {
|
||||
thin: '!p-1',
|
||||
sm: '!p-1.5'
|
||||
},
|
||||
noBorder: {
|
||||
true: 'border-0'
|
||||
},
|
||||
size: {
|
||||
md: 'py-1 px-3 text-md font-medium',
|
||||
sm: 'py-1 px-2 text-xs font-medium'
|
||||
},
|
||||
justify: {
|
||||
left: 'justify-left',
|
||||
center: ''
|
||||
},
|
||||
variant: {
|
||||
default: [
|
||||
'bg-gray-50 shadow-sm hover:bg-gray-100 active:bg-gray-50 dark:bg-transparent',
|
||||
'dark:active:bg-gray-600 dark:hover:bg-gray-550 dark:active:opacity-80',
|
||||
'border-gray-100 hover:border-gray-200 active:border-gray-200',
|
||||
'dark:border-transparent dark:active:border-gray-600 dark:hover:border-gray-500',
|
||||
'text-gray-700 hover:text-gray-900 active:text-gray-600',
|
||||
'dark:text-gray-200 dark:active:text-white dark:hover:text-white'
|
||||
],
|
||||
gray: [
|
||||
'bg-gray-100 shadow-sm hover:bg-gray-200 active:bg-gray-100 dark:bg-gray-500 dark:hover:bg-gray-500 dark:bg-opacity-80 dark:hover:bg-opacity-100 dark:active:opacity-80',
|
||||
'border-gray-200 hover:border-gray-300 active:border-gray-200 dark:border-gray-500 dark:hover:border-gray-500',
|
||||
'text-gray-700 hover:text-gray-900 active:text-gray-600 dark:text-gray-200 dark:active:text-white dark:hover:text-white'
|
||||
],
|
||||
primary: [
|
||||
'bg-primary-600 text-white shadow-sm active:bg-primary-600 hover:bg-primary border-primary-500 hover:border-primary-500 active:border-primary-700'
|
||||
],
|
||||
colored: ['text-white shadow-sm hover:bg-opacity-90 active:bg-opacity-100'],
|
||||
selected: [
|
||||
'bg-gray-100 dark:bg-gray-500 text-black hover:text-black active:text-black dark:hover:text-white dark:text-white'
|
||||
]
|
||||
}
|
||||
},
|
||||
defaultVariants: {
|
||||
size: 'md',
|
||||
justify: 'center',
|
||||
variant: 'default'
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
export const Button = forwardRef<
|
||||
HTMLButtonElement | HTMLAnchorElement,
|
||||
ButtonProps | LinkButtonProps
|
||||
>(
|
||||
(
|
||||
{ loading, justifyLeft, className, pressEffect, noBorder, noPadding, size, variant, ...props },
|
||||
ref
|
||||
) => {
|
||||
className = clsx(
|
||||
'border rounded-md items-center transition-colors duration-100 cursor-default disabled:opacity-50 disabled:cursor-not-allowed',
|
||||
{
|
||||
'opacity-70': loading,
|
||||
'!p-1': noPadding,
|
||||
'justify-center': !justifyLeft,
|
||||
'active:translate-y-[1px]': pressEffect,
|
||||
'border-0': noBorder
|
||||
},
|
||||
sizes[size || 'default'],
|
||||
variants[variant || 'default'],
|
||||
className
|
||||
);
|
||||
>(({ className, ...props }, ref) => {
|
||||
className = clsx(styles(props), className);
|
||||
|
||||
return hasHref(props) ? (
|
||||
<a {...props} ref={ref as any} className={clsx(className, 'no-underline')}>
|
||||
<>
|
||||
{props.icon}
|
||||
{props.children}
|
||||
</>
|
||||
</a>
|
||||
) : (
|
||||
<button {...(props as ButtonProps)} ref={ref as any} className={className}>
|
||||
<>
|
||||
{props.icon}
|
||||
{props.children}
|
||||
</>
|
||||
</button>
|
||||
);
|
||||
}
|
||||
);
|
||||
let children = (
|
||||
<>
|
||||
{props.icon}
|
||||
{props.children}
|
||||
</>
|
||||
);
|
||||
|
||||
return hasHref(props) ? (
|
||||
<a {...props} ref={ref as any} className={clsx(className, 'no-underline inline-block')}>
|
||||
{children}
|
||||
</a>
|
||||
) : (
|
||||
<button {...(props as ButtonProps)} ref={ref as any} className={className}>
|
||||
{children}
|
||||
</button>
|
||||
);
|
||||
});
|
||||
|
||||
export const ButtonLink = forwardRef<
|
||||
HTMLLinkElement,
|
||||
ButtonBaseProps & LinkProps & React.RefAttributes<HTMLAnchorElement>
|
||||
>(
|
||||
(
|
||||
{ loading, justifyLeft, className, pressEffect, noBorder, noPadding, size, variant, ...props },
|
||||
ref
|
||||
) => {
|
||||
className = clsx(
|
||||
'border rounded-md items-center transition-colors duration-100 cursor-default',
|
||||
{ 'opacity-70': loading, '!p-1': noPadding },
|
||||
{ 'justify-center': !justifyLeft },
|
||||
sizes[size || 'default'],
|
||||
variants[variant || 'default'],
|
||||
{ 'active:translate-y-[1px]': pressEffect },
|
||||
{ 'border-0': noBorder },
|
||||
'disabled:opacity-50 disabled:cursor-not-allowed',
|
||||
className
|
||||
);
|
||||
>(({ className, to, ...props }, ref) => {
|
||||
className = clsx(
|
||||
styles(props),
|
||||
'no-underline disabled:opacity-50 disabled:cursor-not-allowed',
|
||||
className
|
||||
);
|
||||
|
||||
return (
|
||||
<Link {...props} ref={ref as any} className={clsx(className, 'no-underline')}>
|
||||
<>
|
||||
{props.icon}
|
||||
{props.children}
|
||||
</>
|
||||
</Link>
|
||||
);
|
||||
}
|
||||
);
|
||||
return (
|
||||
<Link to={to} ref={ref as any} className={className}>
|
||||
<>
|
||||
{props.icon}
|
||||
{props.children}
|
||||
</>
|
||||
</Link>
|
||||
);
|
||||
});
|
||||
|
|
Loading…
Reference in a new issue