UNPKG

sanity

Version:

Sanity is a real-time content infrastructure with a scalable, hosted backend featuring a Graph Oriented Query Language (GROQ), asset pipelines and fast edge caches

126 lines (110 loc) 3.81 kB
import { type HotkeyOptions, PortableTextEditor, usePortableTextEditor, usePortableTextEditorSelection, } from '@sanity/portable-text-editor' import { type ObjectSchemaType, type Path, type PortableTextBlock, type PortableTextChild, } from '@sanity/types' import {useCallback, useMemo} from 'react' import {type FIXME} from '../../../../FIXME' import {useTranslation} from '../../../../i18n' import {useUnique} from '../../../../util' import {getPTEToolbarActionGroups} from './helpers' import {type BlockStyleItem, type PTEToolbarAction, type PTEToolbarActionGroup} from './types' export function useFocusBlock(): PortableTextBlock | undefined { const editor = usePortableTextEditor() const selection = usePortableTextEditorSelection() // eslint-disable-next-line react-hooks/exhaustive-deps return useMemo(() => PortableTextEditor.focusBlock(editor), [editor, selection]) // selection must be an additional dep here } export function useFocusChild(): PortableTextChild | undefined { const editor = usePortableTextEditor() const selection = usePortableTextEditorSelection() // eslint-disable-next-line react-hooks/exhaustive-deps return useMemo(() => PortableTextEditor.focusChild(editor), [editor, selection]) // selection must be an additional dep here } export function useActionGroups({ hotkeys, onMemberOpen, resolveInitialValue, disabled, }: { hotkeys: HotkeyOptions onMemberOpen: (relativePath: Path) => void resolveInitialValue: (type: ObjectSchemaType) => FIXME disabled: boolean }): PTEToolbarActionGroup[] { const editor = usePortableTextEditor() const {t} = useTranslation() const handleInsertAnnotation = useCallback( async (schemaType: ObjectSchemaType) => { const initialValue = await resolveInitialValue(schemaType) const paths = PortableTextEditor.addAnnotation(editor, schemaType, initialValue) if (paths && paths.markDefPath) { onMemberOpen(paths.markDefPath) } }, [editor, onMemberOpen, resolveInitialValue], ) return useMemo( () => editor ? getPTEToolbarActionGroups(editor, disabled, handleInsertAnnotation, hotkeys, t) : [], [disabled, editor, handleInsertAnnotation, hotkeys, t], ) } export function useActiveActionKeys({ actions, }: { actions: Array<PTEToolbarAction & {firstInGroup?: true}> }): string[] { const editor = usePortableTextEditor() const selection = usePortableTextEditorSelection() return useUnique( useMemo( () => { const activeAnnotationKeys = PortableTextEditor.activeAnnotations(editor).map( (a) => a._type, ) return actions .filter((a) => { if (a.type === 'annotation') { return activeAnnotationKeys.includes(a.key) } if (a.type === 'listStyle') { return PortableTextEditor.hasListStyle(editor, a.key) } return PortableTextEditor.isMarkActive(editor, a.key) }) .map((a) => a.key) }, // eslint-disable-next-line react-hooks/exhaustive-deps [ editor, // This is needed so that active actions update as `selection` changes selection, ], ), ) } export function useActiveStyleKeys({items}: {items: BlockStyleItem[]}): string[] { const editor = usePortableTextEditor() const focusBlock = useFocusBlock() const selection = usePortableTextEditorSelection() return useUnique( useMemo( () => items.filter((i) => PortableTextEditor.hasBlockStyle(editor, i.style)).map((i) => i.style), // eslint-disable-next-line react-hooks/exhaustive-deps [ focusBlock, // This is needed so that active styles update as `selection` changes selection, ], ), ) }