├── .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 | [](https://www.npmjs.com/package/react-fusionui) [](https://www.npmjs.com/package/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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
--------------------------------------------------------------------------------