Skip to content

Instantly share code, notes, and snippets.

@shofel
Created September 12, 2017 09:14
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save shofel/f33e5210603745821ffb4adb3133e9d4 to your computer and use it in GitHub Desktop.
Save shofel/f33e5210603745821ffb4adb3133e9d4 to your computer and use it in GitHub Desktop.
A cycle component for message input.
/**
* We use this component as an inputarea where user writes his message and then
* sends it by pressing the 'Return' button or clicking the 'Send' button.
*
* It appeared, that cleaning a textarea is not so trivial task o_O.
*
* Component has two streams as an input:
* - DOM:
* - To listen 'return' keypresses on the textarea.
* - To listen changes in the textarea value.
* - sendIntent:
* - When some value in this stream emitted, it treated the same way as if 'Return' were pressed.
* That is, 1: the draft value is emitted to the 'newMessage' sink, and 2: the textarea clears.
*
* And two streams as an output:
* - DOM: to render the textarea.
* - newMessage: to emit the text of a new message.
*/
import xs from 'xstream'
import {makeDOMDriver, div, textarea, input} from '@cycle/dom'
import {run} from '@cycle/run';
import sampleCombine from 'xstream/extra/sampleCombine'
/* Ad hoc utils */
let prop = key => obj => obj[key]
let last = arr => arr.slice(-1)[0]
let trim = x => x.trim()
let nonEmpty = x => x !== ''
let concat = (acc, x) => acc.concat(x)
let replace = pattern => replacement => s => s.replace(pattern, replacement)
let suppressNewline = replace(/^\n$/)('')
/** You can send message pressing enter or with custom event. */
let MessageTextInput = sources => {
let textarea$ = sources.DOM.select('textarea')
let returnPressed$ =
textarea$
.events('keypress')
.filter(e => e.charCode === 13)
.mapTo('The return button pressed.')
let sendIntent =
xs.merge(
sources.sendIntent,
returnPressed$)
.map(event => `Intent to send a chat message: ${event}`)
let draft$ =
textarea$
.events('input')
.map(prop('target'))
.map(prop('value'))
.map(suppressNewline)
.startWith('')
let newMessageSink =
sendIntent
.compose(sampleCombine(draft$))
.map(last)
/* Filter out empty messages. */
.map(trim)
.filter(nonEmpty)
let clearDraft$ = newMessageSink.mapTo(null)
let DOMSink =
xs.merge(draft$, clearDraft$)
.map(draft => textarea('.input', {props: {value: draft}}))
return {
DOM: DOMSink,
newMessage: newMessageSink
}
}
/* And then run it like this. */
run(MessageTextInput, {
DOM: makeDOMDriver('.app'),
sendIntent: clickSend$) // Usually you pass a stream with clicks of a 'Send' button.
})
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment