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' import { useEffect, useState } from 'react'
let titleIsFlashing = false let titleIsFlashing = false
let originalTitle let originalTitle = ''
let flashIntervalHandle let flashIntervalHandle: ReturnType<typeof setInterval>
function flashTitle(message) { function flashTitle(message: string) {
if (document.hasFocus() || titleIsFlashing) { if (document.hasFocus() || titleIsFlashing) {
return return
} }
@ -30,11 +30,11 @@ function stopFlashingTitle() {
clearInterval(flashIntervalHandle) clearInterval(flashIntervalHandle)
window.document.title = originalTitle window.document.title = originalTitle
originalTitle = undefined originalTitle = ''
titleIsFlashing = false titleIsFlashing = false
} }
function setTitle(title) { function setTitle(title: string) {
if (titleIsFlashing) { if (titleIsFlashing) {
originalTitle = title originalTitle = title
} else { } else {

View file

@ -1,17 +1,17 @@
import { useCallback, useRef } from 'react' import { useCallback, useRef } from 'react'
export default function useCallbackHandlers() { 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) handlersRef.current.add(handler)
}, []) }, [])
const deleteHandler = useCallback(handler => { const deleteHandler = useCallback((handler: (...args: unknown[]) => void) => {
handlersRef.current.delete(handler) handlersRef.current.delete(handler)
}, []) }, [])
const callHandlers = useCallback((...args) => { const callHandlers = useCallback((...args: unknown[]) => {
for (const handler of handlersRef.current) { for (const handler of handlersRef.current) {
handler(...args) handler(...args)
} }

View file

@ -6,7 +6,7 @@ import { useEffect, useState } from 'react'
* @param {number} delay * @param {number} delay
* @returns {T} * @returns {T}
*/ */
export default function useDebounce(value, delay = 0) { export default function useDebounce<T>(value: T, delay = 0) {
const [debouncedValue, setDebouncedValue] = useState(value) const [debouncedValue, setDebouncedValue] = useState(value)
useEffect(() => { useEffect(() => {

View file

@ -1,8 +1,11 @@
import { useEffect, useRef } from 'react' import { useEffect, useRef } from 'react'
import _ from 'lodash' import _ from 'lodash'
export default function useDeepCompareEffect(callback, dependencies) { export default function useDeepCompareEffect<T>(
const ref = useRef() callback: () => void,
dependencies: T[]
) {
const ref = useRef<T[]>()
return useEffect(() => { return useEffect(() => {
if (_.isEqual(dependencies, ref.current)) { if (_.isEqual(dependencies, ref.current)) {
return return

View file

@ -1,22 +1,27 @@
import { useCallback, useEffect } from 'react' import { useCallback, useEffect } from 'react'
import { useDetachContext } from '../context/detach-context' import { useDetachContext } from '../context/detach-context'
import getMeta from '../../utils/meta' import getMeta from '../../utils/meta'
import { DetachRole, DetachTargetRole, Message } from './use-detach-state'
const debugPdfDetach = getMeta('ol-debugPdfDetach') const debugPdfDetach = getMeta('ol-debugPdfDetach')
export default function useDetachAction( function useDetachAction<
actionName, A,
actionFunction, S extends DetachRole,
senderRole, T extends DetachTargetRole<S>
targetRole >(
actionName: string,
actionFunction: (...args: A[]) => void,
senderRole: S,
targetRole: T
) { ) {
const { role, broadcastEvent, addEventHandler, deleteEventHandler } = const { role, broadcastEvent, addEventHandler, deleteEventHandler } =
useDetachContext() useDetachContext()
const eventName = `action-${actionName}` const eventName: Message['event'] = `action-${actionName}`
const triggerFn = useCallback( const triggerFn = useCallback(
(...args) => { (...args: A[]) => {
if (role === senderRole) { if (role === senderRole) {
broadcastEvent(eventName, { args }) broadcastEvent(eventName, { args })
} else { } else {
@ -27,7 +32,7 @@ export default function useDetachAction(
) )
const handleActionEvent = useCallback( const handleActionEvent = useCallback(
message => { (message: Message<A>) => {
if (message.event !== eventName) { if (message.event !== eventName) {
return return
} }
@ -49,3 +54,5 @@ export default function useDetachAction(
return triggerFn return triggerFn
} }
export default useDetachAction

View file

@ -24,7 +24,7 @@ export default function useDetachLayout() {
// redundant // redundant
const [isRedundant, setIsRedundant] = useState(false) const [isRedundant, setIsRedundant] = useState(false)
const uiTimeoutRef = useRef() const uiTimeoutRef = useRef<ReturnType<typeof setTimeout>>()
useEffect(() => { useEffect(() => {
if (debugPdfDetach) { if (debugPdfDetach) {
@ -153,7 +153,7 @@ export default function useDetachLayout() {
message => { message => {
if (role === 'detacher') { if (role === 'detacher') {
if (message.role === 'detacher') { if (message.role === 'detacher') {
handleEventForDetacherFromDetacher(message) handleEventForDetacherFromDetacher()
} else if (message.role === 'detached') { } else if (message.role === 'detached') {
handleEventForDetacherFromDetached(message) 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') const debugPdfDetach = getMeta('ol-debugPdfDetach')
export default function useDetachState( export type DetachRole = 'detacher' | 'detached'
key, export type DetachTargetRole<T extends DetachRole> = T extends 'detacher'
defaultValue, ? 'detached'
senderRole, : 'detacher'
targetRole 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 [value, setValue] = useState(defaultValue)
const { const {
@ -20,7 +32,7 @@ export default function useDetachState(
deleteEventHandler, deleteEventHandler,
} = useDetachContext() } = useDetachContext()
const eventName = `state-${key}` const eventName: Message['event'] = `state-${key}`
// lastDetachedConnectedAt is added as a dependency in order to re-broadcast // lastDetachedConnectedAt is added as a dependency in order to re-broadcast
// all states when a new detached tab connects // all states when a new detached tab connects
@ -38,7 +50,7 @@ export default function useDetachState(
]) ])
const handleStateEvent = useCallback( const handleStateEvent = useCallback(
message => { (message: Message) => {
if (message.event !== eventName) { if (message.event !== eventName) {
return return
} }
@ -60,3 +72,5 @@ export default function useDetachState(
return [value, setValue] return [value, setValue]
} }
export default useDetachState

View file

@ -5,7 +5,7 @@ export default function useDropdown(defaultOpen = false) {
const [open, setOpen] = useState(defaultOpen) const [open, setOpen] = useState(defaultOpen)
// store the dropdown node for use in the "click outside" event listener // 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 // react-bootstrap v0.x passes `component` instead of `node` to the ref callback
const handleRef = useCallback( const handleRef = useCallback(

View file

@ -3,13 +3,16 @@ import classNames from 'classnames'
function useExpandCollapse({ function useExpandCollapse({
initiallyExpanded = false, initiallyExpanded = false,
collapsedSize = '0', collapsedSize = 0,
dimension = 'height', dimension = 'height',
classes = {}, classes = { container: '', containerCollapsed: '' },
} = {}) { } = {}) {
const ref = useRef() const ref = useRef<{ scrollHeight: number; scrollWidth: number }>()
const [isExpanded, setIsExpanded] = useState(initiallyExpanded) const [isExpanded, setIsExpanded] = useState(initiallyExpanded)
const [sizing, setSizing] = useState({ const [sizing, setSizing] = useState<{
size: number | null
needsExpandCollapse: boolean | null
}>({
size: null, size: null,
needsExpandCollapse: null, needsExpandCollapse: null,
}) })

View file

@ -1,7 +1,7 @@
import { useEffect, useRef } from 'react' import { useEffect, useRef } from 'react'
export default function usePreviousValue(value) { export default function usePreviousValue<T>(value: T) {
const ref = useRef() const ref = useRef<T>()
useEffect(() => { useEffect(() => {
ref.current = value ref.current = value
}) })

View file

@ -1,16 +1,14 @@
import { useCallback } from 'react' import { useCallback } from 'react'
import { useIdeContext } from '../context/ide-context' import { useIdeContext } from '../context/ide-context'
/** export default function useScopeEventEmitter(
* @param {string} eventName eventName: string,
* @param {boolean} [broadcast] broadcast = true
* @returns function ) {
*/
export default function useScopeEventEmitter(eventName, broadcast = true) {
const { $scope } = useIdeContext() const { $scope } = useIdeContext()
return useCallback( return useCallback(
(...detail) => { (...detail: unknown[]) => {
if (broadcast) { if (broadcast) {
$scope.$broadcast(eventName, ...detail) $scope.$broadcast(eventName, ...detail)
} else { } else {

View file

@ -1,11 +1,10 @@
import { useEffect } from 'react' import { useEffect } from 'react'
import { useIdeContext } from '../context/ide-context' import { useIdeContext } from '../context/ide-context'
/** export default function useScopeEventListener(
* @param {string} eventName eventName: string,
* @param {function} [listener] listener: (...args: unknown[]) => void
*/ ) {
export default function useScopeEventListener(eventName, listener) {
const { $scope } = useIdeContext() const { $scope } = useIdeContext()
useEffect(() => { useEffect(() => {

View file

@ -3,14 +3,23 @@ import { useDetachCompileContext as useCompileContext } from '../context/detach-
import { useProjectContext } from '../context/project-context' import { useProjectContext } from '../context/project-context'
import * as eventTracking from '../../infrastructure/event-tracking' import * as eventTracking from '../../infrastructure/event-tracking'
export function useStopOnFirstError(opts = {}) { type UseStopOnFirstErrorProps = {
eventSource?: string
}
export function useStopOnFirstError(opts: UseStopOnFirstErrorProps = {}) {
const { eventSource } = opts const { eventSource } = opts
const { stopOnFirstError, setStopOnFirstError } = useCompileContext() const { stopOnFirstError, setStopOnFirstError } = useCompileContext()
const { _id: projectId } = useProjectContext() const { _id: projectId } = useProjectContext()
type Opts = {
projectId: string
source?: UseStopOnFirstErrorProps['eventSource']
}
const enableStopOnFirstError = useCallback(() => { const enableStopOnFirstError = useCallback(() => {
if (!stopOnFirstError) { if (!stopOnFirstError) {
const opts = { projectId } const opts: Opts = { projectId }
if (eventSource) { if (eventSource) {
opts.source = eventSource opts.source = eventSource
} }
@ -20,7 +29,7 @@ export function useStopOnFirstError(opts = {}) {
}, [eventSource, projectId, stopOnFirstError, setStopOnFirstError]) }, [eventSource, projectId, stopOnFirstError, setStopOnFirstError])
const disableStopOnFirstError = useCallback(() => { const disableStopOnFirstError = useCallback(() => {
const opts = { projectId } const opts: Opts = { projectId }
if (eventSource) { if (eventSource) {
opts.source = eventSource opts.source = eventSource
} }