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 ? (
+

+ ) : (
+
+ )}
+ {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;