feat(ui): added ui components for new dashboard

This commit is contained in:
akhilmhdh
2023-03-19 00:32:59 +05:30
parent 51f4ab473b
commit 686d3c409d
21 changed files with 445 additions and 41 deletions

View File

@@ -20,7 +20,7 @@ type EncryptAsymmetricProps = {
* @param {Number} obj.time - number of iterations
* @param {Number} obj.parallelism - desired parallelism
* @param {Number} obj.hashLen - desired hash length (i.e. byte-length of derived key)
* @returns
* @returns
*/
const deriveArgonKey = async ({
password,
@@ -39,8 +39,8 @@ const deriveArgonKey = async ({
}) => {
let derivedKey;
try {
derivedKey = await argon2.hash({
pass: password,
derivedKey = await argon2.hash({
pass: password,
salt,
type: argon2.ArgonType.Argon2id,
mem,
@@ -53,7 +53,7 @@ const deriveArgonKey = async ({
}
return derivedKey;
}
};
/**
* Return assymmetrically encrypted [plaintext] using [publicKey] where
@@ -173,6 +173,7 @@ type DecryptSymmetricProps = {
*
*/
const decryptSymmetric = ({ ciphertext, iv, tag, key }: DecryptSymmetricProps): string => {
if (!ciphertext) return '';
let plaintext;
try {
plaintext = aes.decrypt({ ciphertext, iv, tag, secret: key });
@@ -184,10 +185,10 @@ const decryptSymmetric = ({ ciphertext, iv, tag, key }: DecryptSymmetricProps):
return plaintext;
};
export {
decryptAssymmetric,
decryptSymmetric,
export {
decryptAssymmetric,
decryptSymmetric,
deriveArgonKey,
encryptAssymmetric,
encryptSymmetric
encryptAssymmetric,
encryptSymmetric
};

View File

@@ -36,7 +36,7 @@ const buttonVariants = cva(
star: 'text-bunker-200 bg-mineshaft-500'
},
isDisabled: {
true: 'bg-mineshaft opacity-40 cursor-not-allowed',
true: 'bg-mineshaft text-white opacity-50 cursor-not-allowed',
false: ''
},
isFullWidth: {
@@ -155,26 +155,26 @@ export const Button = forwardRef<HTMLButtonElement, ButtonProps>(
className="absolute rounded-xl"
/>
)}
<span
<div
className={twMerge(
'shrink-0 cursor-pointer transition-all',
'inline-flex shrink-0 cursor-pointer items-center justify-center transition-all',
loadingToggleClass,
leftIcon && 'ml-2',
size === 'xs' ? 'mr-1' : 'mr-3'
size === 'xs' ? 'mr-1' : 'mr-2'
)}
>
{leftIcon}
</span>
<span className={twMerge('transition-all', loadingToggleClass)}>{children}</span>
<span
</div>
<span className={twMerge('w-full transition-all', loadingToggleClass)}>{children}</span>
<div
className={twMerge(
'shrink-0 cursor-pointer transition-all',
'inline-flex shrink-0 cursor-pointer items-center justify-center transition-all',
loadingToggleClass,
size === 'xs' ? 'ml-1' : 'ml-3'
size === 'xs' ? 'ml-1' : 'ml-2'
)}
>
{rightIcon}
</span>
</div>
</button>
);
}

View File

@@ -0,0 +1,32 @@
import type { Meta, StoryObj } from '@storybook/react';
import { Button } from '../Button';
import { Drawer, DrawerContent, DrawerContentProps, DrawerTrigger } from './Drawer';
const meta: Meta<typeof Drawer> = {
title: 'Components/Drawer',
component: Drawer,
tags: ['v2'],
argTypes: {}
};
export default meta;
type Story = StoryObj<typeof DrawerContent>;
const Template = (args: DrawerContentProps) => (
<Drawer>
<DrawerTrigger asChild>
<Button>Open</Button>
</DrawerTrigger>
<DrawerContent {...args}>Hello world</DrawerContent>
</Drawer>
);
export const Basic: Story = {
render: (args) => <Template {...args} />,
args: {
title: 'Title',
subTitle: 'Something as subtitle',
footerContent: 'footer content'
}
};

View File

@@ -0,0 +1,84 @@
import { forwardRef, ReactNode } from 'react';
import { faTimes } from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import * as DialogPrimitive from '@radix-ui/react-dialog';
import { cva, VariantProps } from 'cva';
import { twMerge } from 'tailwind-merge';
import { Card, CardBody, CardFooter, CardTitle } from '../Card';
import { IconButton } from '../IconButton';
export type DrawerContentProps = DialogPrimitive.DialogContentProps & {
title?: ReactNode;
subTitle?: ReactNode;
footerContent?: ReactNode;
onClose?: () => void;
} & VariantProps<typeof drawerContentVariation>;
const drawerContentVariation = cva(
'fixed ease-in-out duration-300 z-[90] border border-mineshaft-600 drop-shadow-2xl',
{
variants: {
direction: {
right: [
'right-0 top-0',
'h-full w-96',
'data-[state=open]:animate-drawerRightIn data-[state=closed]:animate-drawerRightOut'
]
}
}
}
);
export const DrawerContent = forwardRef<HTMLDivElement, DrawerContentProps>(
(
{ children, title, subTitle, className, footerContent, direction = 'right', onClose, ...props },
forwardedRef
) => (
<DialogPrimitive.Portal>
<DialogPrimitive.Overlay
className="fixed inset-0 z-[70] h-full w-full"
style={{ backgroundColor: 'rgba(0, 0, 0, 0.7)' }}
/>
<DialogPrimitive.Content
{...props}
ref={forwardedRef}
className={twMerge(drawerContentVariation({ direction, className }))}
>
<Card isRounded={false} className="h-full w-full">
{title && (
<CardTitle subTitle={subTitle} className="px-4">
{title}
</CardTitle>
)}
<CardBody className="flex-grow overflow-y-auto overflow-x-hidden px-4">
{children}
</CardBody>
{footerContent && <CardFooter>{footerContent}</CardFooter>}{' '}
<DialogPrimitive.Close aria-label="Close" asChild onClick={onClose}>
<IconButton
variant="plain"
ariaLabel="close"
className="absolute top-4 right-6 rounded text-bunker-400 hover:text-bunker-50"
>
<FontAwesomeIcon icon={faTimes} size="lg" className="cursor-pointer" />
</IconButton>
</DialogPrimitive.Close>
</Card>
</DialogPrimitive.Content>
</DialogPrimitive.Portal>
)
);
DrawerContent.displayName = 'ModalContent';
export type DrawerProps = Omit<DialogPrimitive.DialogProps, 'open'> & { isOpen?: boolean };
export const Drawer = ({ isOpen, ...props }: DrawerProps) => (
<DialogPrimitive.Root open={isOpen} {...props} />
);
export const DrawerTrigger = DialogPrimitive.Trigger;
export type DrawerTriggerProps = DialogPrimitive.DialogTriggerProps;
export const DrawerClose = DialogPrimitive.Close;
export type DrawerCloseProps = DialogPrimitive.DialogCloseProps;

View File

@@ -0,0 +1 @@
export { Drawer, DrawerClose,DrawerContent, DrawerTrigger } from './Drawer';

View File

@@ -0,0 +1,36 @@
import type { Meta, StoryObj } from '@storybook/react';
import { Button } from '../Button';
import {
HoverCard,
HoverCardContent,
HoverCardContentProps,
HoverCardTrigger
} from './HoverCardv2';
const meta: Meta<typeof HoverCardContent> = {
title: 'Components/HoverCard',
component: HoverCard,
tags: ['v2']
};
export default meta;
type Story = StoryObj<typeof HoverCardContent>;
const Template = (args: HoverCardContentProps) => (
<HoverCard>
<HoverCardTrigger>
<Button>Hello world</Button>
</HoverCardTrigger>
<HoverCardContent {...args} sideOffset={10}>
<div className="bg-mineshaft-600 p-4 text-gray-400">
<h1>This is hover card</h1>
</div>
</HoverCardContent>
</HoverCard>
);
// More on writing stories with args: https://storybook.js.org/docs/7.0/react/writing-stories/args
export const Primary: Story = {
render: (args) => <Template {...args} />
};

View File

@@ -0,0 +1,26 @@
import { ReactNode } from 'react';
import * as HoverCardPrimitive from '@radix-ui/react-hover-card';
import { twMerge } from 'tailwind-merge';
export const HoverCardTrigger = HoverCardPrimitive.Trigger;
export const HoverCard = HoverCardPrimitive.Root;
export type HoverCardContentProps = {
children?: ReactNode;
} & HoverCardPrimitive.HoverCardContentProps;
export const HoverCardContent = ({ children, className, ...props }: HoverCardContentProps) => (
<HoverCardPrimitive.Portal>
<HoverCardPrimitive.Content
{...props}
className={twMerge(
'relative w-64 rounded-md bg-mineshaft-600 fill-mineshaft-600 p-4 pt-6 font-inter text-gray-200 shadow-md',
className
)}
>
{children}
<HoverCardPrimitive.Arrow className="fill-inherit stroke-inherit" />
</HoverCardPrimitive.Content>
</HoverCardPrimitive.Portal>
);

View File

@@ -0,0 +1 @@
export { HoverCard,HoverCardContent, HoverCardTrigger } from './HoverCardv2';

View File

@@ -29,7 +29,8 @@ const iconButtonVariants = cva(
variant: {
solid: '',
outline: ['bg-transparent', 'border-2', 'border-solid'],
plain: ''
plain: '',
star: 'text-bunker-200 bg-mineshaft-500'
},
isDisabled: {
true: 'bg-opacity-70 cursor-not-allowed',
@@ -40,13 +41,23 @@ const iconButtonVariants = cva(
false: ''
},
size: {
xs: ['text-xs', 'py-2', 'px-2'],
xs: ['text-xs', 'py-1.5', 'px-2'],
sm: ['text-sm', 'py-3', 'px-3'],
md: ['text-md', 'py-4', 'px-4'],
lg: ['text-lg', 'py-6', 'px-6']
}
},
compoundVariants: [
{
colorSchema: 'primary',
variant: 'star',
className: 'hover:bg-primary hover:text-black'
},
{
colorSchema: 'danger',
variant: 'star',
className: 'hover:bg-red hover:text-white'
},
{
colorSchema: 'primary',
variant: 'outline',

View File

@@ -9,6 +9,7 @@ type Props = {
leftIcon?: ReactNode;
rightIcon?: ReactNode;
isDisabled?: boolean;
isReadOnly?: boolean;
};
const inputVariants = cva(
@@ -78,6 +79,7 @@ export const Input = forwardRef<HTMLInputElement, InputProps>(
rightIcon,
variant = 'filled',
size = 'md',
isReadOnly,
...props
},
ref
@@ -89,6 +91,7 @@ export const Input = forwardRef<HTMLInputElement, InputProps>(
{...props}
required={isRequired}
ref={ref}
readOnly={isReadOnly}
disabled={isDisabled}
className={twMerge(
leftIcon ? 'pl-10' : 'pl-2.5',

View File

@@ -12,20 +12,24 @@ export type ModalContentProps = DialogPrimitive.DialogContentProps & {
subTitle?: ReactNode;
footerContent?: ReactNode;
onClose?: () => void;
overlayClassName?: string;
};
export const ModalContent = forwardRef<HTMLDivElement, ModalContentProps>(
({ children, title, subTitle, className, footerContent, onClose, ...props }, forwardedRef) => (
(
{ children, title, subTitle, className, overlayClassName, footerContent, onClose, ...props },
forwardedRef
) => (
<DialogPrimitive.Portal>
<DialogPrimitive.Overlay
className="fixed inset-0 h-full w-full animate-fadeIn z-[70]"
className={twMerge('fixed inset-0 z-[70] h-full w-full animate-fadeIn', overlayClassName)}
style={{ backgroundColor: 'rgba(0, 0, 0, 0.7)' }}
/>
<DialogPrimitive.Content {...props} ref={forwardedRef}>
<Card
isRounded
className={twMerge(
'fixed top-1/2 left-1/2 max-w-lg border border-mineshaft-600 -translate-y-2/4 -translate-x-2/4 animate-popIn drop-shadow-2xl z-[90]',
'fixed top-1/2 left-1/2 z-[90] max-w-lg -translate-y-2/4 -translate-x-2/4 animate-popIn border border-mineshaft-600 drop-shadow-2xl',
className
)}
>

View File

@@ -0,0 +1,29 @@
import type { Meta, StoryObj } from '@storybook/react';
import { Button } from '../Button';
import { Popover, PopoverContent, PopoverContentProps, PopoverTrigger } from './Popoverv2';
const meta: Meta<typeof PopoverContent> = {
title: 'Components/Popover',
component: Popover,
tags: ['v2']
};
export default meta;
type Story = StoryObj<typeof PopoverContent>;
const Template = (args: PopoverContentProps) => (
<Popover>
<PopoverTrigger asChild>
<Button>Hello world</Button>
</PopoverTrigger>
<PopoverContent {...args} side="bottom" sideOffset={10}>
<h1>This is popper</h1>
</PopoverContent>
</Popover>
);
// More on writing stories with args: https://storybook.js.org/docs/7.0/react/writing-stories/args
export const Primary: Story = {
render: (args) => <Template {...args} />
};

View File

@@ -0,0 +1,54 @@
import { ReactNode } from 'react';
import { faTimes } from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import * as PopoverPrimitive from '@radix-ui/react-popover';
import { twMerge } from 'tailwind-merge';
import { IconButton } from '../IconButton';
export const PopoverTrigger = PopoverPrimitive.Trigger;
export const Popover = PopoverPrimitive.Root;
export type PopoverContentProps = {
children?: ReactNode;
hideCloseBtn?: boolean;
} & PopoverPrimitive.PopperContentProps;
export const PopoverContent = ({
children,
className,
hideCloseBtn,
...props
}: PopoverContentProps) => (
<PopoverPrimitive.Portal>
<PopoverPrimitive.Content
className={twMerge(
[
'relative z-[100] w-64 rounded-md bg-mineshaft-600 fill-mineshaft-600 p-4 pt-6 font-inter text-gray-200 shadow-md',
// animation
'data-[state=open]:data-[side=bottom]:animate-slideUpAndFade',
'data-[state=open]:data-[side=top]:animate-slideDownAndFade',
'data-[state=open]:data-[side=left]:animate-slideRightAndFade',
'data-[state=open]:data-[side=right]:animate-slideLeftAndFade'
],
className
)}
{...props}
>
{children}
{!hideCloseBtn && (
<PopoverPrimitive.Close aria-label="Close" asChild>
<IconButton
variant="plain"
ariaLabel="close"
className="absolute top-0 right-1 rounded text-bunker-400 hover:text-bunker-50"
>
<FontAwesomeIcon icon={faTimes} size="lg" className="cursor-pointer" />
</IconButton>
</PopoverPrimitive.Close>
)}
<PopoverPrimitive.Arrow className="fill-inherit" />
</PopoverPrimitive.Content>
</PopoverPrimitive.Portal>
);

View File

@@ -0,0 +1 @@
export { Popover,PopoverContent, PopoverTrigger } from './Popoverv2';

View File

@@ -19,7 +19,7 @@ export const Switch = ({
isRequired,
...props
}: SwitchProps): JSX.Element => (
<div className="flex items-center text-bunker-300 font-inter">
<div className="flex items-center font-inter text-bunker-300">
<label className="text-sm" htmlFor={id}>
{children}
{isRequired && <span className="pl-1 text-red">*</span>}
@@ -30,13 +30,13 @@ export const Switch = ({
checked={isChecked}
disabled={isDisabled}
className={twMerge(
'h-5 ml-3 transition-all rounded-full w-9 bg-bunker-300 data-[state=checked]:bg-bunker-200',
'ml-3 h-5 w-9 rounded-full bg-bunker-300 transition-all data-[state=checked]:bg-primary',
isDisabled && 'bg-bunker-400 hover:bg-bunker-400',
className
)}
id={id}
>
<SwitchPrimitive.Thumb className="w-4 h-4 border-none will-change-transform rounded-full shadow bg-black block transition-all translate-x-0.5 data-[state=checked]:translate-x-[18px]" />
<SwitchPrimitive.Thumb className="block h-4 w-4 translate-x-0.5 rounded-full border-none bg-black shadow transition-all will-change-transform data-[state=checked]:translate-x-[18px]" />
</SwitchPrimitive.Root>
</div>
);

View File

@@ -1,24 +1,56 @@
import { ReactNode } from 'react';
import { faClose } from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { cva, VariantProps } from 'cva';
import { twMerge } from 'tailwind-merge';
type Props = {
children: ReactNode;
className?: string;
onClose?: () => void;
color?: string;
isDisabled?: boolean;
} & VariantProps<typeof tagVariants>;
const tagVariants = cva('inline-flex whitespace-nowrap text-sm rounded-sm mr-1.5 text-bunker-200 ', {
variants: {
colorSchema: {
gray: 'bg-mineshaft-500',
red: 'bg-red/80 text-bunker-100'
},
size: {
sm: 'px-1.5 py-0.5'
const tagVariants = cva(
'inline-flex items-center whitespace-nowrap text-sm rounded-sm mr-1.5 text-bunker-200',
{
variants: {
colorSchema: {
gray: 'bg-mineshaft-500',
red: 'bg-red/80 text-bunker-100',
green: 'bg-primary-800 text-white'
},
size: {
sm: 'px-2 py-0.5'
}
}
}
});
export const Tag = ({ children, className, colorSchema = 'gray', size = 'sm' }: Props) => (
<div className={twMerge(tagVariants({ colorSchema, className, size }))}>{children}</div>
);
export const Tag = ({
children,
className,
colorSchema = 'gray',
color,
isDisabled,
size = 'sm',
onClose
}: Props) => (
<div
className={twMerge(tagVariants({ colorSchema, className, size }))}
style={{ backgroundColor: color }}
>
{children}
{onClose && (
<button
type="button"
onClick={onClose}
disabled={isDisabled}
className="ml-2 flex items-center justify-center"
>
<FontAwesomeIcon icon={faClose} />
</button>
)}
</div>
);

View File

@@ -3,6 +3,7 @@ import { cva, VariantProps } from 'cva';
import { twMerge } from 'tailwind-merge';
type Props = {
isDisabled?: boolean;
placeholder?: string;
isFullWidth?: boolean;
isRequired?: boolean;
@@ -10,7 +11,7 @@ type Props = {
};
const textAreaVariants = cva(
'textarea w-full p-2 border border-solid text-gray-400 font-inter placeholder-gray-500 placeholder-opacity-50',
'textarea w-full p-2 focus:ring-2 ring-primary-800 outline-none border border-solid text-gray-400 font-inter placeholder-gray-500 placeholder-opacity-50',
{
variants: {
size: {
@@ -43,7 +44,7 @@ const textAreaVariants = cva(
}
);
export type TextAreaProps = Omit<TextareaHTMLAttributes<HTMLTextAreaElement>, 'size'> &
export type TextAreaProps = Omit<TextareaHTMLAttributes<HTMLTextAreaElement>, 'size' | 'disabled'> &
VariantProps<typeof textAreaVariants> &
Props;
@@ -52,6 +53,7 @@ export const TextArea = forwardRef<HTMLTextAreaElement, TextAreaProps>(
{
className,
isRounded = true,
isDisabled = false,
isError = false,
isRequired,
variant = 'filled',
@@ -66,6 +68,7 @@ export const TextArea = forwardRef<HTMLTextAreaElement, TextAreaProps>(
style={{ resize: reSize }}
required={isRequired}
ref={ref}
disabled={isDisabled}
className={twMerge(textAreaVariants({ className, isError, size, isRounded, variant }))}
/>
)

View File

@@ -0,0 +1,28 @@
import type { Meta, StoryObj } from '@storybook/react';
import { Tooltip, TooltipProps, TooltipProvider } from './Tooltip';
const meta: Meta<typeof Tooltip> = {
title: 'Components/Tooltip',
component: Tooltip,
tags: ['v2'],
args: {
content: 'Hi'
}
};
export default meta;
type Story = StoryObj<typeof Tooltip>;
const Template = (args: TooltipProps) => (
<TooltipProvider>
<Tooltip {...args}>
<div className="text-white">Hello infisical</div>
</Tooltip>
</TooltipProvider>
);
// More on writing stories with args: https://storybook.js.org/docs/7.0/react/writing-stories/args
export const Primary: Story = {
render: (args) => <Template {...args} />
};

View File

@@ -0,0 +1,53 @@
import { ReactNode } from 'react';
import * as TooltipPrimitive from '@radix-ui/react-tooltip';
import { twMerge } from 'tailwind-merge';
export type TooltipProps = {
children: ReactNode;
content?: ReactNode;
isOpen?: boolean;
className?: string;
asChild?: boolean;
onOpenChange?: (isOpen: boolean) => void;
defaultOpen?: boolean;
} & Omit<TooltipPrimitive.TooltipContentProps, 'open'>;
export const Tooltip = ({
children,
content,
isOpen,
onOpenChange,
defaultOpen,
className,
asChild = true,
...props
}: TooltipProps) => (
<TooltipPrimitive.Root
delayDuration={150}
open={isOpen}
defaultOpen={defaultOpen}
onOpenChange={onOpenChange}
>
<TooltipPrimitive.Trigger asChild={asChild}>{children}</TooltipPrimitive.Trigger>
<TooltipPrimitive.Content
side="top"
align="center"
sideOffset={5}
{...props}
className={twMerge(
`z-20 select-none rounded-md bg-mineshaft-500 py-2 px-4 text-sm shadow-md
data-[state=delayed-open]:data-[side=top]:animate-slideDownAndFade
data-[state=delayed-open]:data-[side=right]:animate-slideLeftAndFade
data-[state=delayed-open]:data-[side=left]:animate-slideRightAndFade
data-[state=delayed-open]:data-[side=bottom]:animate-slideUpAndFade
`,
className
)}
>
{content}
<TooltipPrimitive.Arrow width={11} height={5} className="fill-mineshaft-500" />
</TooltipPrimitive.Content>
</TooltipPrimitive.Root>
);
export const TooltipProvider = TooltipPrimitive.Provider;

View File

@@ -0,0 +1 @@
export { Tooltip, TooltipProvider } from './Tooltip';

View File

@@ -2,14 +2,17 @@ export * from './Button';
export * from './Card';
export * from './Checkbox';
export * from './DeleteActionModal';
export * from './Drawer';
export * from './Dropdown';
export * from './EmailServiceSetupModal'
export * from './EmptyState';
export * from './FormControl';
export * from './HoverCardv2';
export * from './IconButton';
export * from './Input';
export * from './Menu';
export * from './Modal';
export * from './Popoverv2';
export * from './Select';
export * from './Skeleton';
export * from './Spinner';
@@ -17,4 +20,5 @@ export * from './Switch';
export * from './Table';
export * from './Tag';
export * from './TextArea';
export * from './Tooltip';
export * from './UpgradePlanModal';