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:
ilkin-overleaf 2023-01-09 16:33:43 +02:00 committed by Copybot
parent 4f35333a39
commit d959dbc236
16 changed files with 117 additions and 74 deletions

View file

@ -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 {

View file

@ -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)
}

View file

@ -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(() => {

View file

@ -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

View file

@ -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

View file

@ -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)
}

View file

@ -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]
}

View file

@ -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

View file

@ -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

View file

@ -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(

View file

@ -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,
})

View file

@ -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
})

View file

@ -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 {

View file

@ -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(() => {

View file

@ -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
}