├── .github
└── workflows
│ ├── main.yml
│ └── publish.yml
├── .gitignore
├── LICENSE
├── README.md
├── example
├── .gitignore
├── .npmignore
├── index.html
├── index.tsx
├── logo.png
├── package-lock.json
├── package.json
├── postcss.config.js
├── src
│ ├── Example.tsx
│ ├── Header.tsx
│ └── TransitionSelect.tsx
├── tailwind.config.js
├── tailwind.css
└── tsconfig.json
├── logo.png
├── package-lock.json
├── package.json
├── src
├── ModalContext.ts
├── components
│ ├── Modal.tsx
│ ├── ModalContainer.tsx
│ └── ModalProvider.tsx
├── hooks
│ ├── useModal.ts
│ ├── useModalContext.ts
│ └── useModalTransition.ts
├── index.tsx
└── styles
│ └── styles.css
├── tailwind.config.js
├── test
└── blah.test.tsx
├── tsconfig.json
└── tsdx.config.js
/.github/workflows/main.yml:
--------------------------------------------------------------------------------
1 | name: CI
2 | on: [push, pull_request]
3 | jobs:
4 | build:
5 | runs-on: ubuntu-latest
6 |
7 | steps:
8 | - name: Begin CI...
9 | uses: actions/checkout@v2
10 |
11 | - name: Use Node 12
12 | uses: actions/setup-node@v1
13 | with:
14 | node-version: 12
15 |
16 | - name: Install dependencies
17 | run: npm ci
18 |
19 | - name: Install peer dependencies
20 | run: npm run install-peers
21 |
22 | - name: Lint
23 | run: npm run lint
24 |
25 | - name: Test
26 | run: npm run test --ci --coverage --maxWorkers=2
27 | env:
28 | CI: true
29 |
30 | - name: Build
31 | run: npm run build
32 |
--------------------------------------------------------------------------------
/.github/workflows/publish.yml:
--------------------------------------------------------------------------------
1 | name: Publish CI
2 |
3 | on:
4 | release:
5 | types: [published]
6 |
7 | jobs:
8 | publish:
9 | runs-on: ubuntu-latest
10 | steps:
11 | - name: Begin CI...
12 | uses: actions/checkout@v2
13 |
14 | - name: Use Node 12
15 | uses: actions/setup-node@v1
16 | with:
17 | node-version: 12
18 | registry-url: 'https://registry.npmjs.org'
19 |
20 | - name: Install dependencies
21 | run: npm ci
22 |
23 | - name: Install peer dependencies
24 | run: npm run install-peers
25 |
26 | - name: Lint
27 | run: npm run lint
28 |
29 | - name: Test
30 | run: npm run test --ci --coverage --maxWorkers=2
31 | env:
32 | CI: true
33 |
34 | - name: Build
35 | run: npm run build
36 |
37 | - name: Publish
38 | run: npm publish
39 | env:
40 | NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
41 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.log
2 | .DS_Store
3 | node_modules
4 | .cache
5 | dist
6 | .idea
7 | .now
8 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2020 Matthew Brookson
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # react-simple-hook-modal
2 |
3 | Need a simple modal that just works? `react-simple-hook-modal` is just that!
4 |
5 | ![build][ci] [![npm latest][npmbadge]][npmlink]
6 |
7 | ![react-simple-hook-modal][logo]
8 |
9 | # Installation
10 |
11 | Simply install the package from npm and you're good to go!
12 |
13 | ```
14 | npm install react-simple-hook-modal
15 |
16 | or
17 |
18 | yarn add react-simple-hook-modal
19 | ```
20 |
21 | # Usage
22 |
23 | ## React
24 |
25 | `react-simple-hook-modal` has a super simple API and utilises React hooks.
26 |
27 | - Simply wrap your app in ``
28 | - Add instances of `` wherever you want to display a modal
29 | - This will not render anything to the DOM here
30 | - Modals are rendered after allthe children in side ``
31 | - Use the `useModal` hook to control your modal's state
32 | - Use the `ModalTransition` enum to optionally set the transition animation
33 | - Currently there are 3 to select from, or choose `NONE` to disable the transitions
34 |
35 | ```tsx
36 | import {
37 | ModalProvider,
38 | Modal,
39 | useModal,
40 | ModalTransition,
41 | } from 'react-simple-hook-modal';
42 |
43 | const MyComponent = () => {
44 | const { isModalOpen, openModal, closeModal } = useModal();
45 |
46 | return (
47 | <>
48 |
49 |
54 |
55 |
56 | >
57 | );
58 | };
59 |
60 | const App = () => (
61 |
62 |
63 |
64 | );
65 | ```
66 |
67 | ## Styles
68 |
69 | `react-simple-hook-modal` uses a subset of [tailwindcss][tailwind] under the hood. The tailwind classes used have a prefix of `rsm` added to avoid potential conflicts with your own styles. You can import the default styles using:
70 |
71 | ```css
72 | import 'react-simple-hook-modal/dist/styles.css';
73 | ```
74 |
75 | `ModalProvider` also takes optional props:
76 |
77 | - `backdropClassName` which can contain one or more classes to append and override the default styles (e.g. Changing the backdrop colour can be done by adding the class `bg-blue-800`).
78 |
79 | `Modal` also takes optional props:
80 |
81 | - `modalClassName` which can contain one or more classes to append to the default modal div.
82 | - `modalZIndex` this can be used to override the default z-index value should you need one higher than 9999.
83 |
84 | # Example
85 |
86 | See the `example` directory in the repository for a full example including multiple stacked modals.
87 |
88 | # Demo
89 |
90 | [Click here][demo] to see a live demo of `react-simple-hook-modal` in action!
91 |
92 | # Issues
93 |
94 | If you have any issues, please create an issue here on GitHub.
95 |
96 | ### Thanks and enjoy!
97 |
98 | [publish]: https://github.com/mbrookson/react-simple-hook-modal/workflows/Publish%20CI/badge.svg?branch=master
99 | [ci]: https://github.com/mbrookson/react-simple-hook-modal/workflows/CI/badge.svg?branch=master
100 | [npmbadge]: https://img.shields.io/npm/v/react-simple-hook-modal.svg
101 | [npmlink]: https://npmjs.org/package/react-simple-hook-modal
102 | [logo]: https://raw.githubusercontent.com/mbrookson/react-simple-hook-modal/master/logo.png
103 | [demo]: https://react-simple-hook-modal.now.sh/
104 | [tailwind]: https://tailwindcss.com/
105 |
--------------------------------------------------------------------------------
/example/.gitignore:
--------------------------------------------------------------------------------
1 | styles.css
2 |
--------------------------------------------------------------------------------
/example/.npmignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | .cache
3 | dist
4 |
--------------------------------------------------------------------------------
/example/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
10 |
14 |
19 |
24 | React Simple Hook Modal
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
--------------------------------------------------------------------------------
/example/index.tsx:
--------------------------------------------------------------------------------
1 | import 'react-app-polyfill/ie11';
2 | import * as React from 'react';
3 | import * as ReactDOM from 'react-dom';
4 | import { ModalProvider, ModalTransition } from '../dist/index';
5 | import '../dist/styles.css';
6 | import './styles.css';
7 | import { Header } from './src/Header';
8 | import { Example } from './src/Example';
9 | import { TransitionSelect } from './src/TransitionSelect';
10 |
11 | const App = () => {
12 | const [transition, setTransition] = React.useState(ModalTransition.SCALE);
13 |
14 | return (
15 |
16 |
17 |
18 |
19 |
20 | );
21 | };
22 |
23 | ReactDOM.render(, document.getElementById('root'));
24 |
--------------------------------------------------------------------------------
/example/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mbrookson/react-simple-hook-modal/83a7c5b7d84b5a0361b28dbc14d97ebb42b76b1d/example/logo.png
--------------------------------------------------------------------------------
/example/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "example",
3 | "version": "1.0.0",
4 | "main": "index.js",
5 | "license": "MIT",
6 | "scripts": {
7 | "start": "npm run build:css && parcel index.html",
8 | "build": "npm run build:css && parcel build index.html",
9 | "build:css": "postcss tailwind.css -o styles.css"
10 | },
11 | "dependencies": {
12 | "@fullhuman/postcss-purgecss": "^2.1.2",
13 | "autoprefixer": "^9.7.6",
14 | "components": "^0.1.0",
15 | "cssnano": "^4.1.10",
16 | "parcel-bundler": "^1.12.4",
17 | "postcss-cli": "^7.1.0",
18 | "react-app-polyfill": "^1.0.0",
19 | "react-icons-kit": "^1.3.1",
20 | "tailwindcss": "^1.4.6",
21 | "typescript": "^3.9.6"
22 | },
23 | "alias": {
24 | "react": "../node_modules/react",
25 | "react-dom": "../node_modules/react-dom/profiling",
26 | "scheduler/tracing": "../node_modules/scheduler/tracing-profiling"
27 | },
28 | "devDependencies": {
29 | "@types/react": "^16.9.11",
30 | "@types/react-dom": "^16.8.4"
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/example/postcss.config.js:
--------------------------------------------------------------------------------
1 | const tailwindcss = require('tailwindcss');
2 | const autoprefixer = require('autoprefixer');
3 | const cssnano = require('cssnano');
4 | const purgecss = require('@fullhuman/postcss-purgecss')({
5 | content: ['./*.html', './*.tsx', './src/*.tsx'],
6 | defaultExtractor: content => content.match(/[\w-/:]+(? {
5 | const { isModalOpen, openModal, closeModal } = useModal();
6 | const [toggle, setToggle] = React.useState(false);
7 |
8 | return (
9 | <>
10 | This modal can be closed by clicking the close button below.
11 |
17 |
23 | This modal can be closed by clicking the backdrop.
24 |
25 |
26 | Open another modal which will appear stacked on top of the current
27 | modal.
28 |
29 |
35 |
36 | Toggle some long content to see how react-simple-hook-modal behaves.
37 |
38 |
44 | {toggle ? (
45 |
46 | {[...Array(30)].map((e, i) => (
47 |
48 | Are you using react-simple-hook-modal yet?
49 |
50 | ))}
51 |
52 | ) : null}
53 | >
54 | );
55 | };
56 |
57 | export const Example = ({ transition }) => {
58 | const { isModalOpen, openModal, closeModal } = useModal();
59 |
60 | return (
61 |
62 |
63 | Click the button below to see an example modal
64 |
65 |
71 |
72 |
73 |
74 |
75 | );
76 | };
77 |
--------------------------------------------------------------------------------
/example/src/Header.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import { Icon } from 'react-icons-kit';
3 | import { github } from 'react-icons-kit/fa/github';
4 | // @ts-ignore
5 | import images from '../*.png';
6 |
7 | export const Header = () => {
8 | return (
9 |
10 |
11 |
12 |
13 |
React Simple Hook Modal
14 |
25 |
26 |
27 |

28 |
29 |
30 |
31 |
32 | );
33 | };
34 |
--------------------------------------------------------------------------------
/example/src/TransitionSelect.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import { ModalTransition } from '../../dist';
3 |
4 | interface Props {
5 | value: ModalTransition;
6 | onChange(transition: ModalTransition);
7 | }
8 | export const TransitionSelect: React.FC = ({ value, onChange }) => (
9 |
10 |
11 |
14 |
15 |
26 |
35 |
36 |
37 |
38 | );
39 |
--------------------------------------------------------------------------------
/example/tailwind.config.js:
--------------------------------------------------------------------------------
1 | const { colors } = require('tailwindcss/defaultTheme');
2 |
3 | module.exports = {
4 | theme: {
5 | container: {
6 | center: true,
7 | padding: {
8 | default: '1rem',
9 | sm: '2rem',
10 | lg: '4rem',
11 | xl: '5rem',
12 | },
13 | },
14 | colors: {
15 | white: colors.white,
16 | gray: colors.gray,
17 | blue: colors.blue,
18 | red: colors.red,
19 | },
20 | },
21 | };
22 |
--------------------------------------------------------------------------------
/example/tailwind.css:
--------------------------------------------------------------------------------
1 | @tailwind base;
2 |
3 | @import url('https://rsms.me/inter/inter.css');
4 | html {
5 | font-family: 'Inter', sans-serif;
6 | }
7 | @supports (font-variation-settings: normal) {
8 | html {
9 | font-family: 'Inter var', sans-serif;
10 | }
11 | }
12 |
13 | @tailwind components;
14 | @tailwind utilities;
15 |
--------------------------------------------------------------------------------
/example/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "allowSyntheticDefaultImports": false,
4 | "target": "es5",
5 | "module": "commonjs",
6 | "jsx": "react",
7 | "moduleResolution": "node",
8 | "noImplicitAny": false,
9 | "noUnusedLocals": false,
10 | "noUnusedParameters": false,
11 | "removeComments": true,
12 | "strictNullChecks": true,
13 | "preserveConstEnums": true,
14 | "sourceMap": true,
15 | "lib": ["es2015", "es2016", "dom"],
16 | "baseUrl": ".",
17 | "types": ["node"]
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mbrookson/react-simple-hook-modal/83a7c5b7d84b5a0361b28dbc14d97ebb42b76b1d/logo.png
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "1.1.0",
3 | "license": "MIT",
4 | "name": "react-simple-hook-modal",
5 | "description": "A simple React modal with hook based API",
6 | "homepage": "https://react-simple-hook-modal.now.sh",
7 | "repository": {
8 | "type": "git",
9 | "url": "https://github.com/mbrookson/react-simple-hook-modal.git"
10 | },
11 | "author": "Matthew Brookson",
12 | "main": "dist/index.js",
13 | "typings": "dist/index.d.ts",
14 | "module": "dist/react-simple-hook-modal.esm.js",
15 | "files": [
16 | "dist"
17 | ],
18 | "engines": {
19 | "node": ">=10"
20 | },
21 | "scripts": {
22 | "start": "tsdx watch",
23 | "build": "tsdx build",
24 | "build:all": "tsdx build && cd example && npm install && npm run build",
25 | "test": "tsdx test --passWithNoTests",
26 | "lint": "tsdx lint",
27 | "install-peers": "install-peers -f"
28 | },
29 | "husky": {
30 | "hooks": {
31 | "pre-commit": "tsdx lint"
32 | }
33 | },
34 | "prettier": {
35 | "printWidth": 80,
36 | "semi": true,
37 | "singleQuote": true,
38 | "trailingComma": "es5"
39 | },
40 | "peerDependencies": {
41 | "react": ">=16"
42 | },
43 | "devDependencies": {
44 | "@fullhuman/postcss-purgecss": "^2.1.2",
45 | "@types/react": "^16.9.34",
46 | "@types/react-dom": "^16.9.6",
47 | "autoprefixer": "^9.7.6",
48 | "cssnano": "^4.1.10",
49 | "husky": "^4.2.5",
50 | "install-peers-cli": "^2.2.0",
51 | "postcss-cli": "^7.1.0",
52 | "react": "^16.13.1",
53 | "react-dom": "^16.13.1",
54 | "rollup-plugin-postcss": "^2.8.2",
55 | "tailwindcss": "^1.4.6",
56 | "tsdx": "^0.13.2",
57 | "tslib": "^1.11.1",
58 | "typescript": "^3.9.6"
59 | },
60 | "keywords": [
61 | "react",
62 | "react-component",
63 | "react-modal",
64 | "modal",
65 | "simple"
66 | ],
67 | "dependencies": {
68 | "hooks": "^0.3.2",
69 | "react-spring": "^8.0.27"
70 | }
71 | }
--------------------------------------------------------------------------------
/src/ModalContext.ts:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | export interface Context {
4 | addOrUpdate(id: string): void;
5 | remove(id: string): void;
6 | getStaggerPixels(id: string): number;
7 | }
8 |
9 | export const ModalContext = React.createContext({} as Context);
10 |
--------------------------------------------------------------------------------
/src/components/Modal.tsx:
--------------------------------------------------------------------------------
1 | import React, { useEffect } from 'react';
2 | import ReactDOM from 'react-dom';
3 | import { useModalContext } from '../hooks/useModalContext';
4 | import { ModalTransition } from '../hooks/useModalTransition';
5 | import { ModalContainer } from './ModalContainer';
6 |
7 | export interface ModalProps {
8 | id: string;
9 | isOpen: boolean;
10 | onBackdropClick?: () => void;
11 | footer?: React.ReactNode;
12 | transition?: ModalTransition;
13 | modalClassName?: string;
14 | modalZIndex?: number;
15 | }
16 |
17 | function hasDOM() {
18 | return !!(
19 | typeof window !== 'undefined' &&
20 | window.document &&
21 | window.document.createElement
22 | );
23 | }
24 |
25 | export const Modal: React.FC = modal => {
26 | const { addOrUpdate, remove, getStaggerPixels } = useModalContext();
27 | const { id, isOpen } = modal;
28 |
29 | const container = hasDOM()
30 | ? document.getElementById('react-simple-modal-container')
31 | : null;
32 |
33 | useEffect(() => {
34 | isOpen ? addOrUpdate(id) : remove(id);
35 | return () => remove(id);
36 | }, [id, isOpen]);
37 |
38 | return container
39 | ? ReactDOM.createPortal(
40 | ,
41 | container
42 | )
43 | : null;
44 | };
45 |
--------------------------------------------------------------------------------
/src/components/ModalContainer.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { animated } from 'react-spring';
3 | import {
4 | useModalTransition,
5 | ModalTransition,
6 | } from '../hooks/useModalTransition';
7 | import { ModalProps } from './Modal';
8 |
9 | interface Props {
10 | transformDistance: number;
11 | }
12 |
13 | export const ModalContainer: React.FC = ({
14 | children,
15 | isOpen,
16 | footer,
17 | transition,
18 | onBackdropClick,
19 | transformDistance,
20 | modalClassName,
21 | modalZIndex,
22 | }) => {
23 | const modalTransitions = useModalTransition(transition, isOpen);
24 |
25 | return (
26 | <>
27 | {modalTransitions.map(
28 | ({ item, key, props }) =>
29 | item && (
30 |
36 | e.stopPropagation()}
50 | >
51 |
{children}
52 | {!footer ? null : (
53 |
{footer}
54 | )}
55 |
56 |
57 | )
58 | )}
59 | >
60 | );
61 | };
62 |
--------------------------------------------------------------------------------
/src/components/ModalProvider.tsx:
--------------------------------------------------------------------------------
1 | import React, { useState, useCallback } from 'react';
2 | import { ModalContext } from '../ModalContext';
3 |
4 | export interface ModalProviderProps {
5 | backdropClassName?: string;
6 | }
7 |
8 | export const ModalProvider: React.FC = ({
9 | children,
10 | backdropClassName,
11 | }) => {
12 | const [openModals, setOpenModals] = useState([]);
13 |
14 | const addOrUpdate = useCallback((_id: string) => {
15 | setOpenModals(prev => {
16 | const exists = prev.some(id => id === _id);
17 | return exists ? prev : [...prev, _id];
18 | });
19 | }, []);
20 |
21 | const remove = useCallback((_id: string) => {
22 | setOpenModals(prev => prev.filter(id => id !== _id));
23 | }, []);
24 |
25 | const modalOpen = openModals.length > 0;
26 |
27 | const getStaggerPixels = useCallback(
28 | (_id: string) => {
29 | const index = openModals.findIndex(id => id === _id);
30 | return openModals.length * 8 - (index + 1) * 8;
31 | },
32 | [openModals]
33 | );
34 |
35 | return (
36 |
37 | {children}
38 | {!modalOpen ? null : (
39 |
45 | )}
46 |
47 |
48 | );
49 | };
50 |
--------------------------------------------------------------------------------
/src/hooks/useModal.ts:
--------------------------------------------------------------------------------
1 | import { useState, useCallback } from 'react';
2 |
3 | export const useModal = () => {
4 | const [isModalOpen, setIsModalOpen] = useState(false);
5 |
6 | const openModal = useCallback(() => setIsModalOpen(true), []);
7 | const closeModal = useCallback(() => setIsModalOpen(false), []);
8 |
9 | return {
10 | isModalOpen,
11 | openModal,
12 | closeModal,
13 | };
14 | };
15 |
--------------------------------------------------------------------------------
/src/hooks/useModalContext.ts:
--------------------------------------------------------------------------------
1 | import { useContext } from 'react';
2 | import { ModalContext, Context } from '../ModalContext';
3 |
4 | export const useModalContext = () => useContext(ModalContext);
5 |
--------------------------------------------------------------------------------
/src/hooks/useModalTransition.ts:
--------------------------------------------------------------------------------
1 | import { useTransition } from 'react-spring';
2 |
3 | export enum ModalTransition {
4 | NONE = 'NONE',
5 | SCALE = 'SCALE',
6 | TOP_DOWN = 'TOP_DOWN',
7 | BOTTOM_UP = 'BOTTOM_UP',
8 | }
9 |
10 | const ModalTransitionConfig: { [key in ModalTransition]: object } = {
11 | NONE: {
12 | from: { transform: 'scale(1)' },
13 | enter: { transform: 'scale(1)' },
14 | leave: { transform: 'scale(1)' },
15 | },
16 | SCALE: {
17 | from: { transform: 'scale(0)', opacity: 0 },
18 | enter: { transform: 'scale(1)', opacity: 1 },
19 | leave: { transform: 'scale(0)', opacity: 0 },
20 | config: { tension: 800, friction: 25 },
21 | },
22 | TOP_DOWN: {
23 | from: { transform: 'translateY(-150%)' },
24 | enter: { transform: 'translateY(0)' },
25 | leave: { transform: 'translateY(-150%)' },
26 | config: { tension: 500, friction: 25 },
27 | },
28 | BOTTOM_UP: {
29 | from: { transform: 'translateY(150%)' },
30 | enter: { transform: 'translateY(0)' },
31 | leave: { transform: 'translateY(150%)' },
32 | config: { tension: 500, friction: 25 },
33 | },
34 | };
35 |
36 | export const useModalTransition = (
37 | transition: ModalTransition = ModalTransition.SCALE,
38 | isOpen: boolean
39 | ) => {
40 | return useTransition(isOpen, null, ModalTransitionConfig[transition]);
41 | };
42 |
--------------------------------------------------------------------------------
/src/index.tsx:
--------------------------------------------------------------------------------
1 | import './styles/styles.css';
2 | export * from './components/Modal';
3 | export * from './components/ModalProvider';
4 | export * from './hooks/useModal';
5 | export { ModalTransition } from './hooks/useModalTransition';
6 |
--------------------------------------------------------------------------------
/src/styles/styles.css:
--------------------------------------------------------------------------------
1 | @tailwind base;
2 | @tailwind utilities;
3 |
--------------------------------------------------------------------------------
/tailwind.config.js:
--------------------------------------------------------------------------------
1 | const { colors } = require('tailwindcss/defaultTheme');
2 |
3 | module.exports = {
4 | prefix: 'rsm-',
5 | corePlugins: [
6 | 'alignItems',
7 | 'boxShadow',
8 | 'borderRadius',
9 | 'backgroundColor',
10 | 'display',
11 | 'flex',
12 | 'flexDirection',
13 | 'inset',
14 | 'justifyContent',
15 | 'margin',
16 | 'maxHeight',
17 | 'opacity',
18 | 'overflow',
19 | 'padding',
20 | 'position',
21 | 'preflight',
22 | 'textAlign',
23 | 'textColor',
24 | 'width',
25 | 'zIndex',
26 | ],
27 | theme: {
28 | colors: {
29 | white: colors.white,
30 | gray: colors.gray,
31 | },
32 | zIndex: {
33 | 40: 40,
34 | 9999: 9999,
35 | auto: 'auto',
36 | },
37 | },
38 | };
39 |
--------------------------------------------------------------------------------
/test/blah.test.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import * as ReactDOM from 'react-dom';
3 |
4 | describe('it', () => {
5 | it('renders without crashing', () => {
6 | const div = document.createElement('div');
7 | ReactDOM.render(, div);
8 | ReactDOM.unmountComponentAtNode(div);
9 | });
10 | });
11 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "include": ["src", "types"],
3 | "compilerOptions": {
4 | "module": "esnext",
5 | "lib": ["dom", "esnext"],
6 | "importHelpers": true,
7 | "declaration": true,
8 | "sourceMap": true,
9 | "rootDir": "./src",
10 | "strict": true,
11 | "noUnusedLocals": true,
12 | "noUnusedParameters": true,
13 | "noImplicitReturns": true,
14 | "noFallthroughCasesInSwitch": true,
15 | "moduleResolution": "node",
16 | "baseUrl": "./",
17 | "paths": {
18 | "*": ["src/*", "node_modules/*"]
19 | },
20 | "jsx": "react",
21 | "esModuleInterop": true
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/tsdx.config.js:
--------------------------------------------------------------------------------
1 | const postcss = require('rollup-plugin-postcss');
2 | const tailwindcss = require('tailwindcss');
3 | const autoprefixer = require('autoprefixer');
4 | const cssnano = require('cssnano');
5 | const purgecss = require('@fullhuman/postcss-purgecss')({
6 | content: ['./src/**/*.html', './src/**/*.tsx'],
7 | defaultExtractor: content => content.match(/[\w-/:]+(?