Mercurial > urweb
view demo/more/grid.ur @ 1739:c414850f206f
Add support for -boot flag, which allows in-tree execution of Ur/Web
The boot flag rewrites most hardcoded paths to point to the build
directory, and also forces static compilation. This is convenient
for developing Ur/Web, or if you cannot 'sudo make install' Ur/Web.
The following changes were made:
* Header files were moved to include/urweb instead of include;
this lets FFI users point their C_INCLUDE_PATH at this directory
at write <urweb/urweb.h>. For internal Ur/Web executables,
we simply pass -I$PATH/include/urweb as normal.
* Differentiate between LIB and SRCLIB; SRCLIB is Ur and JavaScript
source files, while LIB is compiled products from libtool. For
in-tree compilation these live in different places.
* No longer reference Config for paths; instead use Settings; these
settings can be changed dynamically by Compiler.enableBoot ()
(TODO: add a disableBoot function.)
* config.h is now generated directly in include/urweb/config.h,
for consistency's sake (especially since it gets installed
along with the rest of the headers!)
* All of the autotools build products got updated.
* The linkStatic field in protocols now only contains the name of the
build product, and not the absolute path.
Future users have to be careful not to reference the Settings files
to early, lest they get an old version (this was the source of two
bugs during development of this patch.)
author | Edward Z. Yang <ezyang@mit.edu> |
---|---|
date | Wed, 02 May 2012 17:17:57 -0400 |
parents | 9253765d7724 |
children | e6bc6bbd7a32 |
line wrap: on
line source
con colMeta' = fn (row :: Type) (input :: Type) (filter :: Type) => {Header : string, Project : row -> transaction input, Update : row -> input -> transaction row, Display : input -> xbody, Edit : input -> xbody, Validate : input -> signal bool, CreateFilter : transaction filter, DisplayFilter : filter -> xbody, Filter : filter -> row -> signal bool, Sort : option (row -> row -> bool)} con colMeta = fn (row :: Type) (global :: Type, input :: Type, filter :: Type) => {Initialize : transaction global, Handlers : global -> colMeta' row input filter} con aggregateMeta = fn (row :: Type) (acc :: Type) => {Initial : acc, Step : row -> acc -> acc, Display : acc -> xbody} functor Make(M : sig type row type key val keyOf : row -> key val list : transaction (list row) val new : transaction row val save : key -> row -> transaction unit val delete : key -> transaction unit con cols :: {(Type * Type * Type)} val cols : $(map (colMeta row) cols) val folder : folder cols con aggregates :: {Type} val aggregates : $(map (aggregateMeta row) aggregates) val aggFolder : folder aggregates val pageLength : option int end) = struct style tabl style tr style th style td style agg fun make (row : M.row) [input] [filter] (m : colMeta' M.row input filter) : transaction input = m.Project row fun makeAll cols row = @@Monad.exec [transaction] _ [map snd3 M.cols] (@map2 [fst3] [colMeta M.row] [fn p => transaction (snd3 p)] (fn [p] data meta => make row (meta.Handlers data)) M.folder cols M.cols) (@@Folder.mp [_] [_] M.folder) type listT = {Row : source M.row, Cols : source ($(map snd3 M.cols)), Updating : source bool, Selected : source bool} type grid = {Cols : $(map fst3 M.cols), Rows : Dlist.dlist listT, Selection : source bool, Filters : $(map thd3 M.cols), Sort : source (option (M.row -> M.row -> bool)), Position : source int} fun newRow cols row = rowS <- source row; cols <- makeAll cols row; colsS <- source cols; ud <- source False; sd <- source False; return {Row = rowS, Cols = colsS, Updating = ud, Selected = sd} fun addRow cols rows row = r <- newRow cols row; Monad.ignore (Dlist.append rows r) val grid = cols <- @Monad.mapR _ [colMeta M.row] [fst3] (fn [nm :: Name] [p :: (Type * Type * Type)] meta => meta.Initialize) M.folder M.cols; filters <- @Monad.mapR2 _ [colMeta M.row] [fst3] [thd3] (fn [nm :: Name] [p :: (Type * Type * Type)] meta state => (meta.Handlers state).CreateFilter) M.folder M.cols cols; rows <- Dlist.create; sel <- source False; sort <- source None; pos <- source 0; return {Cols = cols, Rows = rows, Selection = sel, Filters = filters, Sort = sort, Position = pos} fun sync {Cols = cols, Rows = rows, ...} = Dlist.clear rows; init <- rpc M.list; rs <- List.mapM (newRow cols) init; Dlist.replace rows rs fun myFilter grid all = row <- signal all.Row; @foldR3 [colMeta M.row] [fst3] [thd3] [fn _ => M.row -> signal bool] (fn [nm :: Name] [p :: (Type * Type * Type)] [rest :: {(Type * Type * Type)}] [[nm] ~ rest] meta state filter combinedFilter row => previous <- combinedFilter row; this <- (meta.Handlers state).Filter filter row; return (previous && this)) (fn _ => return True) M.folder M.cols grid.Cols grid.Filters row fun render (grid : grid) = <xml> <table class={tabl}> <tr class={tr}> <th/> <th/> <th><button value="No sort" onclick={set grid.Sort None}/></th> {@mapX2 [fst3] [colMeta M.row] [tr] (fn [nm :: Name] [p :: (Type * Type * Type)] [rest :: {(Type * Type * Type)}] [[nm] ~ rest] data (meta : colMeta M.row p) => <xml><th class={th}> {case (meta.Handlers data).Sort of None => txt (meta.Handlers data).Header | sort => <xml><button value={(meta.Handlers data).Header} onclick={set grid.Sort sort}/></xml>} </th></xml>) M.folder grid.Cols M.cols} </tr> {Dlist.render (fn {Row = rowS, Cols = colsS, Updating = ud, Selected = sd} pos => let val delete = Dlist.delete pos; row <- get rowS; rpc (M.delete (M.keyOf row)) val update = set ud True val cancel = set ud False; row <- get rowS; cols <- makeAll grid.Cols row; set colsS cols val save = cols <- get colsS; errors <- @Monad.foldR3 _ [fst3] [colMeta M.row] [snd3] [fn _ => option string] (fn [nm :: Name] [p :: (Type * Type * Type)] [rest :: {(Type * Type * Type)}] [[nm] ~ rest] data meta v errors => b <- current ((meta.Handlers data).Validate v); return (if b then errors else case errors of None => Some ((meta.Handlers data).Header) | Some s => Some ((meta.Handlers data).Header ^ ", " ^ s))) None M.folder grid.Cols M.cols cols; case errors of Some s => alert ("Can't save because the following columns have invalid values:\n" ^ s) | None => set ud False; row <- get rowS; row' <- @Monad.foldR3 _ [fst3] [colMeta M.row] [snd3] [fn _ => M.row] (fn [nm :: Name] [t :: (Type * Type * Type)] [rest :: {(Type * Type * Type)}] [[nm] ~ rest] data meta v row' => (meta.Handlers data).Update row' v) row M.folder grid.Cols M.cols cols; rpc (M.save (M.keyOf row) row'); set rowS row'; cols <- makeAll grid.Cols row'; set colsS cols in <xml><tr class={tr}> <td> <dyn signal={b <- signal grid.Selection; return (if b then <xml><ccheckbox source={sd}/></xml> else <xml/>)}/> </td> <td> <dyn signal={b <- signal ud; return (if b then <xml><button value="Save" onclick={save}/></xml> else <xml><button value="Update" onclick={update}/></xml>)}/> </td> <td><dyn signal={b <- signal ud; return (if b then <xml><button value="Cancel" onclick={cancel}/></xml> else <xml><button value="Delete" onclick={delete}/></xml>)}/> </td> <dyn signal={cols <- signal colsS; return (@mapX3 [fst3] [colMeta M.row] [snd3] [_] (fn [nm :: Name] [t :: (Type * Type * Type)] [rest :: {(Type * Type * Type)}] [[nm] ~ rest] data meta v => <xml><td class={td}> <dyn signal={b <- signal ud; return (if b then (meta.Handlers data).Edit v else (meta.Handlers data).Display v)}/> <dyn signal={b <- signal ud; if b then valid <- (meta.Handlers data).Validate v; return (if valid then <xml/> else <xml>!</xml>) else return <xml/>}/> </td></xml>) M.folder grid.Cols M.cols cols)}/> </tr></xml> end) {StartPosition = case M.pageLength of None => return None | Some len => avail <- Dlist.numPassing (myFilter grid) grid.Rows; pos <- signal grid.Position; return (Some (if pos >= avail then 0 else pos)), MaxLength = return M.pageLength, Filter = myFilter grid, Sort = f <- signal grid.Sort; return (Option.mp (fn f r1 r2 => r1 <- signal r1.Row; r2 <- signal r2.Row; return (f r1 r2)) f)} grid.Rows} <dyn signal={rows <- Dlist.foldl (fn row : listT => @Monad.mapR2 _ [aggregateMeta M.row] [ident] [ident] (fn [nm :: Name] [t :: Type] meta acc => Monad.mp (fn v => meta.Step v acc) (signal row.Row)) M.aggFolder M.aggregates) (@mp [aggregateMeta M.row] [ident] (fn [t] meta => meta.Initial) M.aggFolder M.aggregates) grid.Rows; return <xml><tr> <th colspan={3}>Aggregates</th> {@mapX2 [aggregateMeta M.row] [ident] [_] (fn [nm :: Name] [t :: Type] [rest :: {Type}] [[nm] ~ rest] meta acc => <xml><td class={agg}>{meta.Display acc}</td></xml>) M.aggFolder M.aggregates rows} </tr></xml>}/> <tr><th colspan={3}>Filters</th> {@mapX3 [colMeta M.row] [fst3] [thd3] [_] (fn [nm :: Name] [p :: (Type * Type * Type)] [rest :: {(Type * Type * Type)}] [[nm] ~ rest] meta state filter => <xml><td>{(meta.Handlers state).DisplayFilter filter}</td></xml>) M.folder M.cols grid.Cols grid.Filters} </tr> </table> {case M.pageLength of None => <xml/> | Some plen => <xml> <dyn signal={avail <- Dlist.numPassing (myFilter grid) grid.Rows; return (if avail <= plen then <xml/> else let val numPages = avail / plen val numPages = if numPages * plen < avail then numPages + 1 else numPages fun pages n = if n * plen >= avail then <xml/> else <xml> <dyn signal={pos <- signal grid.Position; return (if n * plen = pos then <xml><b>{[n + 1]}</b></xml> else <xml> <button value={show (n + 1)} onclick={set grid.Position (n * plen) }/></xml>)}/> {if (n + 1) * plen >= avail then <xml/> else <xml>|</xml>} {pages (n + 1)} </xml> in <xml><p><b>Pages:</b> {pages 0}</p></xml> end)}/> </xml>} <button value="New row" onclick={row <- rpc M.new; addRow grid.Cols grid.Rows row}/> <button value="Refresh" onclick={sync grid}/> </xml> fun showSelection grid = grid.Selection fun selection grid = Dlist.foldl (fn {Row = rowS, Selected = sd, ...} ls => sd <- signal sd; if sd then row <- signal rowS; return (row :: ls) else return ls) [] grid.Rows end