refactor(useResizable): refactor and fix useCallback dependency warning

This commit is contained in:
molvqingtai 2023-11-15 16:07:58 +08:00
parent fd0ecf579d
commit 387576c16c
7 changed files with 548 additions and 144 deletions

View file

@ -44,6 +44,7 @@
"@typescript-eslint/no-misused-promises": "off", "@typescript-eslint/no-misused-promises": "off",
"@typescript-eslint/consistent-type-assertions": "off", "@typescript-eslint/consistent-type-assertions": "off",
"import/no-absolute-path": "off", "import/no-absolute-path": "off",
"@typescript-eslint/no-base-to-string": "off",
"@typescript-eslint/no-unused-vars": "warn" "@typescript-eslint/no-unused-vars": "warn"
} }
} }

View file

@ -61,7 +61,7 @@
"peerjs": "^1.5.1", "peerjs": "^1.5.1",
"react": "^18.2.0", "react": "^18.2.0",
"react-dom": "^18.2.0", "react-dom": "^18.2.0",
"react-markdown": "^9.0.0", "react-markdown": "^9.0.1",
"react-nice-avatar": "^1.4.1", "react-nice-avatar": "^1.4.1",
"react-use": "^17.4.0", "react-use": "^17.4.0",
"remark-breaks": "^4.0.0", "remark-breaks": "^4.0.0",
@ -74,7 +74,7 @@
"type-fest": "^4.7.1" "type-fest": "^4.7.1"
}, },
"devDependencies": { "devDependencies": {
"@commitlint/cli": "^18.4.0", "@commitlint/cli": "^18.4.1",
"@commitlint/config-conventional": "^18.4.0", "@commitlint/config-conventional": "^18.4.0",
"@types/node": "^20.9.0", "@types/node": "^20.9.0",
"@types/react": "^18.2.37", "@types/react": "^18.2.37",
@ -96,13 +96,13 @@
"lint-staged": "^15.1.0", "lint-staged": "^15.1.0",
"npm-run-all": "^4.1.5", "npm-run-all": "^4.1.5",
"postcss": "^8.4.31", "postcss": "^8.4.31",
"prettier": "^3.0.3", "prettier": "^3.1.0",
"rimraf": "^5.0.5", "rimraf": "^5.0.5",
"tailwindcss": "^3.3.5", "tailwindcss": "^3.3.5",
"tailwindcss-animate": "^1.0.7", "tailwindcss-animate": "^1.0.7",
"typescript": "^5.2.2", "typescript": "^5.2.2",
"webext-bridge": "^6.0.1", "webext-bridge": "^6.0.1",
"wxt": "^0.9.0" "wxt": "^0.10.0"
}, },
"lint-staged": { "lint-staged": {
"*.{js,jsx,ts,tsx}": "eslint --fix" "*.{js,jsx,ts,tsx}": "eslint --fix"

File diff suppressed because it is too large Load diff

View file

@ -12,8 +12,8 @@ export default defineContentScript({
matches: ['*://*.example.com/*', '*://*.google.com/*', '*://*.v2ex.com/*'], matches: ['*://*.example.com/*', '*://*.google.com/*', '*://*.v2ex.com/*'],
async main(ctx) { async main(ctx) {
const store = Remesh.store({ const store = Remesh.store({
externs: [StorageImpl], externs: [StorageImpl]
inspectors: [RemeshLogger()] // inspectors: [RemeshLogger()]
}) })
const ui = await createContentScriptUi(ctx, { const ui = await createContentScriptUi(ctx, {

View file

@ -7,7 +7,7 @@ export interface AppContainerProps {
const AppContainer: FC<AppContainerProps> = ({ children }) => { const AppContainer: FC<AppContainerProps> = ({ children }) => {
const { size, ref } = useResizable({ const { size, ref } = useResizable({
initSize: 375, initSize: 375,
maxSize: 1000, maxSize: 750,
minSize: 375, minSize: 375,
direction: 'left' direction: 'left'
}) })

View file

@ -1,4 +1,5 @@
import { useCallback, useEffect, useRef, useState } from 'react' import { useCallback, useRef, useState } from 'react'
import { isInRange } from '@/utils'
export interface ResizableOptions { export interface ResizableOptions {
minSize: number minSize: number
@ -12,73 +13,79 @@ const useResizable = (options: ResizableOptions) => {
const [size, setSize] = useState(initSize) const [size, setSize] = useState(initSize)
const [position, setPosition] = useState(0) const position = useRef(0)
const [isMove, setIsMove] = useState(false) const isMove = useRef(false)
const directionXY = direction === 'left' || direction === 'right' ? 'X' : 'Y' const isHorizontal = direction === 'left' || direction === 'right'
const handleStart = (e: MouseEvent) => { const handleMove = useCallback(
const { screenY, screenX } = e (e: MouseEvent) => {
setIsMove(true) if (isMove.current) {
setPosition(directionXY === 'Y' ? screenY : screenX)
document.documentElement.style.userSelect = 'none'
document.documentElement.style.cursor = directionXY === 'Y' ? 'ns-resize' : 'ew-resize'
}
const handleEnd = () => {
setIsMove(false)
document.documentElement.style.cursor = ''
document.documentElement.style.userSelect = ''
}
useEffect(() => {
const handleMove = (e: MouseEvent) => {
if (isMove) {
console.log('move')
const { screenY, screenX } = e const { screenY, screenX } = e
let delta = 0 let delta = 0
switch (direction) { switch (direction) {
case 'left': case 'left':
delta = position - screenX delta = position.current - screenX
break break
case 'right': case 'right':
delta = screenX - position delta = screenX - position.current
break break
case 'top': case 'top':
delta = position - screenY delta = position.current - screenY
break break
case 'bottom': case 'bottom':
delta = screenY - position delta = screenY - position.current
break break
} }
const newSize = size + delta const newSize = size + delta
if (size !== newSize && newSize >= minSize && newSize <= maxSize) { if (isInRange(newSize, minSize, maxSize)) {
setSize(newSize) position.current = isHorizontal ? screenX : screenY
} }
} else { if (newSize !== size) {
document.removeEventListener('mousemove', handleMove) setSize(clamp(newSize, minSize, maxSize))
} }
} }
},
[direction, isHorizontal, maxSize, minSize, size]
)
document.addEventListener('mousemove', handleMove) const handleEnd = useCallback(() => {
return () => { isMove.current = false
document.removeEventListener('mousemove', handleMove) document.documentElement.style.cursor = ''
} document.documentElement.style.userSelect = ''
}, [isMove]) }, [])
const handleStart = useCallback(
(e: MouseEvent) => {
const { screenY, screenX } = e
isMove.current = true
position.current = isHorizontal ? screenX : screenY
document.documentElement.style.userSelect = 'none'
document.documentElement.style.cursor = isHorizontal ? 'ew-resize' : 'ns-resize'
},
[isHorizontal]
)
const ref = useRef<HTMLElement | null>(null) const ref = useRef<HTMLElement | null>(null)
const setRef = useCallback((node: HTMLElement | null) => {
// Watch ref: https://medium.com/@teh_builder/ref-objects-inside-useeffect-hooks-eb7c15198780
const setRef = useCallback(
(node: HTMLElement | null) => {
if (ref.current) { if (ref.current) {
ref.current.removeEventListener('mousedown', handleStart) ref.current.removeEventListener('mousedown', handleStart)
document.removeEventListener('mouseup', handleEnd) document.removeEventListener('mouseup', handleEnd)
document.removeEventListener('mousemove', handleMove)
} }
if (node) { if (node) {
node.addEventListener('mousedown', handleStart) node.addEventListener('mousedown', handleStart)
document.addEventListener('mouseup', handleEnd) document.addEventListener('mouseup', handleEnd)
document.addEventListener('mousemove', handleMove)
} }
ref.current = node ref.current = node
}, []) },
[handleEnd, handleMove, handleStart]
)
return { size, ref: setRef } return { size, ref: setRef }
} }

View file

@ -22,6 +22,9 @@ export const createElement = <T extends Element>(template: string) => {
export const chunk = <T = any>(array: T[], size: number) => export const chunk = <T = any>(array: T[], size: number) =>
Array.from({ length: Math.ceil(array.length / size) }, (_v, i) => array.slice(i * size, i * size + size)) Array.from({ length: Math.ceil(array.length / size) }, (_v, i) => array.slice(i * size, i * size + size))
export const clamp = (number: number, min: number, max: number) => Math.min(Math.max(number, min), max)
export const isInRange = (number: number, min: number, max: number) => number >= min && number <= max
export const getWebSiteInfo = (): WebSiteInfo => { export const getWebSiteInfo = (): WebSiteInfo => {
return { return {
host: document.location.host, host: document.location.host,