Skip to content
This repository has been archived by the owner on Mar 16, 2020. It is now read-only.

Commit

Permalink
Added a singleLine boolean prop
Browse files Browse the repository at this point in the history
Pasing it as true prevents entry of linebreaks

- Pressing the Enter key blurs the component
- Pasted linebreaks will be converted to spaces

Closes #2
  • Loading branch information
insin committed Feb 26, 2015
1 parent dbac0f5 commit 3f86c6d
Show file tree
Hide file tree
Showing 4 changed files with 97 additions and 29 deletions.
9 changes: 6 additions & 3 deletions CHANGES.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
## master

Added a `noTrim` boolean prop to disable trimming of leading and trailing
whitespace in text passed to the `onBlur` and `onChange` callbacks.

**Breaking change:** replaced `html` prop with `value` prop.

Plain text should be passed for the new `value` prop.
Expand Down Expand Up @@ -31,6 +28,12 @@ Value returned in 2.0.0:
3
```

Added a `singleLine` boolean prop to prevent linebreaks being added to the
`contentEditable`.

Added a `noTrim` boolean prop to disable trimming of leading and trailing
whitespace in text passed to the `onBlur` and `onChange` callbacks.

## 1.1.0 / 2015-02-23

Added an `autoFocus` prop to give focus to the `contentEditable` when the
Expand Down
20 changes: 15 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,11 @@ If `true` when the component mounts, the `contentEditable` will be given focus.

An additional CSS class to append to the default `PlainEditable` CSS class.

##### `component: String|ReactCompositeComponent` (default: `'div'`)

The HTML tag name or React component to be created for use as a
`contentEditable` in `PlainEditable`'s `render()` method.

##### `noTrim: Boolean`

Pass this prop to disable trimming of leading and trailing whitespace in text
Expand All @@ -123,11 +128,6 @@ passed to the `onBlur` and `onChange` callbacks.
<PlainEditable onBlur={this._onBlur} noTrim/>
```

##### `component: String|ReactCompositeComponent` (default: `'div'`)

The HTML tag name or React component to be created for use as a
`contentEditable` in `PlainEditable`'s `render()` method.

##### `onFocus: Function(event: SyntheticEvent, selecting: Boolean)`

This callback prop is accepted because this event is already used with the
Expand All @@ -153,6 +153,16 @@ match this prop when it gains focus.
This can be used to make it more convenient for users to edit an initial value
you provide as a placeholder.

##### `singleLine: Boolean`

Pass this prop to disable entry of linebreaks into the `contentEditable`.
Pressing the Enter key will force a `blur()` and linebreaks in pasted content
will be converted to spaces.

```html
<PlainEditable onBlur={this._onBlur} singleLine/>
```

##### `spellcheck: String` (default: `'false'`)

Coinfig for the `contentEditable`'s `spellcheck` prop, which is disabled by
Expand Down
55 changes: 43 additions & 12 deletions demo/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -80,12 +80,7 @@

var App = React.createClass({
getInitialState() {
return {
defaultOptionsBlurValue: '',
defaultOptionsChangeValue: '',
noTrimBlurValue: '',
noTrimChangeValue: ''
}
return {}
},

_onBlur(e, blurValue) {
Expand Down Expand Up @@ -124,8 +119,12 @@ <h3>Features</h3>
</p>
</li>
<li><p>
Leading &amp; trailing whitespace is trimmed by default - disable this by passing a
<code>noTrim</code> prop.
Leading &amp; trailing whitespace is trimmed by default - disable this by passing
a <code>noTrim</code> prop.
</p></li>
<li><p>
Linebreaks may be created and pasted in by default - restrict content to a single line
by passing a <code>singleLine</code> prop.
</p></li>
<li><p>
If a <code>placeholder</code> prop is provided, text in the <code>contentEditable</code>
Expand Down Expand Up @@ -156,7 +155,7 @@ <h3>Demo - Default Options</h3>
<div className="container">
<div className="row">
<div className="col">
<label><code>&lt;PlainEditable&gt;</code></label>
<label><code>&lt;PlainEditable/&gt;</code></label>
</div>
<div className="col">
<label><code>onBlur(event, value)</code></label>
Expand All @@ -175,7 +174,6 @@ <h3>Demo - Default Options</h3>
value={'One\n\nTwo\n\nThree'}
/>
</div>
<label><code>&lt;/PlainEditable&gt;</code></label>
</div>
<div className="col">
<div className="valueWrap">{this.state.defaultOptionsBlurValue}</div>
Expand All @@ -190,7 +188,7 @@ <h3>Demo - Disable Trimming</h3>
<div className="container">
<div className="row">
<div className="col">
<label><code>&lt;PlainEditable noTrim&gt;</code></label>
<label><code>&lt;PlainEditable noTrim/&gt;</code></label>
</div>
<div className="col">
<label><code>onBlur(event, value)</code></label>
Expand All @@ -210,7 +208,6 @@ <h3>Demo - Disable Trimming</h3>
value={'One\n\nTwo\n\nThree'}
/>
</div>
<label><code>&lt;/PlainEditable&gt;</code></label>
</div>
<div className="col">
<div className="valueWrap">{this.state.noTrimBlurValue}</div>
Expand All @@ -221,6 +218,40 @@ <h3>Demo - Disable Trimming</h3>
</div>
</div>

<h3>Demo - Single Line</h3>
<div className="container">
<div className="row">
<div className="col">
<label><code>&lt;PlainEditable singleLine/&gt;</code></label>
</div>
<div className="col">
<label><code>onBlur(event, value)</code></label>
</div>
<div className="col">
<label><code>onChange(event, value)</code></label>
</div>
</div>
<div className="row">
<div className="col">
<div className="wrap">
<PlainEditable
id="singleLine"
singleLine
onBlur={this._onBlur}
onChange={this._onChange}
value={'One\n\nTwo\n\nThree'}
/>
</div>
</div>
<div className="col">
<div className="valueWrap">{this.state.singleLineBlurValue}</div>
</div>
<div className="col">
<div className="valueWrap">{this.state.singleLineChangeValue}</div>
</div>
</div>
</div>

<footer><a href="https://github.com/insin/react-plain-editable">Source on GitHub</a></footer>
</div>
}
Expand Down
42 changes: 33 additions & 9 deletions src/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ var isIE = (typeof window !== 'undefined' && 'ActiveXObject' in window)

// =================================================================== Utils ===

var brRE = /<br>/g
var linebreaksRE = /\r\n|\r|\n/g

var escapeHTML = (() => {
var escapeRE = /[&><\u00A0]/g
var escapes = {'&': '&amp;', '>': '&gt;', '<': '&lt;', '\u00A0': '&nbsp;'}
Expand All @@ -23,12 +26,10 @@ var unescapeHTML = (() => {
})()

var linebreaksToBr = (() => {
var linebreaksRE = /\r\n|\r|\n/g
return (text) => text.replace(linebreaksRE, '<br>')
})()

var brsToLinebreak = (() => {
var brRE = /<br>/g
return (text) => text.replace(brRE, '\n')
})()

Expand Down Expand Up @@ -57,7 +58,10 @@ function htmlToText(html) {
return unescapeHTML(brsToLinebreak(html))
}

function textToHTML(text) {
function textToHTML(text, singleLine) {
if (singleLine && linebreaksRE.test(text)) {
text = text.replace(linebreaksRE, ' ')
}
return linebreaksToBr(escapeHTML(text))
}

Expand Down Expand Up @@ -104,21 +108,23 @@ var PlainEditable = React.createClass({
autoFocus: React.PropTypes.bool,
className: React.PropTypes.string,
component: React.PropTypes.any,
value: React.PropTypes.string,
noTrim: React.PropTypes.bool,
onBlur: React.PropTypes.func,
onChange: React.PropTypes.func,
onFocus: React.PropTypes.func,
onKeyDown: React.PropTypes.func,
onKeyUp: React.PropTypes.func,
placeholder: React.PropTypes.string
placeholder: React.PropTypes.string,
singleLine: React.PropTypes.bool,
value: React.PropTypes.string
},

getDefaultProps() {
return {
component: 'div',
noTrim: false,
placeholder: '',
singleLine: false,
spellCheck: 'false',
value: ''
}
Expand All @@ -144,13 +150,31 @@ var PlainEditable = React.createClass({
if (!innerHTML) {
e.target.innerHTML = DEFAULT_CONTENTEDITABLE_HTML
}

var html
if (innerHTML && (this.props.singleLine || this.props.onChange)) {
html = normaliseContentEditableHTML(innerHTML, !this.props.noTrim)
}

// If we're in single-line mode, replace any linebreaks which were pasted in
// with spaces.
if (html && this.props.singleLine && brRE.test(html)) {
html = html.replace(brRE, ' ')
e.target.innerHTML = html
}

if (this.props.onChange) {
var html = normaliseContentEditableHTML(innerHTML, !this.props.noTrim)
this.props.onChange(e, htmlToText(html))
}
},

_onKeyDown(e) {
if (this.props.singleLine && e.key == 'Enter') {
e.preventDefault()
e.target.blur()
return
}

if (this.props.onKeyDown) {
this.props.onKeyDown(e)
}
Expand Down Expand Up @@ -193,12 +217,12 @@ var PlainEditable = React.createClass({
noTrim,
onBlur, onChange, onFocus, onKeyDown, onKeyUp,
placeholder,
spellCheck,
singleLine, spellCheck,
value,
...props
} = this.props

var html = value ? textToHTML(value) : DEFAULT_CONTENTEDITABLE_HTML
var html = value ? textToHTML(value, singleLine) : DEFAULT_CONTENTEDITABLE_HTML

return <this.props.component
{...props}
Expand All @@ -208,7 +232,7 @@ var PlainEditable = React.createClass({
onBlur={onBlur && this._onBlur}
onInput={this._onInput}
onFocus={(onFocus || placeholder) && this._onFocus}
onKeyDown={(onKeyDown || isIE) && this._onKeyDown}
onKeyDown={(onKeyDown || singleLine || isIE) && this._onKeyDown}
onKeyUp={(onKeyUp || isIE) && this._onKeyUp}
spellCheck={spellCheck}
style={{minHeight: '1em'}}
Expand Down

0 comments on commit 3f86c6d

Please sign in to comment.