Create a showcase for a controlled editor
See original GitHub issueDescription
I mentioned in https://github.com/ifiokjr/remirror/issues/23 that I was attempting to try my hand at creating a controlled editor, and looking for guidance from the documentation. I’m going to post my code below to see if this is either helpful for people on a similar path to me, and also see if anyone who has been down this path can help.
My usecase is that I’d like the user to be able to click a button outside of the editor, and the editor to respond with an updated value. Essentially, an HTML string should be turned into a state object for prose mirror.
Blocker
This code is a work in progress, and is not yet functional.
I am getting this error:
Uncaught TypeError: _this.state.newState.applyTransaction is not a function
at EditorView.<anonymous> (react.esm.js:608)
at EditorView.dispatch (index.js:4637)
at readDOMChange (index.js:2576)
at DOMObserver.handleDOMChange (index.js:3013)
at DOMObserver.flush (index.js:2959)
at MutationObserver.DOMObserver.observer (index.js:2874)
Proposed
I’ve based this code off of the markdown/wysiwg editor showcase.
import { logger } from '@myOrg/utils';
import {
createDocumentNode,
EditorState,
ProsemirrorNode,
} from '@remirror/core';
import { BoldExtension, LinkExtension } from '@remirror/core-extensions';
import {
ManagedRemirrorProvider,
RemirrorExtension,
RemirrorManager,
RemirrorManagerProps,
RemirrorProviderProps,
useRemirrorManager,
} from '@remirror/react';
import React, { FC, ReactElement, useEffect } from 'react';
import { useSetState } from 'react-hanger';
import { MirrorEditorInner } from './MirrorEditorInner';
export type InternalEditorProps = Omit<
RemirrorProviderProps,
'childAsRoot' | 'children'
>;
interface IMirrorEditorProps {
value: string;
onChange: (val: string) => void;
disabled?: boolean;
}
const defaultPlaceholder: RemirrorManagerProps['placeholder'] = [
'Start typing...',
{
color: '#aaa',
fontStyle: 'normal',
fontWeight: 300,
letterSpacing: '0.5px',
position: 'absolute',
},
];
interface IMirrorEditorProviderProps extends IMirrorEditorProps {
children: ReactElement;
}
const MirrorEditorProvider: React.FC<IMirrorEditorProviderProps> = ({
children,
value,
disabled,
onChange,
...props
}) => {
const { schema } = useRemirrorManager();
const initalContent = createDocumentNode({ content: value, schema });
const raw = useSetState<ProsemirrorNode>(initalContent);
const editor = useSetState<EditorState<any>>(null);
useEffect(() => {
editor.setState({
...editor.state,
doc: createDocumentNode({ content: value, schema }),
});
}, [value]);
return (
<>
<ManagedRemirrorProvider
onStateChange={({ newState, getText }) => {
logger.info('onStateChange', { text: getText() });
editor.setState(newState);
raw.setState(newState.doc);
onChange(getText());
}}
value={editor.state}
initialContent={raw.state}
withoutEmotion
editable={!disabled}
>
{children}
</ManagedRemirrorProvider>
</>
);
};
export const MirrorEditor: FC<IMirrorEditorProps> = props => {
return (
<RemirrorManager placeholder={defaultPlaceholder}>
<RemirrorExtension Constructor={BoldExtension} />
<RemirrorExtension Constructor={LinkExtension} />
<MirrorEditorProvider {...props}>
<MirrorEditorInner />
</MirrorEditorProvider>
</RemirrorManager>
);
};
import { faBold } from '@fortawesome/pro-light-svg-icons';
import { logger, Button } from '@myOrg/utils';
import { useRemirror } from '@remirror/react';
import React, { FC } from 'react';
export const MirrorEditorInner: FC<IProps> = ({}) => {
const { getRootProps, actions } = useRemirror();
return (
<div className="mirror-outer">
<div className="mirror-menu">
<Button
active={actions.bold.isActive()}
disabled={!actions.bold.isEnabled()}
onClick={() => {
actions.bold.command();
}}
icon={faBold}
/>
</div>
<div {...getRootProps()} className="mirror-root" />
</div>
);
};
–
I will throw this into one of those great code sandbox tools out there so that others can reproduce.
Issue Analytics
- State:
- Created 4 years ago
- Comments:28 (27 by maintainers)
I had some time today to look at the controlled editor and I think I’ve managed to fix all the bugs mentioned above. You can watch my progress in #149
There’s still a fair bit of work to go into that branch, but the markdown editor will be part of the next major release.
No problem at all. We’re hoping to use Remirror using markdown as the interchange format in our app. Previously we were using DraftJS and had a custom markup (inspired by Slack’s markup) for simple text that just had tags/mentions/etc, and we were just storing the DraftJS content object for everything else. We’d like to use Markdown as the interchange format, whilst still allowing features like collaboration - the plan is to use Prosemirror with markdown import/export and to use raw prosemirror transactions with the collaboration plugin when we need that. At this point we’re still experimenting with remirror (which seems very promising, and certainly beats us building our own bundle of prosemirror which I had previously started on), and are keen to help get it to production-readiness for our needs.