I can see there is the function findAncestorOffsetKey() which returns the parent offsetKey for a given node. The offset key appears to start with the content block key.
Is there a nicer way given a text node to get the content block key than string manipulation on the findAncestorOffsetKey?
My use case for this is implementing focus at point, so that you can pass a point and have the caret appear there.
NVM just found the DraftOffsetKey.decode() function.
Actually reopening as these methods appear to be private... I wonder if there is a better way to do what I want using the things publicly exposed by Draft? Not having to include the source code for these twice would be great.
My use case for this is implementing focus at point, so that you can pass a point and have the caret appear there.
This is what the SelectionState object is used for. The EditorState tracks the current SelectionState to indicate where the selection should be rendered.
@hellendag I think there's a misunderstanding. I need this to create the SelectionState there is nothing but an empty one before that.
I am just given a coordinate and need to put the cursor there.
Specifically I am worried about the use of findAncestorOffsetKey() DraftOffsetKey.decode() as they appear to be private methods and could be changed under my feet and I have to import the code twice to use them.
See my current implementation:
let node;
let offset;
if (document.caretPositionFromPoint) {
const range = document.caretPositionFromPoint(point.x, point.y);
node = range.offsetNode;
offset = range.offset;
} else if (document.caretRangeFromPoint) {
const range = document.caretRangeFromPoint(point.x, point.y);
node = range.startContainer;
offset = range.startOffset;
}
const offsetKey = findAncestorOffsetKey(node);
if (offsetKey) {
const { blockKey } = DraftOffsetKey.decode(offsetKey);
const selectionState = new SelectionState({
anchorKey: blockKey,
anchorOffset: offset,
focusKey: blockKey,
focusOffset: offset,
});
const editorState = EditorState.forceSelection(
this.state.editorState,
selectionState,
);
this.setState({ editorState });
return;
}
Is there a way to do this with the publicly provided methods?
Ah I see, so you have an actual {x,y} coordinate that you want to translate. My mistake -- I thought you meant that you had a block/offset point.
It seems like getDraftEditorSelectionWithNodes might provide the implementation you're looking for. Do you want to hack that in and see if it does the job for you? Perhaps the ideal solution would be for the library to provide a wrapper function on that could expose the ability to provide a simple {x,y} coordinate and have it return a SelectionState.
I have kept the top-level Draft module pretty sparse in order to keep things simple during the initial OS rollout, but my assumption has been that a lot of the utilities in the library would become useful to developers over time. I think it may make sense to have a module that provides selection translation utilities like this one, for cases like yours.
@hellendag Thanks that function works great lets me simplify my code a fair a bit. There's still the issue that it is "private", or at least not accessible directly through the built copy. Do you consider these private internally? As in would you warn before changing these or would these affect versioning?
Here's the new code for reference incase someone stumbles on here looking for the same:
let node;
let offset;
if (document.caretPositionFromPoint) {
const range = document.caretPositionFromPoint(point.x, point.y);
node = range.offsetNode;
offset = range.offset;
} else if (document.caretRangeFromPoint) {
const range = document.caretRangeFromPoint(point.x, point.y);
node = range.startContainer;
offset = range.startOffset;
}
const { selectionState } = getDraftEditorSelectionWithNodes(
this.state.editorState,
null,
node,
offset,
node,
offset,
);
const editorState = EditorState.forceSelection(
this.state.editorState,
selectionState,
);
this.setState({ editorState });
Not sure if passing null as the root element was wrong there but it seems to work, and wasn't quite sure what it was expecting. Seemed like it was only for some internal usage.
Actually using that function can be dangerous as it throws if passed any node not in the editor or without a parent offset key. Which could be possible with a { x, y } coordinate.
Using findAncestorOffsetKey() and ignoring null seems to be a simpler and faster method than checking the nodes are contained first.
Exposing the functions shouldn't be a problem for versioning, assuming they aren't going to be removed later. As I mentioned, I'm open to exposing functions if it becomes clear that they are needed for developers -- it just made sense to start narrow and expand from there.
For the root parameter, you should pass in the root element of the editor itself. Take a look at how the function is used within Draft internals to get a sense of it.
Are you able to filter out mouse events that are not occurring on your editor? It might be nice to have a mouse handler on your top-level component for those events, to ensure that your {x,y} values are always within the editor.
This is a bit of a special case where the editor is hidden until this point and then shown and focused at the same time (hence why I am focusing manually instead of letting the natural events occur), most people can use that safely though, I'm happy to check the node is contained by the editor manually.
Thanks for the info, nice to see you will be exposing more.
Perhaps the ideal solution would be for the library to provide a wrapper function on that could expose the ability to provide a simple {x,y} coordinate and have it return a SelectionState.
@hellendag 馃憤 for this feature
Closing this thread for now, but if there is still interest in this discussion please feel free to reopen/comment or create a new issue around it.
Most helpful comment
NVM just found the
DraftOffsetKey.decode()function.