adamc@915
|
1 con colMeta' = fn (row :: Type) (t :: Type) =>
|
adamc@915
|
2 {Header : string,
|
adamc@915
|
3 Project : row -> transaction t,
|
adamc@915
|
4 Update : row -> t -> transaction row,
|
adamc@915
|
5 Display : t -> xbody,
|
adamc@915
|
6 Edit : t -> xbody,
|
adamc@915
|
7 Validate : t -> signal bool}
|
adamc@915
|
8
|
adamc@915
|
9 con colMeta = fn (row :: Type) (global_t :: (Type * Type)) =>
|
adamc@915
|
10 {Initialize : transaction global_t.1,
|
adamc@915
|
11 Handlers : global_t.1 -> colMeta' row global_t.2}
|
adamc@915
|
12
|
adamc@935
|
13 con aggregateMeta = fn (row :: Type) (acc :: Type) =>
|
adamc@935
|
14 {Initial : acc,
|
adamc@935
|
15 Step : row -> acc -> acc,
|
adamc@935
|
16 Display : acc -> xbody}
|
adamc@935
|
17
|
adamc@915
|
18 functor Make(M : sig
|
adamc@915
|
19 type row
|
adamc@936
|
20 type key
|
adamc@936
|
21 val keyOf : row -> key
|
adamc@936
|
22
|
adamc@915
|
23 val list : transaction (list row)
|
adamc@915
|
24 val new : transaction row
|
adamc@936
|
25 val save : key -> row -> transaction unit
|
adamc@936
|
26 val delete : key -> transaction unit
|
adamc@915
|
27
|
adamc@915
|
28 con cols :: {(Type * Type)}
|
adamc@915
|
29 val cols : $(map (colMeta row) cols)
|
adamc@915
|
30
|
adamc@915
|
31 val folder : folder cols
|
adamc@935
|
32
|
adamc@935
|
33 con aggregates :: {Type}
|
adamc@935
|
34 val aggregates : $(map (aggregateMeta row) aggregates)
|
adamc@937
|
35 val aggFolder : folder aggregates
|
adamc@915
|
36 end) = struct
|
adamc@915
|
37 style tabl
|
adamc@915
|
38 style tr
|
adamc@915
|
39 style th
|
adamc@915
|
40 style td
|
adamc@937
|
41 style agg
|
adamc@915
|
42
|
adamc@915
|
43 fun make (row : M.row) [t] (m : colMeta' M.row t) : transaction t = m.Project row
|
adamc@915
|
44
|
adamc@915
|
45 fun makeAll cols row = @@Monad.exec [transaction] _ [map snd M.cols]
|
adamc@915
|
46 (map2 [fst] [colMeta M.row] [fn p :: (Type * Type) => transaction p.2]
|
adamc@915
|
47 (fn [p] data meta => make row [_] (meta.Handlers data))
|
adamc@915
|
48 [_] M.folder cols M.cols)
|
adamc@915
|
49 (@@Folder.mp [_] [_] M.folder)
|
adamc@915
|
50
|
adamc@940
|
51 type grid = {Cols : $(map fst M.cols),
|
adamc@940
|
52 Rows : Dlist.dlist {Row : source M.row,
|
adamc@940
|
53 Cols : source ($(map snd M.cols)),
|
adamc@940
|
54 Updating : source bool,
|
adamc@940
|
55 Selected : source bool},
|
adamc@940
|
56 Selection : source bool}
|
adamc@940
|
57
|
adamc@915
|
58 fun addRow cols rows row =
|
adamc@915
|
59 rowS <- source row;
|
adamc@915
|
60 cols <- makeAll cols row;
|
adamc@915
|
61 colsS <- source cols;
|
adamc@915
|
62 ud <- source False;
|
adamc@940
|
63 sd <- source False;
|
adamc@915
|
64 Monad.ignore (Dlist.append rows {Row = rowS,
|
adamc@915
|
65 Cols = colsS,
|
adamc@940
|
66 Updating = ud,
|
adamc@940
|
67 Selected = sd})
|
adamc@915
|
68
|
adamc@915
|
69 val createMetas = Monad.mapR [colMeta M.row] [fst]
|
adamc@915
|
70 (fn [nm :: Name] [p :: (Type * Type)] meta => meta.Initialize)
|
adamc@915
|
71 [_] M.folder M.cols
|
adamc@915
|
72
|
adamc@915
|
73 val grid =
|
adamc@915
|
74 cols <- createMetas;
|
adamc@915
|
75 rows <- Dlist.create;
|
adamc@940
|
76 sel <- source False;
|
adamc@940
|
77 return {Cols = cols, Rows = rows, Selection = sel}
|
adamc@915
|
78
|
adamc@940
|
79 fun sync {Cols = cols, Rows = rows, ...} =
|
adamc@915
|
80 Dlist.clear rows;
|
adamc@915
|
81 init <- rpc M.list;
|
adamc@915
|
82 List.app (addRow cols rows) init
|
adamc@915
|
83
|
adamc@915
|
84 fun render grid = <xml>
|
adamc@915
|
85 <table class={tabl}>
|
adamc@915
|
86 <tr class={tr}>
|
adamc@915
|
87 <th/> <th/>
|
adamc@915
|
88 {foldRX2 [fst] [colMeta M.row] [_]
|
adamc@937
|
89 (fn [nm :: Name] [p :: (Type * Type)] [rest :: {(Type * Type)}] [[nm] ~ rest]
|
adamc@937
|
90 data (meta : colMeta M.row p) =>
|
adamc@937
|
91 <xml><th class={th}>{[(meta.Handlers data).Header]}</th></xml>)
|
adamc@937
|
92 [_] M.folder grid.Cols M.cols}
|
adamc@937
|
93 </tr>
|
adamc@915
|
94
|
adamc@940
|
95 {Dlist.render (fn {Row = rowS, Cols = colsS, Updating = ud, Selected = sd} pos =>
|
adamc@937
|
96 let
|
adamc@937
|
97 val delete =
|
adamc@937
|
98 Dlist.delete pos;
|
adamc@937
|
99 row <- get rowS;
|
adamc@937
|
100 rpc (M.delete (M.keyOf row))
|
adamc@915
|
101
|
adamc@937
|
102 val update = set ud True
|
adamc@915
|
103
|
adamc@937
|
104 val cancel =
|
adamc@937
|
105 set ud False;
|
adamc@937
|
106 row <- get rowS;
|
adamc@937
|
107 cols <- makeAll grid.Cols row;
|
adamc@937
|
108 set colsS cols
|
adamc@937
|
109
|
adamc@937
|
110 val save =
|
adamc@937
|
111 cols <- get colsS;
|
adamc@937
|
112 errors <- Monad.foldR3 [fst] [colMeta M.row] [snd] [fn _ => option string]
|
adamc@937
|
113 (fn [nm :: Name] [p :: (Type * Type)] [rest :: {(Type * Type)}]
|
adamc@937
|
114 [[nm] ~ rest] data meta v errors =>
|
adamc@937
|
115 b <- current ((meta.Handlers data).Validate v);
|
adamc@937
|
116 return (if b then
|
adamc@937
|
117 errors
|
adamc@937
|
118 else
|
adamc@937
|
119 case errors of
|
adamc@937
|
120 None => Some ((meta.Handlers data).Header)
|
adamc@937
|
121 | Some s => Some ((meta.Handlers data).Header
|
adamc@937
|
122 ^ ", " ^ s)))
|
adamc@937
|
123 None [_] M.folder grid.Cols M.cols cols;
|
adamc@915
|
124
|
adamc@937
|
125 case errors of
|
adamc@937
|
126 Some s => alert ("Can't save because the following columns have invalid values:\n"
|
adamc@937
|
127 ^ s)
|
adamc@937
|
128 | None =>
|
adamc@937
|
129 set ud False;
|
adamc@937
|
130 row <- get rowS;
|
adamc@937
|
131 row' <- Monad.foldR3 [fst] [colMeta M.row] [snd] [fn _ => M.row]
|
adamc@937
|
132 (fn [nm :: Name] [t :: (Type * Type)]
|
adamc@937
|
133 [rest :: {(Type * Type)}]
|
adamc@937
|
134 [[nm] ~ rest] data meta v row' =>
|
adamc@937
|
135 (meta.Handlers data).Update row' v)
|
adamc@937
|
136 row [_] M.folder grid.Cols M.cols cols;
|
adamc@937
|
137 rpc (M.save (M.keyOf row) row');
|
adamc@937
|
138 set rowS row';
|
adamc@937
|
139
|
adamc@937
|
140 cols <- makeAll grid.Cols row';
|
adamc@937
|
141 set colsS cols
|
adamc@937
|
142 in
|
adamc@937
|
143 <xml><tr class={tr}>
|
adamc@937
|
144 <td>
|
adamc@940
|
145 <dyn signal={b <- signal grid.Selection;
|
adamc@940
|
146 return (if not b then
|
adamc@940
|
147 <xml><ccheckbox source={sd}/></xml>
|
adamc@940
|
148 else
|
adamc@940
|
149 <xml>No</xml>)}/>
|
adamc@940
|
150 </td>
|
adamc@940
|
151
|
adamc@940
|
152 <td>
|
adamc@937
|
153 <dyn signal={b <- signal ud;
|
adamc@937
|
154 return (if b then
|
adamc@937
|
155 <xml><button value="Save" onclick={save}/></xml>
|
adamc@937
|
156 else
|
adamc@937
|
157 <xml><button value="Update" onclick={update}/></xml>)}/>
|
adamc@937
|
158 </td>
|
adamc@937
|
159
|
adamc@937
|
160 <td><dyn signal={b <- signal ud;
|
adamc@937
|
161 return (if b then
|
adamc@937
|
162 <xml><button value="Cancel" onclick={cancel}/></xml>
|
adamc@937
|
163 else
|
adamc@937
|
164 <xml><button value="Delete" onclick={delete}/></xml>)}/>
|
adamc@937
|
165 </td>
|
adamc@937
|
166
|
adamc@937
|
167 <dyn signal={cols <- signal colsS;
|
adamc@937
|
168 return (foldRX3 [fst] [colMeta M.row] [snd] [_]
|
adamc@915
|
169 (fn [nm :: Name] [t :: (Type * Type)]
|
adamc@915
|
170 [rest :: {(Type * Type)}]
|
adamc@937
|
171 [[nm] ~ rest] data meta v =>
|
adamc@937
|
172 <xml><td class={td}>
|
adamc@937
|
173 <dyn signal={b <- signal ud;
|
adamc@937
|
174 return (if b then
|
adamc@937
|
175 (meta.Handlers data).Edit v
|
adamc@937
|
176 else
|
adamc@937
|
177 (meta.Handlers data).Display
|
adamc@937
|
178 v)}/>
|
adamc@937
|
179 <dyn signal={b <- signal ud;
|
adamc@937
|
180 if b then
|
adamc@937
|
181 valid <-
|
adamc@937
|
182 (meta.Handlers data).Validate v;
|
adamc@937
|
183 return (if valid then
|
adamc@937
|
184 <xml/>
|
adamc@937
|
185 else
|
adamc@937
|
186 <xml>!</xml>)
|
adamc@937
|
187 else
|
adamc@937
|
188 return <xml/>}/>
|
adamc@937
|
189 </td></xml>)
|
adamc@937
|
190 [_] M.folder grid.Cols M.cols cols)}/>
|
adamc@937
|
191 </tr></xml>
|
adamc@937
|
192 end) grid.Rows}
|
adamc@915
|
193
|
adamc@937
|
194 <dyn signal={rows <- Dlist.foldl (fn row => Monad.mapR2 [aggregateMeta M.row] [id] [id]
|
adamc@937
|
195 (fn [nm :: Name] [t :: Type] meta acc =>
|
adamc@937
|
196 Monad.mp (fn v => meta.Step v acc)
|
adamc@937
|
197 (signal row.Row))
|
adamc@937
|
198 [_] M.aggFolder M.aggregates)
|
adamc@937
|
199 (mp [aggregateMeta M.row] [id]
|
adamc@937
|
200 (fn [t] meta => meta.Initial)
|
adamc@937
|
201 [_] M.aggFolder M.aggregates) grid.Rows;
|
adamc@937
|
202 return <xml><tr>
|
adamc@937
|
203 <td/><td/>
|
adamc@937
|
204 {foldRX2 [aggregateMeta M.row] [id] [_]
|
adamc@937
|
205 (fn [nm :: Name] [t :: Type] [rest :: {Type}] [[nm] ~ rest] meta acc =>
|
adamc@937
|
206 <xml><td class={agg}>{meta.Display acc}</td></xml>)
|
adamc@937
|
207 [_] M.aggFolder M.aggregates rows}
|
adamc@937
|
208 </tr></xml>}/>
|
adamc@915
|
209 </table>
|
adamc@915
|
210
|
adamc@915
|
211 <button value="New row" onclick={row <- rpc M.new;
|
adamc@915
|
212 addRow grid.Cols grid.Rows row}/>
|
adamc@915
|
213 <button value="Refresh" onclick={sync grid}/>
|
adamc@915
|
214 </xml>
|
adamc@940
|
215
|
adamc@940
|
216 fun showSelection grid = grid.Selection
|
adamc@940
|
217
|
adamc@940
|
218 fun selection grid = Dlist.foldl (fn {Row = rowS, Selected = sd, ...} ls =>
|
adamc@940
|
219 sd <- signal sd;
|
adamc@940
|
220 if sd then
|
adamc@940
|
221 row <- signal rowS;
|
adamc@940
|
222 return (row :: ls)
|
adamc@940
|
223 else
|
adamc@940
|
224 return ls) [] grid.Rows
|
adamc@915
|
225 end
|