mirror of
https://github.com/spacedriveapp/spacedrive
synced 2024-07-18 13:09:15 +00:00
Merge remote-tracking branch 'origin/main' into general-fixes
This commit is contained in:
commit
447e2bbba6
|
@ -32,7 +32,7 @@ export function AppLayout() {
|
|||
>
|
||||
<Sidebar />
|
||||
<div className="relative flex w-full h-screen max-h-screen bg-white dark:bg-gray-650">
|
||||
<Suspense fallback={<p>Loading...</p>}>
|
||||
<Suspense>
|
||||
<Outlet />
|
||||
</Suspense>
|
||||
</div>
|
||||
|
@ -60,7 +60,7 @@ function Toasts() {
|
|||
// }, []);
|
||||
|
||||
return (
|
||||
<div className="fixed flex right-0">
|
||||
<div className="fixed right-0 flex">
|
||||
<ToastPrimitive.Provider>
|
||||
<>
|
||||
{toasts.map((toast) => (
|
||||
|
@ -81,7 +81,7 @@ function Toasts() {
|
|||
)}
|
||||
>
|
||||
<div className="flex">
|
||||
<div className="w-0 flex-1 flex items-center pl-5 py-4">
|
||||
<div className="flex items-center flex-1 w-0 py-4 pl-5">
|
||||
<div className="w-full radix">
|
||||
<ToastPrimitive.Title className="text-sm font-medium text-gray-900 dark:text-gray-100">
|
||||
{toast.title}
|
||||
|
@ -95,11 +95,11 @@ function Toasts() {
|
|||
</div>
|
||||
<div className="flex">
|
||||
<div className="flex flex-col px-3 py-2 space-y-1">
|
||||
<div className="h-0 flex-1 flex">
|
||||
<div className="flex flex-1 h-0">
|
||||
{toast.actionButton && (
|
||||
<ToastPrimitive.Action
|
||||
altText="view now"
|
||||
className="w-full border border-transparent rounded-lg px-3 py-2 flex items-center justify-center text-sm font-medium text-primary dark:text-primary hover:bg-white/10 focus:z-10 focus:outline-none focus-visible:ring focus-visible:ring-primary focus-visible:ring-opacity-75"
|
||||
className="flex items-center justify-center w-full px-3 py-2 text-sm font-medium border border-transparent rounded-lg text-primary dark:text-primary hover:bg-white/10 focus:z-10 focus:outline-none focus-visible:ring focus-visible:ring-primary focus-visible:ring-opacity-75"
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
toast.actionButton?.onClick();
|
||||
|
@ -110,8 +110,8 @@ function Toasts() {
|
|||
</ToastPrimitive.Action>
|
||||
)}
|
||||
</div>
|
||||
<div className="h-0 flex-1 flex">
|
||||
<ToastPrimitive.Close className="w-full border border-transparent rounded-lg px-3 py-2 flex items-center justify-center text-sm font-medium text-gray-700 dark:text-gray-100 hover:bg-white/10 focus:z-10 focus:outline-none focus-visible:ring focus-visible:ring-primary focus-visible:ring-opacity-75">
|
||||
<div className="flex flex-1 h-0">
|
||||
<ToastPrimitive.Close className="flex items-center justify-center w-full px-3 py-2 text-sm font-medium text-gray-700 border border-transparent rounded-lg dark:text-gray-100 hover:bg-white/10 focus:z-10 focus:outline-none focus-visible:ring focus-visible:ring-primary focus-visible:ring-opacity-75">
|
||||
Dismiss
|
||||
</ToastPrimitive.Close>
|
||||
</div>
|
||||
|
|
|
@ -47,7 +47,7 @@ export function AppRouter() {
|
|||
useInvalidateQuery();
|
||||
|
||||
return (
|
||||
<Suspense fallback={<p>Loading...</p>}>
|
||||
<Suspense>
|
||||
<Routes>
|
||||
<Route path="onboarding" element={<OnboardingScreen />} />
|
||||
<Route element={<AppLayout />}>
|
||||
|
@ -56,7 +56,7 @@ export function AppRouter() {
|
|||
<Route
|
||||
path="*"
|
||||
element={
|
||||
<h1 className="text-white p-4">
|
||||
<h1 className="p-4 text-white">
|
||||
Please select or create a library in the sidebar.
|
||||
</h1>
|
||||
}
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
import { KeyIcon } from '@heroicons/react/24/outline';
|
||||
import { CogIcon, LockClosedIcon } from '@heroicons/react/24/solid';
|
||||
import { Button } from '@sd/ui';
|
||||
import { Loader } from '@sd/ui';
|
||||
import { Cloud, Desktop, DeviceMobileCamera, DotsSixVertical, Laptop } from 'phosphor-react';
|
||||
import { useState } from 'react';
|
||||
|
||||
import FileItem from '../explorer/FileItem';
|
||||
import Loader from '../primitive/Loader';
|
||||
import ProgressBar from '../primitive/ProgressBar';
|
||||
import { Tooltip } from '../tooltip/Tooltip';
|
||||
|
||||
|
|
|
@ -1,10 +1,9 @@
|
|||
import { useBridgeMutation } from '@sd/client';
|
||||
import { Input } from '@sd/ui';
|
||||
import { Dialog } from '@sd/ui';
|
||||
import { useQueryClient } from '@tanstack/react-query';
|
||||
import { PropsWithChildren, useState } from 'react';
|
||||
|
||||
import Dialog from '../layout/Dialog';
|
||||
|
||||
export default function CreateLibraryDialog({
|
||||
children,
|
||||
onSubmit
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
import { useBridgeMutation } from '@sd/client';
|
||||
import { Dialog } from '@sd/ui';
|
||||
import { useQueryClient } from '@tanstack/react-query';
|
||||
import { useState } from 'react';
|
||||
|
||||
import Dialog from '../layout/Dialog';
|
||||
|
||||
interface Props {
|
||||
children: React.ReactNode;
|
||||
libraryUuid: string;
|
||||
|
|
|
@ -47,7 +47,7 @@ export const Inspector = (props: Props) => {
|
|||
});
|
||||
|
||||
return (
|
||||
<div className="p-2 pr-1 overflow-x-hidden custom-scroll inspector-scroll pb-[55px]">
|
||||
<div className="p-2 pr-1 w-full overflow-x-hidden custom-scroll inspector-scroll pb-[55px]">
|
||||
{!!props.data && (
|
||||
<>
|
||||
<div className="flex bg-black items-center justify-center w-full h-64 mb-[10px] overflow-hidden rounded-lg ">
|
||||
|
|
205
packages/interface/src/components/key/KeyManager.tsx
Normal file
205
packages/interface/src/components/key/KeyManager.tsx
Normal file
|
@ -0,0 +1,205 @@
|
|||
import { InformationCircleIcon } from '@heroicons/react/24/outline';
|
||||
import {
|
||||
EyeIcon,
|
||||
EyeSlashIcon,
|
||||
KeyIcon,
|
||||
LockClosedIcon,
|
||||
LockOpenIcon,
|
||||
PlusIcon,
|
||||
TrashIcon,
|
||||
XMarkIcon
|
||||
} from '@heroicons/react/24/solid';
|
||||
import { Button, Input } 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';
|
||||
|
||||
export type KeyManagerProps = DefaultProps;
|
||||
|
||||
interface FakeKey {
|
||||
id: string;
|
||||
name: string;
|
||||
mounted?: boolean;
|
||||
locked?: boolean;
|
||||
stats?: {
|
||||
objectCount?: number;
|
||||
containerCount?: number;
|
||||
};
|
||||
// Nodes this key is mounted on
|
||||
nodes?: string[]; // will be node object
|
||||
}
|
||||
|
||||
const Heading: React.FC<{ children: React.ReactNode }> = ({ children }) => (
|
||||
<div className="mt-1 mb-1 text-xs font-semibold text-gray-300">{children}</div>
|
||||
);
|
||||
|
||||
const Key: React.FC<{ data: FakeKey; 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'
|
||||
)}
|
||||
>
|
||||
<div className="flex items-center">
|
||||
<KeyIcon
|
||||
className={clsx(
|
||||
'w-5 h-5 ml-1 mr-3',
|
||||
data.mounted
|
||||
? data.locked
|
||||
? 'text-primary-600'
|
||||
: 'text-primary-600'
|
||||
: '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="inline ml-2 px-1 text-[8pt] font-medium text-gray-300 bg-gray-500 rounded">
|
||||
{data.nodes?.length || 0 > 0 ? `${data.nodes?.length || 0} nodes` : 'This node'}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
{/* <div className="text-xs text-gray-300 opacity-30">#{data.id}</div> */}
|
||||
{/* {data.stats && (
|
||||
<div className="flex flex-row space-x-3">
|
||||
{data.stats.objectCount && (
|
||||
<div className="text-[8pt] text-gray-300 opacity-30">
|
||||
{data.stats.objectCount} Objects
|
||||
</div>
|
||||
)}
|
||||
{data.stats.containerCount && (
|
||||
<div className="text-[8pt] text-gray-300 opacity-30">
|
||||
{data.stats.containerCount} Containers
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)} */}
|
||||
</div>
|
||||
</div>
|
||||
<div className="space-x-1">
|
||||
{data.mounted ? (
|
||||
<>
|
||||
<Tooltip label="Browse files">
|
||||
<Button noPadding>
|
||||
<EyeIcon className="w-4 h-4 text-gray-400" />
|
||||
</Button>
|
||||
</Tooltip>
|
||||
|
||||
{data.locked ? (
|
||||
<Tooltip label="Unlock key">
|
||||
<Button noPadding>
|
||||
<LockClosedIcon className="w-4 h-4 text-gray-400" />
|
||||
</Button>
|
||||
</Tooltip>
|
||||
) : (
|
||||
<Tooltip label="Lock key">
|
||||
<Button noPadding>
|
||||
<LockOpenIcon className="w-4 h-4 text-gray-400" />
|
||||
</Button>
|
||||
</Tooltip>
|
||||
)}
|
||||
</>
|
||||
) : (
|
||||
<Tooltip label="Dismount key">
|
||||
<Button noPadding>
|
||||
<XMarkIcon className="w-4 h-4 text-gray-400" />
|
||||
</Button>
|
||||
</Tooltip>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export function KeyManager(props: KeyManagerProps) {
|
||||
const [showKey, setShowKey] = useState(false);
|
||||
const [toggle, setToggle] = useState(false);
|
||||
|
||||
const CurrentEyeIcon = showKey ? EyeSlashIcon : EyeIcon;
|
||||
|
||||
return (
|
||||
<div className="flex flex-col h-full">
|
||||
<div className="p-3 pt-3">
|
||||
<Heading>Mount key</Heading>
|
||||
<div className="flex space-x-2">
|
||||
<div className="relative flex flex-grow">
|
||||
<Input autoFocus type={showKey ? 'text' : 'password'} className="flex-grow !py-0.5" />
|
||||
<Button
|
||||
onClick={() => setShowKey(!showKey)}
|
||||
noBorder
|
||||
noPadding
|
||||
className="absolute right-[5px] top-[5px]"
|
||||
>
|
||||
<CurrentEyeIcon className="w-4 h-4" />
|
||||
</Button>
|
||||
</div>
|
||||
<Tooltip className="flex" label="Mount key">
|
||||
<Button variant="gray" noPadding>
|
||||
<Plus weight="fill" className="w-4 h-4 mx-1" />
|
||||
</Button>
|
||||
</Tooltip>
|
||||
</div>
|
||||
<div className="flex flex-row items-center mt-3 mb-1">
|
||||
<Toggle className="dark:bg-gray-400/30" size="sm" value={toggle} onChange={setToggle} />
|
||||
<span className="ml-3 mt-[1px] font-medium text-xs">Sync with Library</span>
|
||||
<Tooltip label="This key will be mounted on all devices running your Library">
|
||||
<InformationCircleIcon className="w-4 h-4 ml-1.5 text-gray-400" />
|
||||
</Tooltip>
|
||||
</div>
|
||||
{/* <p className="pt-1.5 ml-0.5 text-[8pt] leading-snug text-gray-300 opacity-50 w-[90%]">
|
||||
Files encrypted with this key will be revealed and decrypted on the fly.
|
||||
</p> */}
|
||||
</div>
|
||||
<hr className="border-gray-500" />
|
||||
<div className="p-3 custom-scroll overlay-scroll">
|
||||
<div className="">
|
||||
<Heading>Mounted keys</Heading>
|
||||
<div className="pt-1 space-y-1.5">
|
||||
<Key
|
||||
index={0}
|
||||
data={{
|
||||
id: 'af5570f5a1810b7a',
|
||||
name: 'OBS Recordings',
|
||||
mounted: true,
|
||||
|
||||
nodes: ['node1', 'node2'],
|
||||
stats: { objectCount: 235, containerCount: 2 }
|
||||
}}
|
||||
/>
|
||||
<Key
|
||||
index={1}
|
||||
data={{
|
||||
id: 'af5570f5a1810b7a',
|
||||
name: 'Unknown Key',
|
||||
locked: true,
|
||||
mounted: true,
|
||||
stats: { objectCount: 45 }
|
||||
}}
|
||||
/>
|
||||
<Key index={2} data={{ id: '7324695a52da67b1', name: 'Spacedrive Company' }} />
|
||||
<Key index={3} data={{ id: 'b02303d68d05a562', name: 'Key 4' }} />
|
||||
<Key index={3} data={{ id: 'b02303d68d05a562', name: 'Key 5' }} />
|
||||
<Key index={3} data={{ id: 'b02303d68d05a562', name: 'Key 6' }} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex w-full p-2 bg-gray-600 border-t border-gray-500 rounded-b-md">
|
||||
<Button size="sm" variant="gray">
|
||||
Unmount All
|
||||
</Button>
|
||||
<div className="flex-grow" />
|
||||
<Button size="sm" variant="gray">
|
||||
Close
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
|
@ -1,63 +0,0 @@
|
|||
import * as DialogPrimitive from '@radix-ui/react-dialog';
|
||||
import { Button } from '@sd/ui';
|
||||
import clsx from 'clsx';
|
||||
import { ReactNode } from 'react';
|
||||
|
||||
import Loader from '../primitive/Loader';
|
||||
|
||||
export interface DialogProps extends DialogPrimitive.DialogProps {
|
||||
trigger: ReactNode;
|
||||
ctaLabel?: string;
|
||||
ctaDanger?: boolean;
|
||||
ctaAction?: () => void;
|
||||
title?: string;
|
||||
description?: string;
|
||||
children?: ReactNode;
|
||||
loading?: boolean;
|
||||
submitDisabled?: boolean;
|
||||
}
|
||||
|
||||
export default function Dialog(props: DialogProps) {
|
||||
return (
|
||||
<DialogPrimitive.Root open={props.open} onOpenChange={props.onOpenChange}>
|
||||
<DialogPrimitive.Trigger asChild>{props.trigger}</DialogPrimitive.Trigger>
|
||||
<DialogPrimitive.Portal>
|
||||
<DialogPrimitive.Overlay className="fixed top-0 dialog-overlay bottom-0 left-0 right-0 z-50 grid overflow-y-auto bg-black bg-opacity-50 rounded-xl place-items-center m-[1px]">
|
||||
<DialogPrimitive.Content className="min-w-[300px] max-w-[400px] dialog-content rounded-md bg-gray-650 text-white border border-gray-550 shadow-deep">
|
||||
<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">
|
||||
{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">
|
||||
{props.loading && <Loader />}
|
||||
<div className="flex-grow" />
|
||||
<DialogPrimitive.Close asChild>
|
||||
<Button loading={props.loading} disabled={props.loading} size="sm" variant="gray">
|
||||
Close
|
||||
</Button>
|
||||
</DialogPrimitive.Close>
|
||||
<Button
|
||||
onClick={props.ctaAction}
|
||||
type="submit"
|
||||
size="sm"
|
||||
loading={props.loading}
|
||||
disabled={props.loading || props.submitDisabled}
|
||||
variant={props.ctaDanger ? 'colored' : 'primary'}
|
||||
className={clsx(props.ctaDanger && 'bg-red-500 border-red-500')}
|
||||
>
|
||||
{props.ctaLabel}
|
||||
</Button>
|
||||
</div>
|
||||
</form>
|
||||
</DialogPrimitive.Content>
|
||||
</DialogPrimitive.Overlay>
|
||||
</DialogPrimitive.Portal>
|
||||
</DialogPrimitive.Root>
|
||||
);
|
||||
}
|
|
@ -250,6 +250,7 @@ export function Sidebar() {
|
|||
</NavLink>
|
||||
<OverlayPanel
|
||||
className="focus:outline-none"
|
||||
transformOrigin="bottom left"
|
||||
disabled={!library}
|
||||
trigger={
|
||||
<Button
|
||||
|
|
|
@ -5,11 +5,12 @@ import {
|
|||
useExplorerStore,
|
||||
useLibraryMutation
|
||||
} from '@sd/client';
|
||||
import { Dropdown } from '@sd/ui';
|
||||
import { Dropdown, OverlayPanel } from '@sd/ui';
|
||||
import clsx from 'clsx';
|
||||
import {
|
||||
Aperture,
|
||||
ArrowsClockwise,
|
||||
Cloud,
|
||||
FilmStrip,
|
||||
IconProps,
|
||||
Image,
|
||||
|
@ -26,47 +27,43 @@ import { useNavigate } from 'react-router-dom';
|
|||
|
||||
import { useOperatingSystem } from '../../hooks/useOperatingSystem';
|
||||
import { KeybindEvent } from '../../util/keybind';
|
||||
import { KeyManager } from '../key/KeyManager';
|
||||
import { Shortcut } from '../primitive/Shortcut';
|
||||
import { DefaultProps } from '../primitive/types';
|
||||
import { Tooltip } from '../tooltip/Tooltip';
|
||||
|
||||
export type TopBarProps = DefaultProps;
|
||||
export interface TopBarButtonProps
|
||||
extends DetailedHTMLProps<HTMLAttributes<HTMLButtonElement>, HTMLButtonElement> {
|
||||
export interface TopBarButtonProps {
|
||||
icon: React.ComponentType<IconProps>;
|
||||
group?: boolean;
|
||||
active?: boolean;
|
||||
left?: boolean;
|
||||
right?: boolean;
|
||||
className?: string;
|
||||
onClick?: () => void;
|
||||
}
|
||||
|
||||
const TopBarButton: React.FC<TopBarButtonProps> = ({
|
||||
icon: Icon,
|
||||
left,
|
||||
right,
|
||||
group,
|
||||
active,
|
||||
className,
|
||||
...props
|
||||
}) => {
|
||||
return (
|
||||
<button
|
||||
{...props}
|
||||
className={clsx(
|
||||
'mr-[1px] flex py-0.5 px-0.5 text-md font-medium hover:bg-gray-150 dark:transparent dark:hover:bg-gray-550 rounded-md transition-colors duration-100',
|
||||
{
|
||||
'rounded-r-none rounded-l-none': group && !left && !right,
|
||||
'rounded-r-none': group && left,
|
||||
'rounded-l-none': group && right,
|
||||
'dark:bg-gray-500': active
|
||||
},
|
||||
className
|
||||
)}
|
||||
>
|
||||
<Icon weight={'regular'} className="m-0.5 w-5 h-5 text-gray-450 dark:text-gray-150" />
|
||||
</button>
|
||||
);
|
||||
};
|
||||
const TopBarButton = forwardRef<HTMLButtonElement, TopBarButtonProps>(
|
||||
({ icon: Icon, left, right, group, active, className, ...props }, ref) => {
|
||||
return (
|
||||
<button
|
||||
{...props}
|
||||
ref={ref}
|
||||
className={clsx(
|
||||
'mr-[1px] flex py-0.5 px-0.5 text-md font-medium hover:bg-gray-150 dark:transparent dark:hover:bg-gray-550 rounded-md open:dark:bg-gray-550 transition-colors duration-100 outline-none !cursor-normal',
|
||||
{
|
||||
'rounded-r-none rounded-l-none': group && !left && !right,
|
||||
'rounded-r-none': group && left,
|
||||
'rounded-l-none': group && right,
|
||||
'dark:bg-gray-500': active
|
||||
},
|
||||
className
|
||||
)}
|
||||
>
|
||||
<Icon weight={'regular'} className="m-0.5 w-5 h-5 text-gray-450 dark:text-gray-150" />
|
||||
</button>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
const SearchBar = forwardRef<HTMLInputElement, DefaultProps>((props, forwardedRef) => {
|
||||
const {
|
||||
|
@ -119,6 +116,8 @@ const SearchBar = forwardRef<HTMLInputElement, DefaultProps>((props, forwardedRe
|
|||
);
|
||||
});
|
||||
|
||||
export type TopBarProps = DefaultProps;
|
||||
|
||||
export const TopBar: React.FC<TopBarProps> = (props) => {
|
||||
const platform = useOperatingSystem(false);
|
||||
const os = useOperatingSystem(true);
|
||||
|
@ -256,20 +255,24 @@ export const TopBar: React.FC<TopBarProps> = (props) => {
|
|||
<SearchBar ref={searchRef} />
|
||||
|
||||
<div className="flex mx-8 space-x-2">
|
||||
<Tooltip label="Major Key Alert">
|
||||
<TopBarButton icon={Key} />
|
||||
</Tooltip>
|
||||
{/* <Tooltip label="Cloud">
|
||||
<OverlayPanel
|
||||
className="focus:outline-none"
|
||||
trigger={
|
||||
// <Tooltip label="Major Key Alert">
|
||||
<TopBarButton icon={Key} />
|
||||
// </Tooltip>
|
||||
}
|
||||
>
|
||||
<div className="block w-[350px] h-[435px]">
|
||||
<KeyManager />
|
||||
</div>
|
||||
</OverlayPanel>
|
||||
<Tooltip label="Cloud">
|
||||
<TopBarButton icon={Cloud} />
|
||||
</Tooltip> */}
|
||||
{/* <Tooltip label="Refresh">
|
||||
<TopBarButton
|
||||
icon={ArrowsClockwise}
|
||||
onClick={() => {
|
||||
// generateThumbsForLocation({ id: locationId, path: '' });
|
||||
}}
|
||||
/>
|
||||
</Tooltip> */}
|
||||
</Tooltip>
|
||||
<Tooltip label="Refresh">
|
||||
<TopBarButton icon={ArrowsClockwise} />
|
||||
</Tooltip>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex mr-3 space-x-2">
|
||||
|
@ -279,7 +282,17 @@ export const TopBar: React.FC<TopBarProps> = (props) => {
|
|||
className="my-2"
|
||||
icon={SidebarSimple}
|
||||
/>
|
||||
<Dropdown
|
||||
<OverlayPanel
|
||||
className="focus:outline-none"
|
||||
trigger={
|
||||
// <Tooltip label="Major Key Alert">
|
||||
<TopBarButton icon={List} className="my-2" />
|
||||
// </Tooltip>
|
||||
}
|
||||
>
|
||||
<div className="block w-[250px] h-[335px]">{/* <KeyManager /> */}</div>
|
||||
</OverlayPanel>
|
||||
{/* <Dropdown
|
||||
// className="absolute block h-6 w-44 top-2 right-4"
|
||||
align="right"
|
||||
items={[
|
||||
|
@ -300,7 +313,7 @@ export const TopBar: React.FC<TopBarProps> = (props) => {
|
|||
]
|
||||
]}
|
||||
buttonComponent={<TopBarButton icon={List} />}
|
||||
/>
|
||||
/> */}
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
|
|
|
@ -1,13 +1,12 @@
|
|||
import { TrashIcon } from '@heroicons/react/24/solid';
|
||||
import { useLibraryMutation } from '@sd/client';
|
||||
import { Location, Node } from '@sd/client';
|
||||
import { Button } from '@sd/ui';
|
||||
import { Button, Dialog } from '@sd/ui';
|
||||
import clsx from 'clsx';
|
||||
import { Repeat } from 'phosphor-react';
|
||||
import React, { useState } from 'react';
|
||||
|
||||
import { Folder } from '../icons/Folder';
|
||||
import Dialog from '../layout/Dialog';
|
||||
|
||||
interface LocationListItemProps {
|
||||
location: Location & { node: Node };
|
||||
|
|
|
@ -5,6 +5,7 @@ export interface ToggleProps {
|
|||
value: boolean;
|
||||
onChange?: (newValue: boolean) => void;
|
||||
size?: 'sm' | 'md';
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export const Toggle: React.FC<ToggleProps> = (props) => {
|
||||
|
@ -16,9 +17,10 @@ export const Toggle: React.FC<ToggleProps> = (props) => {
|
|||
onChange={onChange}
|
||||
className={clsx(
|
||||
'transition relative flex-shrink-0 inline-flex items-center h-6 w-11 rounded-full bg-gray-200 dark:bg-gray-550',
|
||||
props.className,
|
||||
{
|
||||
'bg-primary-500 dark:bg-primary-500': isEnabled,
|
||||
'h-6 w-11': size === 'sm',
|
||||
'!bg-primary-500 dark:!bg-primary-500': isEnabled,
|
||||
'h-[20px] w-[35px]': size === 'sm',
|
||||
'h-8 w-[55px]': size === 'md'
|
||||
}
|
||||
)}
|
||||
|
@ -28,8 +30,9 @@ export const Toggle: React.FC<ToggleProps> = (props) => {
|
|||
'transition inline-block w-4 h-4 transform bg-white rounded-full',
|
||||
isEnabled ? 'translate-x-6' : 'translate-x-1',
|
||||
{
|
||||
'w-4 h-4': size === 'sm',
|
||||
'w-3 h-3': size === 'sm',
|
||||
'h-6 w-6': size === 'md',
|
||||
'translate-x-5': size === 'sm' && isEnabled,
|
||||
'translate-x-7': size === 'md' && isEnabled
|
||||
}
|
||||
)}
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
export const SettingsContainer = ({ children }: { children: React.ReactNode }) => (
|
||||
<div className="flex flex-col flex-grow max-w-4xl space-y-6 w-ful">{children}</div>
|
||||
<div className="flex flex-col flex-grow w-full max-w-4xl space-y-6">{children}</div>
|
||||
);
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import clsx from 'clsx';
|
||||
import { ReactNode } from 'react';
|
||||
|
||||
interface SettingsHeaderProps {
|
||||
|
@ -18,3 +19,16 @@ export const SettingsHeader: React.FC<SettingsHeaderProps> = (props) => {
|
|||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export const SettingsIcon = ({ component: Icon, ...props }: any) => (
|
||||
<Icon weight="bold" {...props} className={clsx('w-4 h-4 mr-2', props.className)} />
|
||||
);
|
||||
|
||||
export const SettingsHeading: React.FC<{ className?: string; children: string }> = ({
|
||||
children,
|
||||
className
|
||||
}) => (
|
||||
<div className={clsx('mt-5 mb-1 ml-1 text-xs font-semibold text-gray-400', className)}>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -1,39 +0,0 @@
|
|||
import clsx from 'clsx';
|
||||
import { Outlet } from 'react-router';
|
||||
|
||||
interface SettingsScreenContainerProps {
|
||||
children: React.ReactNode;
|
||||
}
|
||||
|
||||
export const SettingsIcon = ({ component: Icon, ...props }: any) => (
|
||||
<Icon weight="bold" {...props} className={clsx('w-4 h-4 mr-2', props.className)} />
|
||||
);
|
||||
|
||||
export const SettingsHeading: React.FC<{ className?: string; children: string }> = ({
|
||||
children,
|
||||
className
|
||||
}) => (
|
||||
<div className={clsx('mt-5 mb-1 ml-1 text-xs font-semibold text-gray-400', className)}>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
|
||||
export const SettingsScreenContainer: React.FC<SettingsScreenContainerProps> = (props) => {
|
||||
return (
|
||||
<div className="flex flex-row w-full">
|
||||
<div className="h-full border-r max-w-[200px] flex-shrink-0 border-gray-100 w-60 dark:border-gray-550">
|
||||
<div data-tauri-drag-region className="w-full h-7" />
|
||||
<div className="p-5 pt-0">{props.children}</div>
|
||||
</div>
|
||||
<div className="w-full">
|
||||
<div data-tauri-drag-region className="w-full h-7" />
|
||||
<div className="flex flex-grow-0 w-full h-full max-h-screen custom-scroll page-scroll">
|
||||
<div className="flex flex-grow px-12 pb-5">
|
||||
<Outlet />
|
||||
<div className="block h-20" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
|
@ -0,0 +1,84 @@
|
|||
import { CogIcon, HeartIcon, KeyIcon, ShieldCheckIcon, TagIcon } from '@heroicons/react/24/outline';
|
||||
import { BuildingLibraryIcon } from '@heroicons/react/24/solid';
|
||||
import {
|
||||
FlyingSaucer,
|
||||
HardDrive,
|
||||
KeyReturn,
|
||||
PaintBrush,
|
||||
PuzzlePiece,
|
||||
Receipt,
|
||||
ShareNetwork
|
||||
} from 'phosphor-react';
|
||||
|
||||
import { SidebarLink } from '../layout/Sidebar';
|
||||
import { SettingsHeading, SettingsIcon } from './SettingsHeader';
|
||||
|
||||
export const SettingsSidebar = () => {
|
||||
return (
|
||||
<div className="h-full border-r max-w-[200px] flex-shrink-0 border-gray-100 w-60 dark:border-gray-550">
|
||||
<div data-tauri-drag-region className="w-full h-7" />
|
||||
<div className="px-4 py-2.5">
|
||||
<SettingsHeading className="!mt-0">Client</SettingsHeading>
|
||||
<SidebarLink to="/settings/general">
|
||||
<SettingsIcon component={CogIcon} />
|
||||
General
|
||||
</SidebarLink>
|
||||
<SidebarLink to="/settings/libraries">
|
||||
<SettingsIcon component={BuildingLibraryIcon} />
|
||||
Libraries
|
||||
</SidebarLink>
|
||||
<SidebarLink to="/settings/privacy">
|
||||
<SettingsIcon component={ShieldCheckIcon} />
|
||||
Privacy
|
||||
</SidebarLink>
|
||||
<SidebarLink to="/settings/appearance">
|
||||
<SettingsIcon component={PaintBrush} />
|
||||
Appearance
|
||||
</SidebarLink>
|
||||
<SidebarLink to="/settings/keybindings">
|
||||
<SettingsIcon component={KeyReturn} />
|
||||
Keybindings
|
||||
</SidebarLink>
|
||||
<SidebarLink to="/settings/extensions">
|
||||
<SettingsIcon component={PuzzlePiece} />
|
||||
Extensions
|
||||
</SidebarLink>
|
||||
|
||||
<SettingsHeading>Library</SettingsHeading>
|
||||
<SidebarLink to="/settings/library">
|
||||
<SettingsIcon component={CogIcon} />
|
||||
General
|
||||
</SidebarLink>
|
||||
<SidebarLink to="/settings/nodes">
|
||||
<SettingsIcon component={ShareNetwork} />
|
||||
Nodes
|
||||
</SidebarLink>
|
||||
<SidebarLink to="/settings/locations">
|
||||
<SettingsIcon component={HardDrive} />
|
||||
Locations
|
||||
</SidebarLink>
|
||||
<SidebarLink to="/settings/tags">
|
||||
<SettingsIcon component={TagIcon} />
|
||||
Tags
|
||||
</SidebarLink>
|
||||
<SidebarLink to="/settings/keys">
|
||||
<SettingsIcon component={KeyIcon} />
|
||||
Keys
|
||||
</SidebarLink>
|
||||
<SettingsHeading>Resources</SettingsHeading>
|
||||
<SidebarLink to="/settings/about">
|
||||
<SettingsIcon component={FlyingSaucer} />
|
||||
About
|
||||
</SidebarLink>
|
||||
<SidebarLink to="/settings/changelog">
|
||||
<SettingsIcon component={Receipt} />
|
||||
Changelog
|
||||
</SidebarLink>
|
||||
<SidebarLink to="/settings/support">
|
||||
<SettingsIcon component={HeartIcon} />
|
||||
Support
|
||||
</SidebarLink>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
|
@ -1,23 +1,22 @@
|
|||
import * as TooltipPrimitive from '@radix-ui/react-tooltip';
|
||||
|
||||
export const Tooltip = ({
|
||||
children,
|
||||
label,
|
||||
position = 'bottom'
|
||||
}: {
|
||||
export interface TooltipProps {
|
||||
children: React.ReactNode;
|
||||
label: string;
|
||||
position?: 'top' | 'right' | 'bottom' | 'left';
|
||||
}) => {
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export const Tooltip = ({ children, label, position = 'bottom', className }: TooltipProps) => {
|
||||
return (
|
||||
<TooltipPrimitive.Provider>
|
||||
<TooltipPrimitive.Root>
|
||||
<TooltipPrimitive.Trigger asChild>
|
||||
<span>{children}</span>
|
||||
<span className={className}>{children}</span>
|
||||
</TooltipPrimitive.Trigger>
|
||||
<TooltipPrimitive.Content
|
||||
side={position}
|
||||
className="text-xs rounded px-2 py-1 mb-[2px] bg-gray-300 dark:!bg-gray-900 dark:text-gray-100"
|
||||
className="text-xs max-w-[200px] z-50 text-center rounded px-2 py-1 mb-[2px] bg-gray-300 dark:!bg-gray-900 dark:text-gray-100"
|
||||
>
|
||||
<TooltipPrimitive.Arrow className="fill-gray-300 dark:!fill-gray-900" />
|
||||
{label}
|
||||
|
|
|
@ -2,6 +2,7 @@ import { ExclamationCircleIcon, PlusIcon } from '@heroicons/react/24/solid';
|
|||
import { useBridgeQuery, useLibraryQuery, usePlatform } from '@sd/client';
|
||||
import { Statistics } from '@sd/client';
|
||||
import { Button, Input } from '@sd/ui';
|
||||
import { Dialog } from '@sd/ui';
|
||||
import byteSize from 'byte-size';
|
||||
import clsx from 'clsx';
|
||||
import { useEffect } from 'react';
|
||||
|
@ -10,7 +11,6 @@ import 'react-loading-skeleton/dist/skeleton.css';
|
|||
import create from 'zustand';
|
||||
|
||||
import { Device } from '../components/device/Device';
|
||||
import Dialog from '../components/layout/Dialog';
|
||||
import useCounter from '../hooks/useCounter';
|
||||
|
||||
interface StatItemProps {
|
||||
|
@ -123,6 +123,7 @@ export default function OverviewScreen() {
|
|||
<div className="flex flex-col w-full h-screen overflow-x-hidden custom-scroll page-scroll">
|
||||
<div data-tauri-drag-region className="flex flex-shrink-0 w-full h-5" />
|
||||
{/* PAGE */}
|
||||
|
||||
<div className="flex flex-col w-full h-screen px-4">
|
||||
{/* STAT HEADER */}
|
||||
<div className="flex w-full">
|
||||
|
@ -179,13 +180,9 @@ export default function OverviewScreen() {
|
|||
</div>
|
||||
</div>
|
||||
<div className="flex flex-col pb-4 mt-4 space-y-4">
|
||||
<Device name={`James' MacBook Pro`} size="1TB" locations={[]} type="desktop" />
|
||||
<Device name={`James' iPhone 12`} size="47.7GB" locations={[]} type="phone" />
|
||||
<Device name={`Spacedrive Server`} size="5GB" locations={[]} type="server" />
|
||||
</div>
|
||||
<div className="px-5 py-3 text-sm text-gray-400 rounded-md bg-gray-50 dark:text-gray-400 dark:bg-gray-600">
|
||||
<b>Note: </b>This is a pre-alpha build of Spacedrive, many features are yet to be
|
||||
functional.
|
||||
{/* <Device name={`James' MacBook Pro`} size="1TB" locations={[]} type="desktop" /> */}
|
||||
{/* <Device name={`James' iPhone 12`} size="47.7GB" locations={[]} type="phone" />
|
||||
<Device name={`Spacedrive Server`} size="5GB" locations={[]} type="server" /> */}
|
||||
</div>
|
||||
<div className="flex flex-shrink-0 w-full h-4" />
|
||||
</div>
|
||||
|
|
|
@ -1,103 +1,23 @@
|
|||
import { CogIcon, HeartIcon, KeyIcon, ShieldCheckIcon, TagIcon } from '@heroicons/react/24/outline';
|
||||
import { BuildingLibraryIcon } from '@heroicons/react/24/solid';
|
||||
import {
|
||||
FlyingSaucer,
|
||||
HardDrive,
|
||||
KeyReturn,
|
||||
PaintBrush,
|
||||
PuzzlePiece,
|
||||
Receipt,
|
||||
ShareNetwork
|
||||
} from 'phosphor-react';
|
||||
import { Suspense } from 'react';
|
||||
import { Outlet } from 'react-router';
|
||||
|
||||
import { SidebarLink } from '../../components/layout/Sidebar';
|
||||
import {
|
||||
SettingsHeading,
|
||||
SettingsIcon,
|
||||
SettingsScreenContainer
|
||||
} from '../../components/settings/SettingsScreenContainer';
|
||||
import { SettingsSidebar } from '../../components/settings/SettingsSidebar';
|
||||
|
||||
export default function SettingsScreen() {
|
||||
return (
|
||||
<SettingsScreenContainer>
|
||||
<SettingsHeading className="!mt-0">Client</SettingsHeading>
|
||||
<SidebarLink to="/settings/general">
|
||||
<SettingsIcon component={CogIcon} />
|
||||
General
|
||||
</SidebarLink>
|
||||
<SidebarLink to="/settings/libraries">
|
||||
<SettingsIcon component={BuildingLibraryIcon} />
|
||||
Libraries
|
||||
</SidebarLink>
|
||||
<SidebarLink to="/settings/privacy">
|
||||
<SettingsIcon component={ShieldCheckIcon} />
|
||||
Privacy
|
||||
</SidebarLink>
|
||||
<SidebarLink to="/settings/appearance">
|
||||
<SettingsIcon component={PaintBrush} />
|
||||
Appearance
|
||||
</SidebarLink>
|
||||
<SidebarLink to="/settings/keybindings">
|
||||
<SettingsIcon component={KeyReturn} />
|
||||
Keybindings
|
||||
</SidebarLink>
|
||||
<SidebarLink to="/settings/extensions">
|
||||
<SettingsIcon component={PuzzlePiece} />
|
||||
Extensions
|
||||
</SidebarLink>
|
||||
|
||||
<SettingsHeading>Library</SettingsHeading>
|
||||
<SidebarLink to="/settings/library">
|
||||
<SettingsIcon component={CogIcon} />
|
||||
General
|
||||
</SidebarLink>
|
||||
<SidebarLink to="/settings/nodes">
|
||||
<SettingsIcon component={ShareNetwork} />
|
||||
Nodes
|
||||
</SidebarLink>
|
||||
<SidebarLink to="/settings/locations">
|
||||
<SettingsIcon component={HardDrive} />
|
||||
Locations
|
||||
</SidebarLink>
|
||||
<SidebarLink to="/settings/tags">
|
||||
<SettingsIcon component={TagIcon} />
|
||||
Tags
|
||||
</SidebarLink>
|
||||
<SidebarLink to="/settings/keys">
|
||||
<SettingsIcon component={KeyIcon} />
|
||||
Keys
|
||||
</SidebarLink>
|
||||
{/* <SidebarLink to="/settings/backups">
|
||||
<SettingsIcon component={DatabaseIcon} />
|
||||
Backups
|
||||
</SidebarLink>
|
||||
<SidebarLink to="/settings/backups">
|
||||
<SettingsIcon component={ShareNetwork} />
|
||||
Sync
|
||||
</SidebarLink> */}
|
||||
{/* <SettingsHeading>Advanced</SettingsHeading>
|
||||
<SidebarLink to="/settings/p2p">
|
||||
<SettingsIcon component={ShareNetwork} />
|
||||
Networking
|
||||
</SidebarLink>
|
||||
<SidebarLink to="/settings/experimental">
|
||||
<SettingsIcon component={TerminalIcon} />
|
||||
Developer
|
||||
</SidebarLink> */}
|
||||
|
||||
<SettingsHeading>Resources</SettingsHeading>
|
||||
<SidebarLink to="/settings/about">
|
||||
<SettingsIcon component={FlyingSaucer} />
|
||||
About
|
||||
</SidebarLink>
|
||||
<SidebarLink to="/settings/changelog">
|
||||
<SettingsIcon component={Receipt} />
|
||||
Changelog
|
||||
</SidebarLink>
|
||||
<SidebarLink to="/settings/support">
|
||||
<SettingsIcon component={HeartIcon} />
|
||||
Support
|
||||
</SidebarLink>
|
||||
</SettingsScreenContainer>
|
||||
<div className="flex flex-row w-full">
|
||||
<SettingsSidebar />
|
||||
<div className="w-full">
|
||||
<div data-tauri-drag-region className="w-full h-7" />
|
||||
<div className="flex flex-grow-0 w-full h-full max-h-screen custom-scroll page-scroll">
|
||||
<div className="flex flex-grow px-12 pb-5">
|
||||
<Suspense>
|
||||
<Outlet />
|
||||
</Suspense>
|
||||
<div className="block h-20" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -2,13 +2,13 @@ import { TrashIcon } from '@heroicons/react/24/outline';
|
|||
import { Tag, useLibraryMutation, useLibraryQuery } from '@sd/client';
|
||||
import { TagUpdateArgs } from '@sd/client';
|
||||
import { Button, Input } from '@sd/ui';
|
||||
import { Dialog } 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 Dialog from '../../../components/layout/Dialog';
|
||||
import { Toggle } from '../../../components/primitive';
|
||||
import { InputContainer } from '../../../components/primitive/InputContainer';
|
||||
import { PopoverPicker } from '../../../components/primitive/PopoverPicker';
|
||||
|
|
|
@ -78,6 +78,25 @@ body {
|
|||
}
|
||||
}
|
||||
|
||||
.overlay-scroll {
|
||||
// overflow: overlay;
|
||||
&::-webkit-scrollbar {
|
||||
height: 6px;
|
||||
width: 5px;
|
||||
}
|
||||
&::-webkit-scrollbar-track {
|
||||
@apply bg-transparent my-[5px];
|
||||
}
|
||||
&::-webkit-scrollbar-thumb {
|
||||
@apply rounded-[6px] opacity-0 bg-gray-300 dark:bg-gray-950 w-[5px];
|
||||
}
|
||||
&:hover {
|
||||
&::-webkit-scrollbar-thumb {
|
||||
@apply opacity-100;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes fadeIn {
|
||||
from {
|
||||
opacity: 0;
|
||||
|
|
|
@ -4,5 +4,5 @@
|
|||
"target": "es2020",
|
||||
"outDir": "./dist"
|
||||
},
|
||||
"include": ["src"]
|
||||
"include": ["src", "../ui/src/Dialog.tsx", "../ui/src/Loader.tsx"]
|
||||
}
|
||||
|
|
|
@ -1,62 +1,65 @@
|
|||
{
|
||||
"name": "@sd/ui",
|
||||
"version": "0.0.0",
|
||||
"license": "GPL-3.0-only",
|
||||
"main": "src/index.ts",
|
||||
"exports": {
|
||||
".": "./src/index.ts",
|
||||
"./postcss": "./style/postcss.config.js",
|
||||
"./tailwind": "./style/tailwind.js",
|
||||
"./style": "./style/index.js",
|
||||
"./style/style.scss": "./style/style.scss",
|
||||
"./package.json": "./package.json"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "tsc",
|
||||
"storybook": "start-storybook -p 6006",
|
||||
"storybook:build": "build-storybook"
|
||||
},
|
||||
"dependencies": {
|
||||
"@headlessui/react": "^1.7.3",
|
||||
"@heroicons/react": "^2.0.12",
|
||||
"@radix-ui/react-context-menu": "^1.0.0",
|
||||
"@radix-ui/react-dropdown-menu": "^1.0.0",
|
||||
"@tailwindcss/forms": "^0.5.3",
|
||||
"class-variance-authority": "^0.2.3",
|
||||
"clsx": "^1.2.1",
|
||||
"phosphor-react": "^1.4.1",
|
||||
"postcss": "^8.4.17",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-router-dom": "6.4.2",
|
||||
"storybook": "^6.5.12",
|
||||
"tailwindcss": "^3.1.8"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.19.3",
|
||||
"@sd/config": "workspace:*",
|
||||
"@storybook/addon-actions": "^6.5.12",
|
||||
"@storybook/addon-essentials": "^6.5.12",
|
||||
"@storybook/addon-interactions": "^6.5.12",
|
||||
"@storybook/addon-links": "^6.5.12",
|
||||
"@storybook/addon-postcss": "2.0.0",
|
||||
"@storybook/builder-webpack5": "^6.5.12",
|
||||
"@storybook/manager-webpack5": "^6.5.12",
|
||||
"@storybook/preset-scss": "^1.0.3",
|
||||
"@storybook/react": "^6.5.12",
|
||||
"@storybook/testing-library": "^0.0.13",
|
||||
"@tailwindcss/line-clamp": "^0.4.2",
|
||||
"@tailwindcss/typography": "^0.5.7",
|
||||
"@types/react": "^18.0.21",
|
||||
"@types/react-dom": "^18.0.6",
|
||||
"autoprefixer": "^10.4.12",
|
||||
"babel-loader": "^8.2.5",
|
||||
"css-loader": "^6.7.1",
|
||||
"postcss-loader": "^7.0.1",
|
||||
"sass": "^1.55.0",
|
||||
"sass-loader": "^13.0.2",
|
||||
"storybook-tailwind-dark-mode": "^1.0.15",
|
||||
"style-loader": "^3.3.1",
|
||||
"typescript": "^4.8.4"
|
||||
}
|
||||
"name": "@sd/ui",
|
||||
"version": "0.0.0",
|
||||
"license": "GPL-3.0-only",
|
||||
"main": "src/index.ts",
|
||||
"exports": {
|
||||
".": "./src/index.ts",
|
||||
"./postcss": "./style/postcss.config.js",
|
||||
"./tailwind": "./style/tailwind.js",
|
||||
"./style": "./style/index.js",
|
||||
"./style/style.scss": "./style/style.scss",
|
||||
"./package.json": "./package.json"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "tsc",
|
||||
"storybook": "start-storybook -p 6006",
|
||||
"storybook:build": "build-storybook"
|
||||
},
|
||||
"dependencies": {
|
||||
"@headlessui/react": "^1.7.3",
|
||||
"@heroicons/react": "^2.0.12",
|
||||
"@radix-ui/react-context-menu": "^1.0.0",
|
||||
"@radix-ui/react-dialog": "^1.0.0",
|
||||
"@radix-ui/react-dropdown-menu": "^1.0.0",
|
||||
"@tailwindcss/forms": "^0.5.3",
|
||||
"class-variance-authority": "^0.2.3",
|
||||
"clsx": "^1.2.1",
|
||||
"phosphor-react": "^1.4.1",
|
||||
"postcss": "^8.4.17",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-router-dom": "6.4.2",
|
||||
"react-loading-icons": "^1.1.0",
|
||||
"react-spring": "^9.5.5",
|
||||
"storybook": "^6.5.12",
|
||||
"tailwindcss": "^3.1.8"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.19.3",
|
||||
"@sd/config": "workspace:*",
|
||||
"@storybook/addon-actions": "^6.5.12",
|
||||
"@storybook/addon-essentials": "^6.5.12",
|
||||
"@storybook/addon-interactions": "^6.5.12",
|
||||
"@storybook/addon-links": "^6.5.12",
|
||||
"@storybook/addon-postcss": "2.0.0",
|
||||
"@storybook/builder-webpack5": "^6.5.12",
|
||||
"@storybook/manager-webpack5": "^6.5.12",
|
||||
"@storybook/preset-scss": "^1.0.3",
|
||||
"@storybook/react": "^6.5.12",
|
||||
"@storybook/testing-library": "^0.0.13",
|
||||
"@tailwindcss/line-clamp": "^0.4.2",
|
||||
"@tailwindcss/typography": "^0.5.7",
|
||||
"@types/react": "^18.0.21",
|
||||
"@types/react-dom": "^18.0.6",
|
||||
"autoprefixer": "^10.4.12",
|
||||
"babel-loader": "^8.2.5",
|
||||
"css-loader": "^6.7.1",
|
||||
"postcss-loader": "^7.0.1",
|
||||
"sass": "^1.55.0",
|
||||
"sass-loader": "^13.0.2",
|
||||
"storybook-tailwind-dark-mode": "^1.0.15",
|
||||
"style-loader": "^3.3.1",
|
||||
"typescript": "^4.8.4"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@ import { Link, LinkProps } from 'react-router-dom';
|
|||
|
||||
const sizes = {
|
||||
default: 'py-1 px-3 text-md font-medium',
|
||||
sm: 'py-1 px-2 text-sm font-medium'
|
||||
sm: 'py-1 px-2 text-xs font-medium'
|
||||
};
|
||||
|
||||
const variants = {
|
||||
|
@ -118,14 +118,16 @@ export const Button = forwardRef<
|
|||
ref
|
||||
) => {
|
||||
className = clsx(
|
||||
'border rounded-md items-center transition-colors duration-100 cursor-default',
|
||||
{ 'opacity-70': loading, '!p-1': noPadding },
|
||||
{ 'justify-center': !justifyLeft },
|
||||
'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'],
|
||||
{ 'active:translate-y-[1px]': pressEffect },
|
||||
{ 'border-0': noBorder },
|
||||
'disabled:opacity-50 disabled:cursor-not-allowed',
|
||||
className
|
||||
);
|
||||
|
||||
|
|
99
packages/ui/src/Dialog.tsx
Normal file
99
packages/ui/src/Dialog.tsx
Normal file
|
@ -0,0 +1,99 @@
|
|||
import * as DialogPrimitive from '@radix-ui/react-dialog';
|
||||
import clsx from 'clsx';
|
||||
import { ReactNode, useState } from 'react';
|
||||
import { animated, config, useTransition } from 'react-spring';
|
||||
|
||||
import { Button, Loader } from '../';
|
||||
|
||||
export interface DialogProps extends DialogPrimitive.DialogProps {
|
||||
trigger: ReactNode;
|
||||
ctaLabel?: string;
|
||||
ctaDanger?: boolean;
|
||||
ctaAction?: () => void;
|
||||
title?: string;
|
||||
description?: string;
|
||||
children?: ReactNode;
|
||||
transformOrigin?: string;
|
||||
loading?: boolean;
|
||||
submitDisabled?: boolean;
|
||||
}
|
||||
|
||||
export function Dialog(props: DialogProps) {
|
||||
const [open, setOpen] = useState(false);
|
||||
|
||||
const transitions = useTransition(open, {
|
||||
from: {
|
||||
opacity: 0,
|
||||
transform: `translateY(20px)`,
|
||||
transformOrigin: props.transformOrigin || 'bottom'
|
||||
},
|
||||
enter: { opacity: 1, transform: `translateY(0px)` },
|
||||
leave: { opacity: 0, transform: `translateY(20px)` },
|
||||
config: { mass: 0.4, tension: 200, friction: 10 }
|
||||
});
|
||||
|
||||
return (
|
||||
<DialogPrimitive.Root open={open} onOpenChange={setOpen}>
|
||||
<DialogPrimitive.Trigger asChild>{props.trigger}</DialogPrimitive.Trigger>
|
||||
{transitions(
|
||||
(styles, show) =>
|
||||
show && (
|
||||
<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]"
|
||||
style={{
|
||||
opacity: styles.opacity
|
||||
}}
|
||||
>
|
||||
<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"
|
||||
>
|
||||
<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">
|
||||
{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">
|
||||
{props.loading && <Loader />}
|
||||
<div className="flex-grow" />
|
||||
<DialogPrimitive.Close asChild>
|
||||
<Button
|
||||
loading={props.loading}
|
||||
disabled={props.loading}
|
||||
size="sm"
|
||||
variant="gray"
|
||||
>
|
||||
Close
|
||||
</Button>
|
||||
</DialogPrimitive.Close>
|
||||
<Button
|
||||
onClick={props.ctaAction}
|
||||
type="submit"
|
||||
size="sm"
|
||||
loading={props.loading}
|
||||
disabled={props.loading || props.submitDisabled}
|
||||
variant={props.ctaDanger ? 'colored' : 'primary'}
|
||||
className={clsx(props.ctaDanger && 'bg-red-500 border-red-500')}
|
||||
>
|
||||
{props.ctaLabel}
|
||||
</Button>
|
||||
</div>
|
||||
</form>
|
||||
</animated.div>
|
||||
</DialogPrimitive.Content>
|
||||
</animated.div>
|
||||
</DialogPrimitive.Overlay>
|
||||
</DialogPrimitive.Portal>
|
||||
)
|
||||
)}
|
||||
</DialogPrimitive.Root>
|
||||
);
|
||||
}
|
|
@ -25,8 +25,7 @@ const variants = {
|
|||
|
||||
dark:text-white
|
||||
placeholder-gray-300
|
||||
`,
|
||||
primary: ''
|
||||
`
|
||||
};
|
||||
|
||||
interface InputProps extends React.InputHTMLAttributes<HTMLInputElement> {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import clsx from 'clsx';
|
||||
import { Puff } from 'react-loading-icons';
|
||||
|
||||
export default function Loader(props: { className?: string }) {
|
||||
export function Loader(props: { className?: string }) {
|
||||
return (
|
||||
<Puff
|
||||
stroke="#2599FF"
|
|
@ -1,15 +1,17 @@
|
|||
import * as DropdownMenu from '@radix-ui/react-dropdown-menu';
|
||||
import clsx from 'clsx';
|
||||
import { PropsWithChildren } from 'react';
|
||||
import { PropsWithChildren, useState } from 'react';
|
||||
import { animated, config, useTransition } from 'react-spring';
|
||||
|
||||
interface Props extends DropdownMenu.MenuContentProps {
|
||||
trigger: React.ReactNode;
|
||||
transformOrigin?: string;
|
||||
disabled?: boolean;
|
||||
}
|
||||
|
||||
const MENU_CLASSES = `
|
||||
flex flex-col
|
||||
min-w-[11rem] m-2 space-y-1
|
||||
min-w-[11rem] z-50 m-2 space-y-1
|
||||
text-left text-sm dark:text-gray-100 text-gray-800
|
||||
bg-gray-50 border-gray-200 dark:bg-gray-600
|
||||
border border-gray-300 dark:border-gray-500
|
||||
|
@ -22,19 +24,40 @@ export const OverlayPanel = ({
|
|||
trigger,
|
||||
children,
|
||||
disabled,
|
||||
transformOrigin,
|
||||
className,
|
||||
...props
|
||||
}: 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>
|
||||
<DropdownMenu.Root open={open} onOpenChange={setOpen}>
|
||||
<DropdownMenu.Trigger disabled={disabled} asChild>
|
||||
{trigger}
|
||||
</DropdownMenu.Trigger>
|
||||
<DropdownMenu.Portal>
|
||||
<DropdownMenu.Content {...props} className={clsx(MENU_CLASSES, className)}>
|
||||
{children}
|
||||
</DropdownMenu.Content>
|
||||
</DropdownMenu.Portal>
|
||||
{transitions(
|
||||
(styles, show) =>
|
||||
show && (
|
||||
<DropdownMenu.Portal forceMount>
|
||||
<DropdownMenu.Content forceMount asChild>
|
||||
<animated.div className={clsx(MENU_CLASSES, className)} style={styles}>
|
||||
{children}
|
||||
</animated.div>
|
||||
</DropdownMenu.Content>
|
||||
</DropdownMenu.Portal>
|
||||
)
|
||||
)}
|
||||
</DropdownMenu.Root>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
export * from './Button';
|
||||
export * from './Dropdown';
|
||||
export * from './Dialog';
|
||||
export * from './Loader';
|
||||
export * as ContextMenu from './ContextMenu';
|
||||
export * from './OverlayPanel';
|
||||
export * from './Input';
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
// const colors = require('tailwindcss/colors');
|
||||
// const plugin = require('tailwindcss/plugin');
|
||||
const plugin = require('tailwindcss/plugin');
|
||||
const defaultTheme = require('tailwindcss/defaultTheme');
|
||||
|
||||
module.exports = function (app, options) {
|
||||
|
@ -120,7 +120,13 @@ module.exports = function (app, options) {
|
|||
variants: {
|
||||
extend: {}
|
||||
},
|
||||
plugins: [require('@tailwindcss/forms')]
|
||||
plugins: [
|
||||
require('@tailwindcss/forms'),
|
||||
plugin(({ addVariant }) => {
|
||||
addVariant('open', '&[data-state="open"]');
|
||||
addVariant('closed', '&[data-state="closed"]');
|
||||
})
|
||||
]
|
||||
};
|
||||
if (app === 'landing') {
|
||||
config.plugins.push(require('@tailwindcss/typography'));
|
||||
|
|
144
pnpm-lock.yaml
144
pnpm-lock.yaml
|
@ -487,6 +487,7 @@ importers:
|
|||
'@headlessui/react': ^1.7.3
|
||||
'@heroicons/react': ^2.0.12
|
||||
'@radix-ui/react-context-menu': ^1.0.0
|
||||
'@radix-ui/react-dialog': ^1.0.0
|
||||
'@radix-ui/react-dropdown-menu': ^1.0.0
|
||||
'@sd/config': workspace:*
|
||||
'@storybook/addon-actions': ^6.5.12
|
||||
|
@ -514,7 +515,9 @@ importers:
|
|||
postcss-loader: ^7.0.1
|
||||
react: ^18.2.0
|
||||
react-dom: ^18.2.0
|
||||
react-loading-icons: ^1.1.0
|
||||
react-router-dom: 6.4.2
|
||||
react-spring: ^9.5.5
|
||||
sass: ^1.55.0
|
||||
sass-loader: ^13.0.2
|
||||
storybook: ^6.5.12
|
||||
|
@ -526,6 +529,7 @@ importers:
|
|||
'@headlessui/react': 1.7.3_biqbaboplfbrettd7655fr4n2y
|
||||
'@heroicons/react': 2.0.12_react@18.2.0
|
||||
'@radix-ui/react-context-menu': 1.0.0_rj7ozvcq3uehdlnj3cbwzbi5ce
|
||||
'@radix-ui/react-dialog': 1.0.0_rj7ozvcq3uehdlnj3cbwzbi5ce
|
||||
'@radix-ui/react-dropdown-menu': 1.0.0_rj7ozvcq3uehdlnj3cbwzbi5ce
|
||||
'@tailwindcss/forms': 0.5.3_tailwindcss@3.1.8
|
||||
class-variance-authority: 0.2.3_typescript@4.8.4
|
||||
|
@ -534,7 +538,9 @@ importers:
|
|||
postcss: 8.4.17
|
||||
react: 18.2.0
|
||||
react-dom: 18.2.0_react@18.2.0
|
||||
react-loading-icons: 1.1.0
|
||||
react-router-dom: 6.4.2_biqbaboplfbrettd7655fr4n2y
|
||||
react-spring: 9.5.5_biqbaboplfbrettd7655fr4n2y
|
||||
storybook: 6.5.12_yalvw3r2waubxycyb7k7qsruca
|
||||
tailwindcss: 3.1.8
|
||||
devDependencies:
|
||||
|
@ -3602,6 +3608,117 @@ packages:
|
|||
'@babel/runtime': 7.19.0
|
||||
dev: false
|
||||
|
||||
/@react-spring/animated/9.5.5_react@18.2.0:
|
||||
resolution: {integrity: sha512-glzViz7syQ3CE6BQOwAyr75cgh0qsihm5lkaf24I0DfU63cMm/3+br299UEYkuaHNmfDfM414uktiPlZCNJbQA==}
|
||||
peerDependencies:
|
||||
react: ^16.8.0 || ^17.0.0 || ^18.0.0
|
||||
dependencies:
|
||||
'@react-spring/shared': 9.5.5_react@18.2.0
|
||||
'@react-spring/types': 9.5.5
|
||||
react: 18.2.0
|
||||
dev: false
|
||||
|
||||
/@react-spring/core/9.5.5_react@18.2.0:
|
||||
resolution: {integrity: sha512-shaJYb3iX18Au6gkk8ahaF0qx0LpS0Yd+ajb4asBaAQf6WPGuEdJsbsNSgei1/O13JyEATsJl20lkjeslJPMYA==}
|
||||
peerDependencies:
|
||||
react: ^16.8.0 || ^17.0.0 || ^18.0.0
|
||||
dependencies:
|
||||
'@react-spring/animated': 9.5.5_react@18.2.0
|
||||
'@react-spring/rafz': 9.5.5
|
||||
'@react-spring/shared': 9.5.5_react@18.2.0
|
||||
'@react-spring/types': 9.5.5
|
||||
react: 18.2.0
|
||||
dev: false
|
||||
|
||||
/@react-spring/konva/9.5.5_react@18.2.0:
|
||||
resolution: {integrity: sha512-0CNh+1vCIjNUklTFwMvxg+H83Jo2OWykBrdEA28ccmnpZgkQ8Kq5xyvaPFLzcDKV67OXHnaWiCYKpRbhLy2wng==}
|
||||
peerDependencies:
|
||||
konva: '>=2.6'
|
||||
react: ^16.8.0 || ^17.0.0 || ^18.0.0
|
||||
react-konva: ^16.8.0 || ^17.0.0
|
||||
dependencies:
|
||||
'@react-spring/animated': 9.5.5_react@18.2.0
|
||||
'@react-spring/core': 9.5.5_react@18.2.0
|
||||
'@react-spring/shared': 9.5.5_react@18.2.0
|
||||
'@react-spring/types': 9.5.5
|
||||
react: 18.2.0
|
||||
dev: false
|
||||
|
||||
/@react-spring/native/9.5.5_react@18.2.0:
|
||||
resolution: {integrity: sha512-kauqmyJ8u7aVy2bBs22vl1SdB2i5uYIL4rP53k1KDWrFSqJh4j3efWkbTt9uzR5cMXuNVbkNo9OYVFUcQBz50A==}
|
||||
peerDependencies:
|
||||
react: ^16.8.0 || >=17.0.0 || >=18.0.0
|
||||
react-native: '>=0.58'
|
||||
dependencies:
|
||||
'@react-spring/animated': 9.5.5_react@18.2.0
|
||||
'@react-spring/core': 9.5.5_react@18.2.0
|
||||
'@react-spring/shared': 9.5.5_react@18.2.0
|
||||
'@react-spring/types': 9.5.5
|
||||
react: 18.2.0
|
||||
dev: false
|
||||
|
||||
/@react-spring/rafz/9.5.5:
|
||||
resolution: {integrity: sha512-F/CLwB0d10jL6My5vgzRQxCNY2RNyDJZedRBK7FsngdCmzoq3V4OqqNc/9voJb9qRC2wd55oGXUeXv2eIaFmsw==}
|
||||
dev: false
|
||||
|
||||
/@react-spring/shared/9.5.5_react@18.2.0:
|
||||
resolution: {integrity: sha512-YwW70Pa/YXPOwTutExHZmMQSHcNC90kJOnNR4G4mCDNV99hE98jWkIPDOsgqbYx3amIglcFPiYKMaQuGdr8dyQ==}
|
||||
peerDependencies:
|
||||
react: ^16.8.0 || ^17.0.0 || ^18.0.0
|
||||
dependencies:
|
||||
'@react-spring/rafz': 9.5.5
|
||||
'@react-spring/types': 9.5.5
|
||||
react: 18.2.0
|
||||
dev: false
|
||||
|
||||
/@react-spring/three/9.5.5_react@18.2.0:
|
||||
resolution: {integrity: sha512-9kTIaSceqFIl5EIrdwM7Z53o5I+9BGNVzbp4oZZYMao+GMAWOosnlQdDG5GeqNsIqfW9fZCEquGqagfKAxftcA==}
|
||||
peerDependencies:
|
||||
'@react-three/fiber': '>=6.0'
|
||||
react: ^16.8.0 || ^17.0.0 || ^18.0.0
|
||||
three: '>=0.126'
|
||||
dependencies:
|
||||
'@react-spring/animated': 9.5.5_react@18.2.0
|
||||
'@react-spring/core': 9.5.5_react@18.2.0
|
||||
'@react-spring/shared': 9.5.5_react@18.2.0
|
||||
'@react-spring/types': 9.5.5
|
||||
react: 18.2.0
|
||||
dev: false
|
||||
|
||||
/@react-spring/types/9.5.5:
|
||||
resolution: {integrity: sha512-7I/qY8H7Enwasxr4jU6WmtNK+RZ4Z/XvSlDvjXFVe7ii1x0MoSlkw6pD7xuac8qrHQRm9BTcbZNyeeKApYsvCg==}
|
||||
dev: false
|
||||
|
||||
/@react-spring/web/9.5.5_biqbaboplfbrettd7655fr4n2y:
|
||||
resolution: {integrity: sha512-+moT8aDX/ho/XAhU+HRY9m0LVV9y9CK6NjSRaI+30Re150pB3iEip6QfnF4qnhSCQ5drpMF0XRXHgOTY/xbtFw==}
|
||||
peerDependencies:
|
||||
react: ^16.8.0 || ^17.0.0 || ^18.0.0
|
||||
react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0
|
||||
dependencies:
|
||||
'@react-spring/animated': 9.5.5_react@18.2.0
|
||||
'@react-spring/core': 9.5.5_react@18.2.0
|
||||
'@react-spring/shared': 9.5.5_react@18.2.0
|
||||
'@react-spring/types': 9.5.5
|
||||
react: 18.2.0
|
||||
react-dom: 18.2.0_react@18.2.0
|
||||
dev: false
|
||||
|
||||
/@react-spring/zdog/9.5.5_biqbaboplfbrettd7655fr4n2y:
|
||||
resolution: {integrity: sha512-LZgjo2kLlGmUqfE2fdVnvLXz+4eYyQARRvB9KQ4PTEynaETTG89Xgn9YxLrh1p57DzH7gEmTGDZ5hEw3pWqu8g==}
|
||||
peerDependencies:
|
||||
react: ^16.8.0 || ^17.0.0 || ^18.0.0
|
||||
react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0
|
||||
react-zdog: '>=1.0'
|
||||
zdog: '>=1.0'
|
||||
dependencies:
|
||||
'@react-spring/animated': 9.5.5_react@18.2.0
|
||||
'@react-spring/core': 9.5.5_react@18.2.0
|
||||
'@react-spring/shared': 9.5.5_react@18.2.0
|
||||
'@react-spring/types': 9.5.5
|
||||
react: 18.2.0
|
||||
react-dom: 18.2.0_react@18.2.0
|
||||
dev: false
|
||||
|
||||
/@remix-run/router/1.0.2:
|
||||
resolution: {integrity: sha512-GRSOFhJzjGN+d4sKHTMSvNeUPoZiDHWmRnXfzaxrqe7dE/Nzlc8BiMSJdLDESZlndM7jIUrZ/F4yWqVYlI0rwQ==}
|
||||
engines: {node: '>=14'}
|
||||
|
@ -7014,7 +7131,6 @@ packages:
|
|||
normalize-range: 0.1.2
|
||||
picocolors: 1.0.0
|
||||
postcss-value-parser: 4.2.0
|
||||
dev: false
|
||||
|
||||
/autoprefixer/10.4.12_postcss@8.4.17:
|
||||
resolution: {integrity: sha512-WrCGV9/b97Pa+jtwf5UGaRjgQIg7OK3D06GnoYoZNcG1Xb8Gt3EfuKjlhh9i/VtT16g6PYjZ69jdJ2g8FxSC4Q==}
|
||||
|
@ -15226,6 +15342,30 @@ packages:
|
|||
three: 0.123.0
|
||||
dev: false
|
||||
|
||||
/react-spring/9.5.5_biqbaboplfbrettd7655fr4n2y:
|
||||
resolution: {integrity: sha512-vMGVd2yjgxWcRCzoLn9AD1d24+WpunHBRg5DoehcRdiBocaOH6qgle0xN9C5LPplXfv4yIpS5QWGN5MKrWxSZg==}
|
||||
peerDependencies:
|
||||
react: ^16.8.0 || ^17.0.0 || ^18.0.0
|
||||
react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0
|
||||
dependencies:
|
||||
'@react-spring/core': 9.5.5_react@18.2.0
|
||||
'@react-spring/konva': 9.5.5_react@18.2.0
|
||||
'@react-spring/native': 9.5.5_react@18.2.0
|
||||
'@react-spring/three': 9.5.5_react@18.2.0
|
||||
'@react-spring/web': 9.5.5_biqbaboplfbrettd7655fr4n2y
|
||||
'@react-spring/zdog': 9.5.5_biqbaboplfbrettd7655fr4n2y
|
||||
react: 18.2.0
|
||||
react-dom: 18.2.0_react@18.2.0
|
||||
transitivePeerDependencies:
|
||||
- '@react-three/fiber'
|
||||
- konva
|
||||
- react-konva
|
||||
- react-native
|
||||
- react-zdog
|
||||
- three
|
||||
- zdog
|
||||
dev: false
|
||||
|
||||
/react-style-singleton/2.2.1_iapumuv4e6jcjznwuxpf4tt22e:
|
||||
resolution: {integrity: sha512-ZWj0fHEMyWkHzKYUr2Bs/4zU6XLmq9HsgBURm7g5pAVfyn49DgUiNgY2d4lXRlYSiCif9YBGpQleewkcqddc7g==}
|
||||
engines: {node: '>=10'}
|
||||
|
@ -18152,7 +18292,7 @@ packages:
|
|||
valtio: ^1.2.5
|
||||
dependencies:
|
||||
lodash: 4.17.21
|
||||
valtio: 1.7.0_react@18.2.0+vite@3.1.4
|
||||
valtio: 1.7.0
|
||||
dev: false
|
||||
|
||||
/valtio/1.7.0:
|
||||
|
|
Loading…
Reference in a new issue