├── .editorconfig ├── .gitattributes ├── .gitignore ├── .prettierrc ├── .vscode └── tasks.json ├── LICENSE ├── README.md ├── demo ├── .env ├── .gitignore ├── package-lock.json ├── package.json ├── public │ ├── icon.png │ ├── index.html │ ├── robots.txt │ └── social.png ├── src │ ├── App.tsx │ ├── components │ │ ├── app │ │ │ ├── Section │ │ │ │ ├── Section.module.scss │ │ │ │ └── Section.tsx │ │ │ └── Snippet │ │ │ │ ├── Snippet.module.scss │ │ │ │ └── Snippet.tsx │ │ ├── examples │ │ │ ├── BasicDialog.tsx │ │ │ ├── BasicDialogTS.tsx │ │ │ ├── BasicModal.tsx │ │ │ ├── BasicModalTS.tsx │ │ │ ├── DialogAutoClose.tsx │ │ │ ├── DialogCallback.tsx │ │ │ ├── DialogHighlight.tsx │ │ │ ├── Installation.tsx │ │ │ ├── ModalAutoClose.tsx │ │ │ ├── ModalLifecycle.tsx │ │ │ ├── ModalProps.tsx │ │ │ └── Theming.tsx │ │ └── home │ │ │ ├── Footer │ │ │ ├── Footer.module.scss │ │ │ └── Footer.tsx │ │ │ ├── Header │ │ │ ├── Header.module.scss │ │ │ └── Header.tsx │ │ │ ├── Home │ │ │ ├── Home.module.scss │ │ │ └── Home.tsx │ │ │ └── Topbar │ │ │ ├── Topbar.module.scss │ │ │ └── Topbar.tsx │ ├── index.tsx │ ├── react-app-env.d.ts │ └── styles │ │ ├── app.scss │ │ ├── fusion.module.scss │ │ ├── head.scss │ │ └── reset.scss └── tsconfig.json ├── package-lock.json ├── package.json ├── src ├── FusionContainer.tsx ├── FusionProvider.tsx ├── elements │ ├── Dialog │ │ ├── Dialog.style.ts │ │ └── Dialog.tsx │ ├── Modal │ │ ├── Modal.style.ts │ │ └── Modal.tsx │ └── index.ts ├── hooks │ ├── index.ts │ ├── useDialog.ts │ └── useModal.ts ├── index.ts ├── mappers │ ├── DialogMapper.tsx │ ├── ModalMapper.tsx │ └── index.ts ├── types │ ├── container.ts │ ├── dialog.ts │ ├── index.ts │ ├── modal.ts │ └── provider.ts └── utils │ ├── animate.ts │ ├── index.ts │ ├── sliceFloat.ts │ └── useFusionContext.ts └── tsconfig.json /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = 2 6 | trim_trailing_whitespace = true -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /lib 13 | /dist 14 | /build 15 | 16 | # misc 17 | .DS_Store 18 | .env.local 19 | .env.development.local 20 | .env.test.local 21 | .env.production.local 22 | 23 | npm-debug.log* 24 | yarn-debug.log* 25 | yarn-error.log* 26 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth": 160, 3 | "tabWidth": 2, 4 | "useTabs": false, 5 | "semi": true, 6 | "singleQuote": false, 7 | "quoteProps": "as-needed", 8 | "jsxSingleQuote": false, 9 | "trailingComma": "none", 10 | "bracketSpacing": true, 11 | "jsxBracketSameLine": false, 12 | "arrowParens": "always", 13 | "requirePragma": false, 14 | "insertPragma": false, 15 | "proseWrap": "preserve", 16 | "htmlWhitespaceSensitivity": "css" 17 | } 18 | -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "tasks": [ 4 | { 5 | "label": "FusionUI Dev", 6 | "type": "npm", 7 | "script": "start", 8 | "group": { 9 | "kind": "build", 10 | "isDefault": true 11 | }, 12 | "problemMatcher": [] 13 | } 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Ozan Bolel 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 FusionUI ☢️ 2 | 3 | [![npm](https://img.shields.io/npm/v/react-fusionui.svg)](https://www.npmjs.com/package/react-fusionui) [![npm downloads](https://img.shields.io/npm/dt/react-fusionui.svg)](https://www.npmjs.com/package/react-fusionui) ![npm bundle size](https://img.shields.io/bundlephobia/minzip/react-fusionui) 4 | 5 | Open modals and dialogs with ease! 🚀 FusionUI is an **unopinionated** and minimalist library to power-up your UI. 6 | 7 | ## **Demo with Examples** 8 | 9 | [ozanbolel.github.io/react-fusionui](https://ozanbolel.github.io/react-fusionui) 10 | 11 | ## **Installation** 12 | 13 | ```bash 14 | # npm 15 | npm i react-fusionui 16 | 17 | # yarn 18 | yarn add react-fusionui 19 | ``` 20 | 21 | ## **Basic Usage** 22 | 23 | ```jsx 24 | import React from "react"; 25 | import { FusionContainer, useModal } from "react-fusionui"; 26 | 27 | const Modal = ({ isAnimationDone, isClosing, closeModal }) => { 28 | return

Modal

; 29 | }; 30 | 31 | const Home = () => { 32 | const modal = useModal(); 33 | 34 | return ; 35 | }; 36 | 37 | const MyApp = () => { 38 | return ( 39 | 40 | 41 | 42 | ); 43 | }; 44 | 45 | export default MyApp; 46 | ``` 47 | 48 | > You need to wrap your app within `FusionContainer`. 49 | 50 | > FusionUI passes `isAnimationDone`, `isClosing` and `closeModal` props to your modal component. 51 | 52 | ## **Theming** 53 | 54 | ```jsx 55 | import React from "react"; 56 | import { FusionContainer } from "react-fusionui"; 57 | import css from "./App.module.css"; 58 | 59 | const MyApp = () => { 60 | return ( 61 | 76 | ... 77 | 78 | ); 79 | }; 80 | 81 | export default MyApp; 82 | ``` 83 | 84 | ## **API** 85 | 86 | ### **useModal** 87 | 88 | ##### Args 89 | 90 | | Arg | Description | Type | Required | 91 | | --------------- | ---------------------------------------------- | :-----------------: | :------: | 92 | | **`Component`** | Component you want to render inside the modal. | FunctionalComponent | YES | 93 | | **`config`** | Modal configuration. | Object | | 94 | 95 | ##### Config Object 96 | 97 | | Name | Description | Type | Required | 98 | | --------------- | ------------------------------------------------------------------- | :-----: | :------: | 99 | | **`props`** | Props you want to pass to the component you specified. | Object | | 100 | | **`autoclose`** | Should your component close when user clicks outside of your modal. | Boolean | | 101 | 102 | ### **useDialog** 103 | 104 | ##### Args 105 | 106 | | Arg | Description | Type | Required | 107 | | ------------- | ------------------------------------------ | :-------: | :------: | 108 | | **`content`** | Content of the dialog. | ReactNode | YES | 109 | | **`actions`** | Actions which will be rendered as buttons. | Array | YES | 110 | | **`config`** | Dialog configuration. | Object | | 111 | 112 | ##### Action Object 113 | 114 | | Name | Description | Type | Required | 115 | | --------------- | ------------------------------------- | :------: | :------: | 116 | | **`label`** | Label of the button. | String | YES | 117 | | **`callback`** | Will run when the button is clicked. | Function | | 118 | | **`highlight`** | Apply highlight styles to the button. | Boolean | | 119 | 120 | ##### Config Object 121 | 122 | | Name | Description | Type | Required | 123 | | --------------- | --------------------------------------------------------------- | :-----: | :------: | 124 | | **`autoclose`** | Should the dialog close when user clicks outside of the dialog. | Boolean | | 125 | 126 | ## License 127 | 128 | [MIT](https://github.com/ozanbolel/react-fusionui/blob/master/LICENSE) 129 | -------------------------------------------------------------------------------- /demo/.env: -------------------------------------------------------------------------------- 1 | BROWSER=none -------------------------------------------------------------------------------- /demo/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | -------------------------------------------------------------------------------- /demo/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-fusionui-demo", 3 | "version": "0.0.0", 4 | "author": "ozanbolel", 5 | "homepage": "https://ozanbolel.github.io/react-fusionui/", 6 | "scripts": { 7 | "start": "react-scripts start", 8 | "build": "react-scripts build", 9 | "eject": "react-scripts eject", 10 | "predeploy": "npm run build", 11 | "deploy": "gh-pages -d build" 12 | }, 13 | "dependencies": { 14 | "@types/node": "^12.12.48", 15 | "@types/react": "^16.9.41", 16 | "@types/react-dom": "^16.9.8", 17 | "gh-pages": "^3.1.0", 18 | "node-sass": "^4.14.1", 19 | "prism-react-renderer": "^1.1.1", 20 | "react": "^16.13.1", 21 | "react-dom": "^16.13.1", 22 | "react-fusionui": "file:..", 23 | "react-scripts": "3.4.1", 24 | "typescript": "^3.7.5" 25 | }, 26 | "browserslist": { 27 | "production": [ 28 | ">0.2%", 29 | "not dead", 30 | "not op_mini all" 31 | ], 32 | "development": [ 33 | "last 1 chrome version", 34 | "last 1 firefox version", 35 | "last 1 safari version" 36 | ] 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /demo/public/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ozanbolel/react-fusionui/d1655ea21603371e93678d67c8e64fa7769a6455/demo/public/icon.png -------------------------------------------------------------------------------- /demo/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | React FusionUI 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 |
21 | 22 | 23 | -------------------------------------------------------------------------------- /demo/public/robots.txt: -------------------------------------------------------------------------------- 1 | User-agent: * 2 | Disallow: 3 | -------------------------------------------------------------------------------- /demo/public/social.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ozanbolel/react-fusionui/d1655ea21603371e93678d67c8e64fa7769a6455/demo/public/social.png -------------------------------------------------------------------------------- /demo/src/App.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import { FusionContainer } from "react-fusionui"; 3 | import Home from "./components/home/Home/Home"; 4 | import "./styles/app.scss"; 5 | import css from "./styles/fusion.module.scss"; 6 | 7 | const App: React.FC = () => { 8 | return ( 9 | 24 | 25 | 26 | ); 27 | }; 28 | 29 | export default App; 30 | -------------------------------------------------------------------------------- /demo/src/components/app/Section/Section.module.scss: -------------------------------------------------------------------------------- 1 | @import "../../../styles/head"; 2 | 3 | .section { 4 | margin-bottom: $g-m; 5 | } 6 | 7 | .title { 8 | font-size: $h3; 9 | font-weight: $b; 10 | margin-bottom: $g-s; 11 | } 12 | -------------------------------------------------------------------------------- /demo/src/components/app/Section/Section.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import css from "./Section.module.scss"; 3 | 4 | const Section: React.FC<{ title?: string }> = ({ children, title }) => { 5 | return ( 6 |
7 | {title ?
{title}
: null} 8 | {children} 9 |
10 | ); 11 | }; 12 | 13 | export default Section; 14 | -------------------------------------------------------------------------------- /demo/src/components/app/Snippet/Snippet.module.scss: -------------------------------------------------------------------------------- 1 | @import "../../../styles/head"; 2 | 3 | .snippet { 4 | display: flex; 5 | flex-direction: column; 6 | border-radius: $r; 7 | overflow: hidden; 8 | 9 | &.margin { 10 | margin-bottom: $g-xs; 11 | } 12 | } 13 | 14 | .code { 15 | flex: 2; 16 | overflow-x: auto; 17 | overflow-y: hidden; 18 | } 19 | 20 | .preview { 21 | flex: 1; 22 | display: flex; 23 | justify-content: center; 24 | align-items: center; 25 | background: linear-gradient(-90deg, $c-light-a, $c-light); 26 | padding: $g-l 0; 27 | border-radius: 0 $r $r 0; 28 | @include no-select; 29 | 30 | button { 31 | background: $c-main; 32 | font-size: $h5; 33 | font-weight: $b; 34 | padding: $g-s $g-m; 35 | border-radius: $r; 36 | box-shadow: $s-button; 37 | transition: background $de, color $de; 38 | 39 | &:hover { 40 | background: $c-dark; 41 | color: $c-main; 42 | } 43 | } 44 | } 45 | 46 | // Responsive 47 | 48 | @media only screen and (min-width: 1024px) { 49 | .snippet { 50 | flex-direction: row; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /demo/src/components/app/Snippet/Snippet.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import Highlight, { defaultProps, Language } from "prism-react-renderer"; 3 | import css from "./Snippet.module.scss"; 4 | 5 | const Snippet: React.FC<{ code: string; language?: Language; preview?: JSX.Element; margin?: boolean }> = ({ code, language = "jsx", preview, margin }) => { 6 | return ( 7 |
8 | 9 | {({ style, tokens, getLineProps, getTokenProps }) => ( 10 |
11 |             {tokens.map((line, i) => (
12 |               
13 | {line.map((token, key) => ( 14 | 15 | ))} 16 |
17 | ))} 18 |
19 | )} 20 |
21 | 22 | {preview ?
{preview}
: null} 23 |
24 | ); 25 | }; 26 | 27 | export default Snippet; 28 | -------------------------------------------------------------------------------- /demo/src/components/examples/BasicDialog.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import { useDialog } from "react-fusionui"; 3 | import Section from "../app/Section/Section"; 4 | import Snippet from "../app/Snippet/Snippet"; 5 | 6 | const BasicDialog: React.FC = () => { 7 | const dialog = useDialog(); 8 | 9 | const code = ` 10 | import React from "react"; 11 | import { useDialog } from "react-fusionui"; 12 | 13 | const MyComponent = () => { 14 | const dialog = useDialog(); 15 | 16 | return ; 17 | }; 18 | 19 | export default MyComponent; 20 | `; 21 | 22 | const preview = ; 23 | 24 | return ( 25 |
26 | 27 |
28 | ); 29 | }; 30 | 31 | export default BasicDialog; 32 | -------------------------------------------------------------------------------- /demo/src/components/examples/BasicDialogTS.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import { useDialog, FusionDialogAction } from "react-fusionui"; 3 | import Section from "../app/Section/Section"; 4 | import Snippet from "../app/Snippet/Snippet"; 5 | 6 | const BasicDialogTS: React.FC = () => { 7 | const dialog = useDialog(); 8 | 9 | const code = ` 10 | import * as React from "react"; 11 | import { useDialog, FusionDialogAction } from "react-fusionui"; 12 | 13 | const MyComponent = () => { 14 | const dialog = useDialog(); 15 | 16 | const actions: FusionDialogAction[] = [{ label: "Bye." }]; 17 | 18 | return ; 19 | }; 20 | 21 | export default MyComponent; 22 | `; 23 | 24 | const actions: FusionDialogAction[] = [{ label: "Bye." }]; 25 | 26 | const preview = ; 27 | 28 | return ( 29 |
30 | 31 |
32 | ); 33 | }; 34 | 35 | export default BasicDialogTS; 36 | -------------------------------------------------------------------------------- /demo/src/components/examples/BasicModal.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import { useModal, FusionModalComponent } from "react-fusionui"; 3 | import Section from "../app/Section/Section"; 4 | import Snippet from "../app/Snippet/Snippet"; 5 | 6 | const BasicModal: React.FC = () => { 7 | const modal = useModal(); 8 | 9 | const code = ` 10 | import React from "react"; 11 | import { useModal } from "react-fusionui"; 12 | 13 | const Modal = ({ closeModal }) => { 14 | return

closeModal()}>Click to Close

; 15 | }; 16 | 17 | const MyComponent = () => { 18 | const modal = useModal(); 19 | 20 | return ; 21 | }; 22 | 23 | export default MyComponent; 24 | `; 25 | 26 | const Modal: FusionModalComponent = ({ closeModal }) => { 27 | return

closeModal()}>Click to Close

; 28 | }; 29 | 30 | const preview = ; 31 | 32 | return ( 33 |
34 | 35 |
36 | ); 37 | }; 38 | 39 | export default BasicModal; 40 | -------------------------------------------------------------------------------- /demo/src/components/examples/BasicModalTS.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import { useModal, FusionModalComponent } from "react-fusionui"; 3 | import Section from "../app/Section/Section"; 4 | import Snippet from "../app/Snippet/Snippet"; 5 | 6 | const BasicModalTS: React.FC = () => { 7 | const modal = useModal(); 8 | 9 | const code = ` 10 | import * as React from "react"; 11 | import { FusionModalComponent, useModal } from "react-fusionui"; 12 | 13 | const Modal: FusionModalComponent = ({ closeModal }) => { 14 | return

closeModal()}>Click to Close

; 15 | }; 16 | 17 | const MyComponent: React.FC = () => { 18 | const modal = useModal(); 19 | 20 | return ; 21 | }; 22 | 23 | export default MyComponent; 24 | `; 25 | 26 | const Modal: FusionModalComponent = ({ closeModal }) => { 27 | return

closeModal()}>Click to Close

; 28 | }; 29 | 30 | const preview = ; 31 | 32 | return ( 33 |
34 | 35 |
36 | ); 37 | }; 38 | 39 | export default BasicModalTS; 40 | -------------------------------------------------------------------------------- /demo/src/components/examples/DialogAutoClose.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import { useDialog } from "react-fusionui"; 3 | import Section from "../app/Section/Section"; 4 | import Snippet from "../app/Snippet/Snippet"; 5 | 6 | const DialogAutoClose: React.FC = () => { 7 | const dialog = useDialog(); 8 | 9 | const code = ` 10 | const dialog = useDialog(); 11 | 12 | return ; 13 | `; 14 | 15 | const preview = ; 16 | 17 | return ( 18 |
19 | 20 |
21 | ); 22 | }; 23 | 24 | export default DialogAutoClose; 25 | -------------------------------------------------------------------------------- /demo/src/components/examples/DialogCallback.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import { useDialog, FusionDialogAction } from "react-fusionui"; 3 | import Section from "../app/Section/Section"; 4 | import Snippet from "../app/Snippet/Snippet"; 5 | 6 | const DialogCallback: React.FC = () => { 7 | const dialog = useDialog(); 8 | 9 | const code = ` 10 | const dialog = useDialog(); 11 | 12 | const actions = [ 13 | { 14 | label: "Alert Me", 15 | callback: () => alert("Done.") 16 | }, 17 | { 18 | label: "Close" 19 | } 20 | ]; 21 | 22 | return ; 23 | `; 24 | 25 | const actions: FusionDialogAction[] = [ 26 | { 27 | label: "Alert Me", 28 | callback: () => alert("Done.") 29 | }, 30 | { 31 | label: "Close" 32 | } 33 | ]; 34 | 35 | const preview = ; 36 | 37 | return ( 38 |
39 | 40 |
41 | ); 42 | }; 43 | 44 | export default DialogCallback; 45 | -------------------------------------------------------------------------------- /demo/src/components/examples/DialogHighlight.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import { useDialog, FusionDialogAction } from "react-fusionui"; 3 | import Section from "../app/Section/Section"; 4 | import Snippet from "../app/Snippet/Snippet"; 5 | 6 | const DialogHighlight: React.FC = () => { 7 | const dialog = useDialog(); 8 | 9 | const code = ` 10 | const dialog = useDialog(); 11 | 12 | const actions = [ 13 | { 14 | label: "Delete", 15 | highlight: true 16 | }, 17 | { 18 | label: "Close" 19 | } 20 | ]; 21 | 22 | return ; 23 | `; 24 | 25 | const actions: FusionDialogAction[] = [ 26 | { 27 | label: "Delete", 28 | highlight: true 29 | }, 30 | { 31 | label: "Close" 32 | } 33 | ]; 34 | 35 | const preview = ; 36 | 37 | return ( 38 |
39 | 40 |
41 | ); 42 | }; 43 | 44 | export default DialogHighlight; 45 | -------------------------------------------------------------------------------- /demo/src/components/examples/Installation.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import Section from "../app/Section/Section"; 3 | import Snippet from "../app/Snippet/Snippet"; 4 | 5 | const Installation: React.FC = () => { 6 | const codeBash = ` 7 | # npm 8 | npm i react-fusionui 9 | 10 | # yarn 11 | yarn add react-fusionui 12 | `; 13 | 14 | const codeApp = ` 15 | import React from "react"; 16 | import { FusionContainer } from "react-fusionui"; 17 | 18 | const MyApp = () => { 19 | return ( 20 | 21 | ... 22 | 23 | ); 24 | }; 25 | 26 | export default MyApp; 27 | `; 28 | 29 | return ( 30 |
31 | 32 | 33 |
34 | ); 35 | }; 36 | 37 | export default Installation; 38 | -------------------------------------------------------------------------------- /demo/src/components/examples/ModalAutoClose.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import { useModal, FusionModalComponent } from "react-fusionui"; 3 | import Section from "../app/Section/Section"; 4 | import Snippet from "../app/Snippet/Snippet"; 5 | 6 | const ModalAutoClose: React.FC = () => { 7 | const modal = useModal(); 8 | 9 | const code = ` 10 | const modal = useModal(); 11 | 12 | const Modal = () => { 13 | return

Click outside to close.

; 14 | }; 15 | 16 | return ; 17 | `; 18 | 19 | const Modal: FusionModalComponent = () => { 20 | return

Click outside to close.

; 21 | }; 22 | 23 | const preview = ; 24 | 25 | return ( 26 |
27 | 28 |
29 | ); 30 | }; 31 | 32 | export default ModalAutoClose; 33 | -------------------------------------------------------------------------------- /demo/src/components/examples/ModalLifecycle.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import { useModal, FusionModalComponent } from "react-fusionui"; 3 | import Section from "../app/Section/Section"; 4 | import Snippet from "../app/Snippet/Snippet"; 5 | 6 | const ModalLifecycle: React.FC = () => { 7 | const modal = useModal(); 8 | 9 | const code = ` 10 | const modal = useModal(); 11 | 12 | const Modal = ({ isAnimationDone, isClosing }) => { 13 | useEffect(() => { 14 | if (!isAnimationDone) { 15 | alert("Modal added."); 16 | } 17 | 18 | if (isAnimationDone && !isClosing) { 19 | alert("Modal animation is done."); 20 | } 21 | 22 | if (isClosing) { 23 | alert("Modal is closing."); 24 | } 25 | 26 | return () => { 27 | if (isClosing) { 28 | alert("Modal removed."); 29 | } 30 | }; 31 | }, [isAnimationDone, isClosing]); 32 | 33 | return

Click outside to close.

; 34 | }; 35 | 36 | return ; 37 | `; 38 | 39 | const Modal: FusionModalComponent = ({ isAnimationDone, isClosing }) => { 40 | React.useEffect(() => { 41 | if (!isAnimationDone) { 42 | alert("Modal added."); 43 | } 44 | 45 | if (isAnimationDone && !isClosing) { 46 | alert("Modal animation is done."); 47 | } 48 | 49 | if (isClosing) { 50 | alert("Modal is closing."); 51 | } 52 | 53 | return () => { 54 | if (isClosing) { 55 | alert("Modal removed."); 56 | } 57 | }; 58 | }, [isAnimationDone, isClosing]); 59 | 60 | return

Click outside to close.

; 61 | }; 62 | 63 | const preview = ; 64 | 65 | return ( 66 |
67 | 68 |
69 | ); 70 | }; 71 | 72 | export default ModalLifecycle; 73 | -------------------------------------------------------------------------------- /demo/src/components/examples/ModalProps.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import { useModal, FusionModalComponent, FusionModalConfig } from "react-fusionui"; 3 | import Section from "../app/Section/Section"; 4 | import Snippet from "../app/Snippet/Snippet"; 5 | 6 | const ModalProps: React.FC = () => { 7 | const modal = useModal(); 8 | 9 | const code = ` 10 | const modal = useModal(); 11 | 12 | const Modal = ({ text }) => { 13 | return

{text}

; 14 | }; 15 | 16 | const config = { 17 | props: { text: "Hi there!" }, 18 | autoclose: true 19 | }; 20 | 21 | return ; 22 | `; 23 | 24 | const Modal: FusionModalComponent<{ text?: string }> = ({ text }) => { 25 | return

{text}

; 26 | }; 27 | 28 | const config: FusionModalConfig = { 29 | props: { text: "Hi there!" }, 30 | autoclose: true 31 | }; 32 | 33 | const preview = ; 34 | 35 | return ( 36 |
37 | 38 |
39 | ); 40 | }; 41 | 42 | export default ModalProps; 43 | -------------------------------------------------------------------------------- /demo/src/components/examples/Theming.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import Section from "../app/Section/Section"; 3 | import Snippet from "../app/Snippet/Snippet"; 4 | 5 | const Theming: React.FC = () => { 6 | const code = ` 7 | import React from "react"; 8 | import { FusionContainer } from "react-fusionui"; 9 | import css from "./App.module.css"; 10 | 11 | const MyApp = () => { 12 | return ( 13 | 28 | ... 29 | 30 | ); 31 | }; 32 | 33 | export default MyApp; 34 | `; 35 | 36 | return ( 37 |
38 | 39 |
40 | ); 41 | }; 42 | 43 | export default Theming; 44 | -------------------------------------------------------------------------------- /demo/src/components/home/Footer/Footer.module.scss: -------------------------------------------------------------------------------- 1 | @import "../../../styles/head"; 2 | 3 | .footer { 4 | font-size: $h3; 5 | text-align: center; 6 | margin: $g-m 0; 7 | @include no-select; 8 | 9 | .bold { 10 | font-weight: $b; 11 | } 12 | 13 | a { 14 | border-bottom: solid 1px transparent; 15 | transition: border-color $de; 16 | cursor: pointer; 17 | 18 | &:hover { 19 | border-color: $c-dark; 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /demo/src/components/home/Footer/Footer.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import css from "./Footer.module.scss"; 3 | 4 | const Footer: React.FC = () => { 5 | return ( 6 |
7 | 8 | Created with 9 | 10 | ❤️ 11 | 12 | by 13 | Ozan Bolel 14 | 15 |
16 | ); 17 | }; 18 | 19 | export default Footer; 20 | -------------------------------------------------------------------------------- /demo/src/components/home/Header/Header.module.scss: -------------------------------------------------------------------------------- 1 | @import "../../../styles/head"; 2 | 3 | .header { 4 | @keyframes gradient { 5 | 0% { 6 | background-position: 10% 0%; 7 | } 8 | 50% { 9 | background-position: 90% 100%; 10 | } 11 | 100% { 12 | background-position: 10% 0%; 13 | } 14 | } 15 | 16 | background: linear-gradient(180deg, #ffb7b7 0%, #727272 100%), radial-gradient(60.91% 100% at 50% 0%, #ffd1d1 0%, #260000 100%), 17 | linear-gradient(238.72deg, #ffdddd 0%, #720066 100%), linear-gradient(127.43deg, #00ffff 0%, #ff4444 100%), 18 | radial-gradient(100.22% 100% at 70.57% 0%, #ff0000 0%, #00ffe0 100%), linear-gradient(127.43deg, #b7d500 0%, #3300ff 100%); 19 | background-blend-mode: screen, overlay, hard-light, color-burn, color-dodge, normal; 20 | background-size: 250% 250%; 21 | color: $c-dark; 22 | border-radius: $r; 23 | margin-bottom: $g-m; 24 | animation: gradient 10s $e-io infinite; 25 | overflow: hidden; 26 | @include no-select; 27 | } 28 | 29 | .inner { 30 | background: rgba($c-main, 0.25); 31 | padding: $g-xxl $g-s; 32 | text-align: center; 33 | } 34 | 35 | .title { 36 | font-size: $h2; 37 | font-weight: $b; 38 | margin-bottom: $g-s; 39 | } 40 | 41 | .desc { 42 | font-size: $h4; 43 | } 44 | 45 | .bold { 46 | font-weight: $b; 47 | } 48 | -------------------------------------------------------------------------------- /demo/src/components/home/Header/Header.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import css from "./Header.module.scss"; 3 | 4 | const Header: React.FC = () => { 5 | return ( 6 |
7 |
8 |
Nuclear power-up for your UI.
9 | 10 |
11 | Open modals and dialogs with ease! 12 | 13 | 🚀 14 | 15 |
16 | 17 |
18 | FusionUI is an 19 | unopinionated 20 | and minimalist library to power-up your UI. 21 |
22 |
23 |
24 | ); 25 | }; 26 | 27 | export default Header; 28 | -------------------------------------------------------------------------------- /demo/src/components/home/Home/Home.module.scss: -------------------------------------------------------------------------------- 1 | @import "../../../styles/head"; 2 | 3 | .home { 4 | height: 100%; 5 | padding: 0 $g-s; 6 | } 7 | 8 | // Responsive 9 | 10 | @media only screen and (min-width: 1024px) { 11 | .home { 12 | padding: 0 5%; 13 | } 14 | } 15 | 16 | @media only screen and (min-width: 1280px) { 17 | .home { 18 | padding: 0 10%; 19 | } 20 | } 21 | 22 | @media only screen and (min-width: 1600px) { 23 | .home { 24 | padding: 0 20%; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /demo/src/components/home/Home/Home.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import css from "./Home.module.scss"; 3 | import Topbar from "../Topbar/Topbar"; 4 | import Header from "../Header/Header"; 5 | import Footer from "../Footer/Footer"; 6 | import Install from "../../examples/Installation"; 7 | import Theming from "../../examples/Theming"; 8 | import BasicModal from "../../examples/BasicModal"; 9 | import BasicModalTS from "../../examples/BasicModalTS"; 10 | import ModalAutoClose from "../../examples/ModalAutoClose"; 11 | import ModalProps from "../../examples/ModalProps"; 12 | import ModalLifecycle from "../../examples/ModalLifecycle"; 13 | import BasicDialog from "../../examples/BasicDialog"; 14 | import BasicDialogTS from "../../examples/BasicDialogTS"; 15 | import DialogAutoClose from "../../examples/DialogAutoClose"; 16 | import DialogCallback from "../../examples/DialogCallback"; 17 | import DialogHighlight from "../../examples/DialogHighlight"; 18 | 19 | const Home: React.FC = () => { 20 | return ( 21 | <> 22 | 23 | 24 |
25 | 26 |
27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 |
40 |
41 | 42 | ); 43 | }; 44 | 45 | export default Home; 46 | -------------------------------------------------------------------------------- /demo/src/components/home/Topbar/Topbar.module.scss: -------------------------------------------------------------------------------- 1 | @import "../../../styles/head"; 2 | 3 | .topbar { 4 | z-index: 10; 5 | position: fixed; 6 | top: 0; 7 | left: 0; 8 | right: 0; 9 | display: flex; 10 | background: rgba($c-light, 0.85); 11 | backdrop-filter: blur(15px); 12 | padding: $g-xs $g-s; 13 | border-bottom: solid 1px $c-divider; 14 | @include no-select; 15 | 16 | &.placeholder { 17 | visibility: hidden; 18 | position: unset; 19 | top: unset; 20 | left: unset; 21 | right: unset; 22 | margin-bottom: $g-m; 23 | } 24 | } 25 | 26 | .name { 27 | display: flex; 28 | align-items: center; 29 | font-size: $h3; 30 | font-weight: $b; 31 | cursor: pointer; 32 | 33 | span[role="img"] { 34 | display: none; 35 | margin-left: $g-xxs; 36 | } 37 | } 38 | 39 | .link { 40 | flex: 1; 41 | display: flex; 42 | justify-content: flex-end; 43 | align-items: center; 44 | 45 | button { 46 | display: flex; 47 | align-items: center; 48 | background: $c-dark; 49 | color: $c-light; 50 | font-size: $h5; 51 | font-weight: $b; 52 | padding: $g-xs $g-s; 53 | border-radius: $r; 54 | transition: color $de; 55 | 56 | &:hover { 57 | color: $c-main; 58 | } 59 | 60 | svg { 61 | display: none; 62 | width: $h3; 63 | margin-left: $g-xxs; 64 | } 65 | } 66 | } 67 | 68 | // Responsive 69 | 70 | @media only screen and (min-width: 360px) { 71 | .name { 72 | span[role="img"] { 73 | display: block; 74 | } 75 | } 76 | 77 | .link { 78 | button { 79 | svg { 80 | display: block; 81 | } 82 | } 83 | } 84 | } 85 | 86 | @media only screen and (min-width: 1024px) { 87 | .topbar { 88 | padding: $g-xs 5%; 89 | } 90 | } 91 | 92 | @media only screen and (min-width: 1280px) { 93 | .topbar { 94 | padding: $g-xs 10%; 95 | } 96 | } 97 | 98 | @media only screen and (min-width: 1600px) { 99 | .topbar { 100 | padding: $g-xs 20%; 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /demo/src/components/home/Topbar/Topbar.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import css from "./Topbar.module.scss"; 3 | 4 | const Topbar: React.FC<{ placeholder?: boolean }> = ({ placeholder }) => { 5 | return ( 6 |
7 |
window.scroll({ top: 0, behavior: "smooth" })}> 8 | React FusionUI 9 | 10 | ☢️ 11 | 12 |
13 | 14 |
15 | 16 | 25 | 26 |
27 |
28 | ); 29 | }; 30 | 31 | export default Topbar; 32 | -------------------------------------------------------------------------------- /demo/src/index.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import ReactDOM from "react-dom"; 3 | import App from "./App"; 4 | 5 | ReactDOM.render( 6 | 7 | 8 | , 9 | document.getElementById("root") 10 | ); 11 | -------------------------------------------------------------------------------- /demo/src/react-app-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /demo/src/styles/app.scss: -------------------------------------------------------------------------------- 1 | @import "./head"; 2 | @import "./reset"; 3 | 4 | html { 5 | font-family: $f; 6 | font-size: $f-s; 7 | } 8 | 9 | body { 10 | background: $c-light; 11 | color: $c-dark; 12 | overflow-x: hidden; 13 | } 14 | -------------------------------------------------------------------------------- /demo/src/styles/fusion.module.scss: -------------------------------------------------------------------------------- 1 | @import "./head.scss"; 2 | 3 | // Common 4 | 5 | .container { 6 | z-index: 100; 7 | background: rgba($c-dark, 0.15); 8 | @include no-select; 9 | } 10 | 11 | // Modal 12 | 13 | .modal { 14 | background: $c-light; 15 | padding: $g-m; 16 | border-radius: $r; 17 | box-shadow: $s-overlay; 18 | overflow: hidden; 19 | } 20 | 21 | // Dialog 22 | 23 | .dialog { 24 | min-width: 250px; 25 | background: $c-light; 26 | border-radius: $r; 27 | box-shadow: $s-overlay; 28 | overflow: hidden; 29 | } 30 | 31 | .content { 32 | padding: $g-l; 33 | text-align: center; 34 | } 35 | 36 | .actionContainer { 37 | display: flex; 38 | border-top: solid 1px $c-divider; 39 | } 40 | 41 | .action { 42 | flex: 1; 43 | padding: $g-s 0; 44 | border-right: solid 1px $c-divider; 45 | transition: background $de; 46 | 47 | &:last-of-type { 48 | border-right: none; 49 | } 50 | 51 | &:hover { 52 | background: $c-divider; 53 | } 54 | } 55 | 56 | .actionLabel { 57 | font-weight: $b; 58 | } 59 | 60 | .highlight { 61 | background: $c-main; 62 | } 63 | -------------------------------------------------------------------------------- /demo/src/styles/head.scss: -------------------------------------------------------------------------------- 1 | // Font 2 | 3 | $f: -apple-system, BlinkMacSystemFont, Segoe UI, Helvetica, Arial, sans-serif, Apple Color Emoji, Segoe UI Emoji; 4 | $f-s: 14px; 5 | $b: 700; 6 | 7 | // Text 8 | 9 | $h1: 2rem; 10 | $h2: 1.5rem; 11 | $h3: 1.25rem; 12 | $h4: 1rem; 13 | $h5: 0.875em; 14 | 15 | // Color 16 | 17 | $c-dark: #1b262c; 18 | $c-light: #f9f9f9; 19 | $c-light-a: #e9e9e9; 20 | $c-main: #fbd46d; 21 | $c-divider: #ece8d9; 22 | 23 | // Space 24 | 25 | $g-xxs: 5px; 26 | $g-xs: 10px; 27 | $g-s: 15px; 28 | $g-m: 30px; 29 | $g-l: 45px; 30 | $g-xl: 60px; 31 | $g-xxl: 100px; 32 | 33 | // Radius 34 | 35 | $r: 6px; 36 | $r-100: 100px; 37 | 38 | // Shadow 39 | 40 | $s-button: 0 5px 15px rgba($c-dark, 0.08); 41 | $s-overlay: 0 10px 30px rgba($c-dark, 0.08); 42 | 43 | // Easing 44 | 45 | $e-io: ease-in-out; 46 | $e-i: ease-in; 47 | $e-o: ease-out; 48 | $e-l: linear; 49 | 50 | // Duration 51 | 52 | $d: 0.4s; 53 | $d-l: 0.6s; 54 | $d-xl: 0.8s; 55 | $d-xxl: 1s; 56 | 57 | $de: $d $e-o; 58 | 59 | // Mixins 60 | 61 | @mixin no-select { 62 | -webkit-touch-callout: none; 63 | -webkit-user-select: none; 64 | -ms-user-select: none; 65 | user-select: none; 66 | } 67 | -------------------------------------------------------------------------------- /demo/src/styles/reset.scss: -------------------------------------------------------------------------------- 1 | * { 2 | margin: 0; 3 | padding: 0; 4 | box-sizing: border-box; 5 | word-break: break-word; 6 | word-wrap: break-word; 7 | -webkit-touch-callout: none; 8 | -webkit-tap-highlight-color: transparent; 9 | -webkit-font-smoothing: antialiased; 10 | -moz-osx-font-smoothing: grayscale; 11 | 12 | &:focus { 13 | outline: none; 14 | } 15 | } 16 | 17 | html, 18 | body, 19 | #root { 20 | height: 100%; 21 | } 22 | 23 | a { 24 | color: unset; 25 | text-decoration: none; 26 | -webkit-user-drag: none; 27 | } 28 | 29 | button { 30 | font: unset; 31 | background: unset; 32 | color: unset; 33 | border: none; 34 | cursor: pointer; 35 | } 36 | 37 | input { 38 | font: unset; 39 | background: unset; 40 | color: unset; 41 | border: none; 42 | 43 | &::placeholder { 44 | color: inherit; 45 | } 46 | } 47 | 48 | iframe { 49 | border: none; 50 | } 51 | 52 | select { 53 | font: unset; 54 | border: none; 55 | appearance: none; 56 | } 57 | 58 | option { 59 | font: unset; 60 | } 61 | -------------------------------------------------------------------------------- /demo/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "lib": ["dom", "dom.iterable", "esnext"], 5 | "allowJs": true, 6 | "skipLibCheck": true, 7 | "esModuleInterop": true, 8 | "allowSyntheticDefaultImports": true, 9 | "strict": true, 10 | "forceConsistentCasingInFileNames": true, 11 | "module": "esnext", 12 | "moduleResolution": "node", 13 | "resolveJsonModule": true, 14 | "isolatedModules": true, 15 | "noEmit": true, 16 | "jsx": "react" 17 | }, 18 | "include": ["src"], 19 | "exclude": ["node_modules"] 20 | } 21 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-fusionui", 3 | "version": "1.1.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "@types/prop-types": { 8 | "version": "15.7.3", 9 | "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.3.tgz", 10 | "integrity": "sha512-KfRL3PuHmqQLOG+2tGpRO26Ctg+Cq1E01D2DMriKEATHgWLfeNDmq9e29Q9WIky0dQ3NPkd1mzYH8Lm936Z9qw==", 11 | "dev": true 12 | }, 13 | "@types/react": { 14 | "version": "file:demo/node_modules/@types/react", 15 | "integrity": "sha512-6cFei7F7L4wwuM+IND/Q2cV1koQUvJ8iSV+Gwn0c3kvABZ691g7sp3hfEQHOUBJtccl1gPi+EyNjMIl9nGA0ug==", 16 | "dev": true, 17 | "requires": { 18 | "@types/prop-types": "*", 19 | "csstype": "^2.2.0" 20 | } 21 | }, 22 | "ansi-regex": { 23 | "version": "4.1.0", 24 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", 25 | "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", 26 | "dev": true 27 | }, 28 | "ansi-styles": { 29 | "version": "3.2.1", 30 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", 31 | "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", 32 | "dev": true, 33 | "requires": { 34 | "color-convert": "^1.9.0" 35 | } 36 | }, 37 | "camelcase": { 38 | "version": "5.3.1", 39 | "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", 40 | "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", 41 | "dev": true 42 | }, 43 | "chalk": { 44 | "version": "2.4.2", 45 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", 46 | "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", 47 | "dev": true, 48 | "requires": { 49 | "ansi-styles": "^3.2.1", 50 | "escape-string-regexp": "^1.0.5", 51 | "supports-color": "^5.3.0" 52 | }, 53 | "dependencies": { 54 | "supports-color": { 55 | "version": "5.5.0", 56 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", 57 | "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", 58 | "dev": true, 59 | "requires": { 60 | "has-flag": "^3.0.0" 61 | } 62 | } 63 | } 64 | }, 65 | "cliui": { 66 | "version": "5.0.0", 67 | "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", 68 | "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", 69 | "dev": true, 70 | "requires": { 71 | "string-width": "^3.1.0", 72 | "strip-ansi": "^5.2.0", 73 | "wrap-ansi": "^5.1.0" 74 | } 75 | }, 76 | "color-convert": { 77 | "version": "1.9.3", 78 | "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", 79 | "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", 80 | "dev": true, 81 | "requires": { 82 | "color-name": "1.1.3" 83 | } 84 | }, 85 | "color-name": { 86 | "version": "1.1.3", 87 | "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", 88 | "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", 89 | "dev": true 90 | }, 91 | "concurrently": { 92 | "version": "5.2.0", 93 | "resolved": "https://registry.npmjs.org/concurrently/-/concurrently-5.2.0.tgz", 94 | "integrity": "sha512-XxcDbQ4/43d6CxR7+iV8IZXhur4KbmEJk1CetVMUqCy34z9l0DkszbY+/9wvmSnToTej0SYomc2WSRH+L0zVJw==", 95 | "dev": true, 96 | "requires": { 97 | "chalk": "^2.4.2", 98 | "date-fns": "^2.0.1", 99 | "lodash": "^4.17.15", 100 | "read-pkg": "^4.0.1", 101 | "rxjs": "^6.5.2", 102 | "spawn-command": "^0.0.2-1", 103 | "supports-color": "^6.1.0", 104 | "tree-kill": "^1.2.2", 105 | "yargs": "^13.3.0" 106 | } 107 | }, 108 | "csstype": { 109 | "version": "2.6.11", 110 | "resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.11.tgz", 111 | "integrity": "sha512-l8YyEC9NBkSm783PFTvh0FmJy7s5pFKrDp49ZL7zBGX3fWkO+N4EEyan1qqp8cwPLDcD0OSdyY6hAMoxp34JFw==", 112 | "dev": true 113 | }, 114 | "date-fns": { 115 | "version": "2.14.0", 116 | "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.14.0.tgz", 117 | "integrity": "sha512-1zD+68jhFgDIM0rF05rcwYO8cExdNqxjq4xP1QKM60Q45mnO6zaMWB4tOzrIr4M4GSLntsKeE4c9Bdl2jhL/yw==", 118 | "dev": true 119 | }, 120 | "decamelize": { 121 | "version": "1.2.0", 122 | "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", 123 | "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", 124 | "dev": true 125 | }, 126 | "emoji-regex": { 127 | "version": "7.0.3", 128 | "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", 129 | "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", 130 | "dev": true 131 | }, 132 | "error-ex": { 133 | "version": "1.3.2", 134 | "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", 135 | "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", 136 | "dev": true, 137 | "requires": { 138 | "is-arrayish": "^0.2.1" 139 | } 140 | }, 141 | "escape-string-regexp": { 142 | "version": "1.0.5", 143 | "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", 144 | "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", 145 | "dev": true 146 | }, 147 | "find-up": { 148 | "version": "3.0.0", 149 | "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", 150 | "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", 151 | "dev": true, 152 | "requires": { 153 | "locate-path": "^3.0.0" 154 | } 155 | }, 156 | "get-caller-file": { 157 | "version": "2.0.5", 158 | "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", 159 | "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", 160 | "dev": true 161 | }, 162 | "has-flag": { 163 | "version": "3.0.0", 164 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", 165 | "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", 166 | "dev": true 167 | }, 168 | "hosted-git-info": { 169 | "version": "2.8.8", 170 | "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.8.tgz", 171 | "integrity": "sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg==", 172 | "dev": true 173 | }, 174 | "is-arrayish": { 175 | "version": "0.2.1", 176 | "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", 177 | "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", 178 | "dev": true 179 | }, 180 | "is-fullwidth-code-point": { 181 | "version": "2.0.0", 182 | "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", 183 | "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", 184 | "dev": true 185 | }, 186 | "js-tokens": { 187 | "version": "4.0.0", 188 | "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", 189 | "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", 190 | "dev": true 191 | }, 192 | "json-parse-better-errors": { 193 | "version": "1.0.2", 194 | "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", 195 | "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", 196 | "dev": true 197 | }, 198 | "locate-path": { 199 | "version": "3.0.0", 200 | "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", 201 | "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", 202 | "dev": true, 203 | "requires": { 204 | "p-locate": "^3.0.0", 205 | "path-exists": "^3.0.0" 206 | } 207 | }, 208 | "lodash": { 209 | "version": "4.17.19", 210 | "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.19.tgz", 211 | "integrity": "sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ==", 212 | "dev": true 213 | }, 214 | "loose-envify": { 215 | "version": "1.4.0", 216 | "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", 217 | "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", 218 | "dev": true, 219 | "requires": { 220 | "js-tokens": "^3.0.0 || ^4.0.0" 221 | } 222 | }, 223 | "normalize-package-data": { 224 | "version": "2.5.0", 225 | "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", 226 | "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", 227 | "dev": true, 228 | "requires": { 229 | "hosted-git-info": "^2.1.4", 230 | "resolve": "^1.10.0", 231 | "semver": "2 || 3 || 4 || 5", 232 | "validate-npm-package-license": "^3.0.1" 233 | } 234 | }, 235 | "object-assign": { 236 | "version": "4.1.1", 237 | "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", 238 | "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", 239 | "dev": true 240 | }, 241 | "p-limit": { 242 | "version": "2.3.0", 243 | "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", 244 | "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", 245 | "dev": true, 246 | "requires": { 247 | "p-try": "^2.0.0" 248 | } 249 | }, 250 | "p-locate": { 251 | "version": "3.0.0", 252 | "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", 253 | "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", 254 | "dev": true, 255 | "requires": { 256 | "p-limit": "^2.0.0" 257 | } 258 | }, 259 | "p-try": { 260 | "version": "2.2.0", 261 | "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", 262 | "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", 263 | "dev": true 264 | }, 265 | "parse-json": { 266 | "version": "4.0.0", 267 | "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", 268 | "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", 269 | "dev": true, 270 | "requires": { 271 | "error-ex": "^1.3.1", 272 | "json-parse-better-errors": "^1.0.1" 273 | } 274 | }, 275 | "path-exists": { 276 | "version": "3.0.0", 277 | "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", 278 | "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", 279 | "dev": true 280 | }, 281 | "path-parse": { 282 | "version": "1.0.6", 283 | "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", 284 | "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", 285 | "dev": true 286 | }, 287 | "pify": { 288 | "version": "3.0.0", 289 | "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", 290 | "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", 291 | "dev": true 292 | }, 293 | "prop-types": { 294 | "version": "15.7.2", 295 | "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz", 296 | "integrity": "sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==", 297 | "dev": true, 298 | "requires": { 299 | "loose-envify": "^1.4.0", 300 | "object-assign": "^4.1.1", 301 | "react-is": "^16.8.1" 302 | } 303 | }, 304 | "react": { 305 | "version": "file:https:/registry.npmjs.org/react/-/react-16.13.1.tgz", 306 | "integrity": "sha512-YMZQQq32xHLX0bz5Mnibv1/LHb3Sqzngu7xstSM+vrkE5Kzr9xE0yMByK5kMoTK30YVJE61WfbxIFFvfeDKT1w==", 307 | "dev": true, 308 | "requires": { 309 | "loose-envify": "^1.1.0", 310 | "object-assign": "^4.1.1", 311 | "prop-types": "^15.6.2" 312 | } 313 | }, 314 | "react-dom": { 315 | "version": "file:demo/node_modules/react-dom", 316 | "integrity": "sha512-81PIMmVLnCNLO/fFOQxdQkvEq/+Hfpv24XNJfpyZhTRfO0QcmQIF/PgCa1zCOj2w1hrn12MFLyaJ/G0+Mxtfag==", 317 | "dev": true, 318 | "requires": { 319 | "loose-envify": "^1.1.0", 320 | "object-assign": "^4.1.1", 321 | "prop-types": "^15.6.2", 322 | "scheduler": "^0.19.1" 323 | } 324 | }, 325 | "react-is": { 326 | "version": "16.13.1", 327 | "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", 328 | "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", 329 | "dev": true 330 | }, 331 | "read-pkg": { 332 | "version": "4.0.1", 333 | "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-4.0.1.tgz", 334 | "integrity": "sha1-ljYlN48+HE1IyFhytabsfV0JMjc=", 335 | "dev": true, 336 | "requires": { 337 | "normalize-package-data": "^2.3.2", 338 | "parse-json": "^4.0.0", 339 | "pify": "^3.0.0" 340 | } 341 | }, 342 | "require-directory": { 343 | "version": "2.1.1", 344 | "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", 345 | "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", 346 | "dev": true 347 | }, 348 | "require-main-filename": { 349 | "version": "2.0.0", 350 | "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", 351 | "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", 352 | "dev": true 353 | }, 354 | "resolve": { 355 | "version": "1.17.0", 356 | "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.17.0.tgz", 357 | "integrity": "sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==", 358 | "dev": true, 359 | "requires": { 360 | "path-parse": "^1.0.6" 361 | } 362 | }, 363 | "rxjs": { 364 | "version": "6.6.0", 365 | "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.0.tgz", 366 | "integrity": "sha512-3HMA8z/Oz61DUHe+SdOiQyzIf4tOx5oQHmMir7IZEu6TMqCLHT4LRcmNaUS0NwOz8VLvmmBduMsoaUvMaIiqzg==", 367 | "dev": true, 368 | "requires": { 369 | "tslib": "^1.9.0" 370 | } 371 | }, 372 | "scheduler": { 373 | "version": "0.19.1", 374 | "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.19.1.tgz", 375 | "integrity": "sha512-n/zwRWRYSUj0/3g/otKDRPMh6qv2SYMWNq85IEa8iZyAv8od9zDYpGSnpBEjNgcMNq6Scbu5KfIPxNF72R/2EA==", 376 | "dev": true, 377 | "requires": { 378 | "loose-envify": "^1.1.0", 379 | "object-assign": "^4.1.1" 380 | } 381 | }, 382 | "semver": { 383 | "version": "5.7.1", 384 | "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", 385 | "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", 386 | "dev": true 387 | }, 388 | "set-blocking": { 389 | "version": "2.0.0", 390 | "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", 391 | "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", 392 | "dev": true 393 | }, 394 | "spawn-command": { 395 | "version": "0.0.2-1", 396 | "resolved": "https://registry.npmjs.org/spawn-command/-/spawn-command-0.0.2-1.tgz", 397 | "integrity": "sha1-YvXpRmmBwbeW3Fkpk34RycaSG9A=", 398 | "dev": true 399 | }, 400 | "spdx-correct": { 401 | "version": "3.1.1", 402 | "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz", 403 | "integrity": "sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==", 404 | "dev": true, 405 | "requires": { 406 | "spdx-expression-parse": "^3.0.0", 407 | "spdx-license-ids": "^3.0.0" 408 | } 409 | }, 410 | "spdx-exceptions": { 411 | "version": "2.3.0", 412 | "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", 413 | "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==", 414 | "dev": true 415 | }, 416 | "spdx-expression-parse": { 417 | "version": "3.0.1", 418 | "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", 419 | "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", 420 | "dev": true, 421 | "requires": { 422 | "spdx-exceptions": "^2.1.0", 423 | "spdx-license-ids": "^3.0.0" 424 | } 425 | }, 426 | "spdx-license-ids": { 427 | "version": "3.0.5", 428 | "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.5.tgz", 429 | "integrity": "sha512-J+FWzZoynJEXGphVIS+XEh3kFSjZX/1i9gFBaWQcB+/tmpe2qUsSBABpcxqxnAxFdiUFEgAX1bjYGQvIZmoz9Q==", 430 | "dev": true 431 | }, 432 | "string-width": { 433 | "version": "3.1.0", 434 | "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", 435 | "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", 436 | "dev": true, 437 | "requires": { 438 | "emoji-regex": "^7.0.1", 439 | "is-fullwidth-code-point": "^2.0.0", 440 | "strip-ansi": "^5.1.0" 441 | } 442 | }, 443 | "strip-ansi": { 444 | "version": "5.2.0", 445 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", 446 | "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", 447 | "dev": true, 448 | "requires": { 449 | "ansi-regex": "^4.1.0" 450 | } 451 | }, 452 | "supports-color": { 453 | "version": "6.1.0", 454 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", 455 | "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", 456 | "dev": true, 457 | "requires": { 458 | "has-flag": "^3.0.0" 459 | } 460 | }, 461 | "tree-kill": { 462 | "version": "1.2.2", 463 | "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", 464 | "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", 465 | "dev": true 466 | }, 467 | "tslib": { 468 | "version": "1.13.0", 469 | "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.13.0.tgz", 470 | "integrity": "sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q==", 471 | "dev": true 472 | }, 473 | "typescript": { 474 | "version": "3.9.6", 475 | "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.9.6.tgz", 476 | "integrity": "sha512-Pspx3oKAPJtjNwE92YS05HQoY7z2SFyOpHo9MqJor3BXAGNaPUs83CuVp9VISFkSjyRfiTpmKuAYGJB7S7hOxw==", 477 | "dev": true 478 | }, 479 | "validate-npm-package-license": { 480 | "version": "3.0.4", 481 | "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", 482 | "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", 483 | "dev": true, 484 | "requires": { 485 | "spdx-correct": "^3.0.0", 486 | "spdx-expression-parse": "^3.0.0" 487 | } 488 | }, 489 | "which-module": { 490 | "version": "2.0.0", 491 | "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", 492 | "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", 493 | "dev": true 494 | }, 495 | "wrap-ansi": { 496 | "version": "5.1.0", 497 | "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", 498 | "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", 499 | "dev": true, 500 | "requires": { 501 | "ansi-styles": "^3.2.0", 502 | "string-width": "^3.0.0", 503 | "strip-ansi": "^5.0.0" 504 | } 505 | }, 506 | "y18n": { 507 | "version": "4.0.0", 508 | "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz", 509 | "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==", 510 | "dev": true 511 | }, 512 | "yargs": { 513 | "version": "13.3.2", 514 | "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz", 515 | "integrity": "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==", 516 | "dev": true, 517 | "requires": { 518 | "cliui": "^5.0.0", 519 | "find-up": "^3.0.0", 520 | "get-caller-file": "^2.0.1", 521 | "require-directory": "^2.1.1", 522 | "require-main-filename": "^2.0.0", 523 | "set-blocking": "^2.0.0", 524 | "string-width": "^3.0.0", 525 | "which-module": "^2.0.0", 526 | "y18n": "^4.0.0", 527 | "yargs-parser": "^13.1.2" 528 | } 529 | }, 530 | "yargs-parser": { 531 | "version": "13.1.2", 532 | "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz", 533 | "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==", 534 | "dev": true, 535 | "requires": { 536 | "camelcase": "^5.0.0", 537 | "decamelize": "^1.2.0" 538 | } 539 | } 540 | } 541 | } 542 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-fusionui", 3 | "version": "1.1.2", 4 | "description": "Nuclear power-up for your UI.", 5 | "author": "ozanbolel", 6 | "license": "MIT", 7 | "repository": "https://github.com/ozanbolel/react-fusionui", 8 | "keywords": [ 9 | "react", 10 | "hooks", 11 | "react-component", 12 | "modal", 13 | "dialog" 14 | ], 15 | "main": "lib", 16 | "files": [ 17 | "lib" 18 | ], 19 | "scripts": { 20 | "start": "concurrently \"npm run watch\" \"npm run dev\"", 21 | "watch": "tsc --w", 22 | "dev": "cd demo && npm run start", 23 | "build": "tsc", 24 | "deploy": "cd demo && npm run deploy", 25 | "publish:npm": "npm run build && npm publish" 26 | }, 27 | "peerDependencies": { 28 | "react": "^16.8.0" 29 | }, 30 | "devDependencies": { 31 | "@types/react": "file:demo/node_modules/@types/react", 32 | "concurrently": "^5.2.0", 33 | "react": "file:demo/node_modules/react", 34 | "react-dom": "file:demo/node_modules/react-dom", 35 | "typescript": "^3.9.6" 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/FusionContainer.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import { FusionProvider } from "./FusionProvider"; 3 | import { ModalMapper, DialogMapper } from "./mappers"; 4 | import { IFusionContainerProps } from "./types"; 5 | 6 | export const FusionContainer: React.FC = ({ children, modalClassNames, dialogClassNames }) => { 7 | React.useEffect(() => { 8 | const resizeOps = () => { 9 | document.documentElement.style.setProperty("--fusion-vh", window.innerHeight * 0.01 + "px"); 10 | }; 11 | 12 | resizeOps(); 13 | window.addEventListener("resize", resizeOps); 14 | 15 | return () => { 16 | window.removeEventListener("resize", resizeOps); 17 | }; 18 | }); 19 | 20 | return ( 21 | 22 | {children} 23 | 24 | 25 | 26 | ); 27 | }; 28 | -------------------------------------------------------------------------------- /src/FusionProvider.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import { IState, IAction, IFusionContext, ActionType, IFusionContainerProps } from "./types"; 3 | 4 | let initialState: IState = { 5 | modals: [], 6 | dialogs: [], 7 | modalClassNames: undefined, 8 | dialogClassNames: undefined 9 | }; 10 | 11 | function reducer(state: IState, action: IAction) { 12 | switch (action.type) { 13 | case ActionType.ADD_MODAL: 14 | return Object.assign({}, state, { 15 | modals: [...state.modals, action.payload] 16 | }); 17 | 18 | case ActionType.REMOVE_MODAL: 19 | return Object.assign({}, state, { 20 | modals: state.modals.filter((modal) => modal.id !== action.payload) 21 | }); 22 | 23 | case ActionType.ADD_DIALOG: 24 | return Object.assign({}, state, { 25 | dialogs: [...state.dialogs, action.payload] 26 | }); 27 | 28 | case ActionType.REMOVE_DIALOG: 29 | return Object.assign({}, state, { 30 | dialogs: state.dialogs.filter((dialog) => dialog.id !== action.payload) 31 | }); 32 | 33 | default: 34 | return state; 35 | } 36 | } 37 | 38 | export const FusionContext = React.createContext({} as IFusionContext); 39 | 40 | export const FusionProvider: React.FC = ({ children, modalClassNames, dialogClassNames }) => { 41 | initialState.modalClassNames = modalClassNames; 42 | initialState.dialogClassNames = dialogClassNames; 43 | 44 | const [state, dispatch] = React.useReducer(reducer, initialState); 45 | 46 | return {children}; 47 | }; 48 | -------------------------------------------------------------------------------- /src/elements/Dialog/Dialog.style.ts: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | 3 | export const containerStyles: React.CSSProperties = { 4 | opacity: 0, 5 | position: "fixed", 6 | top: 0, 7 | left: 0, 8 | display: "flex", 9 | justifyContent: "center", 10 | alignItems: "center", 11 | width: "100vw", 12 | height: "calc(var(--fusion-vh, 1vh) * 100)" 13 | }; 14 | 15 | export const dialogStyles: React.CSSProperties = { 16 | transform: "scale(0.25)", 17 | cursor: "default" 18 | }; 19 | -------------------------------------------------------------------------------- /src/elements/Dialog/Dialog.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import { useFusionContext, animate, sliceFloat } from "../../utils"; 3 | import { IDialogObject, ActionType } from "../../types"; 4 | import { containerStyles, dialogStyles } from "./Dialog.style"; 5 | 6 | export const Dialog: React.FC = ({ id, content, actions, config }) => { 7 | const { state, dispatch } = useFusionContext(); 8 | const refContainer = React.useRef(null); 9 | const refDialog = React.useRef(null); 10 | 11 | React.useEffect(() => { 12 | const container = refContainer.current; 13 | const dialog = refDialog.current; 14 | 15 | if (container && dialog) { 16 | animate((value) => (container.style.opacity = value), { from: parseFloat(container.style.opacity), to: 1 }); 17 | animate((value) => (dialog.style.transform = `scale(${value})`), { from: sliceFloat(dialog.style.transform), to: 1 }); 18 | } 19 | }, []); 20 | 21 | const closeDialog = () => { 22 | const container = refContainer.current; 23 | const dialog = refDialog.current; 24 | 25 | if (container && dialog) { 26 | animate((value) => (container.style.opacity = value), { from: parseFloat(container.style.opacity), to: 0 }); 27 | animate((value) => (dialog.style.transform = `scale(${value})`), { 28 | from: sliceFloat(dialog.style.transform), 29 | to: 0, 30 | onRest: () => dispatch({ type: ActionType.REMOVE_DIALOG, payload: id }) 31 | }); 32 | } 33 | }; 34 | 35 | const closeIfContainer = (e: React.MouseEvent) => { 36 | if ((e.target as HTMLElement).dataset.autoclose === "true") { 37 | closeDialog(); 38 | } 39 | }; 40 | 41 | const actionOnClick = (callback?: Function) => { 42 | callback ? callback() : null; 43 | closeDialog(); 44 | }; 45 | 46 | return ( 47 |
closeIfContainer(e) : undefined} 52 | data-autoclose={config?.autoclose} 53 | > 54 |
55 |
{content}
56 | 57 |
58 | {actions.map((action, index) => ( 59 | 66 | ))} 67 |
68 |
69 |
70 | ); 71 | }; 72 | -------------------------------------------------------------------------------- /src/elements/Modal/Modal.style.ts: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | 3 | export const containerStyles: React.CSSProperties = { 4 | opacity: 0, 5 | position: "fixed", 6 | top: 0, 7 | left: 0, 8 | display: "flex", 9 | justifyContent: "center", 10 | alignItems: "center", 11 | width: "100vw", 12 | height: "calc(var(--fusion-vh, 1vh) * 100)" 13 | }; 14 | 15 | export const modalStyles: React.CSSProperties = { 16 | transform: "scale(0.25)", 17 | cursor: "default" 18 | }; 19 | -------------------------------------------------------------------------------- /src/elements/Modal/Modal.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import { useFusionContext, animate, sliceFloat } from "../../utils"; 3 | import { IModalObject, ActionType } from "../../types"; 4 | import { containerStyles, modalStyles } from "./Modal.style"; 5 | 6 | export const Modal: React.FC = ({ id, Component, config }) => { 7 | const [isAnimationDone, setIsAnimationDone] = React.useState(false); 8 | const [isClosing, setIsClosing] = React.useState(false); 9 | const { state, dispatch } = useFusionContext(); 10 | const refContainer = React.useRef(null); 11 | const refModal = React.useRef(null); 12 | 13 | React.useEffect(() => { 14 | const container = refContainer.current; 15 | const modal = refModal.current; 16 | 17 | if (container && modal) { 18 | animate((value) => (container.style.opacity = value), { from: parseFloat(container.style.opacity), to: 1 }); 19 | animate((value) => (modal.style.transform = `scale(${value})`), { 20 | from: sliceFloat(modal.style.transform), 21 | to: 1, 22 | onRest: () => setIsAnimationDone(true) 23 | }); 24 | } 25 | }, []); 26 | 27 | const closeModal = () => { 28 | if (isAnimationDone) { 29 | setIsClosing(true); 30 | 31 | const container = refContainer.current; 32 | const modal = refModal.current; 33 | 34 | if (container && modal) { 35 | animate((value) => (container.style.opacity = value), { from: parseFloat(container.style.opacity), to: 0 }); 36 | animate((value) => (modal.style.transform = `scale(${value})`), { 37 | from: sliceFloat(modal.style.transform), 38 | to: 0, 39 | onRest: () => dispatch({ type: ActionType.REMOVE_MODAL, payload: id }) 40 | }); 41 | } 42 | } 43 | }; 44 | 45 | const closeIfContainer = (e: React.MouseEvent) => { 46 | if ((e.target as HTMLElement).dataset.autoclose === "true") { 47 | closeModal(); 48 | } 49 | }; 50 | 51 | return ( 52 |
closeIfContainer(e) : undefined} 57 | data-autoclose={config?.autoclose} 58 | > 59 |
60 | {React.useMemo( 61 | () => ( 62 | 63 | ), 64 | [closeModal, isAnimationDone, isClosing] 65 | )} 66 |
67 |
68 | ); 69 | }; 70 | -------------------------------------------------------------------------------- /src/elements/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./Modal/Modal"; 2 | export * from "./Dialog/Dialog"; 3 | -------------------------------------------------------------------------------- /src/hooks/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./useModal"; 2 | export * from "./useDialog"; 3 | -------------------------------------------------------------------------------- /src/hooks/useDialog.ts: -------------------------------------------------------------------------------- 1 | import { useFusionContext } from "../utils"; 2 | import { IDialogObject, ActionType } from "../types"; 3 | 4 | export const useDialog = () => { 5 | const { dispatch } = useFusionContext(); 6 | 7 | return (content: IDialogObject["content"], actions: IDialogObject["actions"], config?: IDialogObject["config"]) => { 8 | const id = "dialog-" + new Date().getTime().toString(); 9 | const payload: IDialogObject = { id, content, actions, config }; 10 | 11 | dispatch({ type: ActionType.ADD_DIALOG, payload }); 12 | }; 13 | }; 14 | -------------------------------------------------------------------------------- /src/hooks/useModal.ts: -------------------------------------------------------------------------------- 1 | import { useFusionContext } from "../utils"; 2 | import { IModalObject, ActionType } from "../types"; 3 | 4 | export const useModal = () => { 5 | const { dispatch } = useFusionContext(); 6 | 7 | return (Component: IModalObject["Component"], config?: IModalObject["config"]) => { 8 | const id = "modal-" + new Date().getTime().toString(); 9 | const payload: IModalObject = { id, Component, config }; 10 | 11 | dispatch({ type: ActionType.ADD_MODAL, payload }); 12 | }; 13 | }; 14 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | import { ModalComponent, IModalConfig, IDialogAction, IDialogConfig } from "./types"; 2 | 3 | export * from "./FusionContainer"; 4 | export * from "./hooks"; 5 | 6 | export type FusionModalComponent

= ModalComponent

; 7 | export type FusionModalConfig = IModalConfig; 8 | export type FusionDialogAction = IDialogAction; 9 | export type FusionDialogConfig = IDialogConfig; 10 | -------------------------------------------------------------------------------- /src/mappers/DialogMapper.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import { useFusionContext } from "../utils"; 3 | import { Dialog } from "../elements"; 4 | 5 | export const DialogMapper: React.FC = () => { 6 | const { state } = useFusionContext(); 7 | 8 | return ( 9 | <> 10 | {state.dialogs.map((dialog) => ( 11 |

12 | ))} 13 | 14 | ); 15 | }; 16 | -------------------------------------------------------------------------------- /src/mappers/ModalMapper.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import { useFusionContext } from "../utils"; 3 | import { Modal } from "../elements"; 4 | 5 | export const ModalMapper: React.FC = () => { 6 | const { state } = useFusionContext(); 7 | 8 | return ( 9 | <> 10 | {state.modals.map((modal) => ( 11 | 12 | ))} 13 | 14 | ); 15 | }; 16 | -------------------------------------------------------------------------------- /src/mappers/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./ModalMapper"; 2 | export * from "./DialogMapper"; 3 | -------------------------------------------------------------------------------- /src/types/container.ts: -------------------------------------------------------------------------------- 1 | import { IModalClassNames } from "./modal"; 2 | import { IDialogClassNames } from "./dialog"; 3 | 4 | export interface IFusionContainerProps { 5 | modalClassNames?: IModalClassNames; 6 | dialogClassNames?: IDialogClassNames; 7 | } 8 | -------------------------------------------------------------------------------- /src/types/dialog.ts: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | 3 | export interface IDialogClassNames { 4 | container?: string; 5 | dialog?: string; 6 | content?: string; 7 | actionContainer?: string; 8 | action?: string; 9 | actionLabel?: string; 10 | highlight?: string; 11 | } 12 | 13 | export interface IDialogAction { 14 | label: string; 15 | callback?: Function; 16 | highlight?: boolean; 17 | } 18 | 19 | export interface IDialogConfig { 20 | autoclose?: boolean; 21 | } 22 | 23 | export interface IDialogObject { 24 | id: string; 25 | content: React.ReactNode; 26 | actions: IDialogAction[]; 27 | config?: IDialogConfig; 28 | } 29 | -------------------------------------------------------------------------------- /src/types/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./provider"; 2 | export * from "./container"; 3 | export * from "./modal"; 4 | export * from "./dialog"; 5 | -------------------------------------------------------------------------------- /src/types/modal.ts: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | 3 | export interface IModalClassNames { 4 | container?: string; 5 | modal?: string; 6 | } 7 | 8 | export interface IModalComponentProps { 9 | closeModal: Function; 10 | isAnimationDone: boolean; 11 | isClosing: boolean; 12 | } 13 | 14 | export type ModalComponent

= React.FunctionComponent; 15 | 16 | export interface IModalConfig { 17 | props?: Object; 18 | autoclose?: boolean; 19 | } 20 | 21 | export interface IModalObject { 22 | id: string; 23 | Component: ModalComponent; 24 | config?: IModalConfig; 25 | } 26 | -------------------------------------------------------------------------------- /src/types/provider.ts: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import { IModalObject } from "./modal"; 3 | import { IFusionContainerProps } from "./container"; 4 | import { IDialogObject } from "./dialog"; 5 | 6 | export interface IState extends IFusionContainerProps { 7 | modals: IModalObject[]; 8 | dialogs: IDialogObject[]; 9 | } 10 | 11 | export enum ActionType { 12 | ADD_MODAL, 13 | REMOVE_MODAL, 14 | ADD_DIALOG, 15 | REMOVE_DIALOG 16 | } 17 | 18 | export interface IAction { 19 | type: ActionType; 20 | payload: any; 21 | } 22 | 23 | export interface IFusionContext { 24 | state: IState; 25 | dispatch: React.Dispatch; 26 | } 27 | -------------------------------------------------------------------------------- /src/utils/animate.ts: -------------------------------------------------------------------------------- 1 | const duration = 400; 2 | 3 | const easeInOutSine = (x: number) => { 4 | return -(Math.cos(Math.PI * x) - 1) / 2; 5 | }; 6 | 7 | export const animate = (callback: (value: string) => any, config: { from: number; to: number; onRest?: Function }) => { 8 | const { from, to, onRest } = config; 9 | let start: number; 10 | 11 | const step: FrameRequestCallback = () => { 12 | const now = performance.now(); 13 | if (!start) start = now; 14 | const progress = (now - start) / duration; 15 | 16 | if (progress < 1) { 17 | const value = ((to - from) * easeInOutSine(progress) + from).toString(); 18 | callback(value); 19 | window.requestAnimationFrame(step); 20 | } else { 21 | callback(to.toString()); 22 | if (onRest) onRest(); 23 | } 24 | }; 25 | 26 | window.requestAnimationFrame(step); 27 | }; 28 | -------------------------------------------------------------------------------- /src/utils/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./useFusionContext"; 2 | export * from "./animate"; 3 | export * from "./sliceFloat"; 4 | -------------------------------------------------------------------------------- /src/utils/sliceFloat.ts: -------------------------------------------------------------------------------- 1 | export const sliceFloat = (string: string) => { 2 | const match = string.match(/[+-]?\d+(\.\d+)?/g); 3 | 4 | if (match) { 5 | return parseFloat(match[0]); 6 | } else { 7 | return 0; 8 | } 9 | }; 10 | -------------------------------------------------------------------------------- /src/utils/useFusionContext.ts: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import { FusionContext } from "../FusionProvider"; 3 | 4 | export const useFusionContext = () => { 5 | const context = React.useContext(FusionContext); 6 | 7 | return context; 8 | }; 9 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "lib": ["dom", "esnext"], 5 | "allowJs": true, 6 | "skipLibCheck": true, 7 | "esModuleInterop": true, 8 | "allowSyntheticDefaultImports": true, 9 | "strict": true, 10 | "forceConsistentCasingInFileNames": true, 11 | "module": "commonjs", 12 | "moduleResolution": "node", 13 | "resolveJsonModule": true, 14 | "isolatedModules": true, 15 | "jsx": "react", 16 | "outDir": "lib", 17 | "declaration": true, 18 | "removeComments": true 19 | }, 20 | "include": ["src"], 21 | "exclude": ["node_modules"] 22 | } 23 | --------------------------------------------------------------------------------