From 01e66888f6dc31c752be0ba08ae09925d50acba1 Mon Sep 17 00:00:00 2001 From: Cody Olsen Date: Thu, 19 Dec 2024 13:23:42 +0100 Subject: [PATCH] fix: preload documents on hover --- .../components/paneItem/PaneItem.tsx | 64 ++++++++++++++++--- 1 file changed, 55 insertions(+), 9 deletions(-) diff --git a/packages/sanity/src/structure/components/paneItem/PaneItem.tsx b/packages/sanity/src/structure/components/paneItem/PaneItem.tsx index ddaa8d220be..0a9e3bdf45d 100644 --- a/packages/sanity/src/structure/components/paneItem/PaneItem.tsx +++ b/packages/sanity/src/structure/components/paneItem/PaneItem.tsx @@ -10,9 +10,11 @@ import { type ComponentType, type MouseEvent, type ReactNode, + startTransition, useCallback, useEffect, useMemo, + useReducer, useState, } from 'react' import { @@ -22,6 +24,7 @@ import { SanityDefaultPreview, useDocumentPresence, useDocumentPreviewStore, + useEditState, useSchema, } from 'sanity' @@ -124,14 +127,6 @@ export function PaneItem(props: PaneItemProps) { documentPresence, ]) - const Link = useMemo( - () => - function LinkComponent(linkProps: {children: ReactNode}) { - return - }, - [ChildLink, id], - ) - const handleClick = useCallback((e: MouseEvent) => { if (e.metaKey) { setClicked(false) @@ -144,16 +139,22 @@ export function PaneItem(props: PaneItemProps) { // Reset `clicked` state when `selected` prop changes useEffect(() => setClicked(false), [selected]) + // Preloads the edit state on hover, using concurrent rendering with `startTransition` so preloads can be interrupted and not block rendering + const [handleMouseEnter, preload] = usePreloadEditState(id, schemaType?.name) + return ( {preview} + {preload} ) } + +function usePreloadEditState( + documentId: string, + documentType: string | undefined, +): [() => void, ReactNode] { + const [preloading, preload] = useReducer(() => true, false) + const handleMouseEnter = useCallback(() => startTransition(preload), []) + + return [ + handleMouseEnter, + preloading && documentType && ( + + ), + ] as const +} + +function PreloadDocumentPane(props: {documentId: string; documentType: string}) { + const {documentId, documentType} = props + const [loading, loaded] = useReducer(() => false, true) + const handleReady = useCallback(() => startTransition(loaded), []) + + if (loading) { + return ( + + ) + } + + return null +} +PreloadDocumentPane.displayName = 'PreloadDocumentPane' + +function PreloadEditState(props: {documentId: string; documentType: string; onReady: () => void}) { + const {onReady, documentId, documentType} = props + const editState = useEditState(documentId, documentType) + + useEffect(() => { + if (editState.ready) { + onReady() + } + }, [editState.ready, onReady]) + + return null +} +PreloadEditState.displayName = 'PreloadEditState'