Compare commits
15 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b6e2ae7da5 | ||
|
|
a885931831 | ||
|
|
2eb51f7ca7 | ||
|
|
7e1bd3d18e | ||
|
|
94e5d9b305 | ||
|
|
02e70848b1 | ||
|
|
b8eac52689 | ||
|
|
73b66e2e86 | ||
|
|
0e50efb5ea | ||
|
|
dc6764e387 | ||
|
|
2ba5ac332b | ||
|
|
b9a0b93067 | ||
|
|
6596bce68a | ||
|
|
6d74c9f719 | ||
|
|
02525cabb5 |
4
.vscode/phosphor-home.code-snippets
vendored
4
.vscode/phosphor-home.code-snippets
vendored
@@ -21,8 +21,8 @@
|
|||||||
"{",
|
"{",
|
||||||
"\tname: \"${1:name}\",",
|
"\tname: \"${1:name}\",",
|
||||||
"\tcategories: [IconCategory${2:categories}],",
|
"\tcategories: [IconCategory${2:categories}],",
|
||||||
"\ttags: [${3:tags}],",
|
"\ttags: [\"*new*\", ${3:tags}],",
|
||||||
"\tIcon: ${4:icon},",
|
"\tIcon: Icons.${4:icon},",
|
||||||
"},"
|
"},"
|
||||||
],
|
],
|
||||||
"description": "Create an IconEntry for phosphor-home"
|
"description": "Create an IconEntry for phosphor-home"
|
||||||
|
|||||||
10
package.json
10
package.json
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "phosphor-home",
|
"name": "phosphor-home",
|
||||||
"version": "1.2.0",
|
"version": "1.3.1",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"homepage": "https://phosphoricons.com",
|
"homepage": "https://phosphoricons.com",
|
||||||
"author": {
|
"author": {
|
||||||
@@ -22,9 +22,9 @@
|
|||||||
"private": true,
|
"private": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"file-saver": "^2.0.2",
|
"file-saver": "^2.0.2",
|
||||||
"framer-motion": "^2.1.0",
|
"framer-motion": "^3.10.0",
|
||||||
"fuse.js": "^6.4.1",
|
"fuse.js": "^6.4.1",
|
||||||
"phosphor-react": "^1.2.0",
|
"phosphor-react": "^1.3.1",
|
||||||
"react": "^17.0.1",
|
"react": "^17.0.1",
|
||||||
"react-dom": "^17.0.1",
|
"react-dom": "^17.0.1",
|
||||||
"react-dropdown-select": "^4.4.2",
|
"react-dropdown-select": "^4.4.2",
|
||||||
@@ -32,9 +32,9 @@
|
|||||||
"react-hotkeys-hook": "^3.2.1",
|
"react-hotkeys-hook": "^3.2.1",
|
||||||
"react-scripts": "3.4.1",
|
"react-scripts": "3.4.1",
|
||||||
"react-use": "^15.3.2",
|
"react-use": "^15.3.2",
|
||||||
"recoil": "^0.1.2",
|
"recoil": "^0.1.3",
|
||||||
"svg2png-converter": "^1.0.0",
|
"svg2png-converter": "^1.0.0",
|
||||||
"tinycolor2": "^1.4.1"
|
"tinycolor2": "^1.4.2"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"analyze": "source-map-explorer 'build/static/js/*.js'",
|
"analyze": "source-map-explorer 'build/static/js/*.js'",
|
||||||
|
|||||||
@@ -15,10 +15,10 @@ h2 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
img {
|
img {
|
||||||
user-select: none;
|
|
||||||
-moz-user-select: none;
|
-moz-user-select: none;
|
||||||
-webkit-user-select: none;
|
-webkit-user-select: none;
|
||||||
-webkit-user-drag: none;
|
-webkit-user-drag: none;
|
||||||
|
user-select: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
pre,
|
pre,
|
||||||
@@ -65,9 +65,10 @@ button.main-button {
|
|||||||
transform: translate(0, 0);
|
transform: translate(0, 0);
|
||||||
transition: all 0.2s ease;
|
transition: all 0.2s ease;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
user-select: none;
|
|
||||||
-moz-user-select: none;
|
-moz-user-select: none;
|
||||||
-webkit-user-select: none;
|
-webkit-user-select: none;
|
||||||
|
-webkit-user-drag: none;
|
||||||
|
user-select: none;
|
||||||
margin: 0 24px 24px 0;
|
margin: 0 24px 24px 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -16,10 +16,11 @@
|
|||||||
left: 50%;
|
left: 50%;
|
||||||
-ms-transform: translate(-50%, -50%);
|
-ms-transform: translate(-50%, -50%);
|
||||||
transform: translate(-50%, -50%);
|
transform: translate(-50%, -50%);
|
||||||
user-select: none;
|
|
||||||
-moz-user-select: none;
|
-moz-user-select: none;
|
||||||
-webkit-user-select: none;
|
-webkit-user-select: none;
|
||||||
|
user-select: none;
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
|
text-transform: uppercase;
|
||||||
}
|
}
|
||||||
|
|
||||||
input.color-input {
|
input.color-input {
|
||||||
|
|||||||
@@ -48,9 +48,9 @@ footer .links {
|
|||||||
|
|
||||||
.illustrations-footer {
|
.illustrations-footer {
|
||||||
display: none;
|
display: none;
|
||||||
user-select: none;
|
|
||||||
-moz-user-select: none;
|
-moz-user-select: none;
|
||||||
-webkit-user-select: none;
|
-webkit-user-select: none;
|
||||||
|
user-select: none;
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ header {
|
|||||||
top: -158px;
|
top: -158px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#billiard-ball {
|
.billiard-ball {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
left: 132px;
|
left: 132px;
|
||||||
top: -98px;
|
top: -98px;
|
||||||
@@ -72,13 +72,13 @@ header {
|
|||||||
top: 152px;
|
top: 152px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#warning {
|
.warning {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
left: 394px;
|
left: 394px;
|
||||||
top: -304px;
|
top: -304px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#tablet {
|
.tablet {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
left: 672px;
|
left: 672px;
|
||||||
top: -900px;
|
top: -900px;
|
||||||
@@ -94,18 +94,18 @@ header {
|
|||||||
height: 612px;
|
height: 612px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#cutting-mat {
|
.cutting-mat {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
left: 96px;
|
left: 96px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#receipt {
|
.receipt {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
left: -36px;
|
left: -36px;
|
||||||
top: 190px;
|
top: 190px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#calculator {
|
.calculator {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
left: 632px;
|
left: 632px;
|
||||||
top: 170px;
|
top: 170px;
|
||||||
@@ -131,7 +131,7 @@ header {
|
|||||||
top: -158px;
|
top: -158px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#billiard-ball {
|
.billiard-ball {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
left: 900px;
|
left: 900px;
|
||||||
top: 400px;
|
top: 400px;
|
||||||
@@ -148,30 +148,30 @@ header {
|
|||||||
top: 694px;
|
top: 694px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#warning {
|
.warning {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
left: 1170px;
|
left: 1170px;
|
||||||
top: 400px;
|
top: 400px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#tablet {
|
.tablet {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
left: 578px;
|
left: 578px;
|
||||||
top: -900px;
|
top: -900px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#cutting-mat {
|
.cutting-mat {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
left: 120px;
|
left: 120px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#receipt {
|
.receipt {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
left: -16px;
|
left: -16px;
|
||||||
top: 190px;
|
top: 190px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#calculator {
|
.calculator {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
left: 924px;
|
left: 924px;
|
||||||
top: 114px;
|
top: 114px;
|
||||||
|
|||||||
@@ -41,22 +41,22 @@ const Header: React.FC<HeaderProps> = () => {
|
|||||||
<img src={markerPurple} id="marker-purple" alt="" />
|
<img src={markerPurple} id="marker-purple" alt="" />
|
||||||
<img src={paperclips} id="paperclips" alt="" />
|
<img src={paperclips} id="paperclips" alt="" />
|
||||||
<img src={paperclipsThree} id="paperclips-three" alt="" />
|
<img src={paperclipsThree} id="paperclips-three" alt="" />
|
||||||
<img id="tablet" src={tabletSpec} alt="" />
|
<img className="tablet" src={tabletSpec} alt="" />
|
||||||
<img id="tablet" className="inspectable xray" src={tablet} alt="" />
|
<img className="tablet inspectable xray" src={tablet} alt="" />
|
||||||
<img id="billiard-ball" src={billiardBallSpec} alt="" />
|
<img className="billiard-ball" src={billiardBallSpec} alt="" />
|
||||||
<img
|
<img
|
||||||
id="billiard-ball"
|
className="billiard-ball inspectable xray"
|
||||||
className="inspectable xray"
|
|
||||||
src={billiardBall}
|
src={billiardBall}
|
||||||
alt=""
|
alt=""
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<img id="warning" src={warningSpec} alt="" />
|
<img className="warning" src={warningSpec} alt="" />
|
||||||
<img id="warning" className="inspectable xray" src={warning} alt="" />
|
<img className="warning inspectable xray" src={warning} alt="" />
|
||||||
</div>
|
</div>
|
||||||
<div className="intro">
|
<div className="intro">
|
||||||
<h2>
|
<h2>
|
||||||
Phosphor is a flexible icon family for interfaces, diagrams, presentations —
|
Phosphor is a flexible icon family for interfaces, diagrams,
|
||||||
|
presentations —
|
||||||
<wbr />
|
<wbr />
|
||||||
whatever, really.
|
whatever, really.
|
||||||
</h2>
|
</h2>
|
||||||
@@ -73,19 +73,17 @@ const Header: React.FC<HeaderProps> = () => {
|
|||||||
<Links />
|
<Links />
|
||||||
</div>
|
</div>
|
||||||
<div className="illustrations-bottom">
|
<div className="illustrations-bottom">
|
||||||
<img id="cutting-mat" src={cuttingMatSpec} alt="" />
|
<img className="cutting-mat" src={cuttingMatSpec} alt="" />
|
||||||
<img
|
<img
|
||||||
id="cutting-mat"
|
className="cutting-mat inspectable xray"
|
||||||
className="inspectable xray"
|
|
||||||
src={cuttingMat}
|
src={cuttingMat}
|
||||||
alt=""
|
alt=""
|
||||||
/>
|
/>
|
||||||
<img id="receipt" src={receiptSpec} alt="" />
|
<img className="receipt" src={receiptSpec} alt="" />
|
||||||
<img id="receipt" className="inspectable xray" src={receipt} alt="" />
|
<img className="receipt inspectable xray" src={receipt} alt="" />
|
||||||
<img id="calculator" src={calculatorSpec} alt="" />
|
<img className="calculator" src={calculatorSpec} alt="" />
|
||||||
<img
|
<img
|
||||||
id="calculator"
|
className="calculator inspectable xray"
|
||||||
className="inspectable xray"
|
|
||||||
src={calculator}
|
src={calculator}
|
||||||
alt=""
|
alt=""
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -20,22 +20,22 @@ const panelVariants = {
|
|||||||
open: {
|
open: {
|
||||||
opacity: 1,
|
opacity: 1,
|
||||||
height: "100%",
|
height: "100%",
|
||||||
marginTop: 4,
|
marginTop: "4px",
|
||||||
marginBottom: 4,
|
marginBottom: "4px",
|
||||||
// transition: { stiffness: 600, damping: 32, duration: 0.2 },
|
transition: { type: "tween", duration: 0.1 },
|
||||||
},
|
},
|
||||||
collapsed: {
|
collapsed: {
|
||||||
opacity: 0,
|
opacity: 0,
|
||||||
height: 0,
|
height: "0px",
|
||||||
marginTop: 0,
|
marginTop: "0px",
|
||||||
marginBottom: 0,
|
marginBottom: "0px",
|
||||||
// transition: { stiffness: 600, damping: 32, duration: 0.2 },
|
transition: { type: "tween", duration: 0.1 },
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const contentVariants = {
|
const contentVariants = {
|
||||||
open: { opacity: 1, transition: { duration: 0.2 } },
|
open: { opacity: 1, transition: { duration: 0.2, delay: 0.1 } },
|
||||||
collapsed: { opacity: 0, transition: { duration: 0.1 } },
|
collapsed: { opacity: 0, transition: { duration: 0 } },
|
||||||
};
|
};
|
||||||
|
|
||||||
const buttonColor = "#35313D";
|
const buttonColor = "#35313D";
|
||||||
|
|||||||
@@ -22,9 +22,9 @@
|
|||||||
justify-content: center;
|
justify-content: center;
|
||||||
margin: 4px;
|
margin: 4px;
|
||||||
border-radius: 16px;
|
border-radius: 16px;
|
||||||
user-select: none;
|
|
||||||
-moz-user-select: none;
|
-moz-user-select: none;
|
||||||
-webkit-user-select: none;
|
-webkit-user-select: none;
|
||||||
|
user-select: none;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
/* transition: background-color 100ms ease; */
|
/* transition: background-color 100ms ease; */
|
||||||
}
|
}
|
||||||
@@ -46,6 +46,24 @@
|
|||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@media screen and (max-width: 536px) {
|
||||||
|
.grid-container {
|
||||||
|
padding: 32px 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.grid-item {
|
||||||
|
width: 108px;
|
||||||
|
height: unset;
|
||||||
|
padding: 4px 0;
|
||||||
|
justify-content: flex-start;
|
||||||
|
border: 2px solid transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.grid-item p {
|
||||||
|
padding: 0 4px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.info-box {
|
.info-box {
|
||||||
position: relative;
|
position: relative;
|
||||||
display: flex;
|
display: flex;
|
||||||
@@ -64,6 +82,10 @@
|
|||||||
.icon-usage {
|
.icon-usage {
|
||||||
padding-left: 10% !important;
|
padding-left: 10% !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.snippet pre {
|
||||||
|
padding: 12px 8px 12px 20px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.icon-preview {
|
.icon-preview {
|
||||||
@@ -95,9 +117,9 @@
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
color: black;
|
color: black;
|
||||||
user-select: all;
|
|
||||||
-moz-user-select: all;
|
-moz-user-select: all;
|
||||||
-webkit-user-select: all;
|
-webkit-user-select: all;
|
||||||
|
user-select: all;
|
||||||
}
|
}
|
||||||
|
|
||||||
.snippet pre:focus {
|
.snippet pre:focus {
|
||||||
@@ -106,9 +128,9 @@
|
|||||||
|
|
||||||
@keyframes select {
|
@keyframes select {
|
||||||
to {
|
to {
|
||||||
user-select: text;
|
|
||||||
-moz-user-select: text;
|
-moz-user-select: text;
|
||||||
-webkit-user-select: text;
|
-webkit-user-select: text;
|
||||||
|
user-select: text;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -43,26 +43,6 @@ const Links: React.FC<LinksProps> = () => {
|
|||||||
</OutboundLink>
|
</OutboundLink>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
{/* <div>
|
|
||||||
<ArrowElbowDownRight size={24} />
|
|
||||||
<OutboundLink
|
|
||||||
className="nav-link"
|
|
||||||
to="https://www.figma.com/file/xMCDSp5g0g7Fw8aMyAdVVr/Phosphor-Icon-Library-0.6.0"
|
|
||||||
eventLabel="Figma library"
|
|
||||||
>
|
|
||||||
Figma library
|
|
||||||
</OutboundLink>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<ArrowElbowDownRight size={24} />
|
|
||||||
<OutboundLink
|
|
||||||
className="nav-link"
|
|
||||||
to="https://www.figma.com/community/plugin/892854133443228626/Phosphor-Icons"
|
|
||||||
eventLabel="Figma plugin"
|
|
||||||
>
|
|
||||||
Figma plugin
|
|
||||||
</OutboundLink>
|
|
||||||
</div> */}
|
|
||||||
<div>
|
<div>
|
||||||
<ArrowElbowDownRight size={24} />
|
<ArrowElbowDownRight size={24} />
|
||||||
<a
|
<a
|
||||||
|
|||||||
@@ -56,6 +56,8 @@
|
|||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: flex-end;
|
justify-content: flex-end;
|
||||||
|
-moz-user-select: none;
|
||||||
|
-webkit-user-select: none;
|
||||||
user-select: none;
|
user-select: none;
|
||||||
min-width: 42px;
|
min-width: 42px;
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
|
|||||||
@@ -81,7 +81,7 @@ const SearchInput: React.FC<SearchInputProps> = () => {
|
|||||||
key === "Enter" && currentTarget.blur()
|
key === "Enter" && currentTarget.blur()
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
{!value && !isMobile && <Keys>{isApple ? <Command /> : "Ctrl"} + K</Keys>}
|
{!value && !isMobile && <Keys>{isApple ? <Command /> : "Ctrl + "}K</Keys>}
|
||||||
{value ? (
|
{value ? (
|
||||||
isReady() ? (
|
isReady() ? (
|
||||||
<X className="clear-icon" size={18} onClick={handleCancelSearch} />
|
<X className="clear-icon" size={18} onClick={handleCancelSearch} />
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
menu.toolbar {
|
nav.toolbar {
|
||||||
|
position: -webkit-sticky;
|
||||||
position: sticky;
|
position: sticky;
|
||||||
top: -1px;
|
top: -1px;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
|
|||||||
@@ -10,14 +10,14 @@ type ToolbarProps = {};
|
|||||||
|
|
||||||
const Toolbar: React.FC<ToolbarProps> = () => {
|
const Toolbar: React.FC<ToolbarProps> = () => {
|
||||||
return (
|
return (
|
||||||
<menu className="toolbar" id="toolbar">
|
<nav className="toolbar" id="toolbar">
|
||||||
<div className="toolbar-contents">
|
<div className="toolbar-contents">
|
||||||
<StyleInput />
|
<StyleInput />
|
||||||
<SearchInput />
|
<SearchInput />
|
||||||
<SizeInput />
|
<SizeInput />
|
||||||
<ColorInput />
|
<ColorInput />
|
||||||
</div>
|
</div>
|
||||||
</menu>
|
</nav>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
16
src/hooks/useDebounce.ts
Normal file
16
src/hooks/useDebounce.ts
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
import { DependencyList, useEffect } from "react";
|
||||||
|
import useTimeoutFn from "./useTimeoutFn";
|
||||||
|
|
||||||
|
export type UseDebounceReturn = [() => boolean | null, () => void];
|
||||||
|
|
||||||
|
export default function useDebounce(
|
||||||
|
fn: Function,
|
||||||
|
ms: number = 0,
|
||||||
|
deps: DependencyList = []
|
||||||
|
): UseDebounceReturn {
|
||||||
|
const [isReady, cancel, reset] = useTimeoutFn(fn, ms);
|
||||||
|
|
||||||
|
useEffect(reset, deps);
|
||||||
|
|
||||||
|
return [isReady, cancel];
|
||||||
|
}
|
||||||
@@ -1,13 +1,18 @@
|
|||||||
import { useWindowSize } from "react-use";
|
import { useWindowSize } from "react-use";
|
||||||
|
|
||||||
|
const MOBILE_BREAKPOINT = 536;
|
||||||
|
|
||||||
const GRID_PADDING = 32; // .grid-container { padding }
|
const GRID_PADDING = 32; // .grid-container { padding }
|
||||||
const TOOLBAR_WIDTH = 17; // IS THIS BROWSER-SPECIFIC?
|
const TOOLBAR_WIDTH = 17; // IS THIS BROWSER-SPECIFIC?
|
||||||
const MAX_GRID_WIDTH = 1120; // .grid { max-width }
|
const MAX_GRID_WIDTH = 1120; // .grid { max-width }
|
||||||
const ITEM_WIDTH = 168; // .grid-item { width; height; margin }
|
const ITEM_WIDTH = 168; // .grid-item { width; height; margin }
|
||||||
|
const ITEM_WIDTH_MOBILE = 108; // .grid-item { width; height; margin }
|
||||||
|
|
||||||
export default (): number => {
|
export default (): number => {
|
||||||
const { width } = useWindowSize();
|
const { width } = useWindowSize();
|
||||||
|
const itemWidth = width <= MOBILE_BREAKPOINT ? ITEM_WIDTH_MOBILE : ITEM_WIDTH;
|
||||||
|
|
||||||
return Math.floor(
|
return Math.floor(
|
||||||
Math.min(width - GRID_PADDING - TOOLBAR_WIDTH, MAX_GRID_WIDTH) / ITEM_WIDTH
|
Math.min(width - GRID_PADDING - TOOLBAR_WIDTH, MAX_GRID_WIDTH) / itemWidth
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
37
src/hooks/useThrottle.ts
Normal file
37
src/hooks/useThrottle.ts
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
import { useEffect, useRef, useState } from "react";
|
||||||
|
import useUnmount from "./useUnmount";
|
||||||
|
|
||||||
|
const useThrottle = <T>(value: T, ms: number = 200) => {
|
||||||
|
const [state, setState] = useState<T>(value);
|
||||||
|
const timeout = useRef<ReturnType<typeof setTimeout>>();
|
||||||
|
const nextValue = useRef(null) as any;
|
||||||
|
const hasNextValue = useRef(0) as any;
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!timeout.current) {
|
||||||
|
setState(value);
|
||||||
|
const timeoutCallback = () => {
|
||||||
|
if (hasNextValue.current) {
|
||||||
|
hasNextValue.current = false;
|
||||||
|
setState(nextValue.current);
|
||||||
|
timeout.current = setTimeout(timeoutCallback, ms);
|
||||||
|
} else {
|
||||||
|
timeout.current = undefined;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
timeout.current = setTimeout(timeoutCallback, ms);
|
||||||
|
} else {
|
||||||
|
nextValue.current = value;
|
||||||
|
hasNextValue.current = true;
|
||||||
|
}
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, [value]);
|
||||||
|
|
||||||
|
useUnmount(() => {
|
||||||
|
timeout.current && clearTimeout(timeout.current);
|
||||||
|
});
|
||||||
|
|
||||||
|
return state;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default useThrottle;
|
||||||
44
src/hooks/useTimeoutFn.ts
Normal file
44
src/hooks/useTimeoutFn.ts
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
import { useCallback, useEffect, useRef } from "react";
|
||||||
|
|
||||||
|
export type UseTimeoutFnReturn = [() => boolean | null, () => void, () => void];
|
||||||
|
|
||||||
|
export default function useTimeoutFn(
|
||||||
|
fn: Function,
|
||||||
|
ms: number = 0
|
||||||
|
): UseTimeoutFnReturn {
|
||||||
|
const ready = useRef<boolean | null>(false);
|
||||||
|
const timeout = useRef<ReturnType<typeof setTimeout>>();
|
||||||
|
const callback = useRef(fn);
|
||||||
|
|
||||||
|
const isReady = useCallback(() => ready.current, []);
|
||||||
|
|
||||||
|
const set = useCallback(() => {
|
||||||
|
ready.current = false;
|
||||||
|
timeout.current && clearTimeout(timeout.current);
|
||||||
|
|
||||||
|
timeout.current = setTimeout(() => {
|
||||||
|
ready.current = true;
|
||||||
|
callback.current();
|
||||||
|
}, ms);
|
||||||
|
}, [ms]);
|
||||||
|
|
||||||
|
const clear = useCallback(() => {
|
||||||
|
ready.current = null;
|
||||||
|
timeout.current && clearTimeout(timeout.current);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
// update ref when function changes
|
||||||
|
useEffect(() => {
|
||||||
|
callback.current = fn;
|
||||||
|
}, [fn]);
|
||||||
|
|
||||||
|
// set on mount, clear on unmount
|
||||||
|
useEffect(() => {
|
||||||
|
set();
|
||||||
|
|
||||||
|
return clear;
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, [ms]);
|
||||||
|
|
||||||
|
return [isReady, clear, set];
|
||||||
|
}
|
||||||
12
src/hooks/useUnmount.ts
Normal file
12
src/hooks/useUnmount.ts
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
import { useRef, useEffect } from "react";
|
||||||
|
|
||||||
|
const useUnmount = (fn: () => any): void => {
|
||||||
|
const fnRef = useRef(fn);
|
||||||
|
|
||||||
|
// update the ref each render so if it change the newest callback will be invoked
|
||||||
|
fnRef.current = fn;
|
||||||
|
|
||||||
|
useEffect(() => () => fnRef.current(), []);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default useUnmount;
|
||||||
1970
src/lib/icons.ts
1970
src/lib/icons.ts
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user