Skip to content

Instantly share code, notes, and snippets.

@wyqydsyq
Created July 19, 2016 05:32
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 wyqydsyq/c83a5aab4269f69189c6f9e974dd849c to your computer and use it in GitHub Desktop.
Save wyqydsyq/c83a5aab4269f69189c6f9e974dd849c to your computer and use it in GitHub Desktop.
Login Form
import {div, form, fieldset, legend, label, input, button, i, strong} from '@cycle/dom';
import {makeHTTPDriver} from '@cycle/http';
import isolate from '@cycle/isolate';
import xs from 'xstream';
import classes from 'dependencies/classes';
import styles from '../form/styles.less';
import LabelInput from 'components/label-input';
const dataIni = {
email: '',
password: '',
remember: null
},
stateIni = {
alerts: [],
submitting: false,
data: dataIni
};
function action (type, data = {}) {
return {
type,
effect: data
}
}
function intent ({DOM, HTTP, state$}) {
return {
input$: DOM.select('input').events('input').map(ev => action('input', {target: ev.target.name, value: ev.target.value})),
submit$: DOM.select('button').events('click').map(ev => action('submit', {target: ev.target.name})),
responses$: HTTP.select('user')
.map(response$ => response$.replaceError(error => {
let res = error.response;
res.error = true;
return xs.of(res);
}))
.flatten()
.map(res => action('response', {
success: (typeof res.error == 'undefined' || !res.error),
text: res.body.text
}))
}
}
function model (intent) {
return xs.merge(
intent.input$,
intent.submit$,
intent.responses$
).map(action => state => {
switch (action.type) {
case 'input':
state.data[action.effect.target] = action.effect.value;
break;
case 'submit':
state.submitting = true;
state.alerts = [];
break;
case 'response':
state.submitting = false;
let alert = {
title: action.effect.title || '',
text: action.effect.text
};
if (action.effect.success) alert.className = 'alert-success';
else alert.className = 'alert-danger';
state.alerts.push(alert);
break;
}
return state;
}).fold((state, method) => method(state), stateIni)
}
function LoginForm (sources) {
let actions = intent(sources),
state$ = model(actions),
render = (state) => {
let emailField = LabelInput({state, props: {
name: 'email',
type: 'email',
label: 'Email'
}}),
passwordField = LabelInput({state, props: {
name: 'password',
type: 'password',
label: 'Password'
}});
return form({class: classes(styles.form, styles.formHorizontal)}, [
fieldset([
// show alerts if there's any
state.alerts.length ? div('.alerts',
state.alerts.map(alert => div({class: classes(styles.alert, styles[alert.className || 'alert-info'])}, [
(typeof alert.title != 'undefined' && alert.title) ? strong(alert.title): '',
alert.text
]))
) : '',
legend({class: classes(styles.legend)}, 'Login'),
emailField.DOM,
passwordField.DOM,
div([
button({class: classes(styles.submit), props: {type: 'button', disabled: state.submitting}}, [
state.submitting
? i({class: classes(styles.fa, styles.faSpinner, styles.faSpin)})
: i({class: classes(styles.fa, styles.faSignIn)}),
state.submitting ? ' Logging in...' : ' Login'
])
])
])
])
},
vtree$ = state$.map(render);
return {
DOM: vtree$,
HTTP: xs.combine(actions.submit$, state$.take(1)).map(([action, state]) => ({
url: '/login',
category: 'user',
method: 'POST',
type: 'application/x-www-form-urlencoded',
send: state.data
})),
state$
}
};
export default sources => isolate(LoginForm)(sources);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment