-
Notifications
You must be signed in to change notification settings - Fork 324
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Implement back and forth navigation (#9301)
- Closes enso-org/cloud-v2#940
- Loading branch information
1 parent
c983d08
commit 6c1ba64
Showing
20 changed files
with
310 additions
and
31 deletions.
There are no files selected for viewing
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
23 changes: 23 additions & 0 deletions
23
app/ide-desktop/lib/dashboard/src/hooks/eventCallbackHooks.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
/** | ||
* @file useEventCallback shim | ||
*/ | ||
|
||
import * as React from 'react' | ||
|
||
import * as syncRef from '#/hooks/syncRefHooks' | ||
|
||
/** | ||
* useEvent shim. | ||
* @see https://github.com/reactjs/rfcs/pull/220 | ||
* @see https://github.com/reactjs/rfcs/blob/useevent/text/0000-useevent.md#internal-implementation | ||
*/ | ||
// eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
export function useEventCallback<Func extends (...args: any[]) => unknown>(callback: Func) { | ||
const callbackRef = syncRef.useSyncRef(callback) | ||
|
||
// Make sure that the value of `this` provided for the call to fn is not `ref` | ||
// This type assertion is safe, because it's a transparent wrapper around the original callback | ||
// we mute react-hooks/exhaustive-deps because we don't need to update the callback when the callbackRef changes(it never does) | ||
// eslint-disable-next-line react-hooks/exhaustive-deps, no-restricted-syntax | ||
return React.useCallback(((...args) => callbackRef.current.apply(void 0, args)) as Func, []) | ||
} |
80 changes: 80 additions & 0 deletions
80
app/ide-desktop/lib/dashboard/src/hooks/searchParamsStateHooks.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
/** | ||
* @file | ||
* | ||
* Search params state hook store a value in the URL search params. | ||
*/ | ||
import * as React from 'react' | ||
|
||
import * as reactRouterDom from 'react-router-dom' | ||
|
||
import * as eventCallback from '#/hooks/eventCallbackHooks' | ||
import * as lazyMemo from '#/hooks/useLazyMemoHooks' | ||
|
||
import * as safeJsonParse from '#/utilities/safeJsonParse' | ||
|
||
/** | ||
* The return type of the `useSearchParamsState` hook. | ||
*/ | ||
type SearchParamsStateReturnType<T> = Readonly< | ||
[value: T, setValue: (nextValue: React.SetStateAction<T>) => void, clear: () => void] | ||
> | ||
|
||
/** | ||
* Hook that synchronize a state in the URL search params. It returns the value, a setter and a clear function. | ||
* @param key - The key to store the value in the URL search params. | ||
* @param defaultValue - The default value to use if the key is not present in the URL search params. | ||
* @param predicate - A function to check if the value is of the right type. | ||
*/ | ||
export function useSearchParamsState<T = unknown>( | ||
key: string, | ||
defaultValue: T | (() => T), | ||
predicate: (unknown: unknown) => unknown is T = (unknown): unknown is T => true | ||
): SearchParamsStateReturnType<T> { | ||
const [searchParams, setSearchParams] = reactRouterDom.useSearchParams() | ||
|
||
const lazyDefaultValueInitializer = lazyMemo.useLazyMemoHooks(defaultValue, []) | ||
const predicateEventCallback = eventCallback.useEventCallback(predicate) | ||
|
||
const clear = eventCallback.useEventCallback((replace: boolean = false) => { | ||
searchParams.delete(key) | ||
setSearchParams(searchParams, { replace }) | ||
}) | ||
|
||
const rawValue = React.useMemo<T>(() => { | ||
const maybeValue = searchParams.get(key) | ||
const defaultValueFrom = lazyDefaultValueInitializer() | ||
|
||
return maybeValue != null | ||
? safeJsonParse.safeJsonParse(maybeValue, defaultValueFrom, (unknown): unknown is T => true) | ||
: defaultValueFrom | ||
}, [key, lazyDefaultValueInitializer, searchParams]) | ||
|
||
const isValueValid = predicateEventCallback(rawValue) | ||
|
||
const value = isValueValid ? rawValue : lazyDefaultValueInitializer() | ||
|
||
if (!isValueValid) { | ||
clear(true) | ||
} | ||
|
||
/** | ||
* Set the value in the URL search params. If the next value is the same as the default value, it will remove the key from the URL search params. | ||
* Function reference is always the same. | ||
* @param nextValue - The next value to set. | ||
* @returns void | ||
*/ | ||
const setValue = eventCallback.useEventCallback((nextValue: React.SetStateAction<T>) => { | ||
if (nextValue instanceof Function) { | ||
nextValue = nextValue(value) | ||
} | ||
|
||
if (nextValue === lazyDefaultValueInitializer()) { | ||
clear() | ||
} else { | ||
searchParams.set(key, JSON.stringify(nextValue)) | ||
setSearchParams(searchParams) | ||
} | ||
}) | ||
|
||
return [value, setValue, clear] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
/** | ||
* @file useSyncRef.ts | ||
* | ||
* A hook that returns a ref object whose `current` property is always in sync with the provided value. | ||
*/ | ||
|
||
import * as React from 'react' | ||
|
||
/** | ||
* A hook that returns a ref object whose `current` property is always in sync with the provided value. | ||
*/ | ||
export function useSyncRef<T>(value: T): React.MutableRefObject<T> { | ||
const ref = React.useRef(value) | ||
ref.current = value | ||
|
||
return ref | ||
} |
29 changes: 29 additions & 0 deletions
29
app/ide-desktop/lib/dashboard/src/hooks/useLazyMemoHooks.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
/** | ||
* @file | ||
* | ||
* A hook that returns a memoized function that will only be called once | ||
*/ | ||
|
||
import * as React from 'react' | ||
|
||
const UNSET_VALUE = Symbol('unset') | ||
|
||
/** | ||
* A hook that returns a memoized function that will only be called once | ||
*/ | ||
export function useLazyMemoHooks<T>(factory: T | (() => T), deps: React.DependencyList): () => T { | ||
return React.useMemo(() => { | ||
let cachedValue: T | typeof UNSET_VALUE = UNSET_VALUE | ||
|
||
return (): T => { | ||
if (cachedValue === UNSET_VALUE) { | ||
cachedValue = factory instanceof Function ? factory() : factory | ||
} | ||
|
||
return cachedValue | ||
} | ||
// We assume that the callback should change only when | ||
// the deps change. | ||
// eslint-disable-next-line react-hooks/exhaustive-deps | ||
}, deps) | ||
} |
Oops, something went wrong.