mirror of
https://github.com/spacedriveapp/spacedrive
synced 2024-07-08 04:52:49 +00:00
more theming
This commit is contained in:
parent
df8564b024
commit
5ba07ac6c2
|
@ -89,7 +89,7 @@ export default function NavBar() {
|
|||
<div className="flex-1 lg:hidden" />
|
||||
<Dropdown.Root
|
||||
button={
|
||||
<Button className="ml-[140px] hover:!bg-transparent" padding="thin">
|
||||
<Button className="ml-[140px] hover:!bg-transparent" forIcon>
|
||||
<DotsThreeVertical weight="bold" className="w-6 h-6 " />
|
||||
</Button>
|
||||
}
|
||||
|
|
|
@ -26,6 +26,8 @@
|
|||
"@sd/assets": "workspace:*",
|
||||
"@sd/client": "workspace:*",
|
||||
"@sd/ui": "workspace:*",
|
||||
"@splinetool/react-spline": "^2.2.3",
|
||||
"@splinetool/runtime": "^0.9.128",
|
||||
"@tailwindcss/forms": "^0.5.3",
|
||||
"@tanstack/react-query": "^4.12.0",
|
||||
"@tanstack/react-query-devtools": "^4.12.0",
|
||||
|
|
|
@ -55,6 +55,9 @@ const AssignTagMenuItems = (props: { objectId: number }) => {
|
|||
|
||||
export default function ExplorerContextMenu(props: PropsWithChildren) {
|
||||
const store = getExplorerStore();
|
||||
const { mutate: generateThumbsForLocation } = useLibraryMutation(
|
||||
'jobs.generateThumbsForLocation'
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="relative">
|
||||
|
@ -102,6 +105,8 @@ export default function ExplorerContextMenu(props: PropsWithChildren) {
|
|||
<CM.Item label="PNG" />
|
||||
<CM.Item label="WebP" />
|
||||
</CM.SubMenu>
|
||||
<CM.Item label="Rescan Directory" icon={Package} keybind="⌘B" />
|
||||
<CM.Item label="Regen Thumbnails" icon={Package} keybind="⌘B" />
|
||||
<CM.Item variant="danger" label="Secure delete" icon={TrashSimple} />
|
||||
</CM.SubMenu>
|
||||
|
||||
|
|
|
@ -65,8 +65,8 @@ function FileItem({ data, selected, index, ...rest }: Props) {
|
|||
>
|
||||
<FileThumb
|
||||
className={clsx(
|
||||
'border-4 border-app-line shadow shadow-black/40 object-cover max-w-full max-h-full w-auto overflow-hidden',
|
||||
isVid && 'border-black rounded border-x-0 border-y-[9px]'
|
||||
'border-4 border-white shadow shadow-black/40 object-cover max-w-full max-h-full w-auto overflow-hidden',
|
||||
isVid && '!border-black rounded border-x-0 border-y-[9px]'
|
||||
)}
|
||||
data={data}
|
||||
kind={data.extension === 'zip' ? 'zip' : isVid ? 'video' : 'other'}
|
||||
|
|
|
@ -8,11 +8,12 @@ import {
|
|||
import { QuestionMarkCircleIcon } from '@heroicons/react/24/solid';
|
||||
import { useLibraryQuery } from '@sd/client';
|
||||
import { JobReport } from '@sd/client';
|
||||
import { Button, CategoryHeading } from '@sd/ui';
|
||||
import { Button, CategoryHeading, tw } from '@sd/ui';
|
||||
import clsx from 'clsx';
|
||||
import dayjs from 'dayjs';
|
||||
import { ArrowsClockwise } from 'phosphor-react';
|
||||
|
||||
import ProgressBar from '../primitive/ProgressBar';
|
||||
import { Tooltip } from '../tooltip/Tooltip';
|
||||
|
||||
interface JobNiceData {
|
||||
|
@ -54,62 +55,25 @@ function elapsed(seconds: number) {
|
|||
return new Date(seconds * 1000).toUTCString().match(/(\d\d:\d\d:\d\d)/)?.[0];
|
||||
}
|
||||
|
||||
const HeaderContainer = tw.div`z-20 flex items-center w-full h-10 px-4 border-b border-app-line rounded-t-md`;
|
||||
|
||||
export function JobsManager() {
|
||||
const runningJobs = useLibraryQuery(['jobs.getRunning']);
|
||||
const jobs = useLibraryQuery(['jobs.getHistory']);
|
||||
return (
|
||||
<div className="h-full pb-10 overflow-hidden">
|
||||
{/* <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="z-20 flex items-center w-full h-10 px-4 border-b border-gray-500 rounded-t-md">
|
||||
<HeaderContainer>
|
||||
<CategoryHeading className="mt-1 ">Recent Jobs</CategoryHeading>
|
||||
</div>
|
||||
</HeaderContainer>
|
||||
<div className="h-full mr-1 overflow-x-hidden custom-scroll inspector-scroll">
|
||||
<div className="">
|
||||
<div className="py-1">
|
||||
{jobs.data?.map((job) => {
|
||||
// const color = StatusColors[job.status];
|
||||
const niceData = getNiceData(job)[job.name] || {
|
||||
name: job.name,
|
||||
icon: QuestionMarkCircleIcon
|
||||
};
|
||||
|
||||
return (
|
||||
<div
|
||||
className="flex items-center px-2 py-2 pl-4 border-b border-gray-500 bg-opacity-60"
|
||||
key={job.id}
|
||||
>
|
||||
<Tooltip label={job.status}>
|
||||
<niceData.icon className={clsx('w-5 mr-3')} />
|
||||
</Tooltip>
|
||||
<div className="flex flex-col">
|
||||
<span className="flex mt-0.5 items-center font-semibold truncate">
|
||||
{niceData.name}
|
||||
</span>
|
||||
<div className="flex items-center">
|
||||
<span className="text-xs opacity-60">
|
||||
{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-50">•</span>
|
||||
<span className="text-xs">{dayjs(job.date_created).toNow(true)} ago</span>
|
||||
</div>
|
||||
<span className="text-xs">{job.data}</span>
|
||||
</div>
|
||||
<div className="flex-grow" />
|
||||
<div className="flex flex-row space-x-2">
|
||||
{job.status === 'Failed' && (
|
||||
<Button className="!p-1">
|
||||
<ArrowsClockwise className="w-4" />
|
||||
</Button>
|
||||
)}
|
||||
<Button className="!p-1">
|
||||
<XMarkIcon className="w-4" />
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
{runningJobs.data?.map((job) => (
|
||||
<Job key={job.id} job={job} />
|
||||
))}
|
||||
{jobs.data?.map((job) => (
|
||||
<Job key={job.id} job={job} />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -117,6 +81,58 @@ export function JobsManager() {
|
|||
);
|
||||
}
|
||||
|
||||
function Job({ job }: { job: JobReport }) {
|
||||
const niceData = getNiceData(job)[job.name] || {
|
||||
name: job.name,
|
||||
icon: QuestionMarkCircleIcon
|
||||
};
|
||||
const isRunning = job.status === 'Running';
|
||||
return (
|
||||
<div className="flex items-center px-2 py-2 pl-4 border-b border-app-line bg-opacity-60">
|
||||
<Tooltip label={job.status}>
|
||||
<niceData.icon className={clsx('w-5 mr-3')} />
|
||||
</Tooltip>
|
||||
<div className="flex flex-col w-full ">
|
||||
<span className="flex mt-0.5 items-center font-semibold truncate">
|
||||
{isRunning ? job.message : niceData.name}
|
||||
</span>
|
||||
|
||||
{isRunning && (
|
||||
<div className="w-full my-1">
|
||||
<ProgressBar value={job.completed_task_count} total={job.task_count} />
|
||||
</div>
|
||||
)}
|
||||
<div className="flex items-center">
|
||||
<span className="text-xs opacity-60">
|
||||
{isRunning ? 'Elapsed' : 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-50">•</span>
|
||||
{
|
||||
<span className="text-xs">
|
||||
{isRunning ? 'Unknown time remaining' : dayjs(job.date_created).toNow(true) + ' ago'}
|
||||
</span>
|
||||
}
|
||||
</div>
|
||||
<span className="text-xs">{job.data}</span>
|
||||
</div>
|
||||
<div className="flex-grow" />
|
||||
<div className="flex flex-row space-x-2">
|
||||
{job.status === 'Failed' && (
|
||||
<Button className="!p-1">
|
||||
<ArrowsClockwise className="w-4" />
|
||||
</Button>
|
||||
)}
|
||||
<Button className="!p-1">
|
||||
<XMarkIcon className="w-4" />
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function numberWithCommas(x: number) {
|
||||
return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',');
|
||||
}
|
||||
|
|
|
@ -1,83 +0,0 @@
|
|||
import { Transition } from '@headlessui/react';
|
||||
import { useLibraryQuery } from '@sd/client';
|
||||
import clsx from 'clsx';
|
||||
import { DetailedHTMLProps, HTMLAttributes } from 'react';
|
||||
|
||||
import ProgressBar from '../primitive/ProgressBar';
|
||||
|
||||
const MiddleTruncatedText = ({
|
||||
children,
|
||||
...props
|
||||
}: DetailedHTMLProps<HTMLAttributes<HTMLSpanElement>, HTMLSpanElement>) => {
|
||||
const text = children?.toString() ?? '';
|
||||
const first = text.substring(0, text.length / 2);
|
||||
const last = text.substring(first.length);
|
||||
|
||||
// Literally black magic
|
||||
const fontFaceScaleFactor = 1.61;
|
||||
const startWidth = fontFaceScaleFactor * 5;
|
||||
const endWidth = fontFaceScaleFactor * 4;
|
||||
|
||||
return (
|
||||
<div className="w-full overflow-hidden whitespace-nowrap">
|
||||
<span
|
||||
{...props}
|
||||
style={{
|
||||
maxWidth: `calc(100% - (1em * ${endWidth}))`,
|
||||
minWidth: startWidth
|
||||
}}
|
||||
className={clsx(
|
||||
props?.className,
|
||||
'text-ellipsis inline-block align-bottom whitespace-nowrap overflow-hidden'
|
||||
)}
|
||||
>
|
||||
{first}
|
||||
</span>
|
||||
<span
|
||||
{...props}
|
||||
style={{
|
||||
maxWidth: `calc(100% - (1em * ${startWidth}))`,
|
||||
direction: 'rtl'
|
||||
}}
|
||||
className={clsx(
|
||||
props?.className,
|
||||
'inline-block align-bottom whitespace-nowrap overflow-hidden'
|
||||
)}
|
||||
>
|
||||
{last}
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default function RunningJobsWidget() {
|
||||
const { data: jobs } = useLibraryQuery(['jobs.getRunning']);
|
||||
|
||||
return (
|
||||
<div className="flex flex-col space-y-4">
|
||||
{jobs?.map((job, index) => (
|
||||
<Transition
|
||||
key={job.id + index}
|
||||
show={true}
|
||||
enter="transition-translate ease-in-out duration-200"
|
||||
enterFrom="translate-y-24"
|
||||
enterTo="translate-y-0"
|
||||
leave="transition-translate ease-in-out duration-200"
|
||||
leaveFrom="translate-y-0"
|
||||
leaveTo="translate-y-24"
|
||||
>
|
||||
<div
|
||||
key={job.id}
|
||||
className="flex flex-col px-2 pt-1.5 pb-2 border bg-gray-600 bg-opacity-50 border-gray-500 rounded"
|
||||
>
|
||||
{/* <span className="mb-0.5 text-tiny font-bold text-gray-400">{job.status} Job</span> */}
|
||||
<MiddleTruncatedText className="mb-1.5 text-gray-450 text-tiny">
|
||||
{job.message}
|
||||
</MiddleTruncatedText>
|
||||
<ProgressBar value={job.completed_task_count} total={job.task_count} />
|
||||
</div>
|
||||
</Transition>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
|
@ -1,21 +1,7 @@
|
|||
import { InformationCircleIcon } from '@heroicons/react/24/outline';
|
||||
import {
|
||||
EllipsisVerticalIcon,
|
||||
EyeIcon,
|
||||
EyeSlashIcon,
|
||||
KeyIcon,
|
||||
LockClosedIcon,
|
||||
LockOpenIcon,
|
||||
PlusIcon,
|
||||
TrashIcon,
|
||||
XMarkIcon
|
||||
} from '@heroicons/react/24/solid';
|
||||
import { Button, Input, Select, SelectOption } from '@sd/ui';
|
||||
import { EllipsisVerticalIcon, EyeIcon, KeyIcon } from '@heroicons/react/24/solid';
|
||||
import { Button } from '@sd/ui';
|
||||
import clsx from 'clsx';
|
||||
import { Eject, EjectSimple, Plus } from 'phosphor-react';
|
||||
import { useState } from 'react';
|
||||
|
||||
import { Toggle } from '../primitive';
|
||||
import { DefaultProps } from '../primitive/types';
|
||||
import { Tooltip } from '../tooltip/Tooltip';
|
||||
|
||||
|
@ -36,13 +22,10 @@ export interface Key {
|
|||
}
|
||||
|
||||
export const Key: React.FC<{ data: Key; index: number }> = ({ data, index }) => {
|
||||
const odd = (index || 0) % 2 === 0;
|
||||
|
||||
return (
|
||||
<div
|
||||
className={clsx(
|
||||
'flex items-center justify-between px-2 py-1.5 shadow-gray-900/20 text-sm text-gray-300 bg-gray-500/30 shadow-lg border-gray-500 rounded-lg'
|
||||
// !odd && 'bg-opacity-10'
|
||||
'flex items-center justify-between px-2 py-1.5 shadow-app-shade/10 text-sm bg-app-box shadow-lg rounded-lg'
|
||||
)}
|
||||
>
|
||||
<div className="flex items-center">
|
||||
|
@ -85,12 +68,12 @@ export const Key: React.FC<{ data: Key; index: number }> = ({ data, index }) =>
|
|||
<div className="space-x-1">
|
||||
{data.mounted && (
|
||||
<Tooltip label="Browse files">
|
||||
<Button padding="thin">
|
||||
<Button forIcon>
|
||||
<EyeIcon className="w-4 h-4 text-gray-400" />
|
||||
</Button>
|
||||
</Tooltip>
|
||||
)}
|
||||
<Button padding="thin">
|
||||
<Button forIcon>
|
||||
<EllipsisVerticalIcon className="w-4 h-4 text-gray-400" />
|
||||
</Button>
|
||||
</div>
|
||||
|
|
|
@ -1,11 +1,6 @@
|
|||
import { Button, CategoryHeading, Input, Select, SelectOption } from '@sd/ui';
|
||||
import clsx from 'clsx';
|
||||
import { Eject, EjectSimple, Plus } from 'phosphor-react';
|
||||
import { useState } from 'react';
|
||||
import { Button } from '@sd/ui';
|
||||
|
||||
import { Toggle } from '../primitive';
|
||||
import { DefaultProps } from '../primitive/types';
|
||||
import { Tooltip } from '../tooltip/Tooltip';
|
||||
import { Key } from './Key';
|
||||
|
||||
export type KeyListProps = DefaultProps;
|
||||
|
@ -45,7 +40,7 @@ export function KeyList(props: KeyListProps) {
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex w-full p-2 bg-gray-600 border-t border-gray-500 rounded-b-md">
|
||||
<div className="flex w-full p-2 border-t border-app-line rounded-b-md">
|
||||
<Button size="sm" variant="gray">
|
||||
Unmount All
|
||||
</Button>
|
||||
|
|
|
@ -42,7 +42,7 @@ export function KeyMounter() {
|
|||
/>
|
||||
<Button
|
||||
onClick={() => setShowKey(!showKey)}
|
||||
padding="thin"
|
||||
forIcon
|
||||
className="border-none absolute right-[5px] top-[5px]"
|
||||
>
|
||||
<CurrentEyeIcon className="w-4 h-4" />
|
||||
|
|
|
@ -1,15 +0,0 @@
|
|||
import clsx from 'clsx';
|
||||
import { PropsWithChildren } from 'react';
|
||||
|
||||
export default function Card(props: PropsWithChildren<{ className?: string }>) {
|
||||
return (
|
||||
<div
|
||||
className={clsx(
|
||||
'flex w-full px-4 py-2 border border-app-line rounded-lg bg-app-box',
|
||||
props.className
|
||||
)}
|
||||
>
|
||||
{props.children}
|
||||
</div>
|
||||
);
|
||||
}
|
|
@ -180,6 +180,7 @@ export function Sidebar() {
|
|||
|
||||
<Dropdown.Root
|
||||
className="mt-2"
|
||||
itemsClassName="dark:bg-sidebar-box"
|
||||
button={
|
||||
<Dropdown.Button
|
||||
variant="gray"
|
||||
|
@ -240,8 +241,6 @@ export function Sidebar() {
|
|||
|
||||
<div className="flex-grow " />
|
||||
|
||||
{library && <RunningJobsWidget />}
|
||||
|
||||
{/* <div className="fixed w-[174px] bottom-[2px] left-[2px] h-20 rounded-[8px] bg-gradient-to-t from-sidebar-box/90 via-sidebar-box/50 to-transparent" /> */}
|
||||
|
||||
<div className="fixed bottom-0 flex flex-col mt-2 mb-3">
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { TrashIcon } from '@heroicons/react/24/solid';
|
||||
import { useLibraryMutation } from '@sd/client';
|
||||
import { Location, Node } from '@sd/client';
|
||||
import { Button, Dialog } from '@sd/ui';
|
||||
import { Button, Card, Dialog } from '@sd/ui';
|
||||
import clsx from 'clsx';
|
||||
import { Repeat } from 'phosphor-react';
|
||||
import { useState } from 'react';
|
||||
|
@ -29,12 +29,12 @@ export default function LocationListItem({ location }: LocationListItemProps) {
|
|||
if (hide) return <></>;
|
||||
|
||||
return (
|
||||
<div className="flex w-full px-4 py-2 border border-gray-500 rounded-lg bg-gray-550">
|
||||
<Card>
|
||||
<Folder size={30} className="mr-3" />
|
||||
<div className="flex flex-col">
|
||||
<div className="grid grid-cols-1 min-w-[110px]">
|
||||
<h1 className="pt-0.5 text-sm font-semibold">{location.name}</h1>
|
||||
<p className="mt-0.5 text-sm select-text text-gray-250">
|
||||
<span className="py-[1px] px-1 bg-gray-500 rounded mr-1">{location.node.name}</span>
|
||||
<p className="mt-0.5 text-sm truncate select-text text-gray-250">
|
||||
<span className="py-[1px] px-1 bg-app-button rounded mr-1">{location.node.name}</span>
|
||||
{location.local_path}
|
||||
</p>
|
||||
</div>
|
||||
|
@ -82,6 +82,6 @@ export default function LocationListItem({ location }: LocationListItemProps) {
|
|||
<CogIcon className="w-4 h-4" />
|
||||
</Button> */}
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
import Spline from '@splinetool/react-spline';
|
||||
|
||||
export default function PhotosScreen() {
|
||||
return (
|
||||
<div className="flex flex-col w-full h-screen p-5 custom-scroll page-scroll app-background">
|
||||
|
@ -6,6 +8,12 @@ export default function PhotosScreen() {
|
|||
<b>Note: </b>This is a pre-alpha build of Spacedrive, many features are yet to be
|
||||
functional.
|
||||
</p>
|
||||
<Spline
|
||||
style={{ height: 500 }}
|
||||
height={500}
|
||||
className="rounded-md shadow-sm pointer-events-auto"
|
||||
scene="https://prod.spline.design/KUmO4nOh8IizEiCx/scene.splinecode"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -17,14 +17,14 @@ export default function AppearanceSettings() {
|
|||
title="UI Animations"
|
||||
description="Dialogs and other UI elements will animate when opening and closing."
|
||||
>
|
||||
<Switch value={uiAnimations} onChange={setUiAnimations} className="m-2 ml-4" />
|
||||
<Switch checked={uiAnimations} onCheckedChange={setUiAnimations} className="m-2 ml-4" />
|
||||
</InputContainer>
|
||||
<InputContainer
|
||||
mini
|
||||
title="Blur Effects"
|
||||
description="Some components will have a blur effect applied to them."
|
||||
>
|
||||
<Switch value={blurEffects} onChange={setBlurEffects} className="m-2 ml-4" />
|
||||
<Switch checked={blurEffects} onCheckedChange={setBlurEffects} className="m-2 ml-4" />
|
||||
</InputContainer>
|
||||
</SettingsContainer>
|
||||
);
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { Button, Input } from '@sd/ui';
|
||||
import { Button, Card, GridLayout, Input } from '@sd/ui';
|
||||
import { MagnifyingGlass } from 'phosphor-react';
|
||||
|
||||
import { SettingsContainer } from '../../../components/settings/SettingsContainer';
|
||||
|
@ -45,13 +45,14 @@ function ExtensionItem(props: { extension: ExtensionItemData }) {
|
|||
const { installed, name, description } = props.extension;
|
||||
|
||||
return (
|
||||
<div className="flex flex-col w-[290px] px-4 py-4 bg-gray-600 border border-gray-500 rounded">
|
||||
<h3 className="m-0 text-sm font-bold">{name}</h3>
|
||||
<p className="mt-1 mb-1 text-xs text-gray-300 ">{description}</p>
|
||||
<Button size="sm" className="mt-2" variant={installed ? 'gray' : 'primary'}>
|
||||
<Card className="flex-col">
|
||||
<h3 className="mt-2 text-sm font-bold">{name}</h3>
|
||||
<p className="mt-1 mb-1 text-xs text-gray-300">{description}</p>
|
||||
<div className="flex-grow" />
|
||||
<Button size="sm" className="my-2" variant={installed ? 'gray' : 'accent'}>
|
||||
{installed ? 'Installed' : 'Install'}
|
||||
</Button>
|
||||
</div>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -71,11 +72,11 @@ export default function ExtensionSettings() {
|
|||
}
|
||||
/>
|
||||
|
||||
<div className="flex flex-wrap gap-3">
|
||||
<GridLayout>
|
||||
{extensions.map((extension) => (
|
||||
<ExtensionItem key={extension.uuid} extension={extension} />
|
||||
))}
|
||||
</div>
|
||||
</GridLayout>
|
||||
</SettingsContainer>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
import { useBridgeQuery, usePlatform } from '@sd/client';
|
||||
import { Input, Switch, tw } from '@sd/ui';
|
||||
import { Card, Input, Switch, tw } from '@sd/ui';
|
||||
import { Database } from 'phosphor-react';
|
||||
|
||||
import Card from '../../../components/layout/Card';
|
||||
import { SettingsContainer } from '../../../components/settings/SettingsContainer';
|
||||
import { SettingsHeader } from '../../../components/settings/SettingsHeader';
|
||||
|
||||
|
|
|
@ -1,10 +1,30 @@
|
|||
import { Switch } from '@sd/ui';
|
||||
import { useState } from 'react';
|
||||
|
||||
import { InputContainer } from '../../../components/primitive/InputContainer';
|
||||
import { SettingsContainer } from '../../../components/settings/SettingsContainer';
|
||||
import { SettingsHeader } from '../../../components/settings/SettingsHeader';
|
||||
|
||||
export default function PrivacySettings() {
|
||||
const [uiAnimations, setUiAnimations] = useState(true);
|
||||
const [blurEffects, setBlurEffects] = useState(true);
|
||||
return (
|
||||
<SettingsContainer>
|
||||
<SettingsHeader title="Privacy" description="How Spacedrive handles your data" />
|
||||
<SettingsHeader title="Permissions" description="" />
|
||||
<InputContainer
|
||||
mini
|
||||
title="UI Animations"
|
||||
description="Dialogs and other UI elements will animate when opening and closing."
|
||||
>
|
||||
<Switch checked={uiAnimations} onCheckedChange={setUiAnimations} className="m-2 ml-4" />
|
||||
</InputContainer>
|
||||
<InputContainer
|
||||
mini
|
||||
title="Blur Effects"
|
||||
description="Some components will have a blur effect applied to them."
|
||||
>
|
||||
<Switch checked={blurEffects} onCheckedChange={setBlurEffects} className="m-2 ml-4" />
|
||||
</InputContainer>
|
||||
</SettingsContainer>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,13 +1,12 @@
|
|||
import { TrashIcon } from '@heroicons/react/24/outline';
|
||||
import { Tag, useLibraryMutation, useLibraryQuery } from '@sd/client';
|
||||
import { TagUpdateArgs } from '@sd/client';
|
||||
import { Button, Dialog, Input, Switch } from '@sd/ui';
|
||||
import { Button, Card, Dialog, Input, Switch } from '@sd/ui';
|
||||
import clsx from 'clsx';
|
||||
import { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import { Controller, useForm } from 'react-hook-form';
|
||||
import { useDebounce } from 'rooks';
|
||||
|
||||
import Card from '../../../components/layout/Card';
|
||||
import { InputContainer } from '../../../components/primitive/InputContainer';
|
||||
import { PopoverPicker } from '../../../components/primitive/PopoverPicker';
|
||||
import { SettingsContainer } from '../../../components/settings/SettingsContainer';
|
||||
|
@ -105,7 +104,7 @@ export default function TagsSettings() {
|
|||
</div>
|
||||
}
|
||||
/>
|
||||
<Card className="!px-2 dark:bg-gray-800">
|
||||
<Card className="!px-2">
|
||||
<div className="flex flex-wrap gap-2 m-1">
|
||||
{tags?.map((tag) => (
|
||||
<div
|
||||
|
|
|
@ -1,13 +1,12 @@
|
|||
import { PencilIcon, TrashIcon } from '@heroicons/react/24/outline';
|
||||
import { useBridgeMutation, useBridgeQuery } from '@sd/client';
|
||||
import { LibraryConfigWrapped } from '@sd/client';
|
||||
import { Button, ButtonLink } from '@sd/ui';
|
||||
import { Button, ButtonLink, Card } from '@sd/ui';
|
||||
import { DotsSixVertical } from 'phosphor-react';
|
||||
import { useState } from 'react';
|
||||
|
||||
import CreateLibraryDialog from '../../../components/dialog/CreateLibraryDialog';
|
||||
import DeleteLibraryDialog from '../../../components/dialog/DeleteLibraryDialog';
|
||||
import Card from '../../../components/layout/Card';
|
||||
import { SettingsContainer } from '../../../components/settings/SettingsContainer';
|
||||
import { SettingsHeader } from '../../../components/settings/SettingsHeader';
|
||||
|
||||
|
@ -25,14 +24,14 @@ function LibraryListItem(props: { library: LibraryConfigWrapped }) {
|
|||
<DotsSixVertical weight="bold" className="mt-[15px] mr-3 opacity-30" />
|
||||
<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>
|
||||
<p className="mt-0.5 text-xs text-ink-dull">{props.library.uuid}</p>
|
||||
</div>
|
||||
<div className="flex flex-row space-x-2 items-center">
|
||||
<ButtonLink to="/settings/library" variant="gray" padding="sm">
|
||||
<div className="flex flex-row items-center space-x-2">
|
||||
<ButtonLink forIcon to="/settings/library" variant="gray">
|
||||
<PencilIcon className="w-4 h-4" />
|
||||
</ButtonLink>
|
||||
<DeleteLibraryDialog libraryUuid={props.library.uuid}>
|
||||
<Button variant="gray" padding="sm">
|
||||
<Button forIcon variant="gray">
|
||||
<TrashIcon className="w-4 h-4" />
|
||||
</Button>
|
||||
</DeleteLibraryDialog>
|
||||
|
|
|
@ -41,7 +41,7 @@ export function Dialog(props: DialogProps) {
|
|||
<DialogPrimitive.Portal forceMount>
|
||||
<DialogPrimitive.Overlay asChild>
|
||||
<animated.div
|
||||
className="fixed top-0 bottom-0 left-0 right-0 z-50 grid overflow-y-auto bg-black bg-opacity-50 rounded-xl place-items-center m-[1px]"
|
||||
className="fixed top-0 bottom-0 left-0 right-0 z-50 grid overflow-y-auto bg-app bg-opacity-50 rounded-xl place-items-center m-[1px]"
|
||||
style={{
|
||||
opacity: styles.opacity
|
||||
}}
|
||||
|
@ -49,19 +49,19 @@ export function Dialog(props: DialogProps) {
|
|||
<DialogPrimitive.Content forceMount asChild>
|
||||
<animated.div
|
||||
style={styles}
|
||||
className="min-w-[300px] max-w-[400px] rounded-md bg-gray-650 text-white border border-gray-550 shadow-deep"
|
||||
className="min-w-[300px] max-w-[400px] rounded-md bg-app-box border border-app-line text-ink shadow-app-shade"
|
||||
>
|
||||
<form onSubmit={(e) => e.preventDefault()}>
|
||||
<div className="p-5">
|
||||
<DialogPrimitive.Title className="mb-2 font-bold">
|
||||
{props.title}
|
||||
</DialogPrimitive.Title>
|
||||
<DialogPrimitive.Description className="text-sm text-gray-300">
|
||||
<DialogPrimitive.Description className="text-sm text-ink-dull">
|
||||
{props.description}
|
||||
</DialogPrimitive.Description>
|
||||
{props.children}
|
||||
</div>
|
||||
<div className="flex flex-row justify-end px-3 py-3 space-x-2 bg-gray-600 border-t border-gray-550">
|
||||
<div className="flex flex-row justify-end px-3 py-3 space-x-2 border-t bg-app-selected border-app-line">
|
||||
{props.loading && <Loader />}
|
||||
<div className="flex-grow" />
|
||||
<DialogPrimitive.Close asChild>
|
||||
|
@ -80,7 +80,7 @@ export function Dialog(props: DialogProps) {
|
|||
size="sm"
|
||||
loading={props.loading}
|
||||
disabled={props.loading || props.submitDisabled}
|
||||
variant={props.ctaDanger ? 'colored' : 'primary'}
|
||||
variant={props.ctaDanger ? 'colored' : 'accent'}
|
||||
className={clsx(props.ctaDanger && 'bg-red-500 border-red-500')}
|
||||
>
|
||||
{props.ctaLabel}
|
||||
|
|
5
packages/ui/src/Layout.tsx
Normal file
5
packages/ui/src/Layout.tsx
Normal file
|
@ -0,0 +1,5 @@
|
|||
import { tw } from './utils';
|
||||
|
||||
export const Card = tw.div`flex w-full px-4 py-2 border border-app-line rounded-lg bg-app-box`;
|
||||
|
||||
export const GridLayout = tw.div`grid grid-cols-2 gap-3 lg:grid-cols-3`;
|
|
@ -2,6 +2,7 @@ export * from './Button';
|
|||
export * as Dropdown from './Dropdown';
|
||||
export * from './Dialog';
|
||||
export * from './Loader';
|
||||
export * from './Layout';
|
||||
export * as ContextMenu from './ContextMenu';
|
||||
export * from './OverlayPanel';
|
||||
export * from './Input';
|
||||
|
|
|
@ -19,7 +19,6 @@
|
|||
--color-sidebar-divider: 230, 15%, 93%;
|
||||
--color-sidebar-button: 230, 15%, 100%;
|
||||
--color-sidebar-selected: 230, 10%, 80%;
|
||||
--color-sidebar-separator: 230, 15%, 100%;
|
||||
--color-sidebar-shade: 230, 15%, 100%;
|
||||
|
||||
// main
|
||||
|
@ -35,6 +34,7 @@
|
|||
--color-app-hover: 230, 5%, 100%;
|
||||
--color-app-shade: 230, 15%, 50%;
|
||||
--color-app-frame: 0, 0%, 100%;
|
||||
|
||||
// menu
|
||||
--color-menu: 230, 16%, 14%;
|
||||
--color-menu-line: 230, 5%, 18%;
|
||||
|
@ -61,12 +61,11 @@
|
|||
--color-ink-faint: 230, 10%, 55%;
|
||||
// sidebar
|
||||
--color-sidebar: 230, 15%, 7%;
|
||||
--color-sidebar-box: 230, 15%, 13%;
|
||||
--color-sidebar-box: 230, 15%, 16%;
|
||||
--color-sidebar-line: 230, 15%, 23%;
|
||||
--color-sidebar-divider: 230, 15%, 17%;
|
||||
--color-sidebar-button: 230, 15%, 18%;
|
||||
--color-sidebar-selected: 230, 15%, 24%;
|
||||
--color-sidebar-separator: 230, 15%, 23%;
|
||||
--color-sidebar-shade: 230, 15%, 23%;
|
||||
// main
|
||||
--color-app: 230, 16%, 14%;
|
||||
|
|
|
@ -54,7 +54,6 @@ module.exports = function (app, options) {
|
|||
divider: alpha('--color-sidebar-divider'),
|
||||
button: alpha('--color-sidebar-button'),
|
||||
selected: alpha('--color-sidebar-selected'),
|
||||
separator: alpha('--color-sidebar-separator'),
|
||||
shade: alpha('--color-sidebar-shade')
|
||||
},
|
||||
app: {
|
||||
|
|
|
@ -427,6 +427,8 @@ importers:
|
|||
'@sd/client': workspace:*
|
||||
'@sd/config': workspace:*
|
||||
'@sd/ui': workspace:*
|
||||
'@splinetool/react-spline': ^2.2.3
|
||||
'@splinetool/runtime': ^0.9.128
|
||||
'@tailwindcss/forms': ^0.5.3
|
||||
'@tanstack/react-query': ^4.12.0
|
||||
'@tanstack/react-query-devtools': ^4.12.0
|
||||
|
@ -475,6 +477,8 @@ importers:
|
|||
'@sd/assets': link:../assets
|
||||
'@sd/client': link:../client
|
||||
'@sd/ui': link:../ui
|
||||
'@splinetool/react-spline': 2.2.3_lzjdincs7igsg3b2ch4qc2di4m
|
||||
'@splinetool/runtime': 0.9.128
|
||||
'@tailwindcss/forms': 0.5.3_tailwindcss@3.1.8
|
||||
'@tanstack/react-query': 4.12.0_biqbaboplfbrettd7655fr4n2y
|
||||
'@tanstack/react-query-devtools': 4.12.0_pqnxmwujmmnpcx44ucekqkefny
|
||||
|
@ -4987,6 +4991,25 @@ packages:
|
|||
engines: {node: '>=10'}
|
||||
dev: true
|
||||
|
||||
/@splinetool/react-spline/2.2.3_lzjdincs7igsg3b2ch4qc2di4m:
|
||||
resolution: {integrity: sha512-KdvyP2cCLLG/PcHp0ObE147nhawY4R/iqI6733MSWBx4KSpeo4oD589rCPuXeGdgyr2lCtZqUQOkQj4QCnXxmw==}
|
||||
peerDependencies:
|
||||
'@splinetool/runtime': '*'
|
||||
react: '>=17.0.0'
|
||||
react-dom: '>=17.0.0'
|
||||
dependencies:
|
||||
'@splinetool/runtime': 0.9.128
|
||||
react: 18.2.0
|
||||
react-dom: 18.2.0_react@18.2.0
|
||||
react-merge-refs: 1.1.0
|
||||
dev: false
|
||||
|
||||
/@splinetool/runtime/0.9.128:
|
||||
resolution: {integrity: sha512-QYEmO3Yn03GENQah8a5/OKpjzZLidZLgHjhMcXN3fqsG4R5ahv51xuHH13vYi52MztSee2twMB1dlh1208aF7w==}
|
||||
dependencies:
|
||||
on-change: 4.0.1
|
||||
dev: false
|
||||
|
||||
/@storybook/addon-actions/6.5.12_biqbaboplfbrettd7655fr4n2y:
|
||||
resolution: {integrity: sha512-yEbyKjBsSRUr61SlS+SOTqQwdumO8Wa3GoHO3AfmvoKfzdGrM7w8G5Zs9Iev16khWg/7bQvoH3KZsg/hQuKnNg==}
|
||||
peerDependencies:
|
||||
|
@ -7374,7 +7397,6 @@ packages:
|
|||
dependencies:
|
||||
tailwindcss: 3.1.8
|
||||
transitivePeerDependencies:
|
||||
- postcss
|
||||
- ts-node
|
||||
dev: true
|
||||
|
||||
|
@ -16465,6 +16487,11 @@ packages:
|
|||
resolution: {integrity: sha512-LMJTtvgc/nugXj0Vcrrs68Mn2D1r0zf630VNtqtpI1FEO7e+O9FP4gqs9AcnBaSEeoHIPm28u6qgPR0oyEpGSw==}
|
||||
dev: true
|
||||
|
||||
/on-change/4.0.1:
|
||||
resolution: {integrity: sha512-Gxmr/8NsLTigoUUEPvnAIUGl7uxwfC3Mm1G+qTmODEZcT4ZGfeaFyQgfGCdbKVS2pICIFYB82RH6MUHToPcS4Q==}
|
||||
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
|
||||
dev: false
|
||||
|
||||
/on-finished/2.3.0:
|
||||
resolution: {integrity: sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww==}
|
||||
engines: {node: '>= 0.8'}
|
||||
|
@ -18016,7 +18043,6 @@ packages:
|
|||
|
||||
/react-merge-refs/1.1.0:
|
||||
resolution: {integrity: sha512-alTKsjEL0dKH/ru1Iyn7vliS2QRcBp9zZPGoWxUOvRGWPUYgjo+V01is7p04It6KhgrzhJGnIj9GgX8W4bZoCQ==}
|
||||
dev: true
|
||||
|
||||
/react-native-codegen/0.69.2:
|
||||
resolution: {integrity: sha512-yPcgMHD4mqLbckqnWjFBaxomDnBREfRjDi2G/WxNyPBQLD+PXUEmZTkDx6QoOXN+Bl2SkpnNOSsLE2+/RUHoPw==}
|
||||
|
|
Loading…
Reference in a new issue