Skip to content

Commit

Permalink
feat: add Dropdown component
Browse files Browse the repository at this point in the history
  • Loading branch information
bacali95 committed Mar 15, 2022
1 parent 96dc14c commit 169b46e
Show file tree
Hide file tree
Showing 11 changed files with 354 additions and 16 deletions.
40 changes: 40 additions & 0 deletions public/images/dropdown-dark.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
40 changes: 40 additions & 0 deletions public/images/dropdown-light.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
35 changes: 22 additions & 13 deletions src/Root.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { FC, lazy, Suspense, useState } from 'react';
import { FC, Suspense, useState } from 'react';
import {
HiAnnotation,
HiArrowCircleDown,
HiBadgeCheck,
HiBell,
HiChevronDoubleRight,
Expand All @@ -17,18 +18,19 @@ import { Route, Routes } from 'react-router-dom';

import { DarkThemeToggle, Navbar, Sidebar, SidebarItem, Spinner } from './components';

const DashboardPage = lazy(() => import('./pages/DashboardPage'));
const AlertsPage = lazy(() => import('./pages/AlertsPage'));
const AccordionPage = lazy(() => import('./pages/AccordionPage'));
const BadgesPage = lazy(() => import('./pages/BadgesPage'));
const BreadcrumbPage = lazy(() => import('./pages/BreadcrumbPage'));
const ButtonsPage = lazy(() => import('./pages/ButtonsPage'));
const ButtonGroupPage = lazy(() => import('./pages/ButtonGroupPage'));
const CardPage = lazy(() => import('./pages/CardPage'));
const CarouselPage = lazy(() => import('./pages/CarouselPage'));
const ListGroupPage = lazy(() => import('./pages/ListGroupPage'));
const SpinnersPage = lazy(() => import('./pages/SpinnersPage'));
const TooltipsPage = lazy(() => import('./pages/TooltipsPage'));
import DashboardPage from './pages/DashboardPage';
import AlertsPage from './pages/AlertsPage';
import AccordionPage from './pages/AccordionPage';
import BadgesPage from './pages/BadgesPage';
import BreadcrumbPage from './pages/BreadcrumbPage';
import ButtonsPage from './pages/ButtonsPage';
import ButtonGroupPage from './pages/ButtonGroupPage';
import CardPage from './pages/CardPage';
import CarouselPage from './pages/CarouselPage';
import DropdownPage from './pages/DropdownPage';
import ListGroupPage from './pages/ListGroupPage';
import SpinnersPage from './pages/SpinnersPage';
import TooltipsPage from './pages/TooltipsPage';

export const Root: FC = () => {
const [collapsed, setCollapsed] = useState(false);
Expand Down Expand Up @@ -88,6 +90,12 @@ export const Root: FC = () => {
title: 'Carousel',
href: '/carousel',
},
{
group: false,
icon: HiArrowCircleDown,
title: 'Dropdown',
href: '/dropdown',
},
{
group: false,
icon: HiClipboardList,
Expand Down Expand Up @@ -151,6 +159,7 @@ export const Root: FC = () => {
<Route path="button-group" element={<ButtonGroupPage />} />
<Route path="card" element={<CardPage />} />
<Route path="carousel" element={<CarouselPage />} />
<Route path="dropdown" element={<DropdownPage />} />
<Route path="list-group" element={<ListGroupPage />} />
<Route path="spinners" element={<SpinnersPage />} />
<Route path="tooltips" element={<TooltipsPage />} />
Expand Down
11 changes: 8 additions & 3 deletions src/components/Tooltip.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,18 @@ import { FC, ReactNode, useEffect, useRef, useState } from 'react';
import classNames from 'classnames';

export type TooltipProps = {
className?: string;
content: ReactNode;
placement?: Placement;
trigger?: 'hover' | 'click';
style?: 'dark' | 'light';
animation?: false | `duration-${string}`;
style?: 'dark' | 'light' | 'auto';
animation?: false | `duration-${number}`;
arrow?: boolean;
};

export const Tooltip: FC<TooltipProps> = ({
children,
className,
content,
placement = 'top',
trigger = 'hover',
Expand Down Expand Up @@ -47,7 +49,7 @@ export const Tooltip: FC<TooltipProps> = ({
popperInstance.current?.update();
};

const hide = () => setVisible(false);
const hide = () => setTimeout(() => setVisible(false), 100);

return (
<>
Expand All @@ -59,7 +61,10 @@ export const Tooltip: FC<TooltipProps> = ({
'invisible opacity-0': !visible,
'bg-gray-900 text-white dark:bg-gray-700': style === 'dark',
'border border-gray-200 bg-white text-gray-900': style === 'light',
'border border-gray-200 bg-white text-gray-900 dark:border-none dark:bg-gray-700 dark:text-white':
style === 'auto',
},
className,
)}
ref={tooltipRef}
role="tooltip"
Expand Down
71 changes: 71 additions & 0 deletions src/components/dropdown/Dropdown.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import { ComponentProps, FC, PropsWithChildren, ReactNode, useMemo } from 'react';
import { HiOutlineChevronDown, HiOutlineChevronLeft, HiOutlineChevronRight, HiOutlineChevronUp } from 'react-icons/hi';
import classNames from 'classnames';

import { Button, ButtonProps } from '../Button';
import { Tooltip, TooltipProps } from '../Tooltip';
import { DropdownItem } from './DropdownItem';
import { DropdownDivider } from './DropdownDivider';
import { DropdownHeader } from './DropdownHeader';

export type DropdownProps = ButtonProps &
Omit<TooltipProps, 'content' | 'style' | 'animation'> & {
className?: string;
label: ReactNode;
inline?: boolean;
};

const icons: Record<string, FC<ComponentProps<'svg'>>> = {
top: HiOutlineChevronUp,
right: HiOutlineChevronRight,
bottom: HiOutlineChevronDown,
left: HiOutlineChevronLeft,
};

const DropdownComponent: FC<DropdownProps> = (props) => {
const { children, className, label, inline, ...restProps } = props;
const {
placement = inline ? 'bottom-start' : 'bottom',
arrow = false,
trigger = 'click',
...buttonProps
} = restProps;

const Icon = useMemo(() => {
const [p] = placement.split('-');

return icons[p] ?? HiOutlineChevronDown;
}, [placement]);
const content = useMemo(() => <ul className="py-1">{children}</ul>, [children]);

const TriggerWrapper = ({ children }: PropsWithChildren<any>) =>
inline ? <button className="flex items-center">{children}</button> : <Button {...buttonProps}>{children}</Button>;

return (
<Tooltip
className={classNames('w-44 !rounded !p-0', className)}
content={content}
style="auto"
animation="duration-100"
placement={placement}
arrow={arrow}
trigger={trigger}
>
<TriggerWrapper>
{label}
<Icon className="ml-2 h-4 w-4" />
</TriggerWrapper>
</Tooltip>
);
};

DropdownComponent.displayName = 'Dropdown';
DropdownItem.displayName = 'Dropdown.Item';
DropdownHeader.displayName = 'Dropdown.Header';
DropdownDivider.displayName = 'Dropdown.Divider';

export const Dropdown = Object.assign(DropdownComponent, {
Item: DropdownItem,
Header: DropdownHeader,
Divider: DropdownDivider,
});
3 changes: 3 additions & 0 deletions src/components/dropdown/DropdownDivider.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { FC } from 'react';

export const DropdownDivider: FC = () => <div className="my-1 h-px bg-gray-100 dark:bg-gray-600" />;
9 changes: 9 additions & 0 deletions src/components/dropdown/DropdownHeader.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { FC } from 'react';
import { DropdownDivider } from './DropdownDivider';

export const DropdownHeader: FC = ({ children }) => (
<>
<div className="block py-2 px-4 text-sm text-gray-700 dark:text-gray-200">{children}</div>
<DropdownDivider />
</>
);
14 changes: 14 additions & 0 deletions src/components/dropdown/DropdownItem.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { FC } from 'react';

export type DropdownItemProps = {
onClick?: () => void;
};

export const DropdownItem: FC<DropdownItemProps> = ({ children, onClick }) => (
<li
className="block cursor-pointer py-2 px-4 text-sm text-gray-700 hover:bg-gray-100 dark:text-gray-200 dark:hover:bg-gray-600 dark:hover:text-white"
onClick={onClick}
>
{children}
</li>
);
1 change: 1 addition & 0 deletions src/components/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ export * from './Button';
export * from './ButtonGroup';
export * from './Card';
export * from './Carousel';
export * from './dropdown/Dropdown';
export * from './DarkThemeToggle';
export * from './Navbar';
export * from './Sidebar';
Expand Down
6 changes: 6 additions & 0 deletions src/pages/DashboardPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,12 @@ const DashboardPage: FC = () => {
className: 'w-36',
images: { light: 'card-light.svg', dark: 'card-dark.svg' },
},
{
title: 'Dropdown',
href: '/dropdown',
className: 'w-28',
images: { light: 'dropdown-light.svg', dark: 'dropdown-dark.svg' },
},
{
title: 'List group',
href: '/list-group',
Expand Down
Loading

1 comment on commit 169b46e

@vercel
Copy link

@vercel vercel bot commented on 169b46e Mar 17, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.