├── .babelrc
├── .eslintrc.js
├── .gitignore
├── .idea
├── misc.xml
└── vcs.xml
├── .prettierrc
├── README.md
├── dist
├── ChildButton.d.ts
├── FloatingMenu.d.ts
├── MainButton.d.ts
├── index.css
├── index.d.ts
├── index.js.map
├── index.modern.js
├── index.modern.js.map
└── index.test.d.ts
├── example
├── README.md
├── package.json
├── public
│ ├── favicon.ico
│ ├── index.html
│ └── manifest.json
├── src
│ ├── App.test.tsx
│ ├── App.tsx
│ ├── index.css
│ ├── index.tsx
│ ├── react-app-env.d.ts
│ └── setupTests.ts
├── tsconfig.json
└── yarn.lock
├── package.json
├── src
├── .eslintrc
├── ChildButton.tsx
├── FloatingMenu.tsx
├── MainButton.tsx
├── index.test.tsx
├── index.tsx
├── styles.module.css
└── typings.d.ts
├── tsconfig.json
├── tsconfig.test.json
└── yarn.lock
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": ["@babel/preset-react"],
3 | }
4 |
--------------------------------------------------------------------------------
/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | parser: '@typescript-eslint/parser', // Specifies the ESLint parser
3 | parserOptions: {
4 | ecmaVersion: 2020, // Allows for the parsing of modern ECMAScript features
5 | sourceType: 'module', // Allows for the use of imports
6 | ecmaFeatures: {
7 | jsx: true, // Allows for the parsing of JSX
8 | },
9 | },
10 | extends: [
11 | 'plugin:@typescript-eslint/recommended',
12 | 'plugin:react/recommended',
13 | 'prettier/@typescript-eslint', // Uses eslint-config-prettier to disable ESLint rules from @typescript-eslint/eslint-plugin that would conflict with prettier
14 | 'plugin:prettier/recommended',
15 | ],
16 | plugins: ['react', 'jsx-a11y', 'import'],
17 | rules: {
18 | 'react/jsx-filename-extension': 'off',
19 | },
20 | };
21 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 |
6 | # Runtime data
7 | pids
8 | *.pid
9 | *.seed
10 |
11 | # Directory for instrumented libs generated by jscoverage/JSCover
12 | lib-cov
13 |
14 | # Coverage directory used by tools like istanbul
15 | coverage
16 |
17 | # nyc test coverage
18 | .nyc_output
19 |
20 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
21 | .grunt
22 |
23 | # node-waf configuration
24 | .lock-wscript
25 |
26 | # Compiled binary addons (http://nodejs.org/api/addons.html)
27 | build/Release
28 |
29 | # Dependency directories
30 | node_modules
31 | jspm_packages
32 |
33 | # Optional npm cache directory
34 | .npm
35 |
36 | # Optional REPL history
37 | .node_repl_history
38 |
39 | /.idea
40 |
41 | build
42 | docs/dist
43 | es
44 | umd
45 | ChildButton.js
46 | MainButton.js
47 | FloatingMenu.js
48 | index.js
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "trailingComma": "es5",
3 | "tabWidth": 2,
4 | "semi": true,
5 | "singleQuote": true
6 | }
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # react-floating-button-menu
2 |
3 | > A customizable floating action button menu
4 |
5 | [](https://www.npmjs.com/package/react-floating-button-menu) [](https://standardjs.com)
6 |
7 | Inspired by [react-material-floating-button](https://github.com/nobitagit/react-material-floating-button)
8 |
9 | ## Install
10 |
11 | ```bash
12 | npm install --save react-floating-button-menu
13 | ```
14 |
15 | ## Demo
16 |
17 | See the [project page](https://ifndefdeadmau5.github.io/react-floating-button-menu/)
18 |
19 | ## Usage
20 |
21 | You can customize opening direction, speed, and styles of each button via props. Other options will be added soon
22 |
23 | ```javascript
24 | import {
25 | FloatingMenu,
26 | MainButton,
27 | ChildButton,
28 | Directions
29 | } from 'react-floating-button-menu';
30 | import MdAdd from '@material-ui/icons/add';
31 | import MdClose from '@material-ui/icons/clear';
32 | import MdFavorite from '@material-ui/icons/favorite';
33 |
34 |
35 | state = {
36 | isOpen: false,
37 | }
38 | ...
39 |
45 | }
47 | iconActive={}
48 | background="black"
49 | onClick={() => this.setState({ isOpen: !this.state.isOpen })}
50 | size={56}
51 | />
52 | }
54 | background="white"
55 | size={40}
56 | onClick={() => console.log('First button clicked')}
57 | />
58 | }
60 | background="white"
61 | size={40}
62 | />
63 | }
65 | background="white"
66 | size={40}
67 | />
68 |
69 | ...
70 | ```
71 |
72 | ## License
73 |
74 | MIT © [ifndefdeadmau5](https://github.com/ifndefdeadmau5)
75 |
--------------------------------------------------------------------------------
/dist/ChildButton.d.ts:
--------------------------------------------------------------------------------
1 | import { Directions } from './FloatingMenu';
2 | export interface ChildButtonProps {
3 | icon?: any;
4 | direction?: Directions;
5 | index?: number;
6 | size?: number;
7 | spacing?: number;
8 | isOpen?: boolean;
9 | onClick?: any;
10 | background?: string;
11 | }
12 | declare const ChildButton: ({ direction, index, size, spacing, isOpen, onClick, icon, ...rest }: ChildButtonProps) => JSX.Element;
13 | export default ChildButton;
14 |
--------------------------------------------------------------------------------
/dist/FloatingMenu.d.ts:
--------------------------------------------------------------------------------
1 | export declare const DIRECTIONS: {
2 | up: string;
3 | down: string;
4 | left: string;
5 | right: string;
6 | };
7 | export declare enum Directions {
8 | Up = "up",
9 | Down = "down",
10 | Left = "left",
11 | Right = "right"
12 | }
13 | export interface FloatingMenuProps {
14 | children: JSX.Element[] | JSX.Element | string;
15 | spacing?: number;
16 | slideSpeed?: number;
17 | direction?: Directions;
18 | isOpen: boolean;
19 | }
20 | declare const FloatingMenu: ({ slideSpeed, direction, isOpen, spacing, children, ...rest }: FloatingMenuProps) => JSX.Element;
21 | export default FloatingMenu;
22 |
--------------------------------------------------------------------------------
/dist/MainButton.d.ts:
--------------------------------------------------------------------------------
1 | export interface MainButtonProps {
2 | iconActive: any;
3 | iconResting: any;
4 | isOpen?: boolean;
5 | background: string;
6 | onClick: any;
7 | size: number;
8 | }
9 | declare const MainButton: ({ iconResting, iconActive, isOpen, ...rest }: MainButtonProps) => JSX.Element;
10 | export default MainButton;
11 |
--------------------------------------------------------------------------------
/dist/index.css:
--------------------------------------------------------------------------------
1 | /* add css module styles here (optional) */
2 |
3 | ._3ybTi {
4 | margin: 2em;
5 | padding: 0.5em;
6 | border: 2px solid #000;
7 | font-size: 2em;
8 | text-align: center;
9 | }
10 |
--------------------------------------------------------------------------------
/dist/index.d.ts:
--------------------------------------------------------------------------------
1 | import ChildButton, { ChildButtonProps } from './ChildButton';
2 | import FloatingMenu, { Directions } from './FloatingMenu';
3 | import MainButton, { MainButtonProps } from './MainButton';
4 | export { ChildButton, FloatingMenu, MainButton, Directions, ChildButtonProps, MainButtonProps, };
5 |
--------------------------------------------------------------------------------
/dist/index.js.map:
--------------------------------------------------------------------------------
1 | {"version":3,"file":"index.js","sources":["../src/FloatingMenu.tsx","../src/ChildButton.tsx","../src/MainButton.tsx"],"sourcesContent":["import React from 'react'\nimport styled from 'styled-components'\n\nexport const DIRECTIONS = {\n up: 'column-reverse',\n down: 'column',\n left: 'row-reverse',\n right: 'row'\n}\n\n// @ts-ignore\nconst StyledUl = styled(({ direction, ...rest }) =>
)(\n ({ direction }: any) => ({\n display: 'flex',\n width: 'fit-content',\n listStyle: 'none',\n margin: '0',\n padding: '0',\n flexDirection: DIRECTIONS[direction],\n justifyContent: 'center',\n alignItems: 'center'\n })\n)\n\nexport enum Directions {\n Up = 'up',\n Down = 'down',\n Left = 'left',\n Right = 'right'\n}\n\nexport interface FloatingMenuProps {\n children: JSX.Element[] | JSX.Element | string\n spacing?: number\n slideSpeed?: number\n direction?: Directions\n isOpen: boolean\n}\n\nconst FloatingMenu = ({\n slideSpeed = 500,\n direction = Directions.Down,\n isOpen = false,\n spacing = 8,\n children,\n ...rest\n}: FloatingMenuProps) => {\n const childrenWithProps = React.Children.map(\n children,\n (child: any, index: number) =>\n React.cloneElement(child, {\n isOpen,\n slideSpeed,\n direction,\n index,\n spacing\n })\n )\n\n return (\n \n {childrenWithProps}\n \n )\n}\n\nexport default FloatingMenu\n","/* eslint-disable no-nested-ternary */\nimport React from 'react'\nimport styled from 'styled-components'\nimport { Directions } from './FloatingMenu'\n\n// @ts-ignore\nconst Wrapper = styled(\n ({ isOpen, slideSpeed, background, size, spacing, direction, ...rest }) => (\n \n )\n)(\n // @ts-ignore\n ({ isOpen, slideSpeed, background, size, spacing, direction }: any) => ({\n background,\n display: 'flex',\n border: 'none',\n borderRadius: '50%',\n boxShadow: '0 0 4px rgba(0,0,0,.14),0 4px 8px rgba(0,0,0,.28)',\n cursor: 'pointer',\n outline: 'none',\n padding: '0',\n WebkitUserDrag: 'none',\n fontWeight: 'bold',\n justifyContent: 'center',\n alignItems: 'center',\n opacity: isOpen ? 1 : 0,\n transition: `all ${slideSpeed}ms`,\n width: size,\n height: size,\n marginTop: direction === 'down' ? spacing : 0,\n marginBottom: direction === 'up' ? spacing : 0,\n marginLeft: direction === 'right' ? spacing : 0,\n marginRight: direction === 'left' ? spacing : 0,\n pointerEvents: isOpen ? 'auto' : 'none'\n })\n)\n\nexport interface ChildButtonProps {\n icon?: any\n direction?: Directions\n index?: number\n size?: number\n spacing?: number\n isOpen?: boolean\n onClick?: any\n background?: string\n}\n\nconst ChildButton = ({\n direction = Directions.Up,\n index = 1,\n size = 40,\n spacing = 0,\n isOpen = false,\n onClick = null,\n icon,\n ...rest\n}: ChildButtonProps) => {\n const offsetX =\n direction === 'right'\n ? (size + spacing) * index\n : direction === 'left'\n ? (size + spacing) * index * -1\n : 0\n const offsetY =\n direction === 'down'\n ? (size + spacing) * index\n : direction === 'up'\n ? (size + spacing) * index * -1\n : 0\n\n return (\n \n {icon}\n \n )\n}\n\nexport default ChildButton\n","/* eslint-disable react/no-unused-prop-types */\nimport React from 'react';\nimport styled from 'styled-components';\n\n// @ts-ignore\nconst Wrapper = styled('a')(({ background, size }: any) => ({\n display: 'flex',\n border: 'none',\n borderRadius: '50%',\n boxShadow: '0 0 4px rgba(0,0,0,.14),0 4px 8px rgba(0,0,0,.28)',\n cursor: 'pointer',\n outline: 'none',\n padding: '0',\n WebkitUserDrag: 'none',\n fontWeight: 'bold',\n justifyContent: 'center',\n alignItems: 'center',\n width: size,\n height: size,\n background,\n}));\n\n// @ts-ignore\nconst IconWrapper = styled(({ isOpen, ...rest }) => )(\n ({ isOpen }) => ({\n display: 'flex',\n textDecoration: 'none',\n WebkitTransition: '-webkit-transform 300ms',\n transition: 'transform 300ms',\n WebkitTransform: `rotate(${isOpen ? 180 : 0}deg)`,\n transform: `rotate(${isOpen ? 180 : 0}deg)`,\n })\n);\n\nexport interface MainButtonProps {\n iconActive: any;\n iconResting: any;\n isOpen?: boolean;\n background: string;\n onClick: any;\n size: number;\n}\n\nconst MainButton = ({\n iconResting,\n iconActive,\n isOpen,\n ...rest\n}: MainButtonProps) => {\n return (\n \n \n {isOpen ? iconActive : iconResting}\n \n \n );\n};\n\nexport default MainButton;\n"],"names":["DIRECTIONS","up","down","left","right","StyledUl","styled","rest","React","direction","display","width","listStyle","margin","padding","flexDirection","justifyContent","alignItems","Directions","FloatingMenu","slideSpeed","Down","isOpen","spacing","children","childrenWithProps","Children","map","child","index","cloneElement","Wrapper","background","size","border","borderRadius","boxShadow","cursor","outline","WebkitUserDrag","fontWeight","opacity","transition","height","marginTop","marginBottom","marginLeft","marginRight","pointerEvents","ChildButton","Up","onClick","icon","offsetX","offsetY","style","transform","IconWrapper","textDecoration","WebkitTransition","WebkitTransform","MainButton","iconResting","iconActive"],"mappings":"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"}
--------------------------------------------------------------------------------
/dist/index.modern.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import styled from 'styled-components';
3 |
4 | const DIRECTIONS = {
5 | up: 'column-reverse',
6 | down: 'column',
7 | left: 'row-reverse',
8 | right: 'row'
9 | };
10 | const StyledUl = styled(({
11 | direction,
12 | ...rest
13 | }) => React.createElement("ul", Object.assign({}, rest)))(({
14 | direction
15 | }) => ({
16 | display: 'flex',
17 | width: 'fit-content',
18 | listStyle: 'none',
19 | margin: '0',
20 | padding: '0',
21 | flexDirection: DIRECTIONS[direction],
22 | justifyContent: 'center',
23 | alignItems: 'center'
24 | }));
25 | var Directions;
26 |
27 | (function (Directions) {
28 | Directions["Up"] = "up";
29 | Directions["Down"] = "down";
30 | Directions["Left"] = "left";
31 | Directions["Right"] = "right";
32 | })(Directions || (Directions = {}));
33 |
34 | const FloatingMenu = ({
35 | slideSpeed: _slideSpeed = 500,
36 | direction: _direction = Directions.Down,
37 | isOpen: _isOpen = false,
38 | spacing: _spacing = 8,
39 | children,
40 | ...rest
41 | }) => {
42 | const childrenWithProps = React.Children.map(children, (child, index) => React.cloneElement(child, {
43 | isOpen: _isOpen,
44 | slideSpeed: _slideSpeed,
45 | direction: _direction,
46 | index,
47 | spacing: _spacing
48 | }));
49 | return React.createElement(StyledUl, Object.assign({
50 | direction: _direction
51 | }, rest), childrenWithProps);
52 | };
53 |
54 | const Wrapper = styled(({
55 | isOpen,
56 | slideSpeed,
57 | background,
58 | size,
59 | spacing,
60 | direction,
61 | ...rest
62 | }) => React.createElement("li", Object.assign({}, rest)))(({
63 | isOpen,
64 | slideSpeed,
65 | background,
66 | size,
67 | spacing,
68 | direction
69 | }) => ({
70 | background,
71 | display: 'flex',
72 | border: 'none',
73 | borderRadius: '50%',
74 | boxShadow: '0 0 4px rgba(0,0,0,.14),0 4px 8px rgba(0,0,0,.28)',
75 | cursor: 'pointer',
76 | outline: 'none',
77 | padding: '0',
78 | WebkitUserDrag: 'none',
79 | fontWeight: 'bold',
80 | justifyContent: 'center',
81 | alignItems: 'center',
82 | opacity: isOpen ? 1 : 0,
83 | transition: `all ${slideSpeed}ms`,
84 | width: size,
85 | height: size,
86 | marginTop: direction === 'down' ? spacing : 0,
87 | marginBottom: direction === 'up' ? spacing : 0,
88 | marginLeft: direction === 'right' ? spacing : 0,
89 | marginRight: direction === 'left' ? spacing : 0,
90 | pointerEvents: isOpen ? 'auto' : 'none'
91 | }));
92 |
93 | const ChildButton = ({
94 | direction: _direction = Directions.Up,
95 | index: _index = 1,
96 | size: _size = 40,
97 | spacing: _spacing = 0,
98 | isOpen: _isOpen = false,
99 | onClick: _onClick = null,
100 | icon,
101 | ...rest
102 | }) => {
103 | const offsetX = _direction === 'right' ? (_size + _spacing) * _index : _direction === 'left' ? (_size + _spacing) * _index * -1 : 0;
104 | const offsetY = _direction === 'down' ? (_size + _spacing) * _index : _direction === 'up' ? (_size + _spacing) * _index * -1 : 0;
105 | return React.createElement(Wrapper, Object.assign({
106 | isOpen: _isOpen,
107 | size: _size,
108 | spacing: _spacing,
109 | direction: _direction
110 | }, rest, {
111 | onClick: _isOpen ? _onClick : null,
112 | style: {
113 | transform: `translate(${_isOpen ? 0 : -offsetX}px, ${_isOpen ? 0 : -offsetY}px)`
114 | }
115 | }), icon);
116 | };
117 |
118 | const Wrapper$1 = styled('a')(({
119 | background,
120 | size
121 | }) => ({
122 | display: 'flex',
123 | border: 'none',
124 | borderRadius: '50%',
125 | boxShadow: '0 0 4px rgba(0,0,0,.14),0 4px 8px rgba(0,0,0,.28)',
126 | cursor: 'pointer',
127 | outline: 'none',
128 | padding: '0',
129 | WebkitUserDrag: 'none',
130 | fontWeight: 'bold',
131 | justifyContent: 'center',
132 | alignItems: 'center',
133 | width: size,
134 | height: size,
135 | background
136 | }));
137 | const IconWrapper = styled(({
138 | isOpen,
139 | ...rest
140 | }) => React.createElement("div", Object.assign({}, rest)))(({
141 | isOpen
142 | }) => ({
143 | display: 'flex',
144 | textDecoration: 'none',
145 | WebkitTransition: '-webkit-transform 300ms',
146 | transition: 'transform 300ms',
147 | WebkitTransform: `rotate(${isOpen ? 180 : 0}deg)`,
148 | transform: `rotate(${isOpen ? 180 : 0}deg)`
149 | }));
150 |
151 | const MainButton = ({
152 | iconResting,
153 | iconActive,
154 | isOpen,
155 | ...rest
156 | }) => {
157 | return React.createElement(Wrapper$1, Object.assign({}, rest), React.createElement(IconWrapper, {
158 | isOpen: isOpen
159 | }, isOpen ? iconActive : iconResting));
160 | };
161 |
162 | export { ChildButton, Directions, FloatingMenu, MainButton };
163 | //# sourceMappingURL=index.modern.js.map
164 |
--------------------------------------------------------------------------------
/dist/index.modern.js.map:
--------------------------------------------------------------------------------
1 | {"version":3,"file":"index.modern.js","sources":["../src/FloatingMenu.tsx","../src/ChildButton.tsx","../src/MainButton.tsx"],"sourcesContent":["import React from 'react'\nimport styled from 'styled-components'\n\nexport const DIRECTIONS = {\n up: 'column-reverse',\n down: 'column',\n left: 'row-reverse',\n right: 'row'\n}\n\n// @ts-ignore\nconst StyledUl = styled(({ direction, ...rest }) => )(\n ({ direction }: any) => ({\n display: 'flex',\n width: 'fit-content',\n listStyle: 'none',\n margin: '0',\n padding: '0',\n flexDirection: DIRECTIONS[direction],\n justifyContent: 'center',\n alignItems: 'center'\n })\n)\n\nexport enum Directions {\n Up = 'up',\n Down = 'down',\n Left = 'left',\n Right = 'right'\n}\n\nexport interface FloatingMenuProps {\n children: JSX.Element[] | JSX.Element | string\n spacing?: number\n slideSpeed?: number\n direction?: Directions\n isOpen: boolean\n}\n\nconst FloatingMenu = ({\n slideSpeed = 500,\n direction = Directions.Down,\n isOpen = false,\n spacing = 8,\n children,\n ...rest\n}: FloatingMenuProps) => {\n const childrenWithProps = React.Children.map(\n children,\n (child: any, index: number) =>\n React.cloneElement(child, {\n isOpen,\n slideSpeed,\n direction,\n index,\n spacing\n })\n )\n\n return (\n \n {childrenWithProps}\n \n )\n}\n\nexport default FloatingMenu\n","/* eslint-disable no-nested-ternary */\nimport React from 'react'\nimport styled from 'styled-components'\nimport { Directions } from './FloatingMenu'\n\n// @ts-ignore\nconst Wrapper = styled(\n ({ isOpen, slideSpeed, background, size, spacing, direction, ...rest }) => (\n \n )\n)(\n // @ts-ignore\n ({ isOpen, slideSpeed, background, size, spacing, direction }: any) => ({\n background,\n display: 'flex',\n border: 'none',\n borderRadius: '50%',\n boxShadow: '0 0 4px rgba(0,0,0,.14),0 4px 8px rgba(0,0,0,.28)',\n cursor: 'pointer',\n outline: 'none',\n padding: '0',\n WebkitUserDrag: 'none',\n fontWeight: 'bold',\n justifyContent: 'center',\n alignItems: 'center',\n opacity: isOpen ? 1 : 0,\n transition: `all ${slideSpeed}ms`,\n width: size,\n height: size,\n marginTop: direction === 'down' ? spacing : 0,\n marginBottom: direction === 'up' ? spacing : 0,\n marginLeft: direction === 'right' ? spacing : 0,\n marginRight: direction === 'left' ? spacing : 0,\n pointerEvents: isOpen ? 'auto' : 'none'\n })\n)\n\nexport interface ChildButtonProps {\n icon?: any\n direction?: Directions\n index?: number\n size?: number\n spacing?: number\n isOpen?: boolean\n onClick?: any\n background?: string\n}\n\nconst ChildButton = ({\n direction = Directions.Up,\n index = 1,\n size = 40,\n spacing = 0,\n isOpen = false,\n onClick = null,\n icon,\n ...rest\n}: ChildButtonProps) => {\n const offsetX =\n direction === 'right'\n ? (size + spacing) * index\n : direction === 'left'\n ? (size + spacing) * index * -1\n : 0\n const offsetY =\n direction === 'down'\n ? (size + spacing) * index\n : direction === 'up'\n ? (size + spacing) * index * -1\n : 0\n\n return (\n \n {icon}\n \n )\n}\n\nexport default ChildButton\n","/* eslint-disable react/no-unused-prop-types */\nimport React from 'react';\nimport styled from 'styled-components';\n\n// @ts-ignore\nconst Wrapper = styled('a')(({ background, size }: any) => ({\n display: 'flex',\n border: 'none',\n borderRadius: '50%',\n boxShadow: '0 0 4px rgba(0,0,0,.14),0 4px 8px rgba(0,0,0,.28)',\n cursor: 'pointer',\n outline: 'none',\n padding: '0',\n WebkitUserDrag: 'none',\n fontWeight: 'bold',\n justifyContent: 'center',\n alignItems: 'center',\n width: size,\n height: size,\n background,\n}));\n\n// @ts-ignore\nconst IconWrapper = styled(({ isOpen, ...rest }) => )(\n ({ isOpen }) => ({\n display: 'flex',\n textDecoration: 'none',\n WebkitTransition: '-webkit-transform 300ms',\n transition: 'transform 300ms',\n WebkitTransform: `rotate(${isOpen ? 180 : 0}deg)`,\n transform: `rotate(${isOpen ? 180 : 0}deg)`,\n })\n);\n\nexport interface MainButtonProps {\n iconActive: any;\n iconResting: any;\n isOpen?: boolean;\n background: string;\n onClick: any;\n size: number;\n}\n\nconst MainButton = ({\n iconResting,\n iconActive,\n isOpen,\n ...rest\n}: MainButtonProps) => {\n return (\n \n \n {isOpen ? iconActive : iconResting}\n \n \n );\n};\n\nexport default MainButton;\n"],"names":["DIRECTIONS","up","down","left","right","StyledUl","styled","direction","rest","React","display","width","listStyle","margin","padding","flexDirection","justifyContent","alignItems","Directions","FloatingMenu","slideSpeed","Down","isOpen","spacing","children","childrenWithProps","Children","map","child","index","cloneElement","Wrapper","background","size","border","borderRadius","boxShadow","cursor","outline","WebkitUserDrag","fontWeight","opacity","transition","height","marginTop","marginBottom","marginLeft","marginRight","pointerEvents","ChildButton","Up","onClick","icon","offsetX","offsetY","style","transform","IconWrapper","textDecoration","WebkitTransition","WebkitTransform","MainButton","iconResting","iconActive"],"mappings":";;;AAGO,MAAMA,UAAU,GAAG;AACxBC,EAAAA,EAAE,EAAE,gBADoB;AAExBC,EAAAA,IAAI,EAAE,QAFkB;AAGxBC,EAAAA,IAAI,EAAE,aAHkB;AAIxBC,EAAAA,KAAK,EAAE;AAJiB,CAAnB;AAQP,MAAMC,QAAQ,GAAGC,MAAM,CAAC,CAAC;AAAEC,EAAAA,SAAF;AAAa,KAAGC;AAAhB,CAAD,KAA4BC,mBAAA,KAAA,oBAAQD,KAAR,CAA7B,CAAN,CACf,CAAC;AAAED,EAAAA;AAAF,CAAD,MAAyB;AACvBG,EAAAA,OAAO,EAAE,MADc;AAEvBC,EAAAA,KAAK,EAAE,aAFgB;AAGvBC,EAAAA,SAAS,EAAE,MAHY;AAIvBC,EAAAA,MAAM,EAAE,GAJe;AAKvBC,EAAAA,OAAO,EAAE,GALc;AAMvBC,EAAAA,aAAa,EAAEf,UAAU,CAACO,SAAD,CANF;AAOvBS,EAAAA,cAAc,EAAE,QAPO;AAQvBC,EAAAA,UAAU,EAAE;AARW,CAAzB,CADe,CAAjB;IAaYC;;AAAZ,WAAYA;AACVA,EAAAA,gBAAA,OAAA;AACAA,EAAAA,kBAAA,SAAA;AACAA,EAAAA,kBAAA,SAAA;AACAA,EAAAA,mBAAA,UAAA;AACD,CALD,EAAYA,UAAU,KAAVA,UAAU,KAAA,CAAtB;;AAeA,MAAMC,YAAY,GAAG,CAAC;AACpBC,EAAAA,UAAU,EAAVA,WAAU,GAAG,GADO;AAEpBb,EAAAA,SAAS,EAATA,UAAS,GAAGW,UAAU,CAACG,IAFH;AAGpBC,EAAAA,MAAM,EAANA,OAAM,GAAG,KAHW;AAIpBC,EAAAA,OAAO,EAAPA,QAAO,GAAG,CAJU;AAKpBC,EAAAA,QALoB;AAMpB,KAAGhB;AANiB,CAAD;AAQnB,QAAMiB,iBAAiB,GAAGhB,KAAK,CAACiB,QAAN,CAAeC,GAAf,CACxBH,QADwB,EAExB,CAACI,KAAD,EAAaC,KAAb,KACEpB,KAAK,CAACqB,YAAN,CAAmBF,KAAnB,EAA0B;AACxBN,IAAAA,MAAM,EAANA,OADwB;AAExBF,IAAAA,UAAU,EAAVA,WAFwB;AAGxBb,IAAAA,SAAS,EAATA,UAHwB;AAIxBsB,IAAAA,KAJwB;AAKxBN,IAAAA,OAAO,EAAPA;AALwB,GAA1B,CAHsB,CAA1B;AAYA,SACEd,mBAAA,CAACJ,QAAD;AAAUE,IAAAA,SAAS,EAAEA;KAAeC,KAApC,EACGiB,iBADH,CADF;AAKD,CAzBD;;ACjCA,MAAMM,OAAO,GAAGzB,MAAM,CACpB,CAAC;AAAEgB,EAAAA,MAAF;AAAUF,EAAAA,UAAV;AAAsBY,EAAAA,UAAtB;AAAkCC,EAAAA,IAAlC;AAAwCV,EAAAA,OAAxC;AAAiDhB,EAAAA,SAAjD;AAA4D,KAAGC;AAA/D,CAAD,KACEC,mBAAA,KAAA,oBAAQD,KAAR,CAFkB,CAAN,CAMd,CAAC;AAAEc,EAAAA,MAAF;AAAUF,EAAAA,UAAV;AAAsBY,EAAAA,UAAtB;AAAkCC,EAAAA,IAAlC;AAAwCV,EAAAA,OAAxC;AAAiDhB,EAAAA;AAAjD,CAAD,MAAwE;AACtEyB,EAAAA,UADsE;AAEtEtB,EAAAA,OAAO,EAAE,MAF6D;AAGtEwB,EAAAA,MAAM,EAAE,MAH8D;AAItEC,EAAAA,YAAY,EAAE,KAJwD;AAKtEC,EAAAA,SAAS,EAAE,mDAL2D;AAMtEC,EAAAA,MAAM,EAAE,SAN8D;AAOtEC,EAAAA,OAAO,EAAE,MAP6D;AAQtExB,EAAAA,OAAO,EAAE,GAR6D;AAStEyB,EAAAA,cAAc,EAAE,MATsD;AAUtEC,EAAAA,UAAU,EAAE,MAV0D;AAWtExB,EAAAA,cAAc,EAAE,QAXsD;AAYtEC,EAAAA,UAAU,EAAE,QAZ0D;AAatEwB,EAAAA,OAAO,EAAEnB,MAAM,GAAG,CAAH,GAAO,CAbgD;AActEoB,EAAAA,UAAU,SAAStB,cAdmD;AAetET,EAAAA,KAAK,EAAEsB,IAf+D;AAgBtEU,EAAAA,MAAM,EAAEV,IAhB8D;AAiBtEW,EAAAA,SAAS,EAAErC,SAAS,KAAK,MAAd,GAAuBgB,OAAvB,GAAiC,CAjB0B;AAkBtEsB,EAAAA,YAAY,EAAEtC,SAAS,KAAK,IAAd,GAAqBgB,OAArB,GAA+B,CAlByB;AAmBtEuB,EAAAA,UAAU,EAAEvC,SAAS,KAAK,OAAd,GAAwBgB,OAAxB,GAAkC,CAnBwB;AAoBtEwB,EAAAA,WAAW,EAAExC,SAAS,KAAK,MAAd,GAAuBgB,OAAvB,GAAiC,CApBwB;AAqBtEyB,EAAAA,aAAa,EAAE1B,MAAM,GAAG,MAAH,GAAY;AArBqC,CAAxE,CANc,CAAhB;;AA0CA,MAAM2B,WAAW,GAAG,CAAC;AACnB1C,EAAAA,SAAS,EAATA,UAAS,GAAGW,UAAU,CAACgC,EADJ;AAEnBrB,EAAAA,KAAK,EAALA,MAAK,GAAG,CAFW;AAGnBI,EAAAA,IAAI,EAAJA,KAAI,GAAG,EAHY;AAInBV,EAAAA,OAAO,EAAPA,QAAO,GAAG,CAJS;AAKnBD,EAAAA,MAAM,EAANA,OAAM,GAAG,KALU;AAMnB6B,EAAAA,OAAO,EAAPA,QAAO,GAAG,IANS;AAOnBC,EAAAA,IAPmB;AAQnB,KAAG5C;AARgB,CAAD;AAUlB,QAAM6C,OAAO,GACX9C,UAAS,KAAK,OAAd,GACI,CAAC0B,KAAI,GAAGV,QAAR,IAAmBM,MADvB,GAEItB,UAAS,KAAK,MAAd,GACA,CAAC0B,KAAI,GAAGV,QAAR,IAAmBM,MAAnB,GAA2B,CAAC,CAD5B,GAEA,CALN;AAMA,QAAMyB,OAAO,GACX/C,UAAS,KAAK,MAAd,GACI,CAAC0B,KAAI,GAAGV,QAAR,IAAmBM,MADvB,GAEItB,UAAS,KAAK,IAAd,GACA,CAAC0B,KAAI,GAAGV,QAAR,IAAmBM,MAAnB,GAA2B,CAAC,CAD5B,GAEA,CALN;AAOA,SACEpB,mBAAA,CAACsB,OAAD;AACET,IAAAA,MAAM,EAAEA;AACRW,IAAAA,IAAI,EAAEA;AACNV,IAAAA,OAAO,EAAEA;AACThB,IAAAA,SAAS,EAAEA;KACPC;AACJ2C,IAAAA,OAAO,EAAE7B,OAAM,GAAG6B,QAAH,GAAa;AAC5BI,IAAAA,KAAK,EAAE;AACLC,MAAAA,SAAS,eAAelC,OAAM,GAAG,CAAH,GAAO,CAAC+B,cACpC/B,OAAM,GAAG,CAAH,GAAO,CAACgC;AAFX;IAPT,EAaGF,IAbH,CADF;AAiBD,CAxCD;;AC3CA,MAAMrB,SAAO,GAAGzB,MAAM,CAAC,GAAD,CAAN,CAAY,CAAC;AAAE0B,EAAAA,UAAF;AAAcC,EAAAA;AAAd,CAAD,MAAgC;AAC1DvB,EAAAA,OAAO,EAAE,MADiD;AAE1DwB,EAAAA,MAAM,EAAE,MAFkD;AAG1DC,EAAAA,YAAY,EAAE,KAH4C;AAI1DC,EAAAA,SAAS,EAAE,mDAJ+C;AAK1DC,EAAAA,MAAM,EAAE,SALkD;AAM1DC,EAAAA,OAAO,EAAE,MANiD;AAO1DxB,EAAAA,OAAO,EAAE,GAPiD;AAQ1DyB,EAAAA,cAAc,EAAE,MAR0C;AAS1DC,EAAAA,UAAU,EAAE,MAT8C;AAU1DxB,EAAAA,cAAc,EAAE,QAV0C;AAW1DC,EAAAA,UAAU,EAAE,QAX8C;AAY1DN,EAAAA,KAAK,EAAEsB,IAZmD;AAa1DU,EAAAA,MAAM,EAAEV,IAbkD;AAc1DD,EAAAA;AAd0D,CAAhC,CAAZ,CAAhB;AAkBA,MAAMyB,WAAW,GAAGnD,MAAM,CAAC,CAAC;AAAEgB,EAAAA,MAAF;AAAU,KAAGd;AAAb,CAAD,KAAyBC,mBAAA,MAAA,oBAASD,KAAT,CAA1B,CAAN,CAClB,CAAC;AAAEc,EAAAA;AAAF,CAAD,MAAiB;AACfZ,EAAAA,OAAO,EAAE,MADM;AAEfgD,EAAAA,cAAc,EAAE,MAFD;AAGfC,EAAAA,gBAAgB,EAAE,yBAHH;AAIfjB,EAAAA,UAAU,EAAE,iBAJG;AAKfkB,EAAAA,eAAe,YAAYtC,MAAM,GAAG,GAAH,GAAS,OAL3B;AAMfkC,EAAAA,SAAS,YAAYlC,MAAM,GAAG,GAAH,GAAS;AANrB,CAAjB,CADkB,CAApB;;AAoBA,MAAMuC,UAAU,GAAG,CAAC;AAClBC,EAAAA,WADkB;AAElBC,EAAAA,UAFkB;AAGlBzC,EAAAA,MAHkB;AAIlB,KAAGd;AAJe,CAAD;AAMjB,SACEC,mBAAA,CAACsB,SAAD,oBAAavB,KAAb,EACEC,mBAAA,CAACgD,WAAD;AAAanC,IAAAA,MAAM,EAAEA;GAArB,EACGA,MAAM,GAAGyC,UAAH,GAAgBD,WADzB,CADF,CADF;AAOD,CAbD;;;;"}
--------------------------------------------------------------------------------
/dist/index.test.d.ts:
--------------------------------------------------------------------------------
1 | export {};
2 |
--------------------------------------------------------------------------------
/example/README.md:
--------------------------------------------------------------------------------
1 | This example was bootstrapped with [Create React App](https://github.com/facebook/create-react-app).
2 |
3 | It is linked to the react-floating-button-menu package in the parent directory for development purposes.
4 |
5 | You can run `yarn install` and then `yarn start` to test your package.
6 |
--------------------------------------------------------------------------------
/example/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-floating-button-menu-example",
3 | "homepage": ".",
4 | "version": "0.0.0",
5 | "private": true,
6 | "scripts": {
7 | "start": "node ../node_modules/react-scripts/bin/react-scripts.js start",
8 | "build": "node ../node_modules/react-scripts/bin/react-scripts.js build",
9 | "test": "node ../node_modules/react-scripts/bin/react-scripts.js test",
10 | "eject": "node ../node_modules/react-scripts/bin/react-scripts.js eject"
11 | },
12 | "dependencies": {
13 | "@material-ui/core": "^4.11.0",
14 | "@material-ui/icons": "^4.9.1",
15 | "@testing-library/jest-dom": "link:../node_modules/@testing-library/jest-dom",
16 | "@testing-library/react": "link:../node_modules/@testing-library/react",
17 | "@testing-library/user-event": "link:../node_modules/@testing-library/user-event",
18 | "@types/jest": "link:../node_modules/@types/jest",
19 | "@types/node": "link:../node_modules/@types/node",
20 | "@types/react": "link:../node_modules/@types/react",
21 | "@types/react-dom": "link:../node_modules/@types/react-dom",
22 | "react": "link:../node_modules/react",
23 | "react-dom": "link:../node_modules/react-dom",
24 | "react-floating-button-menu": "link:..",
25 | "react-scripts": "link:../node_modules/react-scripts",
26 | "typescript": "link:../node_modules/typescript"
27 | },
28 | "devDependencies": {
29 | "@babel/plugin-syntax-object-rest-spread": "^7.8.3"
30 | },
31 | "eslintConfig": {
32 | "extends": "react-app"
33 | },
34 | "browserslist": {
35 | "production": [
36 | ">0.2%",
37 | "not dead",
38 | "not op_mini all"
39 | ],
40 | "development": [
41 | "last 1 chrome version",
42 | "last 1 firefox version",
43 | "last 1 safari version"
44 | ]
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/example/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ifndefdeadmau5/react-floating-button-menu/d07b9ad05c22217e903fd9d7e82e7ba8ad9c156c/example/public/favicon.ico
--------------------------------------------------------------------------------
/example/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
10 |
11 |
12 |
16 |
17 |
18 |
27 | react-floating-button-menu
28 |
29 |
30 |
31 |
34 |
35 |
36 |
37 |
47 |
48 |
49 |
--------------------------------------------------------------------------------
/example/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "react-floating-button-menu",
3 | "name": "react-floating-button-menu",
4 | "icons": [
5 | {
6 | "src": "favicon.ico",
7 | "sizes": "64x64 32x32 24x24 16x16",
8 | "type": "image/x-icon"
9 | }
10 | ],
11 | "start_url": ".",
12 | "display": "standalone",
13 | "theme_color": "#000000",
14 | "background_color": "#ffffff"
15 | }
16 |
--------------------------------------------------------------------------------
/example/src/App.test.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import ReactDOM from 'react-dom'
3 | import App from './App'
4 |
5 | it('renders without crashing', () => {
6 | const div = document.createElement('div')
7 | ReactDOM.render(, div)
8 | ReactDOM.unmountComponentAtNode(div)
9 | })
10 |
--------------------------------------------------------------------------------
/example/src/App.tsx:
--------------------------------------------------------------------------------
1 | import React, { useState } from 'react';
2 | import MdAdd from '@material-ui/icons/Add';
3 | import MdClose from '@material-ui/icons/Clear';
4 | import MdEdit from '@material-ui/icons/Edit';
5 | import MdStar from '@material-ui/icons/Star';
6 | import MdFavorite from '@material-ui/icons/Favorite';
7 | import {
8 | MainButton,
9 | ChildButton,
10 | FloatingMenu,
11 | Directions,
12 | } from 'react-floating-button-menu';
13 | import 'react-floating-button-menu/dist/index.css';
14 |
15 | const App = () => {
16 | const [isOpen, setIsOpen] = useState(false);
17 | return (
18 |
19 |
25 | }
28 | iconActive={}
29 | background="linear-gradient(45deg, #FE6B8B 30%, #FF8E53 90%)"
30 | onClick={() => {
31 | setIsOpen((prev) => !prev);
32 | }}
33 | size={56}
34 | />
35 | }
37 | background="linear-gradient(45deg, #FE6B8B 30%, #FF8E53 90%)"
38 | size={40}
39 | onClick={() => console.log('First button clicked')}
40 | />
41 | }
43 | background="linear-gradient(45deg, #FE6B8B 30%, #FF8E53 90%)"
44 | size={40}
45 | />
46 | }
48 | background="linear-gradient(45deg, #FE6B8B 30%, #FF8E53 90%)"
49 | size={40}
50 | />
51 |
52 |
53 | );
54 | };
55 |
56 | export default App;
57 |
--------------------------------------------------------------------------------
/example/src/index.css:
--------------------------------------------------------------------------------
1 | body {
2 | margin: 0;
3 | padding: 0;
4 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
5 | 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
6 | sans-serif;
7 | -webkit-font-smoothing: antialiased;
8 | -moz-osx-font-smoothing: grayscale;
9 | }
10 |
11 | code {
12 | font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
13 | monospace;
14 | }
15 |
--------------------------------------------------------------------------------
/example/src/index.tsx:
--------------------------------------------------------------------------------
1 | import './index.css'
2 |
3 | import React from 'react'
4 | import ReactDOM from 'react-dom'
5 | import App from './App'
6 |
7 | ReactDOM.render(, document.getElementById('root'))
8 |
--------------------------------------------------------------------------------
/example/src/react-app-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/example/src/setupTests.ts:
--------------------------------------------------------------------------------
1 | // jest-dom adds custom jest matchers for asserting on DOM nodes.
2 | // allows you to do things like:
3 | // expect(element).toHaveTextContent(/react/i)
4 | // learn more: https://github.com/testing-library/jest-dom
5 | import '@testing-library/jest-dom/extend-expect';
6 |
--------------------------------------------------------------------------------
/example/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "outDir": "dist",
4 | "module": "esnext",
5 | "lib": [
6 | "dom",
7 | "esnext"
8 | ],
9 | "moduleResolution": "node",
10 | "jsx": "react",
11 | "sourceMap": true,
12 | "declaration": true,
13 | "esModuleInterop": true,
14 | "noImplicitReturns": true,
15 | "noImplicitThis": true,
16 | "noImplicitAny": true,
17 | "strictNullChecks": true,
18 | "suppressImplicitAnyIndexErrors": true,
19 | "noUnusedLocals": true,
20 | "noUnusedParameters": true,
21 | "allowSyntheticDefaultImports": true,
22 | "target": "es5",
23 | "allowJs": true,
24 | "skipLibCheck": true,
25 | "strict": true,
26 | "forceConsistentCasingInFileNames": true,
27 | "resolveJsonModule": true,
28 | "isolatedModules": true,
29 | "noEmit": true
30 | },
31 | "include": [
32 | "src"
33 | ],
34 | "exclude": [
35 | "node_modules",
36 | "build"
37 | ]
38 | }
39 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-floating-button-menu",
3 | "version": "2.0.9",
4 | "description": "A customizable floating action button menu",
5 | "author": "ifndefdeadmau5",
6 | "license": "MIT",
7 | "repository": "ifndefdeadmau5/react-floating-button-menu",
8 | "main": "dist/index.js",
9 | "module": "dist/index.modern.js",
10 | "source": "src/index.tsx",
11 | "engines": {
12 | "node": ">=10"
13 | },
14 | "scripts": {
15 | "build": "microbundle-crl --no-compress --format modern,cjs",
16 | "start": "microbundle-crl watch --no-compress --format modern,cjs",
17 | "prepare": "run-s build",
18 | "test": "run-s test:unit test:lint test:build",
19 | "test:build": "run-s build",
20 | "test:lint": "eslint .",
21 | "test:unit": "cross-env CI=1 react-scripts test --env=jsdom",
22 | "test:watch": "react-scripts test --env=jsdom",
23 | "predeploy": "cd example && yarn install && yarn run build",
24 | "deploy": "gh-pages -d example/build"
25 | },
26 | "peerDependencies": {
27 | "react": "^16.0.0"
28 | },
29 | "devDependencies": {
30 | "@testing-library/jest-dom": "^4.2.4",
31 | "@testing-library/react": "^9.5.0",
32 | "@testing-library/user-event": "^7.2.1",
33 | "@types/jest": "^25.1.4",
34 | "@types/node": "^12.12.38",
35 | "@types/react": "^16.9.27",
36 | "@types/react-dom": "^16.9.7",
37 | "@types/styled-components": "^5.1.4",
38 | "@typescript-eslint/eslint-plugin": "^2.26.0",
39 | "@typescript-eslint/parser": "^2.26.0",
40 | "babel-eslint": "^10.0.3",
41 | "cross-env": "^7.0.2",
42 | "eslint": "^6.8.0",
43 | "eslint-config-prettier": "^6.7.0",
44 | "eslint-config-standard": "^14.1.0",
45 | "eslint-config-standard-react": "^9.2.0",
46 | "eslint-plugin-import": "^2.18.2",
47 | "eslint-plugin-node": "^11.0.0",
48 | "eslint-plugin-prettier": "^3.1.1",
49 | "eslint-plugin-promise": "^4.2.1",
50 | "eslint-plugin-react": "^7.17.0",
51 | "eslint-plugin-standard": "^4.0.1",
52 | "gh-pages": "^2.2.0",
53 | "microbundle-crl": "^0.13.10",
54 | "npm-run-all": "^4.1.5",
55 | "prettier": "^2.0.4",
56 | "react": "^16.13.1",
57 | "react-dom": "^16.13.1",
58 | "react-scripts": "^3.4.1",
59 | "typescript": "^3.7.5"
60 | },
61 | "files": [
62 | "dist"
63 | ],
64 | "dependencies": {
65 | "styled-components": "^5.2.0"
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/src/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "env": {
3 | "jest": true
4 | }
5 | }
6 |
--------------------------------------------------------------------------------
/src/ChildButton.tsx:
--------------------------------------------------------------------------------
1 | /* eslint-disable no-nested-ternary */
2 | import React from 'react'
3 | import styled from 'styled-components'
4 | import { Directions } from './FloatingMenu'
5 |
6 | // @ts-ignore
7 | const Wrapper = styled(
8 | ({ isOpen, slideSpeed, background, size, spacing, direction, ...rest }) => (
9 |
10 | )
11 | )(
12 | // @ts-ignore
13 | ({ isOpen, slideSpeed, background, size, spacing, direction }: any) => ({
14 | background,
15 | display: 'flex',
16 | border: 'none',
17 | borderRadius: '50%',
18 | boxShadow: '0 0 4px rgba(0,0,0,.14),0 4px 8px rgba(0,0,0,.28)',
19 | cursor: 'pointer',
20 | outline: 'none',
21 | padding: '0',
22 | WebkitUserDrag: 'none',
23 | fontWeight: 'bold',
24 | justifyContent: 'center',
25 | alignItems: 'center',
26 | opacity: isOpen ? 1 : 0,
27 | transition: `all ${slideSpeed}ms`,
28 | width: size,
29 | height: size,
30 | marginTop: direction === 'down' ? spacing : 0,
31 | marginBottom: direction === 'up' ? spacing : 0,
32 | marginLeft: direction === 'right' ? spacing : 0,
33 | marginRight: direction === 'left' ? spacing : 0,
34 | pointerEvents: isOpen ? 'auto' : 'none'
35 | })
36 | )
37 |
38 | export interface ChildButtonProps {
39 | icon?: any
40 | direction?: Directions
41 | index?: number
42 | size?: number
43 | spacing?: number
44 | isOpen?: boolean
45 | onClick?: any
46 | background?: string
47 | }
48 |
49 | const ChildButton = ({
50 | direction = Directions.Up,
51 | index = 1,
52 | size = 40,
53 | spacing = 0,
54 | isOpen = false,
55 | onClick = null,
56 | icon,
57 | ...rest
58 | }: ChildButtonProps) => {
59 | const offsetX =
60 | direction === 'right'
61 | ? (size + spacing) * index
62 | : direction === 'left'
63 | ? (size + spacing) * index * -1
64 | : 0
65 | const offsetY =
66 | direction === 'down'
67 | ? (size + spacing) * index
68 | : direction === 'up'
69 | ? (size + spacing) * index * -1
70 | : 0
71 |
72 | return (
73 |
86 | {icon}
87 |
88 | )
89 | }
90 |
91 | export default ChildButton
92 |
--------------------------------------------------------------------------------
/src/FloatingMenu.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import styled from 'styled-components'
3 |
4 | export const DIRECTIONS = {
5 | up: 'column-reverse',
6 | down: 'column',
7 | left: 'row-reverse',
8 | right: 'row'
9 | }
10 |
11 | // @ts-ignore
12 | const StyledUl = styled(({ direction, ...rest }) => )(
13 | ({ direction }: any) => ({
14 | display: 'flex',
15 | width: 'fit-content',
16 | listStyle: 'none',
17 | margin: '0',
18 | padding: '0',
19 | flexDirection: DIRECTIONS[direction],
20 | justifyContent: 'center',
21 | alignItems: 'center'
22 | })
23 | )
24 |
25 | export enum Directions {
26 | Up = 'up',
27 | Down = 'down',
28 | Left = 'left',
29 | Right = 'right'
30 | }
31 |
32 | export interface FloatingMenuProps {
33 | children: JSX.Element[] | JSX.Element | string
34 | spacing?: number
35 | slideSpeed?: number
36 | direction?: Directions
37 | isOpen: boolean
38 | }
39 |
40 | const FloatingMenu = ({
41 | slideSpeed = 500,
42 | direction = Directions.Down,
43 | isOpen = false,
44 | spacing = 8,
45 | children,
46 | ...rest
47 | }: FloatingMenuProps) => {
48 | const childrenWithProps = React.Children.map(
49 | children,
50 | (child: any, index: number) =>
51 | React.cloneElement(child, {
52 | isOpen,
53 | slideSpeed,
54 | direction,
55 | index,
56 | spacing
57 | })
58 | )
59 |
60 | return (
61 |
62 | {childrenWithProps}
63 |
64 | )
65 | }
66 |
67 | export default FloatingMenu
68 |
--------------------------------------------------------------------------------
/src/MainButton.tsx:
--------------------------------------------------------------------------------
1 | /* eslint-disable react/no-unused-prop-types */
2 | import React from 'react';
3 | import styled from 'styled-components';
4 |
5 | // @ts-ignore
6 | const Wrapper = styled('a')(({ background, size }: any) => ({
7 | display: 'flex',
8 | border: 'none',
9 | borderRadius: '50%',
10 | boxShadow: '0 0 4px rgba(0,0,0,.14),0 4px 8px rgba(0,0,0,.28)',
11 | cursor: 'pointer',
12 | outline: 'none',
13 | padding: '0',
14 | WebkitUserDrag: 'none',
15 | fontWeight: 'bold',
16 | justifyContent: 'center',
17 | alignItems: 'center',
18 | width: size,
19 | height: size,
20 | background,
21 | }));
22 |
23 | // @ts-ignore
24 | const IconWrapper = styled(({ isOpen, ...rest }) => )(
25 | ({ isOpen }) => ({
26 | display: 'flex',
27 | textDecoration: 'none',
28 | WebkitTransition: '-webkit-transform 300ms',
29 | transition: 'transform 300ms',
30 | WebkitTransform: `rotate(${isOpen ? 180 : 0}deg)`,
31 | transform: `rotate(${isOpen ? 180 : 0}deg)`,
32 | })
33 | );
34 |
35 | export interface MainButtonProps {
36 | iconActive: any;
37 | iconResting: any;
38 | isOpen?: boolean;
39 | background: string;
40 | onClick: any;
41 | size: number;
42 | }
43 |
44 | const MainButton = ({
45 | iconResting,
46 | iconActive,
47 | isOpen,
48 | ...rest
49 | }: MainButtonProps) => {
50 | return (
51 |
52 |
53 | {isOpen ? iconActive : iconResting}
54 |
55 |
56 | );
57 | };
58 |
59 | export default MainButton;
60 |
--------------------------------------------------------------------------------
/src/index.test.tsx:
--------------------------------------------------------------------------------
1 | import { ExampleComponent } from '.'
2 |
3 | describe('ExampleComponent', () => {
4 | it('is truthy', () => {
5 | expect(ExampleComponent).toBeTruthy()
6 | })
7 | })
8 |
--------------------------------------------------------------------------------
/src/index.tsx:
--------------------------------------------------------------------------------
1 | import ChildButton, { ChildButtonProps } from './ChildButton';
2 | import FloatingMenu, { Directions } from './FloatingMenu';
3 | import MainButton, { MainButtonProps } from './MainButton';
4 |
5 | export {
6 | ChildButton,
7 | FloatingMenu,
8 | MainButton,
9 | Directions,
10 | ChildButtonProps,
11 | MainButtonProps,
12 | };
13 |
--------------------------------------------------------------------------------
/src/styles.module.css:
--------------------------------------------------------------------------------
1 | /* add css module styles here (optional) */
2 |
3 | .test {
4 | margin: 2em;
5 | padding: 0.5em;
6 | border: 2px solid #000;
7 | font-size: 2em;
8 | text-align: center;
9 | }
10 |
--------------------------------------------------------------------------------
/src/typings.d.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Default CSS definition for typescript,
3 | * will be overridden with file-specific definitions by rollup
4 | */
5 | declare module '*.css' {
6 | const content: { [className: string]: string };
7 | export default content;
8 | }
9 |
10 | interface SvgrComponent extends React.StatelessComponent> {}
11 |
12 | declare module '*.svg' {
13 | const svgUrl: string;
14 | const svgComponent: SvgrComponent;
15 | export default svgUrl;
16 | export { svgComponent as ReactComponent }
17 | }
18 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "outDir": "dist",
4 | "module": "esnext",
5 | "lib": ["dom", "esnext"],
6 | "moduleResolution": "node",
7 | "jsx": "react",
8 | "sourceMap": true,
9 | "declaration": true,
10 | "esModuleInterop": true,
11 | "noImplicitReturns": true,
12 | "noImplicitThis": true,
13 | "noImplicitAny": true,
14 | "strictNullChecks": true,
15 | "suppressImplicitAnyIndexErrors": true,
16 | "noUnusedLocals": true,
17 | "noUnusedParameters": true,
18 | "allowSyntheticDefaultImports": true
19 | },
20 | "include": ["src"],
21 | "exclude": ["node_modules", "dist", "example"]
22 | }
23 |
--------------------------------------------------------------------------------
/tsconfig.test.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "compilerOptions": {
4 | "module": "commonjs"
5 | }
6 | }
--------------------------------------------------------------------------------