Skip to content

Commit 9e5ae88

Browse files
committed
Initial commit
0 parents  commit 9e5ae88

16 files changed

+717
-0
lines changed

.github/workflows/main.yml

+34
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
name: CI
2+
on: [push]
3+
jobs:
4+
build:
5+
name: Build, lint, and test on Node ${{ matrix.node }} and ${{ matrix.os }}
6+
7+
runs-on: ${{ matrix.os }}
8+
strategy:
9+
matrix:
10+
node: ['10.x', '12.x', '14.x']
11+
os: [ubuntu-latest, windows-latest, macOS-latest]
12+
13+
steps:
14+
- name: Checkout repo
15+
uses: actions/checkout@v2
16+
17+
- name: Use Node ${{ matrix.node }}
18+
uses: actions/setup-node@v1
19+
with:
20+
node-version: ${{ matrix.node }}
21+
22+
- name: Install deps and build (with cache)
23+
uses: bahmutov/npm-install@v1
24+
with:
25+
useLockFile: false
26+
27+
- name: Lint
28+
run: yarn lint
29+
30+
- name: Test
31+
run: yarn test --ci --coverage --maxWorkers=2
32+
33+
- name: Build
34+
run: yarn build

.github/workflows/size.yml

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
name: size
2+
on: [pull_request]
3+
jobs:
4+
size:
5+
runs-on: ubuntu-latest
6+
env:
7+
CI_JOB_NUMBER: 1
8+
steps:
9+
- uses: actions/checkout@v1
10+
- uses: andresz1/size-limit-action@v1
11+
with:
12+
github_token: ${{ secrets.GITHUB_TOKEN }}

.gitignore

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
*.log
2+
.DS_Store
3+
node_modules
4+
.cache
5+
dist
6+
yarn.lock

LICENSE

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2021 Luca Rath-Heel
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
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
# Responsive Toolbar

example/.gitignore

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
.parcel-cache
2+
node_modules
3+
dist
4+
yarn.lock

example/package.json

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
{
2+
"private": true,
3+
"scripts": {
4+
"start": "parcel public/index.html"
5+
},
6+
"dependencies": {
7+
"react-app-polyfill": "^1.0.0"
8+
},
9+
"alias": {
10+
"react": "../node_modules/react",
11+
"react-dom": "../node_modules/react-dom/profiling",
12+
"scheduler/tracing": "../node_modules/scheduler/tracing-profiling"
13+
},
14+
"devDependencies": {
15+
"@types/react": "^16.9.11",
16+
"@types/react-dom": "^16.8.4",
17+
"parcel": "^2.0.0-beta.2",
18+
"typescript": "^4.2.4"
19+
}
20+
}

example/public/index.html

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8" />
5+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
6+
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
7+
<title>Responsive Toolbar Example</title>
8+
</head>
9+
10+
<body>
11+
<div id="root"></div>
12+
<div id="portal-root"></div>
13+
<script src="../src/index.tsx"></script>
14+
</body>
15+
</html>

example/src/Button.tsx

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import * as React from 'react';
2+
3+
interface ButtonProps {
4+
active: boolean;
5+
children: string;
6+
onClick: () => void;
7+
}
8+
9+
const Button: React.FC<ButtonProps> = ({ active, children, onClick }) => {
10+
return (
11+
<button
12+
onClick={onClick}
13+
style={{
14+
width: 80,
15+
marginInline: 2,
16+
fontWeight: active ? 'bold' : 'normal',
17+
}}
18+
>
19+
{children}
20+
</button>
21+
);
22+
};
23+
24+
export default Button;

example/src/Portal.tsx

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import * as React from 'react';
2+
import * as ReactDOM from 'react-dom';
3+
4+
const domNode = document.getElementById('portal-root') as HTMLDivElement;
5+
6+
const Portal: React.FC = ({ children }) => {
7+
return ReactDOM.createPortal(children, domNode);
8+
};
9+
10+
export default Portal;

example/src/index.tsx

+121
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
import 'react-app-polyfill/ie11';
2+
import * as React from 'react';
3+
import { useState, useEffect } from 'react';
4+
import * as ReactDOM from 'react-dom';
5+
import ResponsiveToolbar, { OverflowButtonProps } from '../../src';
6+
import Portal from './Portal';
7+
import Button from './Button';
8+
9+
interface CustomOverflowButtonProps extends OverflowButtonProps {
10+
overflowMenuOpen: boolean;
11+
toggleOverflowMenu: () => void;
12+
}
13+
14+
const OverflowButton: React.FC<CustomOverflowButtonProps> = ({
15+
children,
16+
shown,
17+
overflowMenuOpen,
18+
toggleOverflowMenu,
19+
}) => {
20+
const buttonRef = React.useRef<HTMLButtonElement>(null);
21+
22+
return (
23+
<>
24+
<button
25+
onClick={toggleOverflowMenu}
26+
ref={buttonRef}
27+
style={{
28+
marginInline: 2,
29+
}}
30+
>
31+
Show More ...
32+
</button>
33+
{overflowMenuOpen && shown && (
34+
<Portal>
35+
<div
36+
style={{
37+
marginTop: 10,
38+
display: 'flex',
39+
flexDirection: 'column',
40+
alignItems: 'flex-end',
41+
padding: '10px',
42+
border: 'thin solid #ccc',
43+
borderRadius: 3,
44+
background: '#eee',
45+
position: 'fixed',
46+
top: buttonRef.current?.getBoundingClientRect().bottom || 20,
47+
left: buttonRef.current?.getBoundingClientRect().right || 20,
48+
transform: 'translateX(-100%)',
49+
}}
50+
>
51+
{React.Children.map(children, child => {
52+
if (!React.isValidElement(child)) {
53+
return child;
54+
}
55+
56+
return React.cloneElement(child, {
57+
style: { ...child.props.style, width: 'auto' },
58+
});
59+
})}
60+
</div>
61+
</Portal>
62+
)}
63+
</>
64+
);
65+
};
66+
67+
const App: React.VFC = () => {
68+
const [overflowMenuOpen, setOverflowMenuOpen] = useState<boolean>(false);
69+
const [activeIndex, setActiveIndex] = useState<number | null>(null);
70+
const [amountButtons, setAmountButtons] = useState<number>(15);
71+
72+
useEffect(() => {
73+
if (activeIndex !== null && activeIndex + 1 > amountButtons) {
74+
setActiveIndex(null);
75+
}
76+
}, [activeIndex, amountButtons]);
77+
78+
return (
79+
<div className="App">
80+
<h1>Responsive Toolbar Example</h1>
81+
82+
<button onClick={() => setAmountButtons(prevAmount => prevAmount + 1)}>
83+
Add Button
84+
</button>
85+
<button
86+
onClick={() =>
87+
setAmountButtons(prevAmount => Math.max(0, prevAmount - 1))
88+
}
89+
disabled={amountButtons === 0}
90+
>
91+
Remove Button
92+
</button>
93+
94+
<hr />
95+
96+
<ResponsiveToolbar
97+
activeIndex={activeIndex}
98+
overflowButton={OverflowButton}
99+
overflowButtonProps={{
100+
overflowMenuOpen,
101+
toggleOverflowMenu: () => setOverflowMenuOpen(prevOpen => !prevOpen),
102+
}}
103+
>
104+
{new Array(amountButtons).fill(null).map((_item, index) => (
105+
<Button
106+
key={index}
107+
active={index === activeIndex}
108+
onClick={() => {
109+
setActiveIndex(prevIndex => (prevIndex === index ? null : index));
110+
setOverflowMenuOpen(false);
111+
}}
112+
>
113+
{`Button ${index + 1}`}
114+
</Button>
115+
))}
116+
</ResponsiveToolbar>
117+
</div>
118+
);
119+
};
120+
121+
ReactDOM.render(<App />, document.getElementById('root'));

example/tsconfig.json

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
{
2+
"include": ["src", "types"],
3+
"compilerOptions": {
4+
"allowSyntheticDefaultImports": false,
5+
"target": "es5",
6+
"module": "commonjs",
7+
"jsx": "react",
8+
"moduleResolution": "node",
9+
"noImplicitAny": false,
10+
"noUnusedLocals": false,
11+
"noUnusedParameters": false,
12+
"removeComments": true,
13+
"strictNullChecks": true,
14+
"preserveConstEnums": true,
15+
"sourceMap": true,
16+
"lib": ["es2015", "es2016", "dom"],
17+
"types": ["node"],
18+
"rootDir": "./src"
19+
}
20+
}

package.json

+64
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
{
2+
"version": "1.0.0",
3+
"license": "MIT",
4+
"main": "dist/index.js",
5+
"typings": "dist/index.d.ts",
6+
"files": [
7+
"dist",
8+
"src"
9+
],
10+
"engines": {
11+
"node": ">=10"
12+
},
13+
"scripts": {
14+
"start": "tsdx watch",
15+
"build": "tsdx build",
16+
"test": "tsdx test --passWithNoTests",
17+
"lint": "tsdx lint src test example/src",
18+
"prepare": "tsdx build",
19+
"size": "size-limit",
20+
"analyze": "size-limit --why"
21+
},
22+
"peerDependencies": {
23+
"react": ">=16"
24+
},
25+
"husky": {
26+
"hooks": {
27+
"pre-commit": "tsdx lint"
28+
}
29+
},
30+
"prettier": {
31+
"printWidth": 80,
32+
"semi": true,
33+
"singleQuote": true,
34+
"trailingComma": "es5"
35+
},
36+
"name": "@lcrh/responsive-toolbar",
37+
"author": "Luca Rath-Heel",
38+
"module": "dist/responsive-toolbar.esm.js",
39+
"size-limit": [
40+
{
41+
"path": "dist/responsive-toolbar.cjs.production.min.js",
42+
"limit": "1 KB"
43+
},
44+
{
45+
"path": "dist/responsive-toolbar.esm.js",
46+
"limit": "1 KB"
47+
}
48+
],
49+
"devDependencies": {
50+
"@size-limit/preset-small-lib": "^4.10.2",
51+
"@types/react": "^17.0.5",
52+
"@types/react-dom": "^17.0.5",
53+
"husky": "^6.0.0",
54+
"react": "^17.0.2",
55+
"react-dom": "^17.0.2",
56+
"size-limit": "^4.10.2",
57+
"tsdx": "^0.14.1",
58+
"tslib": "^2.2.0",
59+
"typescript": "^4.2.4"
60+
},
61+
"dependencies": {
62+
"@react-hook/resize-observer": "^1.2.0"
63+
}
64+
}

0 commit comments

Comments
 (0)