Description
Slatejs editor only in Firefox fails with text selection and caret positioning (by mouse). It always selects position before first character (path[0] offset[0]). You can still use the keyboard to select text and position caret.
Recording
Expectation
It should select text and position caret as it does in other browsers.
Environment
- Slate Version: 0.63.0
- Operating System: Windows 10
- Browser: Firefox 90.0.1 (64-bit)
Editor implementation for reference
import React, { useCallback, useMemo, useState } from "react"; import { BaseEditor, createEditor, Descendant, Editor, Element as SlateElement, Node, Text, Transforms } from "slate"; import { Slate, Editable, ReactEditor, withReact } from "slate-react"; import { HistoryEditor, withHistory } from "slate-history"; export type CustomEditor = BaseEditor & ReactEditor & HistoryEditor export type ParagraphElement = { type: 'paragraph' children: CustomText[] } export type TitleElement = { type: "title" children: CustomText[] } type CustomElement = ParagraphElement | TitleElement; type FormattedText = { text: string, bold?: true }; type CustomText = FormattedText; declare module "slate" { interface CustomTypes { Editor: CustomEditor Element: CustomElement Text: FormattedText } } //////////////////////////////////// // Custom helpers //////////////////////////////////// const customEditor = { isBoldMarkActive(editor: CustomEditor) { const [match] = Editor.nodes(editor, { match: (n: any) => n.bold === true, universal: true, }) return !!match }, isTitleActive(editor: CustomEditor) { const [match] = Editor.nodes(editor, { match: (n: any) => n.type === "title", }) return !!match }, toggleBoldMark(editor: CustomEditor) { const isActive = customEditor.isBoldMarkActive(editor) Transforms.setNodes( editor, { bold: isActive ? undefined : true }, { match: n => Text.isText(n), split: true } ) }, toggleTitle(editor: CustomEditor) { const isActive = customEditor.isTitleActive(editor) Transforms.setNodes( editor, { type: isActive ? undefined : "title" }, { match: n => Editor.isBlock(editor, n) } ) }, } //////////////////////////////////// // Forced layout setup - title + paragraph //////////////////////////////////// const withLayout = (editor: CustomEditor) => { const { normalizeNode } = editor editor.normalizeNode = ([node, path]) => { if (path.length === 0) { if (editor.children.length < 1) { const title: TitleElement = { type: "title", children: [{ text: 'Untitled' }], } Transforms.insertNodes(editor, title, { at: path.concat(0) }) } if (editor.children.length < 2) { const paragraph: ParagraphElement = { type: 'paragraph', children: [{ text: '' }], } Transforms.insertNodes(editor, paragraph, { at: path.concat(1) }) } for (const [child, childPath] of Node.children(editor, path)) { const type = childPath[0] === 0 ? "title" : 'paragraph' if (SlateElement.isElement(child) && child.type !== type) { const newProperties: Partial<SlateElement> = { type } Transforms.setNodes(editor, newProperties, { at: childPath }) } } } return normalizeNode([node, path]); } return editor; } //////////////////////////////////// const TextEditor = () => { const initialValue: Descendant[] = [ { type: 'title', children: [{ text: 'Enter a title...' }], }, { type: 'paragraph', children: [{ text: 'Enter your question'}] } ]; const editor = useMemo(() => withLayout(withHistory(withReact(createEditor()))), []); Transforms.deselect(editor); const [value, setValue] = useState<Descendant[]>(initialValue); const renderElement = useCallback((props) => <Element {...props} />, []) // Define a leaf rendering function that is memoized with `useCallback`. const renderLeaf = useCallback((props) => { return <Leaf {...props} /> }, []); return ( // Add the editable component inside the context. <Slate editor={editor} value={value} onChange={(value) => setValue(value)} > <div> <button onMouseDown={event => { event.preventDefault() customEditor.toggleBoldMark(editor) }} > B </button> <button onMouseDown={event => { event.preventDefault() customEditor.toggleTitle(editor) }} > H2 </button> </div> <Editable renderElement={renderElement} autoFocus renderLeaf={renderLeaf} /> </Slate> ) } export default TextEditor // Define a React component to render leaves with bold text. const Leaf = (props: any) => { return ( <span {...props.attributes} style={{ fontWeight: props.leaf.bold ? 'bold' : 'normal' }} > {props.children} </span> ) } const Element = ({ attributes, children, element }: any) => { switch (element.type) { case 'title': return <h2 {...attributes}>{children}</h2> case 'paragraph': return <p {...attributes}>{children}</p> default: return <p {...attributes}>{children}</p> } }
Any ideas what might have caused it?
Advertisement
Answer
Dumb mistake. My CSS reset file contained: “user-select: none”, which was somehow ignored by Chrome.