Skip to content

Commit

Permalink
fix(portable-text-editor): allow user event handlers on Editable comp…
Browse files Browse the repository at this point in the history
…onent (#5058)

* fix(portable-text-editor): allow overriding onKeyDown, onFocus, onBlur for Editable

These props should be able to be overridden and potentially stopped before performing our internal behaviour.

* fix(portable-text-editor): specify InputEvent event type for TS-type
  • Loading branch information
skogsmaskin committed Oct 24, 2023
1 parent 5233f35 commit 441f637
Show file tree
Hide file tree
Showing 2 changed files with 42 additions and 20 deletions.
60 changes: 41 additions & 19 deletions packages/@sanity/portable-text-editor/src/editor/Editable.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import {BaseRange, Transforms, Text} from 'slate'
import React, {useCallback, useMemo, useEffect, forwardRef, useState} from 'react'
import React, {useCallback, useMemo, useEffect, forwardRef, useState, KeyboardEvent} from 'react'
import {
Editable as SlateEditable,
ReactEditor,
Expand All @@ -12,7 +12,6 @@ import {PortableTextBlock} from '@sanity/types'
import {
EditorChange,
EditorSelection,
OnBeforeInputFn,
OnCopyFn,
OnPasteFn,
OnPasteResult,
Expand Down Expand Up @@ -54,10 +53,10 @@ const EMPTY_DECORATORS: BaseRange[] = []
*/
export type PortableTextEditableProps = Omit<
React.TextareaHTMLAttributes<HTMLDivElement>,
'onPaste' | 'onCopy'
'onPaste' | 'onCopy' | 'onBeforeInput'
> & {
hotkeys?: HotkeyOptions
onBeforeInput?: OnBeforeInputFn
onBeforeInput?: (event: InputEvent) => void
onPaste?: OnPasteFn
onCopy?: OnCopyFn
renderAnnotation?: RenderAnnotationFunction
Expand All @@ -76,11 +75,14 @@ export type PortableTextEditableProps = Omit<
* @public
*/
export const PortableTextEditable = forwardRef(function PortableTextEditable(
props: PortableTextEditableProps & Omit<React.HTMLProps<HTMLDivElement>, 'as' | 'onPaste'>,
props: PortableTextEditableProps &
Omit<React.HTMLProps<HTMLDivElement>, 'as' | 'onPaste' | 'onBeforeInput'>,
forwardedRef: React.ForwardedRef<HTMLDivElement>,
) {
const {
hotkeys,
onBlur,
onFocus,
onBeforeInput,
onPaste,
onCopy,
Expand Down Expand Up @@ -294,29 +296,39 @@ export const PortableTextEditable = forwardRef(function PortableTextEditable(

const handleOnFocus: React.FocusEventHandler<HTMLDivElement> = useCallback(
(event) => {
const selection = PortableTextEditor.getSelection(portableTextEditor)
change$.next({type: 'focus', event})
const newSelection = PortableTextEditor.getSelection(portableTextEditor)
// If the selection is the same, emit it explicitly here as there is no actual onChange event triggered.
if (selection === newSelection) {
change$.next({
type: 'selection',
selection,
})
if (onFocus) {
onFocus(event)
}
if (!event.isDefaultPrevented()) {
const selection = PortableTextEditor.getSelection(portableTextEditor)
change$.next({type: 'focus', event})
const newSelection = PortableTextEditor.getSelection(portableTextEditor)
// If the selection is the same, emit it explicitly here as there is no actual onChange event triggered.
if (selection === newSelection) {
change$.next({
type: 'selection',
selection,
})
}
}
},
[change$, portableTextEditor],
[change$, portableTextEditor, onFocus],
)

const handleOnBlur: React.FocusEventHandler<HTMLDivElement> = useCallback(
(event) => {
change$.next({type: 'blur', event})
if (onBlur) {
onBlur(event)
}
if (!event.isPropagationStopped()) {
change$.next({type: 'blur', event})
}
},
[change$],
[change$, onBlur],
)

const handleOnBeforeInput = useCallback(
(event: Event) => {
(event: InputEvent) => {
if (onBeforeInput) {
onBeforeInput(event)
}
Expand Down Expand Up @@ -354,7 +366,17 @@ export const PortableTextEditable = forwardRef(function PortableTextEditable(
}
}, [handleDOMChange, ref])

const handleKeyDown = slateEditor.pteWithHotKeys
const handleKeyDown = useCallback(
(event: KeyboardEvent<HTMLDivElement>) => {
if (props.onKeyDown) {
props.onKeyDown(event)
}
if (!event.isDefaultPrevented()) {
slateEditor.pteWithHotKeys(event)
}
},
[props, slateEditor],
)

const scrollSelectionIntoViewToSlate = useMemo(() => {
// Use slate-react default scroll into view
Expand Down
2 changes: 1 addition & 1 deletion packages/@sanity/portable-text-editor/src/types/editor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -362,7 +362,7 @@ export interface PasteData {
export type OnPasteFn = (data: PasteData) => OnPasteResultOrPromise

/** @beta */
export type OnBeforeInputFn = (event: Event) => void
export type OnBeforeInputFn = (event: InputEvent) => void

/** @beta */
export type OnCopyFn = (
Expand Down

2 comments on commit 441f637

@vercel
Copy link

@vercel vercel bot commented on 441f637 Oct 24, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

performance-studio – ./

performance-studio-git-next.sanity.build
performance-studio.sanity.build

@vercel
Copy link

@vercel vercel bot commented on 441f637 Oct 24, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

test-studio – ./

test-studio-git-next.sanity.build
test-studio.sanity.build

Please sign in to comment.