Open saidymadi opened 7 years ago
@saidymadi given <MentionsInput markup="{{__type__:__id__}}"
I can insert '{{mention:0067EA03CF283F76}}'
into the string that becomes the value
prop of MentionsInput
.
yes that is the easy part, however, how would i insert it at the current cursor location ?
in my case, i have bunch of buttons each is for a suggested mention
i want the user to be able to click on any of them to insert that into the current cursor location
First, you can pass your own onSelect
handler to MentionsInput
and keep track of the cursor position:
<MentionInput onSelect={(event) => console.log(event.target.selectionStart)}>
Then, you have to map the cursor position in the plain text, to a position in the value with markup. For this purpose we internally use the function mapPlainTextIndex
. While I cannot recommend using private API, I think this would be your only option at the moment.
If you manage to build a working example, I would be glad to take a look at it to see how we could best support this use case with public APIs.
I have managed to achieve this as follow :
you need to update the value but you also need to update the underlying text area in order for it to calculate and adjust the cursor correctly in my application mentionsInputRef is the reference to the <MentionInput and mentionsInputRef.wrappedInstance.refs.input refers to the <Mention textarea or Input control in my text Application I added an insert user handler
this.handleInsertMention = function (userObject) {
// a little uncomfortable must rework to expose add user in a different way
if (this.socialMarkup
&& this.socialMarkup.mentionsInputRef
&& this.socialMarkup.mentionsInputRef.wrappedInstance
&& this.socialMarkup.mentionsInputRef.wrappedInstance.refs.input) {
const inputElement = this.socialMarkup.mentionsInputRef.wrappedInstance.refs.input
const newVal = `${this.socialMarkup.state.value } @[${userObject.display}](user:${userObject.id})`
const updateUnderlyingTextInputArea = function () {
this.socialMarkup.mentionsInputRef.wrappedInstance.handleInsertMention( { target: inputElement })
}.bind(this)
this.socialMarkup.setState({ ...this.socialMarkup.state, value: newVal }, updateUnderlyingTextInputArea)
}
}.bind(this)
@saidymadi , I have a similar requirement as yours. Could you please clarify couple of things...?
If possible could you share the actual code (may be obfuscating any business logic specific to your project)?
Thanks in advance.
Hey I will have some free time towards the end of my working day . In 5-6 hours and I will create a branch with the solution to auto insert based off the main master without any of my other modifications .
Hi, Thanks for responding quickly.
I was able to get add mentions working this way... This code always inserts mention at the beginning of the input and not at the current cursor position. Perhaps, I can take a look at your changes and see if I can get that part working.
Included ref in JSX:
<MentionsInput ...
ref={ e => this.mentionInput = e}.... >
<Mention...>
</MentionsInput>
Add button (part of my wrapper):
<button onClick={this.addMention}> Add Mention </button>
addMention() {
this.mentionInput.wrappedInstance.addMention(
{
id: "Id1",
display: "Dynamic Mention"
}, {
mentionDescriptor: this.mentionInput.props.children[0],
querySequenceStart: 0,
querySequenceEnd: 0,
plainTextValue: ""
}
)
}
querySequenceStart, querySequenceEnd and plainTextValue - these values need to be correctly computed so that insert occurs at the current cursor position.
I advise against a solution relying on .wrappedInstance
as posted above. Be aware that this is private API we might change in the future. This means your app may break even when just upgrading to a new patch release of react-mentions.
The recommended way for programmatically inserting mentions is by updating the value
directly. As outlined above the mapPlainTextIndex
function might be useful for mapping a cursor position to a position in the value
containing the markup. While mapPlainTextIndex
currently is not part of the official API yet, this is the much more futureproof way and we will most probably add this function to the exports of this module soon.
@jfschwarz trying to get around doing this, but I'm having an issue how to tell MentionsInput, to update the selected index.
My current plan of action
But how do I do this? After I update my value manually, cursor keeps jumping to the end when typing in the middle fx.
Thanks a lot :)
If you use autofocus
, or disable mention inserting until the input fires onFocus
, you can get the selection
using onSelect
, and the inputElement
using onFocus
or onBlur
.
Once you have all that you can handle mention insertion as follows:
handleInsert(insert) {
const { value, selection, inputElement, markup } = this.state;
const start = utils.mapPlainTextIndex(value, markup, selection.start);
const end = utils.mapPlainTextIndex(value, markup, selection.end);
const pos = selection.start + insert.length;
this.setState({
value: value.substr(0, start) + insert + value.substr(end)
}, () => {
inputElement.selectionStart = pos;
inputElement.selectionEnd = pos;
inputElement.focus();
});
}
And here, for your edification, is a live working example.
I don't yet know the correct way to make this process accessible and generic and officially blessed, but it is possible currently with mapPlainTextIndex
, as you can see.
If you're wondering how to use utils.mapPlainTextIndex
in the new version 3.0.2
this is how I got it to work.
Basically instead of passing markup
, you need to pass an array of objects containing the regex
, markup
and displayTransform
function. If you have only one Mention
child, this will be an array of size 1.
export const MENTION_MARKUP = "@mention[__display__](__id__)";
export const MENTION_REGEX = /@mention\[(.+?)]\((.+?)\)/;
const start = utils.mapPlainTextIndex(
value,
[
{
regex: MENTION_REGEX,
markup: MENTION_MARKUP,
displayTransform: function(id, display) {
return display;
},
},
],
selection.start
);
const end = utils.mapPlainTextIndex(
value,
[
{
regex: MENTION_REGEX,
markup: MENTION_MARKUP,
displayTransform: function(id, display) {
return display;
},
},
],
selection.end
);
If you use
autofocus
, or disable mention inserting until the input firesonFocus
, you can get theselection
usingonSelect
, and theinputElement
usingonFocus
oronBlur
.Once you have all that you can handle mention insertion as follows:
handleInsert(insert) { const { value, selection, inputElement, markup } = this.state; const start = utils.mapPlainTextIndex(value, markup, selection.start); const end = utils.mapPlainTextIndex(value, markup, selection.end); const pos = selection.start + insert.length; this.setState({ value: value.substr(0, start) + insert + value.substr(end) }, () => { inputElement.selectionStart = pos; inputElement.selectionEnd = pos; inputElement.focus(); }); }
And here, for your edification, is a live working example.
I don't yet know the correct way to make this process accessible and generic and officially blessed, but it is possible currently with
mapPlainTextIndex
, as you can see.
@kalemi19 utils
and its functions are no more accessible in 3.1.0.
after the installation of a package. How did you import it? Thanks.
Edit:
I got it to work by downgrading to3.0.2.
and following your workaround. This version has utils
accessible. But version 3.1.0.
has not. Any ideas on how to access utils at this version?
Edit2:
To got it to work with 3.1.0.
I Edited exports in node_modules/react-mentions/dist/react-mentions.esm.js
. It's just a quick workaround instead of a long term solution. It could be great if utils were exported in future versions.
@filippofilip95 my snippet / workaround is for 3.0.2
. I haven't upgraded to 3.1.0
yet.
P.S. Apologies for the delay.
Since this is a controlled input, when I insert the mention into the textarea the onChange() handler is never fired. Meaning, if I click to add two mentions, my value string shows the two mentions, but I don't have access to the mentions provided by the onChange handler. Any thoughts on how to keep track of this?
Any simple solution exists as of 4.0.1 ?
@raDiesle Did you find a solution to this?
@jfschwarz Any help with this please? There really is no easy way to insert at a cursor position since the utils functions are no longer public.
Thanks
Answer 2021 i can insert key at cursor by click the button and replace key if cursor position at previous key
here code sandbox https://codesandbox.io/s/react-mentions-forked-p7rdt?file=/src/index.js
any simple solution to this? no offense, but that solution^ is like 80+ lines of code
If you're wondering how to use
utils.mapPlainTextIndex
in the new version3.0.2
this is how I got it to work.Basically instead of passing
markup
, you need to pass an array of objects containing theregex
,markup
anddisplayTransform
function. If you have only oneMention
child, this will be an array of size 1.export const MENTION_MARKUP = "@mention[__display__](__id__)"; export const MENTION_REGEX = /@mention\[(.+?)]\((.+?)\)/; const start = utils.mapPlainTextIndex( value, [ { regex: MENTION_REGEX, markup: MENTION_MARKUP, displayTransform: function(id, display) { return display; }, }, ], selection.start ); const end = utils.mapPlainTextIndex( value, [ { regex: MENTION_REGEX, markup: MENTION_MARKUP, displayTransform: function(id, display) { return display; }, }, ], selection.end );
If you use
autofocus
, or disable mention inserting until the input firesonFocus
, you can get theselection
usingonSelect
, and theinputElement
usingonFocus
oronBlur
. Once you have all that you can handle mention insertion as follows:handleInsert(insert) { const { value, selection, inputElement, markup } = this.state; const start = utils.mapPlainTextIndex(value, markup, selection.start); const end = utils.mapPlainTextIndex(value, markup, selection.end); const pos = selection.start + insert.length; this.setState({ value: value.substr(0, start) + insert + value.substr(end) }, () => { inputElement.selectionStart = pos; inputElement.selectionEnd = pos; inputElement.focus(); }); }
And here, for your edification, is a live working example. I don't yet know the correct way to make this process accessible and generic and officially blessed, but it is possible currently with
mapPlainTextIndex
, as you can see.
I'd like this functionality, too
The provided examples above were really helpful. I found it easiest to export the necessary utilities from react-mention
's ESM module, use patch-package
to capture the change for an NPM or Yarn postinstall
process, and use these utils alongside the Mentions config to handle text insertion cleanly.
It would be a very welcome change to see this as a part of the core library.
Something like this, where the method is attached to the inputRef
that can be called imperatively:
const inputRef = useRef(null);
...
<MentionsInput inputRef={inputRef}>
...
</MentionsInput>
<button onClick={() => inputRef.current.addMention(...)}>Add a mention</button>
Hello ,
could you provide an example of how I can programatically insert a mention into the react mention text box ?
for example . I have a button
I want my users to be able to click this button to immediately insert the userA into the current react-mentions component