Skip to content

Commit

Permalink
feat: add Modal components
Browse files Browse the repository at this point in the history
  • Loading branch information
bacali95 committed Mar 30, 2022
1 parent 2b8a1f4 commit df5713c
Show file tree
Hide file tree
Showing 8 changed files with 207 additions and 210 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@
"eslint-plugin-prettier": "^3.4.1",
"eslint-plugin-react": "^7.28.0",
"eslint-plugin-react-hooks": "^4.3.0",
"flowbite": "^1.3.4",
"flowbite": "^1.4.1",
"husky": "^7.0.4",
"postcss": "^8.4.7",
"prettier": "^2.5.1",
Expand Down
32 changes: 32 additions & 0 deletions public/images/modal-dark.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
33 changes: 33 additions & 0 deletions public/images/modal-light.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
21 changes: 18 additions & 3 deletions src/components/modal/Modal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,13 @@ import { ModalFooter } from './ModalFooter';
import { ModalContext } from './ModalContext';

type Size = 'sm' | 'md' | 'lg' | 'xl' | '2xl' | '3xl' | '4xl' | '5xl' | '6xl' | '7xl';
type Placement = `${'top' | 'bottom'}-${'left' | 'center' | 'right'}` | `center${'' | '-left' | '-right'}`;

export type ModalProps = {
show?: boolean;
popup?: boolean;
size?: Size;
placement?: Placement;
onClose?: () => void;
};

Expand All @@ -28,19 +30,32 @@ const sizeClasses: Record<Size, string> = {
'7xl': 'max-w-7xl',
};

const ModalComponent: FC<ModalProps> = ({ children, show, popup, size = '2xl', onClose }) => (
const placementClasses: Record<Placement, string> = {
'top-left': 'items-start justify-start',
'top-center': 'items-start justify-center',
'top-right': 'items-start justify-end',
'center-left': 'items-center justify-start',
center: 'items-center justify-center',
'center-right': 'items-center justify-end',
'bottom-right': 'items-end justify-end',
'bottom-center': 'items-end justify-center',
'bottom-left': 'items-end justify-start',
};

const ModalComponent: FC<ModalProps> = ({ children, show, popup, size = '2xl', placement = 'center', onClose }) => (
<ModalContext.Provider value={{ popup, onClose }}>
<div
className={classNames(
'fixed right-0 left-0 top-4 z-50 h-modal items-center justify-center overflow-y-auto overflow-x-hidden md:inset-0 md:h-full',
'fixed top-0 right-0 left-0 z-50 h-modal overflow-y-auto overflow-x-hidden md:inset-0 md:h-full',
placementClasses[placement],
{
'flex bg-gray-900 bg-opacity-50 dark:bg-opacity-80': show,
hidden: !show,
},
)}
aria-hidden={!show}
>
<div className={classNames('relative h-full w-full px-4 md:h-auto', sizeClasses[size])}>
<div className={classNames('relative h-full w-full p-4 md:h-auto', sizeClasses[size])}>
<div className="relative rounded-lg bg-white shadow dark:bg-gray-700">{children}</div>
</div>
</div>
Expand Down
2 changes: 1 addition & 1 deletion src/components/modal/ModalHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ export const ModalHeader: FC = ({ children }) => {
'border-b p-5': !popup,
})}
>
<h3 className="text-xl font-semibold text-gray-900 dark:text-white lg:text-2xl">{children}</h3>
<h3 className="text-xl font-medium text-gray-900 dark:text-white">{children}</h3>
<button
className="ml-auto inline-flex items-center rounded-lg bg-transparent p-1.5 text-sm text-gray-400 hover:bg-gray-200 hover:text-gray-900 dark:hover:bg-gray-600 dark:hover:text-white"
type="button"
Expand Down
6 changes: 6 additions & 0 deletions src/pages/DashboardPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,12 @@ const DashboardPage: FC = () => {
className: 'w-36',
images: { light: 'list-group-light.svg', dark: 'list-group-dark.svg' },
},
{
title: 'Modal',
href: '/modal',
className: 'w-36',
images: { light: 'modal-light.svg', dark: 'modal-dark.svg' },
},
{
title: 'Spinners',
href: '/spinners',
Expand Down
141 changes: 112 additions & 29 deletions src/pages/ModalPage.tsx
Original file line number Diff line number Diff line change
@@ -1,19 +1,21 @@
import { FC, useState } from 'react';
import { HiOutlineExclamationCircle, HiX } from 'react-icons/hi';
import { HiOutlineExclamationCircle } from 'react-icons/hi';

import { CodeExample, DemoPage } from './DemoPage';
import { Button, Checkbox, Label, Modal, TextInput } from '../components';
import { Button, Checkbox, Label, Modal, Select, TextInput } from '../components';

const ModalPage: FC = () => {
const [openModal, setOpenModal] = useState<number | undefined>();
const [openModal, setOpenModal] = useState<string | undefined>();
const [modalSize, setModalSize] = useState<string>('md');
const [modalPlacement, setModalPlacement] = useState<string>('center');

const examples: CodeExample[] = [
{
title: 'Default modal',
code: (
<div>
<Button onClick={() => setOpenModal(0)}>Toggle modal</Button>
<Modal show={openModal === 0} onClose={() => setOpenModal(undefined)}>
<>
<Button onClick={() => setOpenModal('default')}>Toggle modal</Button>
<Modal show={openModal === 'default'} onClose={() => setOpenModal(undefined)}>
<Modal.Header>Terms of Service</Modal.Header>
<Modal.Body className="space-y-6">
<p className="text-base leading-relaxed text-gray-500 dark:text-gray-400">
Expand All @@ -33,46 +35,40 @@ const ModalPage: FC = () => {
</Button>
</Modal.Footer>
</Modal>
</div>
</>
),
},
{
title: 'Pop-up modal',
code: (
<div>
<Button onClick={() => setOpenModal(1)}>Toggle modal</Button>
<Modal show={openModal === 1} size="md" popup onClose={() => setOpenModal(undefined)}>
<>
<Button onClick={() => setOpenModal('pop-up')}>Toggle modal</Button>
<Modal show={openModal === 'pop-up'} size="md" popup onClose={() => setOpenModal(undefined)}>
<Modal.Header />
<Modal.Body className="text-center">
<HiOutlineExclamationCircle className="mx-auto mb-4 h-14 w-14 text-gray-400 dark:text-gray-200" />
<h3 className="mb-5 text-lg font-normal text-gray-500 dark:text-gray-400">
Are you sure you want to delete this product?
</h3>
<button
className="mr-2 inline-flex items-center rounded-lg bg-red-600 px-5 py-2.5 text-center text-sm font-medium text-white hover:bg-red-800 focus:ring-4 focus:ring-red-300"
onClick={() => setOpenModal(undefined)}
type="button"
>
{"Yes, I'm sure"}
</button>
<button
className="rounded-lg border border-gray-200 bg-white px-5 py-2.5 text-sm font-medium text-gray-500 hover:bg-gray-100 hover:text-gray-900 focus:z-10 focus:ring-4 focus:ring-gray-300 dark:border-gray-500 dark:bg-gray-700 dark:text-gray-300 dark:hover:bg-gray-600 dark:hover:text-white"
onClick={() => setOpenModal(undefined)}
type="button"
>
No, cancel
</button>
<div className="flex justify-center gap-4">
<Button color="red" onClick={() => setOpenModal(undefined)}>
{"Yes, I'm sure"}
</Button>
<Button color="alternative" onClick={() => setOpenModal(undefined)}>
No, cancel
</Button>
</div>
</Modal.Body>
</Modal>
</div>
</>
),
},
{
title: 'Form elements',
code: (
<div>
<Button onClick={() => setOpenModal(2)}>Toggle modal</Button>
<Modal show={openModal === 2} size="md" popup onClose={() => setOpenModal(undefined)}>
<>
<Button onClick={() => setOpenModal('form-elements')}>Toggle modal</Button>
<Modal show={openModal === 'form-elements'} size="md" popup onClose={() => setOpenModal(undefined)}>
<Modal.Header />
<Modal.Body className="space-y-6 px-6 pb-4 sm:pb-6 lg:px-8 xl:pb-8">
<h3 className="text-xl font-medium text-gray-900 dark:text-white">Sign in to our platform</h3>
Expand Down Expand Up @@ -111,7 +107,94 @@ const ModalPage: FC = () => {
</div>
</Modal.Body>
</Modal>
</div>
</>
),
},
{
title: 'Sizing',
code: (
<>
<div className="flex flex-wrap gap-4">
<Select className="w-40" defaultValue="md" onChange={(event) => setModalSize(event.target.value)}>
<option value="sm">sm</option>
<option value="md">md</option>
<option value="lg">lg</option>
<option value="xl">xl</option>
<option value="2xl">2xl</option>
<option value="3xl">3xl</option>
<option value="4xl">4xl</option>
<option value="5xl">5xl</option>
<option value="6xl">6xl</option>
<option value="7xl">7xl</option>
</Select>
<Button onClick={() => setOpenModal('size')}>Toggle modal</Button>
</div>
<Modal show={openModal === 'size'} size={modalSize as any} onClose={() => setOpenModal(undefined)}>
<Modal.Header>Small modal</Modal.Header>
<Modal.Body className="space-y-6 p-6">
<p className="text-base leading-relaxed text-gray-500 dark:text-gray-400">
With less than a month to go before the European Union enacts new consumer privacy laws for its
citizens, companies around the world are updating their terms of service agreements to comply.
</p>
<p className="text-base leading-relaxed text-gray-500 dark:text-gray-400">
The European Union’s General Data Protection Regulation (G.D.P.R.) goes into effect on May 25 and is
meant to ensure a common set of data rights in the European Union. It requires organizations to notify
users as soon as possible of high-risk data breaches that could personally affect them.
</p>
</Modal.Body>
<Modal.Footer>
<Button onClick={() => setOpenModal(undefined)}>I accept</Button>
<Button color="alternative" onClick={() => setOpenModal(undefined)}>
Decline
</Button>
</Modal.Footer>
</Modal>
</>
),
},
{
title: 'Placement',
code: (
<>
<div className="flex flex-wrap gap-4">
<Select className="w-40" defaultValue="center" onChange={(event) => setModalPlacement(event.target.value)}>
<option value="center">Center</option>
<option value="top-left">Top left</option>
<option value="top-center">Top center</option>
<option value="top-right">Top right</option>
<option value="center-left">Center left</option>
<option value="center-right">Center right</option>
<option value="bottom-right">Bottom right</option>
<option value="bottom-center">Bottom center</option>
<option value="bottom-left">Bottom left</option>
</Select>
<Button onClick={() => setOpenModal('placement')}>Toggle modal</Button>
</div>
<Modal
show={openModal === 'placement'}
placement={modalPlacement as any}
onClose={() => setOpenModal(undefined)}
>
<Modal.Header>Small modal</Modal.Header>
<Modal.Body className="space-y-6 p-6">
<p className="text-base leading-relaxed text-gray-500 dark:text-gray-400">
With less than a month to go before the European Union enacts new consumer privacy laws for its
citizens, companies around the world are updating their terms of service agreements to comply.
</p>
<p className="text-base leading-relaxed text-gray-500 dark:text-gray-400">
The European Union’s General Data Protection Regulation (G.D.P.R.) goes into effect on May 25 and is
meant to ensure a common set of data rights in the European Union. It requires organizations to notify
users as soon as possible of high-risk data breaches that could personally affect them.
</p>
</Modal.Body>
<Modal.Footer>
<Button onClick={() => setOpenModal(undefined)}>I accept</Button>
<Button color="alternative" onClick={() => setOpenModal(undefined)}>
Decline
</Button>
</Modal.Footer>
</Modal>
</>
),
},
];
Expand Down
Loading

0 comments on commit df5713c

Please sign in to comment.