Skip to content

Commit

Permalink
refactor(core/inputs): report focusPath on text spans ending with .te…
Browse files Browse the repository at this point in the history
…xt (#5786)

* refactor(form/inputs): PT-input will now report text span focus with paths ending with .text

* fix(core/inputs): compare editor selection focus path to Studio focusPath on same length

This will ensure that any external focusPath is compared with the relevant length of something visible in the editor.
If there is something pointing further into an object, we don't care, because it's only relevant so far regarding setting focus in the editor.

* fix(form/inputs): fix issue where textBlock form member was nont included properly.
  • Loading branch information
skogsmaskin authored Feb 19, 2024
1 parent 15c3bc1 commit 3941d86
Show file tree
Hide file tree
Showing 3 changed files with 31 additions and 7 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import {
PortableTextEditor,
} from '@sanity/portable-text-editor'
import {useTelemetry} from '@sanity/telemetry/react'
import {type PortableTextBlock} from '@sanity/types'
import {isKeySegment, type Path, type PortableTextBlock} from '@sanity/types'
import {Box, useToast} from '@sanity/ui'
import {
type MutableRefObject,
Expand Down Expand Up @@ -146,6 +146,27 @@ export function PortableTextInput(props: PortableTextInputProps) {
}
}, [hasFocusWithin])

const setFocusPathFromEditorSelection = useCallback(
(focusPath: Path) => {
// Report focus on spans with `.text` appended to the reported focusPath.
// This is done to support the Presentation tool which uses this kind of paths to refer to texts.
// The PT-input already supports these paths the other way around.
// It's a bit ugly right here, but it's a rather simple way to support the Presentation tool without
// having to change the PTE's internals.
if (
focusPath.length === 3 &&
focusPath[1] === 'children' &&
isKeySegment(focusPath[2]) &&
!portableTextMemberItems.some(
(item) => isKeySegment(focusPath[2]) && item.key === focusPath[2]._key,
) // Not an inline object
) {
onPathFocus(focusPath.concat('text'))
}
},
[onPathFocus, portableTextMemberItems],
)

// Handle editor changes
const handleEditorChange = useCallback(
(change: EditorChange): void => {
Expand All @@ -165,7 +186,7 @@ export function PortableTextInput(props: PortableTextInputProps) {
// call through startTransition
startTransition(() => {
if (change.selection) {
onPathFocus(change.selection.focus.path)
setFocusPathFromEditorSelection(change.selection.focus.path)
}
})
break
Expand Down Expand Up @@ -193,7 +214,7 @@ export function PortableTextInput(props: PortableTextInputProps) {
default:
}
},
[onBlur, onChange, onPathFocus, toast],
[onBlur, onChange, setFocusPathFromEditorSelection, toast],
)

useEffect(() => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,21 +66,21 @@ const reconcilePortableTextMembers = ({
renderInlineBlock,
renderPreview,
} = props

for (const member of members) {
if (member.kind === 'item') {
const isObjectBlock = !isBlockType(member.item.schemaType)
if (isObjectBlock) {
result.push({kind: 'objectBlock', member, node: member.item})
} else {
// Also include regular text blocks with validation, presence, changes or that are focused by the user.
// Also include regular text blocks with validation, presence, changes or that are open or focused.
// This is a performance optimization to avoid accounting for blocks that
// doesn't need to be re-rendered (which usually is most of the blocks).
if (
member.item.validation.length > 0 ||
member.item.changed ||
member.item.presence?.length ||
member.item.focusPath.length > 0
member.open ||
member.item.focusPath.length
) {
result.push({kind: 'textBlock', member, node: member.item})
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,10 @@ export function useTrackFocusPath(props: Props): void {
}

// Don't do anything if the selection focus path is already equal to the focusPath
if (selection?.focus.path && isEqual(selection.focus.path, focusPath)) {
if (
selection?.focus.path &&
isEqual(selection.focus.path, focusPath.slice(0, selection.focus.path.length))
) {
return
}

Expand Down

0 comments on commit 3941d86

Please sign in to comment.