import {span, button} from '@cycle/dom'
import xs from 'xstream'
const intent = sources => xs.merge(
sources.disabled$.map(i => i ? 'DISABLE' : 'ENABLE'),
sources.DOM.select('.maybe').events('click').map(i => 'MAYBE'),
sources.DOM.select('.cancel').events('click').map(i => 'CANCEL'),
sources.DOM.select('.confirm').events('click').map(i => 'CONFIRM')
)
const model = action$ => action$.fold((s, action) => {
switch (action) {
case 'DISABLE': return 'disabled'
case 'MAYBE': return 'confirm'
case 'ENABLE': return s === 'disabled' ? 'waiting' : s
default: return 'waiting'
}
}, 'disabled')
const view = state$ => state$.map(state => state === 'confirm'
? span([button('.confirm', 'Confirm'), button('.cancel', 'Cancel')])
: button('.maybe', {attrs: {disabled: state === 'disabled'}}, 'Submit')
)
export default sources => {
const action$ = intent(sources)
const state$ = model(action$)
const vtree$ = view(state$)
return {
DOM: vtree$,
confirm$: action$.filter(i => i === 'CONFIRM')
}
}
The same file in a TypeScript implementation using Angular (2.4.9):
import {Component,Input,Output,EventEmitter} from '@angular/core';
@Component({
selector: 'confirm',
template: `
<span *ngIf="mode !== 'confirm'">
<button (click)="maybe()" [disabled]="mode === 'disabled'">Submit</button>
</span>
<span *ngIf="mode === 'confirm'">
<button (click)="changedmymind()">Cancel</button>
<button (click)="yesimsure()">Confirm</button>
</span>
`
})
export class Confirm {
mode: string = 'waiting'
@Output() confirm = new EventEmitter<void>()
@Input() set disabled(bool: boolean){
this.mode = bool ? 'disabled' : 'waiting'
}
maybe() { this.mode = 'confirm' }
changedmymind() { this.mode = 'waiting' }
yesimsure() {
this.confirm.emit()
this.mode = 'waiting'
}
}
The same file in an es6 implementation using Cyclow (0.4.0):
import {Block} from 'cyclow'
const viewConfirm = () => [
{tag: 'button', content: 'Confirm', on: {click: 'confirm'}},
{tag: 'button', content: 'Cancel', on: {click: 'cancel'}}
]
const viewDefault = mode => ({
tag: 'button',
content: 'Submit',
attrs: {disabled: mode === 'disabled'},
on: {click: 'maybe'}
})
const Confirm = () => Block({
inputs: ['disabled'],
outputs: ['confirmation'],
on: {
init: () => state => ({mode: 'disabled'}),
disabled: (disabled, {mode}) => {
if (disabled) {
return state => ({mode: 'disabled'})
} else if (mode === 'disabled') {
return state => ({mode: 'waiting'})
}
},
maybe: () => state => ({mode: 'confirm'}),
cancel: () => state => ({mode: 'waiting'}),
confirm: () => [state => ({mode: 'waiting'}), ['out.confirmation']]
},
view: ({mode}) => mode === 'confirm' ? viewConfirm() : viewDefault(mode)
})
export default Confirm
The same file in a createClass implementation using React (15.2.0):
import React from 'react'
let Confirm = React.createClass({
getInitialState: () => ({mode: 'waiting'}),
maybe () { this.setState({mode: 'confirm'}) },
changedmymind () { this.setState({mode: 'waiting'}) },
yesimsure () {
this.props.confirm()
this.setState({mode: 'waiting'})
},
render () {
return this.state.mode !== 'confirm'
? <button onClick={this.maybe} disabled={this.props.disabled}>Submit</button>
: <span>
<button onClick={this.changedmymind}>Cancel</button>
<button onClick={this.yesimsure}>Confirm</button>
</span>
}
})
export default Confirm