diff --git a/src/Root.tsx b/src/Root.tsx index 65b6e2759..7456d65f8 100644 --- a/src/Root.tsx +++ b/src/Root.tsx @@ -14,6 +14,7 @@ import { HiMenuAlt1, HiPencilAlt, HiStar, + HiUser, } from 'react-icons/hi'; import { BsCreditCard2FrontFill, BsGithub, BsImages } from 'react-icons/bs'; import { FaSpinner } from 'react-icons/fa'; @@ -24,6 +25,7 @@ import { Route, Routes } from 'react-router-dom'; import { DarkThemeToggle, Navbar, Sidebar, SidebarItem, Spinner } from './components'; import DashboardPage from './pages/DashboardPage'; +import AvatarPage from './pages/AvatarPage'; import AlertsPage from './pages/AlertsPage'; import AccordionPage from './pages/AccordionPage'; import BadgesPage from './pages/BadgesPage'; @@ -63,6 +65,12 @@ export const Root: FC = () => { title: 'Accordion', href: '/accordion', }, + { + group: false, + icon: HiUser, + title: 'Avatar', + href: '/avatar', + }, { group: false, icon: HiBadgeCheck, @@ -196,6 +204,7 @@ export const Root: FC = () => { } /> } /> } /> + } /> } /> } /> } /> diff --git a/src/components/Avatar.tsx b/src/components/Avatar.tsx new file mode 100644 index 000000000..599315b77 --- /dev/null +++ b/src/components/Avatar.tsx @@ -0,0 +1,92 @@ +import classNames from 'classnames'; +import React, { PropsWithChildren } from 'react'; + +export type AvatarProps = PropsWithChildren<{ + size?: 'xs' | 'sm' | 'md' | 'lg' | 'xl'; + rounded?: boolean; + bordered?: boolean; + img?: string; + status?: 'offline' | 'online' | 'away' | 'busy'; + statusPosition?: 'top-left' | 'top-right' | 'bottom-right' | 'bottom-left'; +}>; + +const sizeClasses: Record = { + xs: 'w-6 h-6', + sm: 'w-8 h-8', + md: 'w-10 h-10', + lg: 'w-20 h-20', + xl: 'w-36 h-36', +}; + +const statusClasses: Record = { + offline: 'bg-gray-400', + online: 'bg-green-400', + away: 'bg-yellow-400', + busy: 'bg-red-400', +}; + +const statusPositionClasses: Record = { + 'top-left': '-top-1 -right-1', + 'top-right': '-top-1 -left-1', + 'bottom-left': '-bottom-1 -right-1', + 'bottom-right': '-bottom-1 -left-1', +}; + +export const Avatar: React.FC = ({ + img, + status, + children, + statusPosition = 'top-right', + size = 'md', + rounded = false, + bordered = false, +}) => { + return ( +
+
+ {img ? ( + Rounded avatar + ) : ( +
+ + + +
+ )} + {status && ( + + )} +
+ {children &&
{children}
} +
+ ); +}; diff --git a/src/components/Button/index.tsx b/src/components/Button/index.tsx index 6a81f7d9e..c785ba199 100644 --- a/src/components/Button/index.tsx +++ b/src/components/Button/index.tsx @@ -1,4 +1,4 @@ -import { ComponentProps, FC } from 'react'; +import { ComponentProps, FC, ReactNode } from 'react'; import classNames from 'classnames'; type Color = 'blue' | 'alternative' | 'dark' | 'light' | 'green' | 'red' | 'yellow' | 'purple'; @@ -17,6 +17,7 @@ type PositionInGroup = 'start' | 'middle' | 'end'; export type ButtonProps = Omit, 'color'> & { pill?: boolean; outline?: boolean; + label?: ReactNode; color?: Color; size?: Size; icon?: FC>; diff --git a/src/components/dropdown/Dropdown.tsx b/src/components/dropdown/Dropdown.tsx index 57ed03c7e..00b3721c1 100644 --- a/src/components/dropdown/Dropdown.tsx +++ b/src/components/dropdown/Dropdown.tsx @@ -9,10 +9,12 @@ import { DropdownDivider } from './DropdownDivider'; import { DropdownHeader } from './DropdownHeader'; export type DropdownProps = ButtonProps & - Omit & { + Omit & { className?: string; label: ReactNode; inline?: boolean; + tooltipArrow?: boolean; + arrowIcon?: boolean; }; const icons: Record>> = { @@ -23,13 +25,8 @@ const icons: Record>> = { }; const DropdownComponent: FC = (props) => { - const { children, className, label, inline, ...restProps } = props; - const { - placement = inline ? 'bottom-start' : 'bottom', - arrow = false, - trigger = 'click', - ...buttonProps - } = restProps; + const { children, className, label, inline, tooltipArrow, arrowIcon = true, ...restProps } = props; + const { placement = inline ? 'bottom-start' : 'bottom', trigger = 'click', ...buttonProps } = restProps; const Icon = useMemo(() => { const [p] = placement.split('-'); @@ -48,12 +45,12 @@ const DropdownComponent: FC = (props) => { style="auto" animation="duration-100" placement={placement} - arrow={arrow} + arrow={tooltipArrow} trigger={trigger} > {label} - + {arrowIcon && } ); diff --git a/src/components/index.ts b/src/components/index.ts index d3cff4249..ee0ff5334 100644 --- a/src/components/index.ts +++ b/src/components/index.ts @@ -1,5 +1,6 @@ export * from './Alert'; export * from './accordion/Accordion'; +export * from './Avatar'; export * from './Badge'; export * from './Breadcrumb'; export * from './Button'; diff --git a/src/pages/AvatarPage.tsx b/src/pages/AvatarPage.tsx new file mode 100644 index 000000000..58090172b --- /dev/null +++ b/src/pages/AvatarPage.tsx @@ -0,0 +1,108 @@ +import { FC } from 'react'; + +import { Avatar, Dropdown } from '../components'; +import { CodeExample, DemoPage } from './DemoPage'; + +const AvatarPage: FC = () => { + const examples: CodeExample[] = [ + { + title: 'Default Avatar', + code: ( +
+ + +
+ ), + }, + { + title: 'Bordered Avatar', + code: ( +
+ + +
+ ), + }, + { + title: 'Placeholder', + code: ( +
+ + +
+ ), + }, + { + title: 'Dot indicator', + code: ( +
+ + + + +
+ ), + }, + { + title: 'Avatar text', + code: ( + +
+
Jese Leos
+
Joined in August 2014
+
+
+ ), + }, + { + title: 'User dropdown', + code: ( + } + arrowIcon={false} + inline + > + + Bonnie Green + name@flowbite.com + + Dashboard + Settings + Earnings + + Sign out + + ), + }, + { + title: 'Sizing', + code: ( +
+ + + + + +
+ ), + }, + ]; + + return ; +}; + +export default AvatarPage;