import {Block} from 'cyclow'
import Confirm from './confirm'
const Field = () => Block({
outputs: ['submission'],
components: {confirm: Confirm()},
on: {
init: instruction => [state => ({instruction, input: ''}), 'confirm.init'],
text: newText => state => ({...state, input: newText}),
'confirm.confirmation': (_, {input}) => [
state => ({...state, input: ''}),
['out.submission', input]
],
state: ({input}) => [['confirm.disabled', !input]]
},
view: ({instruction, input}, {confirm}) => ({content: [
`${instruction}:`,
{
tag: 'input',
attrs: {value: input},
on: {keyup: (e, next) => next(['text', e.target.value])}
},
confirm
]})
})
export default Field
The same file in a TypeScript implementation using Angular (2.4.9):
import {Component,ViewChild,EventEmitter,Input,Output} from '@angular/core'
@Component({
selector: 'field',
template: `
{{instruction}}: <input #field (input)="0">
<confirm (confirm)="onConfirm()" [disabled]="!field.value.length"></confirm>
`
})
export class Field {
@ViewChild('field') input: any
@Input() instruction: string
@Output() submission = new EventEmitter<string>()
onConfirm() {
this.submission.emit(this.input.nativeElement.value)
this.input.nativeElement.value = ''
}
}
The same file in a composition helper implementation using CycleJS (10.0.5):
import {div, input} from '@cycle/dom'
import Confirm from './confirm'
import xs from 'xstream'
import withComponent from './extras'
const intent = (DOM, confirm$) => {
const input$ = DOM.select('.field').events('input')
const newValue$ = input$
.map(e => ({type: 'INPUT', data: e.target.value}))
const submit$ = input$
.map(i => confirm$.map(s => ({type: 'SUBMIT', data: i.target.value})))
.flatten()
return xs.merge(submit$, newValue$)
}
const model = action$ => action$.fold((state, action) => {
switch (action.type) {
case 'INPUT': return {...state, input: action.data}
case 'SUBMIT': return {submission: action.data, input: ''}
default: return state
}
}, {submission: '', input: ''})
const view = (state$, confirmvtree$, instruction$) =>
xs.combine(state$, confirmvtree$, instruction$).map(([state, confirmvtree, instruction]) =>
div('.child', [
instruction + ': ',
input('.field', {props: {value: state.input}}),
confirmvtree
])
)
function Field (sources) {
const action$ = intent(sources.DOM, sources.childsinks.confirm$)
const state$ = model(action$)
const vtree$ = view(state$, sources.childsinks.DOM, sources.instruction$)
return {
DOM: vtree$,
submit$: action$.filter(a => a.type === 'SUBMIT').map(a => a.data),
disabled$: action$.map(a => a.type === 'SUBMIT' || !a.data)
}
}
export default withComponent(Field, Confirm, 'disabled$')
The same file in a createClass implementation using React (15.2.0):
import React from 'react'
import Confirm from './confirm'
let Field = React.createClass({
getInitialState: () => ({field: ''}),
onConfirm () {
this.props.onSubmission(this.state.field)
this.setState({field: ''})
},
onChange (e) {
this.setState({field: e.target.value})
},
render () {
return (
<div>
{this.props.instruction}: <input value={this.state.field} onChange={this.onChange} />
<Confirm disabled={!this.state.field} confirm={this.onConfirm} />
</div>
)
}
})
export default Field