diff --git a/packages/sanity/package.json b/packages/sanity/package.json index 4d86ff71162..6c2754b2658 100644 --- a/packages/sanity/package.json +++ b/packages/sanity/package.json @@ -132,7 +132,7 @@ "build": "pkg-utils build --tsconfig tsconfig.lib.json", "postbuild": "run-s check:package", "check:package": "pkg-utils --tsconfig tsconfig.lib.json", - "clean": "rimraf _internal.js cli.js desk.js lib router.js", + "clean": "rimraf _internal.js cli.js desk.js lib router.js scratchPad.js", "coverage": "jest --coverage", "prepublishOnly": "run-s write:version write:ui-version", "test": "pkg-utils --strict && jest", diff --git a/packages/sanity/src/core/form/inputs/PortableText/PortableTextInput.tsx b/packages/sanity/src/core/form/inputs/PortableText/PortableTextInput.tsx index c9c1b11e4fa..fb58c9e9276 100644 --- a/packages/sanity/src/core/form/inputs/PortableText/PortableTextInput.tsx +++ b/packages/sanity/src/core/form/inputs/PortableText/PortableTextInput.tsx @@ -235,6 +235,9 @@ export function PortableTextInput(props: PortableTextInputProps) { // Handle editor changes const handleEditorChange = useCallback( (change: EditorChange): void => { + if (editorRef.current && onEditorChange) { + onEditorChange(change, editorRef.current) + } switch (change.type) { case 'mutation': onChange(toFormPatches(change.patches)) @@ -279,7 +282,7 @@ export function PortableTextInput(props: PortableTextInputProps) { default: } }, - [onBlur, onChange, onPathFocus, toast], + [onBlur, onChange, onEditorChange, onPathFocus, toast], ) useEffect(() => { diff --git a/packages/sanity/src/core/form/inputs/PortableText/object/Annotation.tsx b/packages/sanity/src/core/form/inputs/PortableText/object/Annotation.tsx index 569ae9460a0..191415394c2 100644 --- a/packages/sanity/src/core/form/inputs/PortableText/object/Annotation.tsx +++ b/packages/sanity/src/core/form/inputs/PortableText/object/Annotation.tsx @@ -278,7 +278,6 @@ export const DefaultAnnotationComponent = (props: BlockAnnotationProps) => { } return 'default' }, [isLink, hasError, hasWarning]) - return ( field?: ComponentType inlineBlock?: ComponentType - input?: ComponentType + input?: ComponentType item?: ComponentType preview?: ComponentType } diff --git a/packages/sanity/src/core/form/types/inputProps.ts b/packages/sanity/src/core/form/types/inputProps.ts index 7292aee13b4..cdabbf268fd 100644 --- a/packages/sanity/src/core/form/types/inputProps.ts +++ b/packages/sanity/src/core/form/types/inputProps.ts @@ -20,6 +20,7 @@ import { HotkeyOptions, OnCopyFn, OnPasteFn, + PortableTextEditor, RangeDecoration, } from '@sanity/portable-text-editor' import {FormPatch, PatchEvent} from '../patch' @@ -497,7 +498,7 @@ export interface PortableTextInputProps /** * Returns changes from the underlying editor */ - onEditorChange?: (change: EditorChange) => void + onEditorChange?: (change: EditorChange, editor: PortableTextEditor) => void /** * Custom copy function */ diff --git a/packages/sanity/src/scratchPad/components/editor/Editable.tsx b/packages/sanity/src/scratchPad/components/editor/Editable.tsx index 45f80731c8c..0704b339a0a 100644 --- a/packages/sanity/src/scratchPad/components/editor/Editable.tsx +++ b/packages/sanity/src/scratchPad/components/editor/Editable.tsx @@ -1,6 +1,8 @@ -import React, {PropsWithChildren, useCallback, useEffect, useMemo, useRef, useState} from 'react' -import {Stack} from '@sanity/ui' +import React, {PropsWithChildren, useCallback, useEffect, useMemo, useRef} from 'react' +import {BoundaryElementProvider, Stack} from '@sanity/ui' import { + BlockAnnotationRenderProps, + BlockChildRenderProps, BlockDecoratorRenderProps, BlockRenderProps, BlockStyleRenderProps, @@ -12,17 +14,18 @@ import { } from '@sanity/portable-text-editor' import styled, {css} from 'styled-components' import {debounce} from 'lodash' -import isHotkey from 'is-hotkey' +import {PortableTextTextBlock} from '@sanity/types' import {useScratchPad} from '../../hooks/useScratchPad' import {ScratchPadContextValue} from '../../context/ScratchPadProvider' import {PortableTextInputProps} from '../../../core' -import {TextBlock} from '../rendering/renderBlock' import {Style} from '../../../core/form/inputs/PortableText/text/Style' import {Decorator} from '../../../core/form/inputs/PortableText/text' +import {Annotation} from '../../../core/form/inputs/PortableText/object/Annotation' +import {InlineObject} from '../../../core/form/inputs/PortableText/object/InlineObject' +import {TextBlock} from '../../../core/form/inputs/PortableText/text/TextBlock' import {AssistanceRange} from './AssistanceRange' const INLINE_STYLE: React.CSSProperties = {outline: 'none'} -const MODIFIER_KEY = 'Alt+Control' const EditableWrapStack = styled(Stack)(() => { return css` @@ -48,7 +51,6 @@ export function Editable(props: EditableProps) { const editableRef = useRef(null) const editor = usePortableTextEditor() const editorSelection = usePortableTextEditorSelection() - const [modifierKeyPressed, setIsModifierKeyPressed] = useState(false) const {onEditorBeforeInput, onAssistanceRangeSelect, assistanceSelection, editorFocused} = useScratchPad() @@ -68,47 +70,69 @@ export function Editable(props: EditableProps) { const renderPlaceholder = useCallback(() => {placeholder}, [placeholder]) - const handleSetAssistantRangeKey = useCallback((event: React.KeyboardEvent) => { - if (isHotkey(MODIFIER_KEY, event)) { - setIsModifierKeyPressed(true) - } - }, []) - const handleKeyDown = useCallback( (event: React.KeyboardEvent) => { - handleSetAssistantRangeKey(event) if (onKeyDown) { onKeyDown(event) } }, - [handleSetAssistantRangeKey, onKeyDown], + [onKeyDown], ) - const handleKeyUp = useCallback(() => { - setIsModifierKeyPressed(false) - }, []) - - // Update the assistance selection when user is done selecting something, and modifier key is pressed + // Update the assistance selection when user is 'done' selecting something useEffect(() => { createAssistSelectionDebounced(editor, onAssistanceRangeSelect) - }, [editor, modifierKeyPressed, onAssistanceRangeSelect]) + }, [editor, onAssistanceRangeSelect, editorSelection]) - // These render functions piggybacks on the PT-input's rendering components + // These render functions starting with a underscore piggybacks on the PT-input's rendering components const _renderBlock = useCallback( (editorRenderProps: BlockRenderProps) => { - if (!rootElementRef.current) { - return <> - } return ( + floatingBoundary={editorRenderProps.editorElementRef.current} + focused={editorRenderProps.focused} + isFullscreen + // eslint-disable-next-line react/jsx-handler-names + onItemClose={formProps.onItemClose} + // eslint-disable-next-line react/jsx-handler-names + onItemOpen={formProps.onItemOpen} + // eslint-disable-next-line react/jsx-handler-names + onItemRemove={formProps.onItemRemove} + // eslint-disable-next-line react/jsx-handler-names + onPathFocus={formProps.onPathFocus} + path={formProps.path.concat(editorRenderProps.path)} + readOnly={formProps.readOnly} + referenceBoundary={rootElementRef.current} + renderAnnotation={formProps.renderAnnotation} + renderField={formProps.renderField} + renderInlineBlock={formProps.renderInlineBlock} + renderInput={formProps.renderInput} + renderItem={formProps.renderItem} + renderPreview={formProps.renderPreview} + renderBlock={formProps.renderBlock} + schemaType={editorRenderProps.schemaType} + selected={editorRenderProps.selected} + value={editorRenderProps.value as PortableTextTextBlock} + > + {editorRenderProps.children} + ) }, - [formProps], + [ + formProps.onItemClose, + formProps.onItemOpen, + formProps.onItemRemove, + formProps.onPathFocus, + formProps.path, + formProps.readOnly, + formProps.renderAnnotation, + formProps.renderBlock, + formProps.renderField, + formProps.renderInlineBlock, + formProps.renderInput, + formProps.renderItem, + formProps.renderPreview, + ], ) const _renderDecorator = useCallback((editorRenderProps: BlockDecoratorRenderProps) => { @@ -119,24 +143,125 @@ export function Editable(props: EditableProps) { return