mirror of
https://github.com/overleaf/overleaf.git
synced 2025-04-14 20:04:28 +00:00
Merge pull request #11105 from overleaf/ii-shared-hooks-js-to-ts-conversion
[web] Shared React hooks JS to TS conversion GitOrigin-RevId: 0ccdebff236c7424b1a73cd7d6646a9d01a20eb1
This commit is contained in:
parent
4f35333a39
commit
d959dbc236
16 changed files with 117 additions and 74 deletions
services/web/frontend/js/shared/hooks
use-abort-controller.tsuse-browser-window.tsuse-callback-handlers.tsuse-debounce.tsuse-deep-compare-effect.tsuse-detach-action.tsuse-detach-layout.tsuse-detach-state-watcher.jsuse-detach-state-watcher.tsuse-detach-state.tsuse-dropdown.tsuse-expand-collapse.tsuse-previous-value.tsuse-scope-event-emitter.tsuse-scope-event-listener.tsuse-stop-on-first-error.ts
|
@ -1,10 +1,10 @@
|
|||
import { useEffect, useState } from 'react'
|
||||
|
||||
let titleIsFlashing = false
|
||||
let originalTitle
|
||||
let flashIntervalHandle
|
||||
let originalTitle = ''
|
||||
let flashIntervalHandle: ReturnType<typeof setInterval>
|
||||
|
||||
function flashTitle(message) {
|
||||
function flashTitle(message: string) {
|
||||
if (document.hasFocus() || titleIsFlashing) {
|
||||
return
|
||||
}
|
||||
|
@ -30,11 +30,11 @@ function stopFlashingTitle() {
|
|||
|
||||
clearInterval(flashIntervalHandle)
|
||||
window.document.title = originalTitle
|
||||
originalTitle = undefined
|
||||
originalTitle = ''
|
||||
titleIsFlashing = false
|
||||
}
|
||||
|
||||
function setTitle(title) {
|
||||
function setTitle(title: string) {
|
||||
if (titleIsFlashing) {
|
||||
originalTitle = title
|
||||
} else {
|
|
@ -1,17 +1,17 @@
|
|||
import { useCallback, useRef } from 'react'
|
||||
|
||||
export default function useCallbackHandlers() {
|
||||
const handlersRef = useRef(new Set())
|
||||
const handlersRef = useRef(new Set<(...arg: unknown[]) => void>())
|
||||
|
||||
const addHandler = useCallback(handler => {
|
||||
const addHandler = useCallback((handler: (...args: unknown[]) => void) => {
|
||||
handlersRef.current.add(handler)
|
||||
}, [])
|
||||
|
||||
const deleteHandler = useCallback(handler => {
|
||||
const deleteHandler = useCallback((handler: (...args: unknown[]) => void) => {
|
||||
handlersRef.current.delete(handler)
|
||||
}, [])
|
||||
|
||||
const callHandlers = useCallback((...args) => {
|
||||
const callHandlers = useCallback((...args: unknown[]) => {
|
||||
for (const handler of handlersRef.current) {
|
||||
handler(...args)
|
||||
}
|
|
@ -6,7 +6,7 @@ import { useEffect, useState } from 'react'
|
|||
* @param {number} delay
|
||||
* @returns {T}
|
||||
*/
|
||||
export default function useDebounce(value, delay = 0) {
|
||||
export default function useDebounce<T>(value: T, delay = 0) {
|
||||
const [debouncedValue, setDebouncedValue] = useState(value)
|
||||
|
||||
useEffect(() => {
|
|
@ -1,8 +1,11 @@
|
|||
import { useEffect, useRef } from 'react'
|
||||
import _ from 'lodash'
|
||||
|
||||
export default function useDeepCompareEffect(callback, dependencies) {
|
||||
const ref = useRef()
|
||||
export default function useDeepCompareEffect<T>(
|
||||
callback: () => void,
|
||||
dependencies: T[]
|
||||
) {
|
||||
const ref = useRef<T[]>()
|
||||
return useEffect(() => {
|
||||
if (_.isEqual(dependencies, ref.current)) {
|
||||
return
|
|
@ -1,22 +1,27 @@
|
|||
import { useCallback, useEffect } from 'react'
|
||||
import { useDetachContext } from '../context/detach-context'
|
||||
import getMeta from '../../utils/meta'
|
||||
import { DetachRole, DetachTargetRole, Message } from './use-detach-state'
|
||||
|
||||
const debugPdfDetach = getMeta('ol-debugPdfDetach')
|
||||
|
||||
export default function useDetachAction(
|
||||
actionName,
|
||||
actionFunction,
|
||||
senderRole,
|
||||
targetRole
|
||||
function useDetachAction<
|
||||
A,
|
||||
S extends DetachRole,
|
||||
T extends DetachTargetRole<S>
|
||||
>(
|
||||
actionName: string,
|
||||
actionFunction: (...args: A[]) => void,
|
||||
senderRole: S,
|
||||
targetRole: T
|
||||
) {
|
||||
const { role, broadcastEvent, addEventHandler, deleteEventHandler } =
|
||||
useDetachContext()
|
||||
|
||||
const eventName = `action-${actionName}`
|
||||
const eventName: Message['event'] = `action-${actionName}`
|
||||
|
||||
const triggerFn = useCallback(
|
||||
(...args) => {
|
||||
(...args: A[]) => {
|
||||
if (role === senderRole) {
|
||||
broadcastEvent(eventName, { args })
|
||||
} else {
|
||||
|
@ -27,7 +32,7 @@ export default function useDetachAction(
|
|||
)
|
||||
|
||||
const handleActionEvent = useCallback(
|
||||
message => {
|
||||
(message: Message<A>) => {
|
||||
if (message.event !== eventName) {
|
||||
return
|
||||
}
|
||||
|
@ -49,3 +54,5 @@ export default function useDetachAction(
|
|||
|
||||
return triggerFn
|
||||
}
|
||||
|
||||
export default useDetachAction
|
|
@ -24,7 +24,7 @@ export default function useDetachLayout() {
|
|||
// redundant
|
||||
const [isRedundant, setIsRedundant] = useState(false)
|
||||
|
||||
const uiTimeoutRef = useRef()
|
||||
const uiTimeoutRef = useRef<ReturnType<typeof setTimeout>>()
|
||||
|
||||
useEffect(() => {
|
||||
if (debugPdfDetach) {
|
||||
|
@ -153,7 +153,7 @@ export default function useDetachLayout() {
|
|||
message => {
|
||||
if (role === 'detacher') {
|
||||
if (message.role === 'detacher') {
|
||||
handleEventForDetacherFromDetacher(message)
|
||||
handleEventForDetacherFromDetacher()
|
||||
} else if (message.role === 'detached') {
|
||||
handleEventForDetacherFromDetached(message)
|
||||
}
|
|
@ -1,22 +0,0 @@
|
|||
import { useEffect } from 'react'
|
||||
import useDetachState from './use-detach-state'
|
||||
|
||||
export default function useDetachStateWatcher(
|
||||
key,
|
||||
stateValue,
|
||||
senderRole,
|
||||
targetRole
|
||||
) {
|
||||
const [value, setValue] = useDetachState(
|
||||
key,
|
||||
stateValue,
|
||||
senderRole,
|
||||
targetRole
|
||||
)
|
||||
|
||||
useEffect(() => {
|
||||
setValue(stateValue)
|
||||
}, [setValue, stateValue])
|
||||
|
||||
return [value, setValue]
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
import { useEffect } from 'react'
|
||||
import useDetachState, {
|
||||
DetachRole,
|
||||
DetachTargetRole,
|
||||
} from './use-detach-state'
|
||||
|
||||
type UseDetachParams = Parameters<typeof useDetachState>
|
||||
|
||||
function useDetachStateWatcher<
|
||||
S extends DetachRole,
|
||||
T extends DetachTargetRole<S>
|
||||
>(
|
||||
key: UseDetachParams[0],
|
||||
stateValue: UseDetachParams[1],
|
||||
senderRole: S,
|
||||
targetRole: T
|
||||
) {
|
||||
const [value, setValue] = useDetachState(
|
||||
key,
|
||||
stateValue,
|
||||
senderRole,
|
||||
targetRole
|
||||
)
|
||||
|
||||
useEffect(() => {
|
||||
setValue(stateValue)
|
||||
}, [setValue, stateValue])
|
||||
|
||||
return [value, setValue]
|
||||
}
|
||||
|
||||
export default useDetachStateWatcher
|
|
@ -4,12 +4,24 @@ import getMeta from '../../utils/meta'
|
|||
|
||||
const debugPdfDetach = getMeta('ol-debugPdfDetach')
|
||||
|
||||
export default function useDetachState(
|
||||
key,
|
||||
defaultValue,
|
||||
senderRole,
|
||||
targetRole
|
||||
) {
|
||||
export type DetachRole = 'detacher' | 'detached'
|
||||
export type DetachTargetRole<T extends DetachRole> = T extends 'detacher'
|
||||
? 'detached'
|
||||
: 'detacher'
|
||||
export type Message<DataArgs = unknown> = {
|
||||
event: `${'action' | 'state'}-${string}`
|
||||
data: {
|
||||
args: DataArgs[]
|
||||
value: unknown
|
||||
}
|
||||
}
|
||||
|
||||
function useDetachState<S extends DetachRole, T extends DetachTargetRole<S>>(
|
||||
key: string,
|
||||
defaultValue: unknown,
|
||||
senderRole: S,
|
||||
targetRole: T
|
||||
): [unknown, React.Dispatch<unknown>] {
|
||||
const [value, setValue] = useState(defaultValue)
|
||||
|
||||
const {
|
||||
|
@ -20,7 +32,7 @@ export default function useDetachState(
|
|||
deleteEventHandler,
|
||||
} = useDetachContext()
|
||||
|
||||
const eventName = `state-${key}`
|
||||
const eventName: Message['event'] = `state-${key}`
|
||||
|
||||
// lastDetachedConnectedAt is added as a dependency in order to re-broadcast
|
||||
// all states when a new detached tab connects
|
||||
|
@ -38,7 +50,7 @@ export default function useDetachState(
|
|||
])
|
||||
|
||||
const handleStateEvent = useCallback(
|
||||
message => {
|
||||
(message: Message) => {
|
||||
if (message.event !== eventName) {
|
||||
return
|
||||
}
|
||||
|
@ -60,3 +72,5 @@ export default function useDetachState(
|
|||
|
||||
return [value, setValue]
|
||||
}
|
||||
|
||||
export default useDetachState
|
|
@ -5,7 +5,7 @@ export default function useDropdown(defaultOpen = false) {
|
|||
const [open, setOpen] = useState(defaultOpen)
|
||||
|
||||
// store the dropdown node for use in the "click outside" event listener
|
||||
const ref = useRef(null)
|
||||
const ref = useRef<ReturnType<typeof findDOMNode>>(null)
|
||||
|
||||
// react-bootstrap v0.x passes `component` instead of `node` to the ref callback
|
||||
const handleRef = useCallback(
|
|
@ -3,13 +3,16 @@ import classNames from 'classnames'
|
|||
|
||||
function useExpandCollapse({
|
||||
initiallyExpanded = false,
|
||||
collapsedSize = '0',
|
||||
collapsedSize = 0,
|
||||
dimension = 'height',
|
||||
classes = {},
|
||||
classes = { container: '', containerCollapsed: '' },
|
||||
} = {}) {
|
||||
const ref = useRef()
|
||||
const ref = useRef<{ scrollHeight: number; scrollWidth: number }>()
|
||||
const [isExpanded, setIsExpanded] = useState(initiallyExpanded)
|
||||
const [sizing, setSizing] = useState({
|
||||
const [sizing, setSizing] = useState<{
|
||||
size: number | null
|
||||
needsExpandCollapse: boolean | null
|
||||
}>({
|
||||
size: null,
|
||||
needsExpandCollapse: null,
|
||||
})
|
|
@ -1,7 +1,7 @@
|
|||
import { useEffect, useRef } from 'react'
|
||||
|
||||
export default function usePreviousValue(value) {
|
||||
const ref = useRef()
|
||||
export default function usePreviousValue<T>(value: T) {
|
||||
const ref = useRef<T>()
|
||||
useEffect(() => {
|
||||
ref.current = value
|
||||
})
|
|
@ -1,16 +1,14 @@
|
|||
import { useCallback } from 'react'
|
||||
import { useIdeContext } from '../context/ide-context'
|
||||
|
||||
/**
|
||||
* @param {string} eventName
|
||||
* @param {boolean} [broadcast]
|
||||
* @returns function
|
||||
*/
|
||||
export default function useScopeEventEmitter(eventName, broadcast = true) {
|
||||
export default function useScopeEventEmitter(
|
||||
eventName: string,
|
||||
broadcast = true
|
||||
) {
|
||||
const { $scope } = useIdeContext()
|
||||
|
||||
return useCallback(
|
||||
(...detail) => {
|
||||
(...detail: unknown[]) => {
|
||||
if (broadcast) {
|
||||
$scope.$broadcast(eventName, ...detail)
|
||||
} else {
|
|
@ -1,11 +1,10 @@
|
|||
import { useEffect } from 'react'
|
||||
import { useIdeContext } from '../context/ide-context'
|
||||
|
||||
/**
|
||||
* @param {string} eventName
|
||||
* @param {function} [listener]
|
||||
*/
|
||||
export default function useScopeEventListener(eventName, listener) {
|
||||
export default function useScopeEventListener(
|
||||
eventName: string,
|
||||
listener: (...args: unknown[]) => void
|
||||
) {
|
||||
const { $scope } = useIdeContext()
|
||||
|
||||
useEffect(() => {
|
|
@ -3,14 +3,23 @@ import { useDetachCompileContext as useCompileContext } from '../context/detach-
|
|||
import { useProjectContext } from '../context/project-context'
|
||||
import * as eventTracking from '../../infrastructure/event-tracking'
|
||||
|
||||
export function useStopOnFirstError(opts = {}) {
|
||||
type UseStopOnFirstErrorProps = {
|
||||
eventSource?: string
|
||||
}
|
||||
|
||||
export function useStopOnFirstError(opts: UseStopOnFirstErrorProps = {}) {
|
||||
const { eventSource } = opts
|
||||
const { stopOnFirstError, setStopOnFirstError } = useCompileContext()
|
||||
const { _id: projectId } = useProjectContext()
|
||||
|
||||
type Opts = {
|
||||
projectId: string
|
||||
source?: UseStopOnFirstErrorProps['eventSource']
|
||||
}
|
||||
|
||||
const enableStopOnFirstError = useCallback(() => {
|
||||
if (!stopOnFirstError) {
|
||||
const opts = { projectId }
|
||||
const opts: Opts = { projectId }
|
||||
if (eventSource) {
|
||||
opts.source = eventSource
|
||||
}
|
||||
|
@ -20,7 +29,7 @@ export function useStopOnFirstError(opts = {}) {
|
|||
}, [eventSource, projectId, stopOnFirstError, setStopOnFirstError])
|
||||
|
||||
const disableStopOnFirstError = useCallback(() => {
|
||||
const opts = { projectId }
|
||||
const opts: Opts = { projectId }
|
||||
if (eventSource) {
|
||||
opts.source = eventSource
|
||||
}
|
Loading…
Add table
Reference in a new issue