Skip to content

Commit

Permalink
Added RenderInput signavio#622 (#14)
Browse files Browse the repository at this point in the history
  • Loading branch information
JakeHedman authored Feb 7, 2023
2 parents b605f09 + b40908d commit a11f589
Show file tree
Hide file tree
Showing 6 changed files with 120 additions and 41 deletions.
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 @@ -61,7 +61,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 @@ -74,6 +74,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

0 comments on commit a11f589

Please sign in to comment.