mockup: key manager ui

- thanks AndrewTech for help with react spring +  radix combo
This commit is contained in:
Jamie Pine 2022-10-15 21:40:10 -07:00
parent 4cd2dde35c
commit 0e66e6a843
No known key found for this signature in database
GPG key ID: D5AC85A0C2F646E9
14 changed files with 459 additions and 74 deletions

View file

@ -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>

View file

@ -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 ">

View file

@ -0,0 +1,192 @@
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-md'
// !odd && 'bg-opacity-10'
)}
>
<div className="flex items-center">
<KeyIcon
className={clsx(
'w-6 h-6 ml-1 mr-3',
data.mounted ? (data.locked ? 'text-gray-300' : 'text-primary-500') : 'text-gray-400/40'
)}
/>
<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-450/20" size="sm" value={toggle} onChange={setToggle} />
<span className="ml-3 mt-[1px] font-medium">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 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">
<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>
);
}

View file

@ -250,6 +250,7 @@ export function Sidebar() {
</NavLink>
<OverlayPanel
className="focus:outline-none"
transformOrigin="bottom left"
disabled={!library}
trigger={
<Button

View file

@ -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,25 @@ 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"
// disabled={!library}
trigger={
// <Tooltip label="Major Key Alert">
<TopBarButton icon={Key} />
// </Tooltip>
}
>
<div className="block w-[350px] h-[425px]">
<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">

View file

@ -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
}
)}

View file

@ -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}

View file

@ -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">

View file

@ -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;

View file

@ -28,6 +28,7 @@
"postcss": "^8.4.17",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-spring": "^9.5.5",
"storybook": "^6.5.12",
"tailwindcss": "^3.1.8"
},

View file

@ -25,8 +25,7 @@ const variants = {
dark:text-white
placeholder-gray-300
`,
primary: ''
`
};
interface InputProps extends React.InputHTMLAttributes<HTMLInputElement> {

View file

@ -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, transform: `scale(${0.9})` },
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>
);
};

View file

@ -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'));

View file

@ -512,6 +512,7 @@ importers:
postcss-loader: ^7.0.1
react: ^18.2.0
react-dom: ^18.2.0
react-spring: ^9.5.5
sass: ^1.55.0
sass-loader: ^13.0.2
storybook: ^6.5.12
@ -531,6 +532,7 @@ importers:
postcss: 8.4.17
react: 18.2.0
react-dom: 18.2.0_react@18.2.0
react-spring: 9.5.5_biqbaboplfbrettd7655fr4n2y
storybook: 6.5.12_yalvw3r2waubxycyb7k7qsruca
tailwindcss: 3.1.8
devDependencies:
@ -3586,6 +3588,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'}
@ -15198,6 +15311,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'}