jurStv
9/13/2015 - 11:16 PM

The event handler detached in adressForm.js Do not pay attention to Validator instances, it's just for filtering table-items and checking v

The event handler detached in adressForm.js Do not pay attention to Validator instances, it's just for filtering table-items and checking valid.

import {Rx} 	from "@cycle/core";
import {h} 		from '@cycle/dom';

function view( filter$, mainForm$, table$ ) {
  return Rx.Observable.combineLatest( filter$, mainForm$, table$,
    (filter, mainForm, table) => {
      return h("div.content",[
        h("div.row", [ h("div.col-md-5", [h("h1", "Adress Book")]) ]),
        h("div.row",[
            h("div.col-md-3", [
                filter,
                mainForm
              ]),
            h("div.col-md-9", [
                h("legend", "Contact List"),
                h("table.table.table-striped.table-bordered.table-hover.table-condensed",
                  [h("tbody#tbody", [
                      h("tr", [
                          h("th", "First Name"),
                          h("th", "Last Name"),
                          h("th", "Email"),
                          h("th.text-center", "Edit"),
                          h("th.text-center", "Delete")
                        ])
                    ].concat(table)
                  )])
              ])
          ])
      ]);
    }  );
}
export default view;
import {Rx} from "@cycle/core";

function makeLocalStorageSourceDriver(keyName) {
	return () => Rx.Observable.just( localStorage.getItem(keyName) );
}
function makeLocalStorageSinkDriver(keyName) {
	var subject = new Rx.Subject();
	return function (keyValue$) {
		keyValue$.subscribe( keyValue => {
			localStorage.setItem( keyName, keyValue );
			subject.onNext(keyValue);
		} );
		return subject;
	};
}

export default { makeLocalStorageSinkDriver, makeLocalStorageSourceDriver };
import {run} from "@cycle/core";
import {makeDOMDriver} from '@cycle/dom';
import adresses from "./components/adresses";
import Drivers from "./drivers";

const main = adresses;

run( main, {
	DOM: makeDOMDriver("#app"),
	localStorageSource: Drivers.makeLocalStorageSourceDriver("adress-book"),
	localStorageSink: Drivers.makeLocalStorageSinkDriver("adress-book")
} );
import {Rx} 				from "@cycle/core";
import {h} 					from '@cycle/dom';
import filterInput 	from "./filter-input/index";
import adressForm 	from "./adress-form/adress-form";
import tableItem 		from "./table-item/index";
import serialize 		from "./local-storage-sink";
import deserialize 	from "./local-storage-source";
import Validator		from "./form-validator";
import view				 from './view';

var transformToValidator = (str) =>  (new Validator({
		name: RegExp(str),
		lastname: RegExp(str),
		email: RegExp(str)
})) ;

// constructing the state of table

function tableConstructor( {DOM, list$, validator$} ) {
	 var ti$ = Rx.Observable.combineLatest( list$, validator$, ( {list} , valid) => {
		 return list.filter( valid.sel.bind(valid) ).map( adress => tableItem( DOM,  adress) );
	 } );
	 var vtree$ = ti$.map( (tis) => tis.map( (ti) => ti.DOM ) );
	 var edit$ = ti$.flatMapLatest( (tis) =>
	 		tis.map( (ti) => ti.edit$ )
		 		.reduce( (acc, cur) => acc.merge( cur ), new Rx.Subject()  )
		);
		var delete$ = ti$.flatMapLatest( (tis) =>
			 tis.map( (ti) => ti.delete$ )
				 .reduce( (acc, cur) => acc.merge( cur ), new Rx.Subject()  )
		 );
	return {
		DOM: vtree$,
		edit$,
		delete$
	}

}

function model(startList$, delete$, submit$ ){
  
  return startList$.flatMap( ({list}) => {
		let deleteFromList$ = delete$.map( (actAdr) => {
			let index = list.findIndex( (adr) => adr._id === actAdr._id );
			(index !== -1) ? list.splice( index, 1 ) : void 0;
			return {list};
		} );
		let pushToList$ = submit$.map( (actAdr) => {
			list.push(actAdr);
			return {list};
		} );
		return deleteFromList$.merge( pushToList$ );
	} );
}

function adresses( {DOM, localStorageSink, localStorageSource } ) {
  
	var filter = filterInput( DOM ); // returns {DOM, input$}
	var validator$ = filter.input$.startWith("").map( transformToValidator ); //just crazy implementing of filter 
	var startList$ = deserialize( localStorageSource ); //this guy fires 4 times instead of 1
	var sinkList$ = deserialize( localStorageSink );
	var list$ = startList$.concat( sinkList$ );

	var table = tableConstructor( {DOM, list$, validator$} ); // returns {DOM: vtree$,edit$,delete$	}
	var mainForm = adressForm( {DOM, edit$: table.edit$} ); // returns {DOM: vtree$, submit$}
	var removeAfterEditing$ = table.edit$.sample( mainForm.submit$ );
	var delete$ = removeAfterEditing$.merge( table.delete$ );

	var storage$ = model(startList$, delete$, mainForm.submit$);
	var vtree$ = view( filter.DOM, mainForm.DOM, table.DOM );
	
	return {
    DOM: vtree$,
    localStorageSink: serialize( storage$ )
  };
}

export default adresses;
import {Rx}				from "@cycle/core";
import {h} 				from '@cycle/dom';
import Validator	from "../form-validator";
import cuid 			from "cuid";

let formValidator = new Validator({
		name: /^\S+/,
		lastname:/^\S+/,
		email:/^\S+@\S+\.\S+/
});

function makeIcon(name = ""){
	return h("span.input-group-addon", [h(`span.glyphicon${name}`)])
}

function makeInputGroup( {id, placeholder, value, error,  icoName = ".glyphicon-user"} ){
 return h("div.input-group", [
						makeIcon(icoName),
						h(`input${id}.form-control.input-sm${error}`, {
							type: "text",
							form: "form",
							atributes: {placeholder},
							value
						})
					]);
}

function makeForm(
		{email="", firstname="", lastname=""} = {},
		{emailError = true, firstnameError=true, lastnameError=true} = {}
	){
	return h("form#form", [
					h("feildset", [
						h("legend", "Add new contact"),
						makeInputGroup(
								{id: "#firstname",
								placeholder: "First name",
								error: firstnameError ? "" : ".error",
								value: firstname}
							),
						makeInputGroup(
								{id: "#lastname",
								placeholder: "Last name",
								error: lastnameError ? "" : ".error",
								value: lastname}
							),
						makeInputGroup(
								{id:"#email",
								placeholder: "Email",
								error: emailError ? "" : ".error",
								icoName: ".glyphicon-envelope",
								value: email}
							),
						h("button#btn.btn.btn-default.btn-sm", {type: "submit"}, [
							h("span.glyphicon.glyphicon-plus-sign"),
							"  Save"
							])
						])
			])
}

function intent(DOM) {
	return DOM.get("#form", "submit").map( e => {
		e.preventDefault();
		console.log("submitting"); //fires 4 times
		return e.target.elements;
	} );
}

function model( submit$ ) {
	var adres$ = submit$.map(
			({email, firstname, lastname}) => ({email: email.value,
					firstname: firstname.value,
					lastname: lastname.value})
		);
	var valid$ = adres$.filter( formValidator.valid.bind(formValidator) )
		.map( (adr) => {
			adr._id = cuid();
			return adr;
		} );
	var invalid$ = adres$.filter( formValidator.invalid.bind(formValidator) );
	return  { valid$, invalid$ };
}

function view( { valid$, invalid$ }, edit$ ) {
	var vtreeSuccess$ = valid$.map( () => makeForm() );
	var vtreeFail$ = invalid$.map( ( invalidAdr ) => makeForm( invalidAdr, formValidator.checkFields(invalidAdr) ) );
	var vtreeEdit$ = edit$.map( (item) => makeForm( item ));
	var vtree$ = vtreeSuccess$.startWith(makeForm()).merge(vtreeFail$, vtreeEdit$);
	return vtree$;
}

function adressForm( {DOM, edit$} ) {
	var action$ = intent(DOM);
	var states = model(action$);
	var vtree$ = view( states, edit$ );
	return {
		DOM: vtree$,
		submit$: states.valid$
	};
}

export default adressForm;