formidablelabs / use-editable Goto Github PK
View Code? Open in Web Editor NEWA small React hook to turn elements into fully renderable & editable content surfaces, like code editors, using contenteditable (and magic)
License: MIT License
A small React hook to turn elements into fully renderable & editable content surfaces, like code editors, using contenteditable (and magic)
License: MIT License
Hello,
Thank you for creating this wonderful package! Unfortunately, due to the nature of SSR, MutationObserver
objects cannot be created on the server side:
Changing the code above to const observerRef = useRef(typeof window !== "undefined" ? new MutationObserver(addMutationsToQueue) : null);
could serve as an escape hatch.
There is a time delay when deleting all content - about six seconds. Is there a way to identify deletion upon deletion? What can I do to make it faster?
Hi @kitten, thanks for this useful hook!
I ran into an issue where the onSelect event is not firing in browsers that support contenteditable="plaintext-only"
(like Chrome & Safari). This is due to the fact that React only fires the onSelect event when contenteditable="true"
.
I created a sandbox to demonstrate this (see console.log):
https://codesandbox.io/s/awesome-nightingale-s2gjb?file=/src/App.js
The above example works in Firefox (since it doesn't support plaintext-only
).
This event would be handy to get the caret position when the user is navigating with the ← →-keys.
I've been working on a dropdown example, and I made it. I hope you like it and accept it. I'll be so happy with your opinions. codesandbox
Thanks for the great library.
Is it possible to expose selectall functionality? I can achieve this by using the following on click, but it seems like programmatic control over range selections is possible with the library.
document.execCommand("selectAll", false, undefined);
Something like Edit.select(range)
?
When you try to type the text for the first time, it will re-render and defocus from the input box.
So you need to select the input box and type again (it's annoying)
My workaround is force re-rendering for the first load. Is there another solution to fix this problem ??
Here is an example: code sandbox.
Environments
- astro: 4.0.3
- @astrojs/react: 3.0.7
- react: 18.2.0
- use-editable: 2.3.3
- prism-react-renderer: 2.3.0
Lovely approach, thanks a lot!
It may be due to browser (latest Chrome) or React version (17), but I THINK it's because the sample in the README is broken.
When your cursor is after the last character and you press enter, nothing happens. The second time you press enter it adds an enter to the top of the area, not really the intention of anyone I think.
In the Codesandbox everything works fine. When comparing I found that the example in the README only adds a line when not at the end of the array, while the Codepen always adds one.
README: { i < arr.length - 1 ? '\n' : null }
Codesandbox: { "\n" }
For me at least it appears the README example is fixed when always adding a \n
. I know how sensitive these things can be with all browsers, so wanted to verify this is a mistake in the README and not done intentional to prevent other quirks.
Also it seems quite often Chrome loses focus after first getting focus for the first time and typing one character. I can think of some ugly workarounds, but perhaps also something I'm missing, since Codepen code is again not exhibiting this quirk.
Code used as good as a copy paste from README.
EDIT: Just confirmed that the Codesandbox is also broken when replacing it with the code from the README, e.g. "\n"
to {i < tokens.length - 1 ? '\n' : null}
Hello, I've been using your hook on a project feature and when writting tests I noticed that the attribute was not being set. Performing a bit of investigation I detected that we set the contentEditable attribute by doing the next:
element.contentEditable = 'BLA BLA'
After testing it I noticed that even though my test is getting inside the function the property was not being set. So I decided to modify the library and test if using setAttribute fixed the issue and it worked:
element.setAttribute('contenteditable', 'plaintext-only');
What's the reason behind setting directly the property?
Hello! First of all, thank you for building such an amazing hook it is being useful!
While my development I've spotted a weird behavior when using it which I'm not sure how to fix. A video would be ten times more useful than words so here you have:
I'm not sure why does it happens but is something super annoying for some users. Could you help me?
Hi, just bumped into this, the solution is to check selection.rangeCount !== 0
before calling selectino.getRangeAt(0)
I got into this because I called getState()
while disabled option is true
This can be reproduced in codepen example:
Ctrl + Z also works erratically.
Note: this happens only on first time selecting it.
I managed to get it working OK by re-rendering the component (not an ideal solution).
Hi to everyone, thank you for the hook.
I've been using it and everything ok except for the /n line which causes that I cannot get the real length of the string to call a func or get and empty component to show a placeholder via css (I ended up making another empty component to render when the length was === 1).
Is there any possible solution?
Again, thank you!
Hey @kitten, thanks for this project, it's been helping me out a lot (dealing with contentEditable
was a pain before I found your package :)
So I wanted to suggest that a user could pass an option to prevent the content from breaking lines... This would be particularly useful for my project, since I don't want the users to add break lines, and if you don't mind, I'd like to open a PR to add it to your main package.
My current "fix" for this looks something like this:
useEditable(titleRef, (txt) => {
setData({
...data,
title: txt.replace(/\n/g, '')
})
})
That's not working so great because whenever I hit ENTER, the caret loses the position, and the line break kinda blinks for a milisecond, so I imagine there might be a smarter way to disable that if I dig into your code a little bit, probably an if around here: https://github.com/kitten/use-editable/blob/main/src/useEditable.ts#L406
Thanks :)
Issue can be reproduced in your CodeSandbox example by removing all text then hitting backspace: https://codesandbox.io/s/use-editable-0l9kc?file=/src/Edit.js
Error
Failed to execute 'setStart' on 'Range': The offset 4294967295 is larger than the node's length (1).
Happens in Chrome, Firefox, Edge and Sidekick.
Hey @kitten✋ , I was working on my codesandbox dropdown(not ready yet), then I realized something. When I use setCode
sometimes it gives me this error:
Failed to execute 'removeChild' on 'Node': The node to be removed is not a child of this node
.
I worked on it for many hours and I found here's the problem:
line: 295, file: useEditable.ts
mutation.target.removeChild(mutation.addedNodes[i]);
some mutation.addedNodes[i] don't have parents(parentNode , ... = null) and they don't know mutation.target as their parent node.
so I convert the ts to js and I edited the above piece like this:
if (mutation.addedNodes[i].parentNode) {
mutation.target.removeChild(mutation.addedNodes[i]);
}
and it worked successfully.
in the Edit.js of the codesandbox you can comment the regular import and uncomment the relative import comment.
// import { useEditable } from "use-editable";
import { useEditable } from "./useEditable";
to see the successful result.
Can I make the PR for it?
When you first change the content of the element you are interrupted, as if the element loses focus. I have tested this in multiple browsers and in multiple react versions. This problem only ocurs in React 18
Codesandbox
First, thank for this hook, it's really nice work.
When I use Edit.insert(append: string, offset?: number): void
, the text is correctly inserted in the editor but the onChange
handler is not called, which result to a de-synchronization between the editor content and my state.
Here is a case reproduction: https://codesandbox.io/s/use-editable-forked-j2vztb?file=/src/Edit.js
Does I miss something ?
Thanks in advance
Hi! I have
The text input is not controlled by use-editable
. When the text input's value changes, the editable div rerenders.
If I have never edited the contents of the div, focus stays inside the text input as expected. If I first type some content into the div and then type inside the text input, the focus jumps into the div.
I can put together a simple snippet demonstrating if it'd help you reproduce the issue. But I think the main idea is that
state.position
starts null
here, and things behave fine.state.position
is non-null, use-editable
forcibly focuses the div on rerender. I think. Maybe this line.Not sure of the ideal solution -- listening for blurs, or checking the window's selection or something. I've worked around it by forcing the div to remount on blur, which isn't ideal but it's not blocking me.
(Very cool library, thanks for writing it!)
A very common use case is detecting the cursor coordinates on the screen to render auto-suggestion. Would you consider exposing a method to get the coordinates? Something like Edit.getSelection(): Range
.
Also, do you know any autosuggestion dropdown examples out there?
Hey @kitten, I really loved useEditable, And I'm reading its magical source as a junior, Do you have any idea that I can add to the project or some todos.
Thanks.
Hi, I was fiddling around and found a way to associate data with text that is being managed by useEditable.
Right now the use cases for use-editable would be limited because you always get back just textContent, so if you rendered any additional stuff to the dom - it gets lost because not representable by text, which I know is by design, but we can actually quite easily achieve a more versatile rendering engine.
We can serialize also dataset and not just textContent, basically just {...node.dataset}. I changed the internals from toString() to produce an object with a .toString() method Content<Item>
, where Item generic is defined by useEditable caller. For all manipulations we rely on .toString() representation, but in onChange we return content: Content.
This has allowed me to move dom serialization logic to the caller and caller defines how things get serialized, while use-editable stays very small and doesn't care at all what the format is, except that it should provide a .toString() method
export interface Content<Item> {
items: Array<Item>;
toString: () => string;
}
In the end, it allowed me to attach an id to elements and in onChange callback convert content to react elements that have data awareness. A complete wysiwyg can essentially be built on top of this.
I can provide a POC PR with what I did
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.