mirror of
https://github.com/overleaf/overleaf.git
synced 2024-09-16 02:52:31 -04: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
|
@ -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 {
|
|
@ -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)
|
||||||
}
|
}
|
|
@ -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(() => {
|
|
@ -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
|
|
@ -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
|
|
@ -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)
|
||||||
}
|
}
|
|
@ -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')
|
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
|
|
@ -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(
|
|
@ -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,
|
||||||
})
|
})
|
|
@ -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
|
||||||
})
|
})
|
|
@ -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 {
|
|
@ -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(() => {
|
|
@ -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
|
||||||
}
|
}
|
Loading…
Reference in a new issue