feat(ui): fine tuning components library with exiting app design

This commit is contained in:
akhilmhdh
2023-02-03 22:21:09 +05:30
parent 3859a7e09b
commit 48cd84ce77
14 changed files with 192 additions and 47 deletions
+10 -9
View File
@@ -18,7 +18,8 @@ const buttonVariants = cva(
'font-inter font-medium', 'font-inter font-medium',
'cursor-pointer', 'cursor-pointer',
'inline-flex items-center justify-center', 'inline-flex items-center justify-center',
'relative' 'relative',
'whitespace-nowrap'
], ],
{ {
variants: { variants: {
@@ -33,7 +34,7 @@ const buttonVariants = cva(
plain: '' plain: ''
}, },
isDisabled: { isDisabled: {
true: 'bg-opacity-70 cursor-not-allowed', true: 'bg-opacity-40 cursor-not-allowed',
false: '' false: ''
}, },
isFullWidth: { isFullWidth: {
@@ -45,9 +46,9 @@ const buttonVariants = cva(
false: '' false: ''
}, },
size: { size: {
xs: ['text-xs', 'py-1', 'px-2'], xs: ['text-xs', 'py-1', 'px-1'],
sm: ['text-sm', 'py-2', 'px-4'], sm: ['text-sm', 'py-2', 'px-2'],
md: ['text-md', 'py-2', 'px-6'], md: ['text-md', 'py-2', 'px-4'],
lg: ['text-lg', 'py-2', 'px-8'] lg: ['text-lg', 'py-2', 'px-8']
} }
}, },
@@ -75,7 +76,7 @@ const buttonVariants = cva(
{ {
colorSchema: 'secondary', colorSchema: 'secondary',
variant: 'plain', variant: 'plain',
className: 'text-mineshaft' className: 'text-mineshaft-300'
}, },
{ {
colorSchema: 'danger', colorSchema: 'danger',
@@ -101,7 +102,7 @@ export const Button = forwardRef<HTMLButtonElement, ButtonProps>(
children, children,
isDisabled = false, isDisabled = false,
className = '', className = '',
size = 'md', size = 'sm',
variant = 'solid', variant = 'solid',
isFullWidth, isFullWidth,
isRounded = true, isRounded = true,
@@ -144,7 +145,7 @@ export const Button = forwardRef<HTMLButtonElement, ButtonProps>(
)} )}
<span <span
className={twMerge( className={twMerge(
'transition-all shrink-0 cursor-pointer', 'shrink-0 cursor-pointer transition-all',
loadingToggleClass, loadingToggleClass,
size === 'xs' ? 'mr-1' : 'mr-2' size === 'xs' ? 'mr-1' : 'mr-2'
)} )}
@@ -154,7 +155,7 @@ export const Button = forwardRef<HTMLButtonElement, ButtonProps>(
<span className={twMerge('transition-all', loadingToggleClass)}>{children}</span> <span className={twMerge('transition-all', loadingToggleClass)}>{children}</span>
<span <span
className={twMerge( className={twMerge(
'transition-all shrink-0 cursor-pointer', 'shrink-0 cursor-pointer transition-all',
loadingToggleClass, loadingToggleClass,
size === 'xs' ? 'ml-1' : 'ml-2' size === 'xs' ? 'ml-1' : 'ml-2'
)} )}
+2 -2
View File
@@ -29,7 +29,7 @@ export type CardBodyProps = {
}; };
export const CardBody = ({ children, className }: CardBodyProps) => ( export const CardBody = ({ children, className }: CardBodyProps) => (
<div className={twMerge('p-6 pt-0', className)}>{children}</div> <div className={twMerge('p-6 pb-4 pt-0', className)}>{children}</div>
); );
export type CardProps = { export type CardProps = {
@@ -47,7 +47,7 @@ export const Card = forwardRef<HTMLDivElement, CardProps>(
<div <div
ref={ref} ref={ref}
className={twMerge( className={twMerge(
'flex flex-col w-full font-inter text-gray-200 bg-mineshaft-700 shadow-md', 'flex w-full flex-col bg-mineshaft-800 font-inter text-gray-200 shadow-md',
isFullHeight && 'h-full', isFullHeight && 'h-full',
isRounded && 'rounded-md', isRounded && 'rounded-md',
isPlain && 'shadow-none', isPlain && 'shadow-none',
@@ -0,0 +1,89 @@
import { useEffect, useState } from 'react';
import { useToggle } from '@app/hooks';
import { Button } from '../Button';
import { FormControl } from '../FormControl';
import { Input } from '../Input';
import { Modal, ModalContent } from '../Modal';
type Props = {
isOpen?: boolean;
onClose?: () => void;
onChange?: (isOpen: boolean) => void;
deleteKey: string;
title: string;
onDeleteApproved: () => Promise<void>;
};
export const DeleteActionModal = ({
isOpen,
onClose,
onChange,
deleteKey,
onDeleteApproved,
title
}: Props): JSX.Element => {
const [inputData, setInputData] = useState('');
const [isLoading, setIsLoading] = useToggle();
useEffect(() => {
setInputData('');
}, [isOpen]);
const onDelete = async () => {
setIsLoading.on();
try {
await onDeleteApproved();
} catch {
setIsLoading.off();
} finally {
setIsLoading.off();
}
};
return (
<Modal
isOpen={isOpen}
onOpenChange={(isOpenState) => {
setInputData('');
if (onChange) onChange(isOpenState);
}}
>
<ModalContent
title={title}
subTitle="This action is irreversible!!!"
footerContent={
<div className="flex items-center">
<Button
className="mr-4"
colorSchema="danger"
isDisabled={!(deleteKey === inputData) || isLoading}
onClick={onDelete}
isLoading={isLoading}
>
Delete
</Button>
<Button variant="plain" colorSchema="secondary" onClick={onClose}>
Cancel
</Button>
</div>
}
onClose={onClose}
>
<form>
<FormControl
label={
<div className="pb-2 text-sm">
Type <span className="font-bold">{deleteKey}</span> to delete the resource
</div>
}
className="mb-4"
>
<Input value={inputData} onChange={(e) => setInputData(e.target.value)} />
</FormControl>
</form>
</ModalContent>
</Modal>
);
};
@@ -0,0 +1 @@
export { DeleteActionModal } from './DeleteActionModal';
@@ -11,7 +11,7 @@ export type FormLabelProps = {
}; };
export const FormLabel = ({ id, label, isRequired }: FormLabelProps) => ( export const FormLabel = ({ id, label, isRequired }: FormLabelProps) => (
<Label.Root className="text-mineshaft-300 text-sm font-medium block mb-1 ml-0.5" htmlFor={id}> <Label.Root className="mb-1 ml-0.5 block text-sm font-medium text-mineshaft-300" htmlFor={id}>
{label} {label}
{isRequired && <span className="ml-1 text-red">*</span>} {isRequired && <span className="ml-1 text-red">*</span>}
</Label.Root> </Label.Root>
@@ -25,7 +25,7 @@ export type FormHelperTextProps = {
export const FormHelperText = ({ isError, text }: FormHelperTextProps) => ( export const FormHelperText = ({ isError, text }: FormHelperTextProps) => (
<div <div
className={twMerge( className={twMerge(
'text-xs font-inter flex items-center opacity-90 text-mineshaft-300 mt-2', 'mt-2 flex items-center font-inter text-xs text-mineshaft-300 opacity-90',
isError && 'text-red-600' isError && 'text-red-600'
)} )}
> >
@@ -46,6 +46,7 @@ export type FormControlProps = {
helperText?: ReactNode; helperText?: ReactNode;
errorText?: ReactNode; errorText?: ReactNode;
children: JSX.Element; children: JSX.Element;
className?: string;
}; };
export const FormControl = ({ export const FormControl = ({
@@ -55,10 +56,11 @@ export const FormControl = ({
helperText, helperText,
errorText, errorText,
id, id,
isError isError,
className
}: FormControlProps): JSX.Element => { }: FormControlProps): JSX.Element => {
return ( return (
<div> <div className={twMerge('mb-4', className)}>
{typeof label === 'string' ? ( {typeof label === 'string' ? (
<FormLabel label={label} isRequired={isRequired} id={id} /> <FormLabel label={label} isRequired={isRequired} id={id} />
) : ( ) : (
@@ -14,7 +14,7 @@ const iconButtonVariants = cva(
[ [
'button', 'button',
'transition-all', 'transition-all',
'font-inter font-medium', 'font-inter font-medium user-select-none',
'cursor-pointer', 'cursor-pointer',
'inline-flex items-center justify-center', 'inline-flex items-center justify-center',
'relative' 'relative'
+4 -1
View File
@@ -8,6 +8,7 @@ type Props = {
isRequired?: boolean; isRequired?: boolean;
leftIcon?: ReactNode; leftIcon?: ReactNode;
rightIcon?: ReactNode; rightIcon?: ReactNode;
isDisabled?: boolean;
}; };
const inputVariants = cva( const inputVariants = cva(
@@ -46,7 +47,7 @@ const inputParentContainerVariants = cva('inline-flex font-inter items-center bo
}, },
isError: { isError: {
true: 'border-red', true: 'border-red',
false: 'border-mineshaft-400' false: 'border-mineshaft-500'
}, },
isFullWidth: { isFullWidth: {
true: 'w-full', true: 'w-full',
@@ -70,6 +71,7 @@ export const Input = forwardRef<HTMLInputElement, InputProps>(
className, className,
isRounded = true, isRounded = true,
isFullWidth = true, isFullWidth = true,
isDisabled,
isError = false, isError = false,
isRequired, isRequired,
leftIcon, leftIcon,
@@ -87,6 +89,7 @@ export const Input = forwardRef<HTMLInputElement, InputProps>(
{...props} {...props}
required={isRequired} required={isRequired}
ref={ref} ref={ref}
disabled={isDisabled}
className={twMerge( className={twMerge(
leftIcon ? 'pl-9' : 'pl-4', leftIcon ? 'pl-9' : 'pl-4',
rightIcon ? 'pr-9' : 'pr-4', rightIcon ? 'pr-9' : 'pr-4',
+10 -6
View File
@@ -11,31 +11,32 @@ export type ModalContentProps = DialogPrimitive.DialogContentProps & {
title?: ReactNode; title?: ReactNode;
subTitle?: string; subTitle?: string;
footerContent?: ReactNode; footerContent?: ReactNode;
onClose?: () => void;
}; };
export const ModalContent = forwardRef<HTMLDivElement, ModalContentProps>( export const ModalContent = forwardRef<HTMLDivElement, ModalContentProps>(
({ children, title, subTitle, className, footerContent, ...props }, forwardedRef) => ( ({ children, title, subTitle, className, footerContent, onClose, ...props }, forwardedRef) => (
<DialogPrimitive.Portal> <DialogPrimitive.Portal>
<DialogPrimitive.Overlay <DialogPrimitive.Overlay
className="fixed inset-0 w-full h-full animate-fadeIn" className="fixed inset-0 h-full w-full animate-fadeIn"
style={{ backgroundColor: 'rgba(0, 0, 0, 0.5)' }} style={{ backgroundColor: 'rgba(0, 0, 0, 0.7)' }}
/> />
<DialogPrimitive.Content {...props} ref={forwardedRef}> <DialogPrimitive.Content {...props} ref={forwardedRef}>
<Card <Card
isRounded isRounded
className={twMerge( className={twMerge(
'fixed max-w-md animate-popIn top-1/2 left-1/2 -translate-y-2/4 -translate-x-2/4', 'fixed top-1/2 left-1/2 max-w-lg -translate-y-2/4 -translate-x-2/4 animate-popIn drop-shadow-md',
className className
)} )}
> >
{title && <CardTitle subTitle={subTitle}>{title}</CardTitle>} {title && <CardTitle subTitle={subTitle}>{title}</CardTitle>}
<CardBody>{children}</CardBody> <CardBody>{children}</CardBody>
{footerContent && <CardFooter>{footerContent}</CardFooter>} {footerContent && <CardFooter>{footerContent}</CardFooter>}
<DialogPrimitive.Close aria-label="Close" asChild> <DialogPrimitive.Close aria-label="Close" asChild onClick={onClose}>
<IconButton <IconButton
variant="plain" variant="plain"
ariaLabel="close" ariaLabel="close"
className="absolute top-2.5 right-2.5 text-white hover:bg-gray-600 rounded" className="absolute top-2.5 right-2.5 rounded text-white hover:bg-gray-600"
> >
<FontAwesomeIcon icon={faTimes} size="sm" className="cursor-pointer" /> <FontAwesomeIcon icon={faTimes} size="sm" className="cursor-pointer" />
</IconButton> </IconButton>
@@ -55,3 +56,6 @@ export const Modal = ({ isOpen, ...props }: ModalProps) => (
export const ModalTrigger = DialogPrimitive.Trigger; export const ModalTrigger = DialogPrimitive.Trigger;
export type ModalTriggerProps = DialogPrimitive.DialogTriggerProps; export type ModalTriggerProps = DialogPrimitive.DialogTriggerProps;
export const ModalClose = DialogPrimitive.Close;
export type ModalCloseProps = DialogPrimitive.DialogCloseProps;
+2 -2
View File
@@ -1,2 +1,2 @@
export type { ModalContentProps, ModalProps, ModalTriggerProps } from './Modal'; export type { ModalCloseProps, ModalContentProps, ModalProps, ModalTriggerProps } from './Modal';
export { Modal, ModalContent, ModalTrigger } from './Modal'; export { Modal, ModalClose, ModalContent, ModalTrigger } from './Modal';
+12 -10
View File
@@ -22,21 +22,23 @@ export const Select = forwardRef<HTMLButtonElement, SelectProps>(
<SelectPrimitive.Trigger <SelectPrimitive.Trigger
ref={ref} ref={ref}
className={twMerge( className={twMerge(
`inline-flex items-center justify-between data-[placeholder]:text-gray-500 `inline-flex items-center justify-between rounded-md
px-3 py-2 font-inter text-sm text-bunker-200 font-normal rounded-md bg-mineshaft-800 outline-none`, bg-mineshaft-800 px-3 py-2 font-inter text-sm font-normal text-bunker-200 outline-none data-[placeholder]:text-gray-500`,
className className
)} )}
> >
<SelectPrimitive.Value placeholder={placeholder} /> <SelectPrimitive.Value placeholder={placeholder} />
{!props.disabled && <SelectPrimitive.Icon className="ml-3"> {!props.disabled && (
<FontAwesomeIcon icon={faChevronDown} size="sm" /> <SelectPrimitive.Icon className="ml-3">
</SelectPrimitive.Icon>} <FontAwesomeIcon icon={faChevronDown} size="sm" />
</SelectPrimitive.Icon>
)}
</SelectPrimitive.Trigger> </SelectPrimitive.Trigger>
<SelectPrimitive.Portal> <SelectPrimitive.Portal>
<SelectPrimitive.Content <SelectPrimitive.Content
// position="popper" // position="popper"
sideOffset={4} sideOffset={4}
className="overflow-hidden text-bunker-100 rounded-md shadow-md font-inter bg-mineshaft-800" className="overflow-hidden rounded-md bg-mineshaft-800 font-inter text-bunker-100 shadow-md"
style={{ width: 'var(--radix-select-trigger-width) + 6' }} style={{ width: 'var(--radix-select-trigger-width) + 6' }}
> >
<SelectPrimitive.ScrollUpButton> <SelectPrimitive.ScrollUpButton>
@@ -75,11 +77,11 @@ export const SelectItem = forwardRef<HTMLDivElement, SelectItemProps>(
<SelectPrimitive.Item <SelectPrimitive.Item
{...props} {...props}
className={twMerge( className={twMerge(
`text-sm rounded-sm transition-all hover:bg-mineshaft-500 `relative flex cursor-pointer
flex items-center pl-10 pr-4 py-2 cursor-pointer rounded-md select-none items-center rounded-md py-2 pl-10 pr-4 text-sm
select-none outline-none relative`, outline-none transition-all hover:bg-mineshaft-500`,
isSelected && 'bg-primary', isSelected && 'bg-primary',
isDisabled && 'text-gray-600 hover:bg-transparent cursor-not-allowed hover:text-gray-600', isDisabled && 'cursor-not-allowed text-gray-600 hover:bg-transparent hover:text-gray-600',
className className
)} )}
ref={forwardedRef} ref={forwardedRef}
+16 -12
View File
@@ -1,4 +1,4 @@
import { ReactNode } from 'react'; import { HTMLAttributes, ReactNode, TdHTMLAttributes } from 'react';
import { twMerge } from 'tailwind-merge'; import { twMerge } from 'tailwind-merge';
export type TableContainerProps = { export type TableContainerProps = {
@@ -14,7 +14,7 @@ export const TableContainer = ({
}: TableContainerProps): JSX.Element => ( }: TableContainerProps): JSX.Element => (
<div <div
className={twMerge( className={twMerge(
'overflow-x-auto font-inter shadow-md relative border border-solid border-mineshaft-700', 'relative w-full overflow-x-auto border border-solid border-mineshaft-700 font-inter shadow-md',
isRounded && 'rounded-md', isRounded && 'rounded-md',
className className
)} )}
@@ -32,7 +32,7 @@ export type TableProps = {
export const Table = ({ children, className }: TableProps): JSX.Element => ( export const Table = ({ children, className }: TableProps): JSX.Element => (
<table <table
className={twMerge( className={twMerge(
'bg-bunker-800 p-2 roun text-gray-300 w-full text-sm text-left rounded-md', 'w-full rounded rounded-md bg-bunker-800 p-2 text-left text-sm text-gray-300',
className className
)} )}
> >
@@ -47,7 +47,7 @@ export type THeadProps = {
}; };
export const THead = ({ children, className }: THeadProps): JSX.Element => ( export const THead = ({ children, className }: THeadProps): JSX.Element => (
<thead className={twMerge('text-xs bg-bunker text-bunker-300 uppercase', className)}> <thead className={twMerge('bg-bunker text-xs uppercase text-bunker-300', className)}>
{children} {children}
</thead> </thead>
); );
@@ -56,15 +56,17 @@ export const THead = ({ children, className }: THeadProps): JSX.Element => (
export type TrProps = { export type TrProps = {
children: ReactNode; children: ReactNode;
className?: string; className?: string;
}; } & HTMLAttributes<HTMLTableRowElement>;
export const Tr = ({ children, className }: TrProps): JSX.Element => ( export const Tr = ({ children, className, ...props }: TrProps): JSX.Element => (
<tr className={twMerge('border border-solid border-mineshaft-700', className)}>{children}</tr> <tr className={twMerge('border border-solid border-mineshaft-700', className)} {...props}>
{children}
</tr>
); );
// table head columns // table head columns
export type ThProps = { export type ThProps = {
children: ReactNode; children?: ReactNode;
className?: string; className?: string;
}; };
@@ -84,10 +86,12 @@ export const TBody = ({ children, className }: TBodyProps): JSX.Element => (
// table body columns // table body columns
export type TdProps = { export type TdProps = {
children: ReactNode; children?: ReactNode;
className?: string; className?: string;
}; } & TdHTMLAttributes<HTMLTableCellElement>;
export const Td = ({ children, className }: TdProps): JSX.Element => ( export const Td = ({ children, className, ...props }: TdProps): JSX.Element => (
<td className={twMerge('text-left px-6 py-3', className)}>{children}</td> <td className={twMerge('px-6 py-3 text-left', className)} {...props}>
{children}
</td>
); );
@@ -0,0 +1,36 @@
import Link from 'next/link';
import { Button } from '../Button';
import { Modal, ModalClose, ModalContent } from '../Modal';
type Props = {
isOpen?: boolean;
onOpenChange?: (isOpen: boolean) => void;
text: string;
};
export const UpgradePlanModal = ({ text, isOpen, onOpenChange }: Props): JSX.Element => (
<Modal isOpen={isOpen} onOpenChange={onOpenChange}>
<ModalContent
title="Unleash Infisical's Full Power"
footerContent={[
<Link
href={`/settings/billing/${localStorage.getItem('projectData.id') as string}`}
key="upgrade-plan"
>
<Button className="mr-4">Upgrade Plan</Button>
</Link>,
<ModalClose asChild key="upgrade-plan-cancel">
<Button colorSchema="secondary" variant="plain">
Cancel
</Button>
</ModalClose>
]}
>
<p className="mb-4 text-bunker-300">{text}</p>
<p className="font-medium text-bunker-300">
Upgrade and get access to this, as well as to other powerful enhancements.
</p>
</ModalContent>
</Modal>
);
@@ -0,0 +1 @@
export { UpgradePlanModal } from './UpgradePlanModal';
+2
View File
@@ -1,6 +1,7 @@
export * from './Button'; export * from './Button';
export * from './Card'; export * from './Card';
export * from './Checkbox'; export * from './Checkbox';
export * from './DeleteActionModal';
export * from './Dropdown'; export * from './Dropdown';
export * from './FormControl'; export * from './FormControl';
export * from './IconButton'; export * from './IconButton';
@@ -12,3 +13,4 @@ export * from './Spinner';
export * from './Switch'; export * from './Switch';
export * from './Table'; export * from './Table';
export * from './TextArea'; export * from './TextArea';
export * from './UpgradePlanModal';