From 36dc57aa803ed3e17b7b0e279c5493957d020752 Mon Sep 17 00:00:00 2001
From: Cody Olsen <81981+stipsan@users.noreply.github.com>
Date: Fri, 20 Dec 2024 15:10:45 +0100
Subject: [PATCH] fix: preload documents on hover (#8110)
---
package.json | 2 +-
.../src/structure/components/pane/Pane.tsx | 4 +-
.../components/paneItem/PaneItem.tsx | 41 ++++++++++++++-----
3 files changed, 34 insertions(+), 13 deletions(-)
diff --git a/package.json b/package.json
index 5f606446886..cfdb5257d10 100644
--- a/package.json
+++ b/package.json
@@ -17,7 +17,7 @@
"check:deps": "pnpm --recursive --parallel exec depcheck",
"check:format": "prettier . --check",
"check:lint": "turbo run lint --continue -- --quiet",
- "check:react-compiler": "eslint --cache --no-inline-config --no-eslintrc --ext .cjs,.mjs,.js,.jsx,.ts,.tsx --parser @typescript-eslint/parser --plugin react-compiler --rule 'react-compiler/react-compiler: [warn]' --ignore-path .eslintignore.react-compiler --max-warnings 27 .",
+ "check:react-compiler": "eslint --cache --no-inline-config --no-eslintrc --ext .cjs,.mjs,.js,.jsx,.ts,.tsx --parser @typescript-eslint/parser --plugin react-compiler --rule 'react-compiler/react-compiler: [warn]' --ignore-path .eslintignore.react-compiler --max-warnings 26 .",
"report:react-compiler-bailout": "eslint --cache --no-inline-config --no-eslintrc --ext .cjs,.mjs,.js,.jsx,.ts,.tsx --parser @typescript-eslint/parser --plugin react-compiler --rule 'react-compiler/react-compiler: [error,{__unstable_donotuse_reportAllBailouts:true}]' --ignore-path .eslintignore.react-compiler -f ./scripts/reactCompilerBailouts.cjs . || true",
"check:test": "run-s test -- --silent",
"check:types": "tsc && turbo run check:types --filter='./packages/*' --filter='./packages/@sanity/*'",
diff --git a/packages/sanity/src/structure/components/pane/Pane.tsx b/packages/sanity/src/structure/components/pane/Pane.tsx
index 2305019a010..e02f81b24b7 100644
--- a/packages/sanity/src/structure/components/pane/Pane.tsx
+++ b/packages/sanity/src/structure/components/pane/Pane.tsx
@@ -5,8 +5,8 @@ import {
type HTMLProps,
type ReactNode,
useCallback,
- useEffect,
useImperativeHandle,
+ useLayoutEffect,
useMemo,
useRef,
useState,
@@ -89,7 +89,7 @@ export const Pane = forwardRef(function Pane(
ref.current = refValue
}, [])
- useEffect(() => {
+ useLayoutEffect(() => {
if (!rootElement) return undefined
return mount(rootElement, {
currentMinWidth: currentMinWidthProp,
diff --git a/packages/sanity/src/structure/components/paneItem/PaneItem.tsx b/packages/sanity/src/structure/components/paneItem/PaneItem.tsx
index ddaa8d220be..862c0c27354 100644
--- a/packages/sanity/src/structure/components/paneItem/PaneItem.tsx
+++ b/packages/sanity/src/structure/components/paneItem/PaneItem.tsx
@@ -9,10 +9,11 @@ import {Box, type CardProps, Text} from '@sanity/ui'
import {
type ComponentType,
type MouseEvent,
- type ReactNode,
+ startTransition,
useCallback,
useEffect,
useMemo,
+ useRef,
useState,
} from 'react'
import {
@@ -22,6 +23,7 @@ import {
SanityDefaultPreview,
useDocumentPresence,
useDocumentPreviewStore,
+ useEditState,
useSchema,
} from 'sanity'
@@ -124,14 +126,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 +138,31 @@ 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 [preloading, setPreload] = useState(false)
+ const timeoutRef = useRef | null>(null)
+ const handleMouseEnter = useCallback(() => {
+ timeoutRef.current = setTimeout(() => startTransition(() => setPreload(true)), 400)
+ }, [])
+ const handleMouseLeave = useCallback(() => {
+ if (timeoutRef.current) clearTimeout(timeoutRef.current)
+ startTransition(() => setPreload(false))
+ }, [])
+
return (
{preview}
+ {preloading && schemaType?.name && value && isSanityDocument(value) && (
+
+ )}
)
}
+
+function PreloadDocumentPane(props: {documentId: string; documentType: string}) {
+ const {documentId, documentType} = props
+ // Preload the edit state for the document, and keep it alive until mouse leave
+ useEditState(documentId, documentType)
+
+ return null
+}
+PreloadDocumentPane.displayName = 'PreloadDocumentPane'