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

Updates and the Cursor #1

Open
acomito opened this issue Nov 27, 2016 · 18 comments
Open

Updates and the Cursor #1

acomito opened this issue Nov 27, 2016 · 18 comments
Labels

Comments

@acomito
Copy link

acomito commented Nov 27, 2016

I'm using this to build a collaborative editor. I am saving the document on every onChange that fires. This is all working well, when user #1 makes a change, it shows up on user #2's screen instantly (and vice versa). It's working great albeit one problem: whenever the component updates with new text, the cursor is sent back to the front of the input:

http://g.recordit.co/j9VeyvM0ka.gif

Any idea how to avoid this but still have the text/contents update? I'll post an example repo shortly...

@acomito
Copy link
Author

acomito commented Nov 27, 2016

So, this is only happening when html is being injected as the value, but works fine when the value={} is receiving just a string:

http://g.recordit.co/HRoovYVz1K.gif

Here's an example repo: https://github.com/acomito/collab-editor

@sochix
Copy link
Member

sochix commented Nov 27, 2016

I have had the same problem. I decided to remove onChange for every change and fire it only when user press a Save button. But it can be applied to your situation as I understand. So, we need to find a way to move cursor inside a text box after every onChange. I recommend you to look into trymbowyg component. In mean time, I'll explore possibilities to move cursor via jQuery.

@sochix sochix added the bug label Nov 27, 2016
@acomito
Copy link
Author

acomito commented Nov 27, 2016

Thanks @sochix. If it's helpful, I found it the same example app worked fine with react-medium-editor even with html as the value. I haven't had a chance to dig in and compare react-medium-editor and react-trumbowyg to see how they differ in handling updates.

@Alex-D
Copy link
Contributor

Alex-D commented Nov 27, 2016

Yop !
I'm the Trumbowyg's creator.
I think it's due to Trumbowyg core that didn't restore cursor position after updating the HTML content.

@sochix
Copy link
Member

sochix commented Dec 5, 2016

The problem with cursor is deep inside trymbowyg core. So need time to investogate further

@acomito
Copy link
Author

acomito commented Dec 5, 2016

Okay thanks for checking it out @sochix

@bhattjay
Copy link

bhattjay commented Mar 6, 2017

Hey, @sochix @Alex-D Any updates on this issue?

@Alex-D
Copy link
Contributor

Alex-D commented Mar 6, 2017

Nope. I don't have the time to work on Trumbowyg for now.

@sochix
Copy link
Member

sochix commented Mar 6, 2017

@bhattjay I explored it a bit. The problem is deep inside the core of Trumbowyg.

@dmarkrollins
Copy link

dmarkrollins commented Mar 27, 2018

So on edit I populate the data props from incoming container component props and init my internal state value.

On change I update the internal state value with the rich text (html) value - which does nothing to the cursor position since the data value is originally coming from props and state updates have no impact.

When user hits save I take the value from state, since that is always up to date due to being sync'd with the onChanged event, and use that to update the DB.

I am not sure if this is what @acomito meant but it works pretty well so far.

@azizali
Copy link

azizali commented Jul 14, 2018

Any update on this?

@azizali
Copy link

azizali commented Jul 14, 2018

Hey @acomito what did you end up doing. This is the best editor I found but this bug makes it unusable. I am looking for rich text editing plus View Source feature

@azizali
Copy link

azizali commented Jul 15, 2018

@Alex-D & @sochix If you can give me some hints on where to look for this cursor issue, I would be happy to look into it

@tmoody460
Copy link

tmoody460 commented May 3, 2019

I managed an ugly workaround for this (which I'm hiding in this wrapper):
Tl;Dr => Use state to only trigger the editor to change if you modify the value outside the editor.

export interface WsyiwigTextAreaProps {
    value: string;
    onChange(newValue: string): void;
}

export interface WsyiwigTextAreaState {
    value: string;
}
export class WsyiwigTextArea extends React.Component<WsyiwigTextAreaProps, WsyiwigTextAreaState> {
    public constructor(props: WsyiwigTextAreaProps) {
        super(props);
        this.state = {
            value: props.value
        };
    }

    public componentWillReceiveProps(props: WsyiwigTextAreaProps) {
        // We only want to update state if the external props are different from what we have in the editor. Calling the onchange method inside the editor component causes the cursor to be reset
        if (this.props.value != props.value && props.value != $("#react-trumbowyg")[0].innerHTML) {
            this.setState({
                value: props.value
            });
        }
    }

    private handleChange = (e: any): void => {
        this.props.onChange(e.target.innerHTML);
    }

    public render() {
        let buttons = ['formatting', 'strong', 'em', 'del', 'unorderedList', 'orderedList', 'removeformat'];
        return (
            <>
                <Trumbowyg id='react-trumbowyg' data={this.state.value} onChange={this.handleChange} buttons={buttons} />
            </>
        );
    }
}

@McTano
Copy link

McTano commented Oct 29, 2019

@tmoody460
I think this is a good workaround and it should be implemented inside react-trumbowyg somehow.

@AMoldskred
Copy link

AMoldskred commented Nov 28, 2019

I also had this issue and this was my solution:

const Comment: React.FC<Props> = () => {
  const [newComment, handleNewComment] = useState();
  const handleCommentChange = (e: SyntheticEvent<HTMLBaseElement>): void => {
        const nodeList = e.currentTarget.childNodes;
        handleNewComment(
            Array.from(nodeList).reduce((content: string, node: any) => {
                if (node.nodeType === 1) {
                    return content + node.outerHTML;
                }
                if (node.nodeType === 3) {
                    return `${content}<p>${node.textContent}</p>`;
                }
                return content;
            }, "")
        );
   };
  return (
    <Trumbowyg
       autogrow
       data={!newComment ? "" : undefined} // <- This is the important part
       buttons={["btnGrp-semantic", ["undo", "redo"], ["link"]]}
       placeholder={staticFormatMessage(messages.commentPlaceholder)}
       name="comment"
       onChange={handleCommentChange}
       onPaste={handleCommentChange}
       onFocus={() => setActive(true)}
   />
  )
}

The important part is in the data-prop

@evg1n
Copy link

evg1n commented Apr 1, 2020

@andreas0607 I don't understand what we achieve by assigning data prop's value "" or undefined. You can achieve the same thing by just passing data={""}. Then extract the value inside Trumbowyg textarea using state (event.target.innerText). Please see live Example.

Cheers.

@AMoldskred
Copy link

@evg1n I don't remember exactly why I used this solution. In hindsight I should've written a reason for doing it that way, BUT what I imagine was the reason was probably mutations of the parent component causing rerenders of trumbowyg.
When I check if the newComment exists I am disabling the data-prop by setting it to undefined, which in turn stops trumbowyg from reading that the data has updated (which it hasn't, but is implied by a rerender). When I want to reset the field I just use my handleNewComment and set the value to undefined which resets the data-field.
In your live example, this wouldn't be an issue. Because you don't have parents causing rerendering of children. Obviously, my solution is hacky. But if you get the same problem I had, then it is one possible solution.

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

No branches or pull requests

10 participants