mirror of
https://github.com/spacedriveapp/spacedrive
synced 2024-07-18 08:29:12 +00:00
mockup: key manager ui
- thanks AndrewTech for help with react spring + radix combo
This commit is contained in:
parent
4cd2dde35c
commit
0e66e6a843
|
@ -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 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 ">
|
||||
|
|
192
packages/interface/src/components/key/KeyManager.tsx
Normal file
192
packages/interface/src/components/key/KeyManager.tsx
Normal 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>
|
||||
);
|
||||
}
|
|
@ -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,34 +27,29 @@ 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
|
||||
}) => {
|
||||
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 transition-colors duration-100',
|
||||
'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,
|
||||
|
@ -66,7 +62,8 @@ const TopBarButton: React.FC<TopBarButtonProps> = ({
|
|||
<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">
|
||||
<OverlayPanel
|
||||
className="focus:outline-none"
|
||||
// disabled={!library}
|
||||
trigger={
|
||||
// <Tooltip label="Major Key Alert">
|
||||
<TopBarButton icon={Key} />
|
||||
</Tooltip>
|
||||
{/* <Tooltip label="Cloud">
|
||||
// </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">
|
||||
|
|
|
@ -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,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}
|
||||
|
|
|
@ -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">
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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"
|
||||
},
|
||||
|
|
|
@ -25,8 +25,7 @@ const variants = {
|
|||
|
||||
dark:text-white
|
||||
placeholder-gray-300
|
||||
`,
|
||||
primary: ''
|
||||
`
|
||||
};
|
||||
|
||||
interface InputProps extends React.InputHTMLAttributes<HTMLInputElement> {
|
||||
|
|
|
@ -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)}>
|
||||
{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,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'));
|
||||
|
|
137
pnpm-lock.yaml
137
pnpm-lock.yaml
|
@ -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'}
|
||||
|
|
Loading…
Reference in a new issue