import * as React from "react" import * as ReactDOM from "react-dom" import {List, Map, Set} from "immutable" import * as Immutable from "immutable" import {C, unit, bind, Mode} from './core' import {string, number, bool, date, date_time, time} from './primitives' import {button, selector, multi_selector, label, image, file, div} from './html' import {custom, repeat, any, lift_promise, retract, delay, simple_menu} from './combinators' import * as Moment from 'moment' export type FormErrors = { errors:Immutable.Map> } export type FormData = { model:M } & FormErrors export type FormEntry = | { kind:"string", field_name:string, in:(_:M)=>string, out:(_:M)=>(_:string)=>M, get_errors:(_:M)=>Array } | { kind:"number", field_name:string, in:(_:M)=>number, out:(_:M)=>(_:number)=>M, get_errors:(_:M)=>Array } | { kind:"date", field_name:string, in:(_:M)=>Moment.Moment, out:(_:M)=>(_:Moment.Moment)=>M, get_errors:(_:M)=>Array } | { kind:"time", field_name:string, in:(_:M)=>Moment.Moment, out:(_:M)=>(_:Moment.Moment)=>M, get_errors:(_:M)=>Array } | { kind:"datetime", field_name:string, in:(_:M)=>Moment.Moment, out:(_:M)=>(_:Moment.Moment)=>M, get_errors:(_:M)=>Array } | { kind:"image", field_name:string, in:(_:M)=>string, out:(_:M)=>(_:string)=>M, get_errors:(_:M)=>Array } | { kind:"file", field_name:string, filename:(_:M) => string, url:(_:M) => string, in:(_:M)=>File, out:(_:M)=>(_:File)=>M, get_errors:(_:M)=>Array } | { kind:"lazy image", field_name:string, download:(c:M) => C, upload:(c:M) => (src:string) => C } | { kind:"lazy file", field_name:string, filename:(_:M) => string, out:(_:M)=>(_:File)=>M, url:(_:M) => string, upload:(_:M) => (_:File) => C } export let simple_inner_form = function(mode:Mode, model_name:(_:M)=>string, entries:FormEntry[]) : (_:FormData) => C> { return c => repeat>(`${model_name(c.model)}_repeater`)(c => any, FormData>(`${model_name(c.model)}_inner_form`)( entries.map(e => e.kind == "string" ? retract, string>(`${model_name(c.model)}_${e.field_name}_retract`)( c => e.in(c.model), c => s => { let new_c = e.out(c.model)(s) let errors = e.get_errors(new_c) return { model:new_c, errors: errors.length > 0 ? c.errors.set(e.field_name, errors) : c.errors.remove(e.field_name)} }, label(e.field_name, true)(div(`monadic-field ${c.errors.has(e.field_name) ? "monadic-field-error" : ""}`) // c.errors.has(e.field_name) ? // c.errors.get(e.field_name).map(error => // _ => string("view", `${model_name(c.model)}_${e.field_name}_error`)(`Error: ${error}`).ignore()) // : (string(mode, "text", `${model_name(c.model)}_${e.field_name}`)))) : e.kind == "number" ? retract, number>(`${model_name(c.model)}_${e.field_name}_retract`)( c => e.in(c.model), c => s => { let new_c = e.out(c.model)(s) let errors = e.get_errors(new_c) return { model:new_c, errors: errors.length > 0 ? c.errors.set(e.field_name, errors) : c.errors.remove(e.field_name)} }, label(e.field_name, true)(div(`monadic-field ${c.errors.has(e.field_name) ? "monadic-field-error" : ""}`) (number(mode, `${model_name(c.model)}_${e.field_name}`)))) : e.kind == "image" ? retract, string>(`${model_name(c.model)}_${e.field_name}_retract`)( c => e.in(c.model), c => s => { let new_c = e.out(c.model)(s) let errors = e.get_errors(new_c) return { model:new_c, errors: errors.length > 0 ? c.errors.set(e.field_name, errors) : c.errors.remove(e.field_name)} }, label(e.field_name, true)(div(`monadic-field ${c.errors.has(e.field_name) ? "monadic-field-error" : ""}`) (image(mode, `${model_name(c.model)}_${e.field_name}`)))) : e.kind == "lazy image" ? retract, void>(`${model_name(c.model)}_${e.field_name}_retract`)( c => null, c => _ => c, _ => e.download(c.model).then(`${model_name(c.model)}_${e.field_name}_downloader`, src => repeat()((src:string) => label(e.field_name, true)(image(mode, `${model_name(c.model)}_${e.field_name}`))(src).then(`${model_name(c.model)}_${e.field_name}_uploader`, new_src => e.upload(c.model)(new_src)))(src)).ignore()) : e.kind == "file" ? retract, File>(`${model_name(c.model)}_${e.field_name}_retract`)( c => e.in(c.model), c => s => { let new_c = e.out(c.model)(s) let errors = e.get_errors(new_c) return { model:new_c, errors: errors.length > 0 ? c.errors.set(e.field_name, errors) : c.errors.remove(e.field_name)} }, label(e.field_name, true)(div(`monadic-field ${c.errors.has(e.field_name) ? "monadic-field-error" : ""}`) (_ => file(mode, e.filename(c.model), e.url(c.model)).ignore_with(null)))) : e.kind == "lazy file" ? retract, File>(`${model_name(c.model)}_${e.field_name}_retract`)( c => null, c => f => ({...c, model:e.out(c.model)(f)}), _ => label(e.field_name, true)(_ => file(mode, e.filename(c.model), e.url(c.model)).then(`${model_name(c.model)}_${e.field_name}_uploader`, f => e.upload(c.model)(f).ignore_with(f)))(null)) : e.kind == "datetime" ? retract, Moment.Moment>(`${model_name(c.model)}_${e.field_name}_retract`)( c => e.in(c.model), c => s => { let new_c = e.out(c.model)(s) let errors = e.get_errors(new_c) return { model:new_c, errors: errors.length > 0 ? c.errors.set(e.field_name, errors) : c.errors.remove(e.field_name)} }, date_time(mode, e.field_name, () => "Creating date_time formfield") ) : null ))(c))(c) } export let form_errors = function(model_name:(_:M)=>string, entries:FormEntry[]) : ((fd:FormData) => C>) { return fd => any, FormData>(`form-errors`)( entries.map(e => e.kind != "lazy image" && e.kind != "image" ? c => c.errors.has(e.field_name) ? string("view", "text", `${model_name(c.model)}_${e.field_name}`)(`${c.errors.get(e.field_name)}`).ignore(`${model_name(c.model)}_${e.field_name}_error_ignore`).never>() : unit(null).never>() : c => unit(null).never>() ) )(fd).filter(_ => false) } export let simple_form_with_autosave = function(mode:Mode, model_name:(_:M)=>string, entries:FormEntry[], download_M:C, upload_M:(_:M)=>C) : C { return download_M.then(undefined, c => simple_inner_form(mode, model_name, entries)({ model:c, errors:Immutable.Map>() }) .then(`${model_name(c)}_error_recap`, any, FormData>()([ c => form_errors(model_name, entries)(c).ignore_with(c).filter(_ => false), c => unit>(c) ])) .filter(c => c.errors.isEmpty(), `${model_name(c)}_error_filter`) .map(c => c.model).then(`${model_name(c)}_uploader`, delay(200, `${model_name(c)}_delay`)(upload_M)).ignore()) } export let simple_form_with_save_button = function(mode:Mode, model_name:(_:M)=>string, entries:FormEntry[], download_M:C, upload_M:(_:M)=>C) : C { return download_M.then(undefined, c => simple_inner_form(mode, model_name, entries)({ model:c, errors:Immutable.Map>() }).then(`${model_name(c)}_form`, c => any, FormData>()([ form_errors(model_name, entries), c => button>(`save`, !c.errors.isEmpty())(c) ])(c) ).map(c => c.model).then(`${model_name(c)}_uploader`, delay(200, `${model_name(c)}_delayer`)(upload_M)).ignore()) } export let simple_form_with_prev_and_next_buttons = function(mode:Mode, model_name:(_:M)=>string, entries:FormEntry[], prev_enabled:(_:FormData)=>boolean, next_enabled:(_:FormData)=>boolean, prev_visible:(_:FormData)=>boolean, next_visible:(_:FormData)=>boolean, on_prev:(_:M)=>M, on_next:(_:M)=>M) : (_:FormData) => C> { return c => simple_inner_form(mode, model_name, entries)(c).then(`${model_name(c.model)}_form`, c => any, FormData>()([ form_errors(model_name, entries), c => prev_visible(c) ? button>(`prev`, prev_enabled(c))(c).map>(c => ({...c, model:on_prev(c.model)})) : unit>(c).filter(_ => false), c => next_visible(c) ? button>(`next`, next_enabled(c))(c).map>(c => ({...c, model:on_next(c.model)})) : unit>(c).filter(_ => false) ])(c) ) }