Base Components
A consistent way to drag and drop, as well as browse your computer to upload a media file.
npm install @radix-ui/react-slot
file-upload.tsx
// AlignUI FileUpload v0.0.0 import * as React from 'react'; import { Slot } from '@radix-ui/react-slot'; import { cnExt } from '@/utils/cn'; import { PolymorphicComponentProps } from '@/utils/polymorphic'; const FileUpload = React.forwardRef< HTMLLabelElement, React.LabelHTMLAttributes<HTMLLabelElement> & { asChild?: boolean; } >(({ className, asChild, ...rest }, forwardedRef) => { const Component = asChild ? Slot : 'label'; return ( <Component ref={forwardedRef} className={cnExt( 'flex w-full cursor-pointer flex-col items-center gap-5 rounded-xl border border-dashed border-stroke-sub-300 bg-bg-white-0 p-8 text-center', 'transition duration-200 ease-out', // hover 'hover:bg-bg-weak-50', className, )} {...rest} /> ); }); FileUpload.displayName = 'FileUpload'; const FileUploadButton = React.forwardRef< HTMLDivElement, React.HTMLAttributes<HTMLDivElement> & { asChild?: boolean; } >(({ className, asChild, ...rest }, forwardedRef) => { const Component = asChild ? Slot : 'div'; return ( <Component ref={forwardedRef} className={cnExt( 'inline-flex h-8 items-center justify-center gap-2.5 whitespace-nowrap rounded-lg bg-bg-white-0 px-2.5 text-label-sm text-text-sub-600', 'pointer-events-none ring-1 ring-inset ring-stroke-soft-200', className, )} {...rest} /> ); }); FileUploadButton.displayName = 'FileUploadButton'; function FileUploadIcon<T extends React.ElementType>({ className, as, ...rest }: PolymorphicComponentProps<T>) { const Component = as || 'div'; return ( <Component className={cnExt('size-6 text-text-sub-600', className)} {...rest} /> ); } export { FileUpload as Root, FileUploadButton as Button, FileUploadIcon as Icon, };
file-format-icon.tsx
// AlignUI FileFormatIcon v0.0.0 import * as React from 'react'; import { tv, type VariantProps } from '@/utils/tv'; export const fileFormatIconVariants = tv({ slots: { root: 'relative shrink-0', formatBox: 'absolute bottom-1.5 left-0 flex h-4 items-center rounded px-[3px] py-0.5 text-[11px] font-semibold leading-none text-static-white', }, variants: { size: { medium: { root: 'size-10', }, small: { root: 'size-8', }, }, color: { red: { formatBox: 'bg-error-base', }, orange: { formatBox: 'bg-warning-base', }, yellow: { formatBox: 'bg-away-base', }, green: { formatBox: 'bg-success-base', }, sky: { formatBox: 'bg-verified-base', }, blue: { formatBox: 'bg-information-base', }, purple: { formatBox: 'bg-feature-base', }, pink: { formatBox: 'bg-highlighted-base', }, gray: { formatBox: 'bg-faded-base', }, }, }, defaultVariants: { color: 'gray', size: 'medium', }, }); function FileFormatIcon({ format, className, color, size, ...rest }: VariantProps<typeof fileFormatIconVariants> & React.SVGProps<SVGSVGElement>) { const { root, formatBox } = fileFormatIconVariants({ color, size }); return ( <svg width='40' height='40' viewBox='0 0 40 40' fill='none' xmlns='http://www.w3.org/2000/svg' className={root({ class: className })} {...rest} > <path d='M30 39.25H10C7.10051 39.25 4.75 36.8995 4.75 34V6C4.75 3.10051 7.10051 0.75 10 0.75H20.5147C21.9071 0.75 23.2425 1.30312 24.227 2.28769L33.7123 11.773C34.6969 12.7575 35.25 14.0929 35.25 15.4853V34C35.25 36.8995 32.8995 39.25 30 39.25Z' className='fill-bg-white-0 stroke-stroke-sub-300' strokeWidth='1.5' /> <path d='M23 1V9C23 11.2091 24.7909 13 27 13H35' className='stroke-stroke-sub-300' strokeWidth='1.5' /> <foreignObject x='0' y='0' width='40' height='40'> {/* eslint-disable-next-line */} {/* @ts-ignore */} <div xmlns='http://www.w3.org/1999/xhtml' className={formatBox()}> {format} </div> </foreignObject> </svg> ); } export { FileFormatIcon as Root };