ianstormtaylor / slate

A completely customizable framework for building rich text editors. (Currently in beta.)
http://slatejs.org
MIT License
30.03k stars 3.26k forks source link

Allow access to `EditorContext` #3460

Open lcswillems opened 4 years ago

lcswillems commented 4 years ago

Feature

We can't access the EditorContext defined here: https://github.com/ianstormtaylor/slate/blob/master/packages/slate-react/src/hooks/use-editor.tsx#L9

It would be nice to export it here: https://github.com/ianstormtaylor/slate/blob/master/packages/slate-react/src/index.ts

Doing so would allow to separate the toolbar from the editable. In my website, the toolbar is in the header while the editable is lower in the tree.

thesunny commented 4 years ago

I believe the method you are looking for is useSlate and returns the editor instance.

I have used it to keep a separate toolbar. I believe it is exported from slate-react.

cameracker commented 4 years ago

Hey @lcswillems , I'm going to close this issue because I'm pretty sure @thesunny gave the correct answer. If you feel otherwise, please let me know and I can reopen the issue.

lcswillems commented 4 years ago

No, @thesunny doesn't answer correctly.

To be able to use useSlate in a component, this component must be wrapped in EditorContext.Provider but I can't wrap my component in EditorContext.Provider because it is not exported.

I think it should be reopened.

thesunny commented 4 years ago

@lcswillems,

So, I've been using useSlate successfully so it does work. Unless the API has changed (possible), you wrap it in a Slate Component something like this (note: demo quality code):

import {Slate} from 'slate-react'

function MyApp() {
  const editor = useMemo(() => {
    const editor = withHistory(withReact(createEditor()))
    editor.isVoid = isVoid
    editor.isInline = isInline
    return editor
  }, [])
  const [value, setValue] = useState(() => DEFAULT_EDITOR_STATE)
  return (
    <Slate editor={editor} value={value} onChange={setValue}>
        <Toolbar/>
        <Editor readOnly={false} />
      </Body>
    </Slate>
  )
}

Inside the Toolbar component, you should be able to use useSlate to get the editor instance.

Edited to clarify editor definition in code

timbuckley commented 4 years ago

Per @thesunny, it is certainly possible to use the Slate component to get access to the editor state via React context.

But, per @lcswillems, if you want to provide it differently, and with a different specific implementation than one provided inside Slate, but which also consumes it via useEditor, you can't, since the EditorContext itself is not exposed from the Slate package. Exposing it would allow consumers to write their own version of the Slate provider component, while still preserving usage of the useEditor hook.

I think exposing the EditorContext from Slate is a good idea. In the meantime, perhaps you can just set the Slate provider component higher up in the React tree, @lcswillems ?

thesunny commented 4 years ago

@timbuckley It's possible I'm misinterpreting and my apologies if I am, but it reads like @lcswillems is not aware of using <Slate ...> to pass in context for useSlate based on the wording of his post, not that he specifically needs the EditorContext.

I base this on his original use case: "Doing so would allow to separate the toolbar from the editable. In my website, the toolbar is in the header while the editable is lower in the tree." which seems to be the use case for <Slate ...> if I recall correctly. I believe Ian made the change to support this.

With respect to the idea you presented that there may be a value for providing EditorContext outside of <Slate ...> I think that's possible although I have to admit I don't quite understand the use case.

Separately, I noticed that my "demo" quality code didn't express the createEditor. I just cut and pasted it from my code where I had it in a separate module. I'll go back and fix that in my previous post to help clarity.

thesunny commented 4 years ago

Okay, was trying to wrap my mind around the code and after doing a bit of a dive, it appears that useSlate and useEditor both require <Slate ...> in the current implementation in order to create the context.

<Slate ...> provides EditorContext, SlateContext and FocusedContext as well as a bunch of other code designed to get them to work together.

https://github.com/ianstormtaylor/slate/blob/0bbe121d76c5c2313d939de8a7ebed3bfd37f62d/packages/slate-react/src/components/slate.tsx

It also appears that useSlate and useEditor return the same object so it's not that useEditor gives you something different than useSlate:

https://github.com/ianstormtaylor/slate/blob/0bbe121d76c5c2313d939de8a7ebed3bfd37f62d/packages/slate-react/src/hooks/use-editor.tsx

https://github.com/ianstormtaylor/slate/blob/0bbe121d76c5c2313d939de8a7ebed3bfd37f62d/packages/slate-react/src/hooks/use-slate.tsx

The difference is that useSlate causes a re-render when changes occur.

I'm not sure that exposing EditorContext is what we want at this time. If we did, we'd have to also expose SlateContext and FocusedContext and the custom version of the provider would also have to rebuild the wire up code which looks complicated and doesn't seem something that's designed to be customized (at this time anyways).