adamc@915: con colMeta' = fn (row :: Type) (t :: Type) => adamc@915: {Header : string, adamc@915: Project : row -> transaction t, adamc@915: Update : row -> t -> transaction row, adamc@915: Display : t -> xbody, adamc@915: Edit : t -> xbody, adamc@915: Validate : t -> signal bool} adamc@915: adamc@915: con colMeta = fn (row :: Type) (global_t :: (Type * Type)) => adamc@915: {Initialize : transaction global_t.1, adamc@915: Handlers : global_t.1 -> colMeta' row global_t.2} adamc@915: adamc@935: con aggregateMeta = fn (row :: Type) (acc :: Type) => adamc@935: {Initial : acc, adamc@935: Step : row -> acc -> acc, adamc@935: Display : acc -> xbody} adamc@935: adamc@915: functor Make(M : sig adamc@915: type row adamc@936: type key adamc@936: val keyOf : row -> key adamc@936: adamc@915: val list : transaction (list row) adamc@915: val new : transaction row adamc@936: val save : key -> row -> transaction unit adamc@936: val delete : key -> transaction unit adamc@915: adamc@915: con cols :: {(Type * Type)} adamc@915: val cols : $(map (colMeta row) cols) adamc@915: adamc@915: val folder : folder cols adamc@935: adamc@935: con aggregates :: {Type} adamc@935: val aggregates : $(map (aggregateMeta row) aggregates) adamc@937: val aggFolder : folder aggregates adamc@915: end) = struct adamc@915: style tabl adamc@915: style tr adamc@915: style th adamc@915: style td adamc@937: style agg adamc@915: adamc@915: fun make (row : M.row) [t] (m : colMeta' M.row t) : transaction t = m.Project row adamc@915: adamc@915: fun makeAll cols row = @@Monad.exec [transaction] _ [map snd M.cols] adamc@915: (map2 [fst] [colMeta M.row] [fn p :: (Type * Type) => transaction p.2] adamc@915: (fn [p] data meta => make row [_] (meta.Handlers data)) adamc@915: [_] M.folder cols M.cols) adamc@915: (@@Folder.mp [_] [_] M.folder) adamc@915: adamc@915: fun addRow cols rows row = adamc@915: rowS <- source row; adamc@915: cols <- makeAll cols row; adamc@915: colsS <- source cols; adamc@915: ud <- source False; adamc@915: Monad.ignore (Dlist.append rows {Row = rowS, adamc@915: Cols = colsS, adamc@915: Updating = ud}) adamc@915: adamc@915: type grid = {Cols : $(map fst M.cols), adamc@915: Rows : Dlist.dlist {Row : source M.row, Cols : source ($(map snd M.cols)), Updating : source bool}} adamc@915: adamc@915: val createMetas = Monad.mapR [colMeta M.row] [fst] adamc@915: (fn [nm :: Name] [p :: (Type * Type)] meta => meta.Initialize) adamc@915: [_] M.folder M.cols adamc@915: adamc@915: val grid = adamc@915: cols <- createMetas; adamc@915: rows <- Dlist.create; adamc@915: return {Cols = cols, Rows = rows} adamc@915: adamc@915: fun sync {Cols = cols, Rows = rows} = adamc@915: Dlist.clear rows; adamc@915: init <- rpc M.list; adamc@915: List.app (addRow cols rows) init adamc@915: adamc@915: fun render grid = adamc@915: adamc@915: adamc@915: ) adamc@937: [_] M.folder grid.Cols M.cols} adamc@937: adamc@915: adamc@937: {Dlist.render (fn {Row = rowS, Cols = colsS, Updating = ud} pos => adamc@937: let adamc@937: val delete = adamc@937: Dlist.delete pos; adamc@937: row <- get rowS; adamc@937: rpc (M.delete (M.keyOf row)) adamc@915: adamc@937: val update = set ud True adamc@915: adamc@937: val cancel = adamc@937: set ud False; adamc@937: row <- get rowS; adamc@937: cols <- makeAll grid.Cols row; adamc@937: set colsS cols adamc@937: adamc@937: val save = adamc@937: cols <- get colsS; adamc@937: errors <- Monad.foldR3 [fst] [colMeta M.row] [snd] [fn _ => option string] adamc@937: (fn [nm :: Name] [p :: (Type * Type)] [rest :: {(Type * Type)}] adamc@937: [[nm] ~ rest] data meta v errors => adamc@937: b <- current ((meta.Handlers data).Validate v); adamc@937: return (if b then adamc@937: errors adamc@937: else adamc@937: case errors of adamc@937: None => Some ((meta.Handlers data).Header) adamc@937: | Some s => Some ((meta.Handlers data).Header adamc@937: ^ ", " ^ s))) adamc@937: None [_] M.folder grid.Cols M.cols cols; adamc@915: adamc@937: case errors of adamc@937: Some s => alert ("Can't save because the following columns have invalid values:\n" adamc@937: ^ s) adamc@937: | None => adamc@937: set ud False; adamc@937: row <- get rowS; adamc@937: row' <- Monad.foldR3 [fst] [colMeta M.row] [snd] [fn _ => M.row] adamc@937: (fn [nm :: Name] [t :: (Type * Type)] adamc@937: [rest :: {(Type * Type)}] adamc@937: [[nm] ~ rest] data meta v row' => adamc@937: (meta.Handlers data).Update row' v) adamc@937: row [_] M.folder grid.Cols M.cols cols; adamc@937: rpc (M.save (M.keyOf row) row'); adamc@937: set rowS row'; adamc@937: adamc@937: cols <- makeAll grid.Cols row'; adamc@937: set colsS cols adamc@937: in adamc@937: adamc@937: adamc@937: adamc@937: adamc@937: adamc@937: adamc@937: ) adamc@937: [_] M.folder grid.Cols M.cols cols)}/> adamc@937: adamc@937: end) grid.Rows} adamc@915: adamc@937: Monad.mapR2 [aggregateMeta M.row] [id] [id] adamc@937: (fn [nm :: Name] [t :: Type] meta acc => adamc@937: Monad.mp (fn v => meta.Step v acc) adamc@937: (signal row.Row)) adamc@937: [_] M.aggFolder M.aggregates) adamc@937: (mp [aggregateMeta M.row] [id] adamc@937: (fn [t] meta => meta.Initial) adamc@937: [_] M.aggFolder M.aggregates) grid.Rows; adamc@937: return adamc@937: ) adamc@937: [_] M.aggFolder M.aggregates rows} adamc@937: }/> adamc@915:
adamc@915: {foldRX2 [fst] [colMeta M.row] [_] adamc@937: (fn [nm :: Name] [p :: (Type * Type)] [rest :: {(Type * Type)}] [[nm] ~ rest] adamc@937: data (meta : colMeta M.row p) => adamc@937: {[(meta.Handlers data).Header]}
adamc@937: adamc@937: adamc@937: adamc@937: else adamc@937: !) adamc@937: else adamc@937: return }/> adamc@937:
adamc@937: {foldRX2 [aggregateMeta M.row] [id] [_] adamc@937: (fn [nm :: Name] [t :: Type] [rest :: {Type}] [[nm] ~ rest] meta acc => adamc@937: {meta.Display acc}
adamc@915: adamc@915: