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

Added RenderInput #622 #14

Merged
merged 1 commit into from
Feb 7, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/six-actors-clean.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"react-mentions": minor
---

Added RenderInput
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ You can find more examples here: [demo/src/examples](https://github.com/signavio
The `MentionsInput` supports the following props for configuring the widget:

| Prop name | Type | Default value | Description |
| --------------------------- | ------------------------------------------------------- | -------------- | -------------------------------------------------------------------------------------- |
|-----------------------------|---------------------------------------------------------|----------------|----------------------------------------------------------------------------------------|
| value | string | `''` | The value containing markup for mentions |
| onChange | function (event, newValue, newPlainTextValue, mentions) | empty function | A callback that is invoked when the user changes the value in the mentions input |
| onKeyDown | function (event) | empty function | A callback that is invoked when the user presses a key in the mentions input |
Expand All @@ -63,6 +63,7 @@ The `MentionsInput` supports the following props for configuring the widget:
| forceSuggestionsAboveCursor | boolean | false | Forces the SuggestionList to be rendered above the cursor |
| a11ySuggestionsListLabel | string | `''` | This label would be exposed to screen readers when suggestion popup appears |
| customSuggestionsContainer | function(children) | empty function | Allows customizing the container of the suggestions |
| renderInput | React component | undefined | Allows customizing the input element |

Each data source is configured using a `Mention` component, which has the following props:

Expand Down
2 changes: 2 additions & 0 deletions demo/src/examples/Examples.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import SingleLineIgnoringAccents from './SingleLineIgnoringAccents'
import SuggestionPortal from './SuggestionPortal'
import BottomGuard from './BottomGuard'
import CustomSuggestionsContainer from './CustomSuggestionsContainer'
import RendererInput from './RenderInput'

const users = [
{
Expand Down Expand Up @@ -90,6 +91,7 @@ export default function Examples() {
<SuggestionPortal data={users} />
<BottomGuard data={users} />
<CustomSuggestionsContainer data={users} />
<RendererInput data={users} />
</div>
</StylesViaJss>
)
Expand Down
37 changes: 37 additions & 0 deletions demo/src/examples/RenderInput.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import React from 'react'

import { Mention, MentionsInput } from '../../../src'

import { provideExampleValue } from './higher-order'
import defaultStyle from './defaultStyle'
import defaultMentionStyle from './defaultMentionStyle'

const CustomRenderer = React.forwardRef((props, ref) => (
<label>
I am a custom input!
<input type="text" {...props} ref={ref} />
</label>
))

function RenderInput({ value, data, onChange, onAdd }) {
return (
<div className="single-line">
<h3>Single line input</h3>

<MentionsInput
renderInput={CustomRenderer}
value={value}
onChange={onChange}
style={defaultStyle}
placeholder={"Mention people using '@'"}
a11ySuggestionsListLabel={'Suggested mentions'}
>
<Mention data={data} onAdd={onAdd} style={defaultMentionStyle} />
</MentionsInput>
</div>
)
}

const asExample = provideExampleValue('')

export default asExample(RenderInput)
69 changes: 41 additions & 28 deletions src/MentionsInput.js
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,8 @@ const propTypes = {
PropTypes.element,
PropTypes.arrayOf(PropTypes.element),
]).isRequired,

renderInput: PropTypes.elementType,
}

class MentionsInput extends React.Component {
Expand Down Expand Up @@ -177,7 +179,7 @@ class MentionsInput extends React.Component {
)
}

setContainerElement = (el) => {
setContainerElement = el => {
this.containerElement = el
}

Expand Down Expand Up @@ -223,28 +225,35 @@ class MentionsInput extends React.Component {
}

renderControl = () => {
let { singleLine, style } = this.props
let { singleLine, style, renderInput } = this.props
let inputProps = this.getInputProps()

return (
<div {...style('control')}>
{this.renderHighlighter()}
{singleLine
{renderInput
? this.renderCustom(inputProps)
: singleLine
? this.renderInput(inputProps)
: this.renderTextarea(inputProps)}
</div>
)
}

renderInput = (props) => {
renderCustom = props => {
let { renderInput: RenderInput } = this.props
return <RenderInput ref={this.setInputRef} {...props} />
}

renderInput = props => {
return <input type="text" ref={this.setInputRef} {...props} />
}

renderTextarea = (props) => {
renderTextarea = props => {
return <textarea ref={this.setInputRef} {...props} />
}

setInputRef = (el) => {
setInputRef = el => {
this.inputElement = el
const { inputRef } = this.props
if (typeof inputRef === 'function') {
Expand All @@ -254,7 +263,7 @@ class MentionsInput extends React.Component {
}
}

setSuggestionsElement = (el) => {
setSuggestionsElement = el => {
this.suggestionsElement = el
}

Expand All @@ -278,7 +287,7 @@ class MentionsInput extends React.Component {
scrollFocusedIntoView={this.state.scrollFocusedIntoView}
containerRef={this.setSuggestionsElement}
suggestions={this.state.suggestions}
customSuggestionsContainer ={this.props.customSuggestionsContainer}
customSuggestionsContainer={this.props.customSuggestionsContainer}
onSelect={this.addMention}
onMouseDown={this.handleSuggestionsMouseDown}
onMouseEnter={this.handleSuggestionsMouseEnter}
Expand Down Expand Up @@ -319,11 +328,11 @@ class MentionsInput extends React.Component {
)
}

setHighlighterElement = (el) => {
setHighlighterElement = el => {
this.highlighterElement = el
}

handleCaretPositionChange = (position) => {
handleCaretPositionChange = position => {
this.setState({ caretPosition: position })
}

Expand Down Expand Up @@ -487,9 +496,9 @@ class MentionsInput extends React.Component {
}

// Handle input element's change event
handleChange = (ev) => {
handleChange = ev => {
isComposing = false
if(isIE()){
if (isIE()) {
// if we are inside iframe, we need to find activeElement within its contentDocument
const currentDocument =
(document.activeElement && document.activeElement.contentDocument) ||
Expand Down Expand Up @@ -560,7 +569,7 @@ class MentionsInput extends React.Component {
}

// Handle input element's select event
handleSelect = (ev) => {
handleSelect = ev => {
// keep track of selection range / caret position
this.setState({
selectionStart: ev.target.selectionStart,
Expand All @@ -584,7 +593,7 @@ class MentionsInput extends React.Component {
this.props.onSelect(ev)
}

handleKeyDown = (ev) => {
handleKeyDown = ev => {
// do not intercept key events if the suggestions overlay is not shown
const suggestionsCount = countSuggestions(this.state.suggestions)

Expand Down Expand Up @@ -626,7 +635,7 @@ class MentionsInput extends React.Component {
}
}

shiftFocus = (delta) => {
shiftFocus = delta => {
const suggestionsCount = countSuggestions(this.state.suggestions)

this.setState({
Expand All @@ -642,7 +651,7 @@ class MentionsInput extends React.Component {
const { result, queryInfo } = Object.values(suggestions).reduce(
(acc, { results, queryInfo }) => [
...acc,
...results.map((result) => ({ result, queryInfo })),
...results.map(result => ({ result, queryInfo })),
],
[]
)[focusIndex]
Expand All @@ -654,7 +663,7 @@ class MentionsInput extends React.Component {
})
}

handleBlur = (ev) => {
handleBlur = ev => {
const clickedSuggestion = this._suggestionsMouseDown
this._suggestionsMouseDown = false

Expand All @@ -674,11 +683,11 @@ class MentionsInput extends React.Component {
this.props.onBlur(ev, clickedSuggestion)
}

handleSuggestionsMouseDown = (ev) => {
handleSuggestionsMouseDown = ev => {
this._suggestionsMouseDown = true
}

handleSuggestionsMouseEnter = (focusIndex) => {
handleSuggestionsMouseEnter = focusIndex => {
this.setState({
focusIndex,
scrollFocusedIntoView: false,
Expand All @@ -687,7 +696,11 @@ class MentionsInput extends React.Component {

updateSuggestionsPosition = () => {
let { caretPosition } = this.state
const { suggestionsPortalHost, allowSuggestionsAboveCursor, forceSuggestionsAboveCursor } = this.props
const {
suggestionsPortalHost,
allowSuggestionsAboveCursor,
forceSuggestionsAboveCursor,
} = this.props

if (!caretPosition || !this.suggestionsElement) {
return
Expand Down Expand Up @@ -739,9 +752,9 @@ class MentionsInput extends React.Component {
// is small enough to NOT cover up the caret
if (
(allowSuggestionsAboveCursor &&
top + suggestions.offsetHeight > viewportHeight &&
top + suggestions.offsetHeight > viewportHeight &&
suggestions.offsetHeight < top - caretHeight) ||
forceSuggestionsAboveCursor
forceSuggestionsAboveCursor
) {
position.top = Math.max(0, top - suggestions.offsetHeight - caretHeight)
} else {
Expand All @@ -761,12 +774,12 @@ class MentionsInput extends React.Component {
// is small enough to NOT cover up the caret
if (
(allowSuggestionsAboveCursor &&
viewportRelative.top -
highlighter.scrollTop +
suggestions.offsetHeight >
viewportHeight &&
suggestions.offsetHeight <
caretOffsetParentRect.top - caretHeight - highlighter.scrollTop) ||
viewportRelative.top -
highlighter.scrollTop +
suggestions.offsetHeight >
viewportHeight &&
suggestions.offsetHeight <
caretOffsetParentRect.top - caretHeight - highlighter.scrollTop) ||
forceSuggestionsAboveCursor
) {
position.top = top - suggestions.offsetHeight - caretHeight
Expand Down
Loading