Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

contenteditable div #193

Closed
stevemk14ebr opened this issue Jun 24, 2021 · 11 comments
Closed

contenteditable div #193

stevemk14ebr opened this issue Jun 24, 2021 · 11 comments

Comments

@stevemk14ebr
Copy link
Contributor

Please allow the user to replace the textarea element with a user-given html element. This is required for drag and drop interfaces that want to drop into the mdeditor. Since textarea's cannot have children html elements other than text, the necessary hacky logic of putting spans between words to find the drop cursor location cannot be performed. With a contenteditable div the text can be mixed with html elements who's client position can be known (completing drag and drop logic).

@jaywcjlove
Copy link
Member

jaywcjlove commented Jun 25, 2021

@stevemk14ebr

<MDEditor
  highlightEnable={false}
  renderTextarea={(props, { dispatch, onChange }) => {
    return (
      <textarea {...props} onChange={(e) => {
        dispatch && dispatch({ markdown: e.target.value });
        onChange && onChange(e.target.value);
      }}/>
    )
  }}
/>

@jaywcjlove
Copy link
Member

.w-md-editor-text-input {
  ....
  /* overflow: hidden; */
  /* -webkit-text-fill-color: transparent; */
}

@jaywcjlove
Copy link
Member

@stevemk14ebr Upgrade @uiw/react-md-editor v3.4.0

@stevemk14ebr
Copy link
Contributor Author

That was fast thanks! I have issues when using a div instead of a textarea though:

const target = e.target as HTMLTextAreaElement;

throws with error 'can't access property "substr", target.value is undefined'

renderTextarea={(props, { dispatch, onChange }) => {
                    return (
                        <div {...props} spellcheck={true} contentEditable={true} onChange={(e) => {
                            dispatch && dispatch({ markdown: e.target.value });
                            onChange && onChange(e.target.value);
                        }}
                        />
                    )
                }}

I also think that the textareaProps may not be being passed down to the renderTextarea component correctly. I had to put my props directly on the div here to get them to be there. Maybe the cloneElement loses them?

jaywcjlove added a commit that referenced this issue Jun 25, 2021
@jaywcjlove
Copy link
Member

@stevemk14ebr Change to handle it yourself?

jaywcjlove added a commit that referenced this issue Jun 25, 2021
@stevemk14ebr
Copy link
Contributor Author

It going to take me some time to figure this out, the new version doesn't throw but typing is broken entirely and the content never changes, im not sure how the MDEditor, onChange, and onKeyDown all interact at the moment.

@jaywcjlove
Copy link
Member

{renderTextarea ? (
renderTextarea(
{
...otherProps,
value: markdown,
autoComplete: 'off',
autoCorrect: 'off',
spellCheck: 'false',
autoCapitalize: 'off',
className: `${prefixCls}-text-input`,
style: {
WebkitTextFillColor: 'inherit',
overflow: 'auto',
},
},
{ dispatch, onChange },
)
) : (

<MDEditor
  renderTextarea={(props, { dispatch, onChange}) => {
    return (
      <textarea {...props} onChange={(e) => {
        dispatch && dispatch({ markdown: e.target.value });
        onChange && onChange(e.target.value);
      }}/>
    )
  }}
/>

Add a new API, to be forward compatible.
@stevemk14ebr

@jaywcjlove
Copy link
Member

@stevemk14ebr
Copy link
Contributor Author

Ok I will update this PR to use the new interfaces. We can keep the pre and div overlays, I've figured out the CSS issues! Here is an example of how the CSS should be, the font must be explicitly provided so that the div and pre render the same. In the old version a hardcoded padding is used and the wrong positioning is used too.

Correct: https://codesandbox.io/s/happy-feather-hdz1p?file=/src/App.js

@stevemk14ebr
Copy link
Contributor Author

In the current latest version if you click an item on the toolbar the application breaks because the commands assume they are interacting with a textarea, instead of the div. This starts here:

commandOrchestrator && commandOrchestrator.executeCommand(command);

The commandOrchestrator set here:

useContext: { commands, extraCommands, commandOrchestrator: executeRef.current },
must be configurable by the user so that they can override how the orchestrator gets the current text and finds the selection:

It's probably not worth the effort to generalize all the commands that already exist, instead just let the user override the toolbar onClick behaviors entirely.

@stevemk14ebr
Copy link
Contributor Author

stevemk14ebr commented Jun 28, 2021

I'm going to close this, i think it's smarter for a future user to start with a framework like draft-js and just use the MDEditor.Preview component. I'm finding that contenteditable is really finicky with cursor positions and positioning depending on child content.

I think the prop you added is still useful though, and should stay. The full current version I use right now is:

 // cursor class from https://stackoverflow.com/a/41034697/3480193
 renderTextarea={(props, { dispatch, onChange, shortcuts, useContext }) => {
                    let { value, ...otherProps } = props;
                    let texts = value.split("\n").map((item) => {
                        // each line is enclosed with a span already, each space subdivides from the middle
                        let split = item.replaceAll(" ", "</span><span> ");
                        return "<span>" + split + "\n</span>";
                    }).join("");
                    return (
                        <div {...otherProps} style={{ whiteSpace: 'pre', WebkitTextFillColor: 'inherit', overflowY: 'auto', whiteSpace: 'pre-wrap', overflowWrap: 'break-word', wordWrap: 'break-word' }} spellCheck={true} contentEditable={true} suppressContentEditableWarning={true}
                            onInput={(e) => {
                                let pos = Cursor.getCurrentCursorPosition(e.target);
                                setCaretPos(pos);
                                onChange(e.target.innerText);
                                dispatch(setEditorText(e.target.innerText));
                            }}
                            onKeyDown={(e) => {
                                // the way we store/reset cursor doesn't handle newlines (and i can't fix it due to browser issues)
                                if (e.keyCode === 13) {
                                    // on chrome this moves to the next line, firefox has a bug?
                                    var sel = window.getSelection();
                                    sel.modify("move", "forward", "character");
                                }
                            }}
                            dangerouslySetInnerHTML={{ __html: texts }}
                        />
                    )
                }}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants