webmcp
annotate framework/env/ui/list.lua @ 438:ea8419658535
Another change to make <db_object>:try_save() work properly with "document_column"
(use "_col" proxy also for accessing self._col[primary_key.json_doc])
(use "_col" proxy also for accessing self._col[primary_key.json_doc])
| author | jbe | 
|---|---|
| date | Wed Jan 20 21:06:07 2016 +0100 (2016-01-20) | 
| parents | e057f8d3f716 | 
| children | 
| rev | line source | 
|---|---|
| jbe/bsw@0 | 1 --[[-- | 
| jbe/bsw@0 | 2 ui.list{ | 
| jbe/bsw@0 | 3 label = list_label, -- optional label for the whole list | 
| jbe/bsw@0 | 4 style = style, -- "table", "ulli" or "div" | 
| jbe/bsw@0 | 5 prefix = prefix, -- prefix for HTML field names | 
| jbe/bsw@0 | 6 records = records, -- array of records to be displayed as rows in the list | 
| jbe/bsw@0 | 7 columns = { | 
| jbe/bsw@0 | 8 { | 
| jbe/bsw@0 | 9 label = column_label, -- label for the column | 
| jbe/bsw@0 | 10 label_attr = label_attr, -- table with HTML attributes for the heading cell or div | 
| jbe/bsw@0 | 11 field_attr = field_attr, -- table with HTML attributes for the data cell or div | 
| jbe/bsw@0 | 12 name = name, -- name of the field in each record | 
| jbe/bsw@0 | 13 html_name = html_name, -- optional html-name for writable fields (defaults to name) | 
| jbe/bsw@0 | 14 ui_field_type = ui_field_type, -- name of the ui.field.* function to use | 
| jbe/bsw@0 | 15 ...., -- other options for the given ui.field.* functions | 
| jbe/bsw@0 | 16 format = format, -- name of the format function to be used (if not using ui_field_type) | 
| jbe/bsw@0 | 17 format_options = format_options, -- options to be passed to the format function | 
| jbe@386 | 18 content = content -- alternative function to output field data per record | 
| jbe@386 | 19 -- (ignoring name, format, etc. when set) | 
| jbe@386 | 20 -- (receives record as first and integer index as second argument) | 
| jbe/bsw@0 | 21 }, | 
| jbe/bsw@0 | 22 { ... }, | 
| jbe/bsw@0 | 23 ... | 
| jbe/bsw@0 | 24 } | 
| jbe/bsw@0 | 25 } | 
| jbe/bsw@0 | 26 | 
| jbe/bsw@0 | 27 This function takes an array of records to be displayed in a list. The whole list may have a label. The style's "table" (for <table>), "ulli" (for <ul><li>) and "div" (just using <div> tags) are supported. For each column several options must be specified. | 
| jbe/bsw@0 | 28 | 
| jbe/bsw@0 | 29 --]]-- | 
| jbe/bsw@0 | 30 | 
| jbe/bsw@0 | 31 -- TODO: documentation of the prefix option | 
| jbe/bsw@0 | 32 -- TODO: check short descriptions of fields in documentation | 
| jbe/bsw@0 | 33 -- TODO: field_attr is used for the OUTER html tag's attributes, while attr is used for the INNER html tag's attributes (produced by ui.field.*), is that okay? | 
| jbe/bsw@0 | 34 -- TODO: use field information of record class, if no columns are given | 
| jbe/bsw@0 | 35 -- TODO: callback to set row attr's for a specific row | 
| jbe/bsw@0 | 36 | 
| jbe/bsw@0 | 37 function ui.list(args) | 
| jbe/bsw@0 | 38 local args = args or {} | 
| jbe/bsw@0 | 39 local label = args.label | 
| jbe/bsw@0 | 40 local list_type = args.style or "table" | 
| jbe/bsw@0 | 41 local prefix = args.prefix | 
| jbe/bsw@0 | 42 local records = assert(args.records, "ui.list{...} needs records.") | 
| jbe/bsw@0 | 43 local columns = assert(args.columns, "ui.list{...} needs column definitions.") | 
| jbe/bsw@0 | 44 local outer_attr = table.new(args.attr) | 
| jbe/bsw@0 | 45 local header_existent = false | 
| jbe/bsw@0 | 46 for idx, column in ipairs(columns) do | 
| jbe/bsw@0 | 47 if column.label then | 
| jbe/bsw@0 | 48 header_existent = true | 
| jbe/bsw@0 | 49 break | 
| jbe/bsw@0 | 50 end | 
| jbe/bsw@0 | 51 end | 
| jbe/bsw@0 | 52 local slot_state = slot.get_state_table() | 
| jbe@244 | 53 local outer_tag, head_tag, head_tag2, label_tag, body_tag, row_tag, field_tag | 
| jbe/bsw@0 | 54 if list_type == "table" then | 
| jbe/bsw@0 | 55 outer_tag = "table" | 
| jbe/bsw@0 | 56 head_tag = "thead" | 
| jbe/bsw@0 | 57 head_tag2 = "tr" | 
| jbe/bsw@0 | 58 label_tag = "th" | 
| jbe/bsw@0 | 59 body_tag = "tbody" | 
| jbe/bsw@0 | 60 row_tag = "tr" | 
| jbe/bsw@0 | 61 field_tag = "td" | 
| jbe/bsw@0 | 62 elseif list_type == "ulli" then | 
| jbe/bsw@0 | 63 outer_tag = "div" | 
| jbe/bsw@0 | 64 head_tag = "div" | 
| jbe/bsw@0 | 65 label_tag = "div" | 
| jbe/bsw@0 | 66 body_tag = "ul" | 
| jbe/bsw@0 | 67 row_tag = "li" | 
| jbe/bsw@0 | 68 field_tag = "td" | 
| jbe/bsw@0 | 69 elseif list_type == "div" then | 
| jbe/bsw@0 | 70 outer_tag = "div" | 
| jbe/bsw@0 | 71 head_tag = "div" | 
| jbe/bsw@0 | 72 label_tag = "div" | 
| jbe/bsw@0 | 73 body_tag = "div" | 
| jbe/bsw@0 | 74 row_tag = "div" | 
| jbe/bsw@0 | 75 field_tag = "div" | 
| jbe/bsw@0 | 76 else | 
| jbe/bsw@0 | 77 error("Unknown list type specified for ui.list{...}.") | 
| jbe/bsw@0 | 78 end | 
| jbe/bsw@0 | 79 outer_attr.class = outer_attr.class or "ui_list" | 
| jbe/bsw@0 | 80 ui.container{ | 
| jbe/bsw@0 | 81 auto_args = args, | 
| jbe/bsw@0 | 82 content = function() | 
| jbe/bsw@0 | 83 ui.tag{ | 
| jbe/bsw@0 | 84 tag = outer_tag, | 
| jbe/bsw@0 | 85 attr = outer_attr, | 
| jbe/bsw@0 | 86 content = function() | 
| jbe/bsw@0 | 87 if header_existent then | 
| jbe/bsw@0 | 88 ui.tag{ | 
| jbe/bsw@0 | 89 tag = head_tag, | 
| jbe/bsw@0 | 90 attr = { class = "ui_list_head" }, | 
| jbe/bsw@0 | 91 content = function() | 
| jbe/bsw@0 | 92 local function header_content() | 
| jbe/bsw@0 | 93 for idx, column in ipairs(columns) do | 
| jbe/bsw@0 | 94 if column.ui_field_type ~= "hidden" then | 
| jbe/bsw@0 | 95 local label_attr = table.new(column.label_attr) | 
| jbe/bsw@0 | 96 label_attr.class = | 
| jbe/bsw@0 | 97 label_attr.class or { class = "ui_list_label" } | 
| jbe/bsw@0 | 98 ui.tag{ | 
| jbe/bsw@0 | 99 tag = label_tag, | 
| jbe/bsw@0 | 100 attr = label_attr, | 
| jbe/bsw@0 | 101 content = column.label or "" | 
| jbe/bsw@0 | 102 } | 
| jbe/bsw@0 | 103 end | 
| jbe/bsw@0 | 104 end | 
| jbe/bsw@0 | 105 end | 
| jbe/bsw@0 | 106 if head_tag2 then | 
| jbe/bsw@0 | 107 ui.tag{ tag = head_tag2, content = header_content } | 
| jbe/bsw@0 | 108 else | 
| jbe/bsw@0 | 109 header_content() | 
| jbe/bsw@0 | 110 end | 
| jbe/bsw@0 | 111 end | 
| jbe/bsw@0 | 112 } | 
| jbe/bsw@0 | 113 end | 
| jbe/bsw@0 | 114 ui.tag{ | 
| jbe/bsw@0 | 115 tag = body_tag, | 
| jbe/bsw@0 | 116 attr = { class = "ui_list_body" }, | 
| jbe/bsw@0 | 117 content = function() | 
| jbe/bsw@0 | 118 for record_idx, record in ipairs(records) do | 
| jbe/bsw@0 | 119 local row_class | 
| jbe/bsw@0 | 120 if record_idx % 2 == 0 then | 
| jbe/bsw@0 | 121 row_class = "ui_list_row ui_list_even" | 
| jbe/bsw@0 | 122 else | 
| jbe/bsw@0 | 123 row_class = "ui_list_row ui_list_odd" | 
| jbe/bsw@0 | 124 end | 
| jbe/bsw@0 | 125 ui.tag{ | 
| jbe/bsw@0 | 126 tag = row_tag, | 
| jbe/bsw@0 | 127 attr = { class = row_class }, | 
| jbe/bsw@0 | 128 content = function() | 
| jbe/bsw@0 | 129 local old_html_name_prefix, old_form_record | 
| jbe/bsw@0 | 130 if prefix then | 
| jbe/bsw@0 | 131 old_html_name_prefix = slot_state.html_name_prefix | 
| jbe/bsw@0 | 132 old_form_record = slot_state.form_record | 
| jbe/bsw@0 | 133 slot_state.html_name_prefix = prefix .. "[" .. record_idx .. "]" | 
| jbe/bsw@0 | 134 slot_state.form_record = record | 
| jbe/bsw@0 | 135 end | 
| jbe/bsw@0 | 136 local first_column = true | 
| jbe/bsw@0 | 137 for column_idx, column in ipairs(columns) do | 
| jbe/bsw@0 | 138 if column.ui_field_type ~= "hidden" then | 
| jbe/bsw@0 | 139 local field_attr = table.new(column.field_attr) | 
| jbe/bsw@0 | 140 field_attr.class = | 
| jbe/bsw@0 | 141 field_attr.class or { class = "ui_list_field" } | 
| jbe/bsw@0 | 142 local field_content | 
| jbe/bsw@0 | 143 if column.content then | 
| jbe/bsw@0 | 144 field_content = function() | 
| jbe@386 | 145 return column.content(record, record_idx) | 
| jbe/bsw@0 | 146 end | 
| jbe/bsw@0 | 147 elseif column.name then | 
| jbe/bsw@0 | 148 if column.ui_field_type then | 
| jbe/bsw@0 | 149 local ui_field_func = ui.field[column.ui_field_type] | 
| jbe/bsw@0 | 150 if not ui_field_func then | 
| jbe/bsw@0 | 151 error('Unknown ui_field_type "' .. column.ui_field_type .. '".') | 
| jbe/bsw@0 | 152 end | 
| jbe/bsw@0 | 153 local ui_field_options = table.new(column) | 
| jbe/bsw@0 | 154 ui_field_options.record = record | 
| jbe/bsw@0 | 155 ui_field_options.label = nil | 
| jbe/bsw@0 | 156 if not prefix and ui_field_options.readonly == nil then | 
| jbe/bsw@0 | 157 ui_field_options.readonly = true | 
| jbe/bsw@0 | 158 end | 
| jbe/bsw@0 | 159 field_content = function() | 
| jbe/bsw@0 | 160 return ui.field[column.ui_field_type](ui_field_options) | 
| jbe/bsw@0 | 161 end | 
| jbe/bsw@0 | 162 elseif column.format then | 
| jbe/bsw@0 | 163 local formatter = format[column.format] | 
| jbe/bsw@0 | 164 if not formatter then | 
| jbe/bsw@0 | 165 error('Unknown format "' .. column.format .. '".') | 
| jbe/bsw@0 | 166 end | 
| jbe/bsw@0 | 167 field_content = formatter( | 
| jbe/bsw@0 | 168 record[column.name], column.format_options | 
| jbe/bsw@0 | 169 ) | 
| jbe/bsw@0 | 170 else | 
| jbe/bsw@0 | 171 field_content = function() | 
| jbe/bsw@0 | 172 return ui.autofield{ | 
| jbe/bsw@0 | 173 record = record, | 
| jbe/bsw@0 | 174 name = column.name, | 
| jbe/bsw@0 | 175 html_name = column.html_name | 
| jbe/bsw@0 | 176 } | 
| jbe/bsw@0 | 177 end | 
| jbe/bsw@0 | 178 end | 
| jbe/bsw@0 | 179 else | 
| jbe/bsw@0 | 180 error("Each column needs either a 'content' or a 'name'.") | 
| jbe/bsw@0 | 181 end | 
| jbe/bsw@0 | 182 local extended_field_content | 
| jbe/bsw@0 | 183 if first_column then | 
| jbe/bsw@0 | 184 first_column = false | 
| jbe/bsw@0 | 185 extended_field_content = function() | 
| jbe/bsw@0 | 186 for column_idx, column in ipairs(columns) do | 
| jbe/bsw@0 | 187 if column.ui_field_type == "hidden" then | 
| jbe/bsw@0 | 188 local ui_field_options = table.new(column) | 
| jbe/bsw@0 | 189 ui_field_options.record = record | 
| jbe/bsw@0 | 190 ui_field_options.label = nil | 
| jbe/bsw@0 | 191 if not prefix and ui_field_options.readonly == nil then | 
| jbe/bsw@0 | 192 ui_field_options.readonly = true | 
| jbe/bsw@0 | 193 end | 
| jbe/bsw@0 | 194 ui.field.hidden(ui_field_options) | 
| jbe/bsw@0 | 195 end | 
| jbe/bsw@0 | 196 end | 
| jbe/bsw@0 | 197 field_content() | 
| jbe/bsw@0 | 198 end | 
| jbe/bsw@0 | 199 else | 
| jbe/bsw@0 | 200 extended_field_content = field_content | 
| jbe/bsw@0 | 201 end | 
| jbe/bsw@0 | 202 ui.tag{ | 
| jbe/bsw@0 | 203 tag = field_tag, | 
| jbe/bsw@0 | 204 attr = field_attr, | 
| jbe/bsw@0 | 205 content = extended_field_content | 
| jbe/bsw@0 | 206 } | 
| jbe/bsw@0 | 207 end | 
| jbe/bsw@0 | 208 end | 
| jbe/bsw@0 | 209 if prefix then | 
| jbe/bsw@0 | 210 slot_state.html_name_prefix = old_html_name_prefix | 
| jbe/bsw@0 | 211 slot_state.form_record = old_form_record | 
| jbe/bsw@0 | 212 end | 
| jbe/bsw@0 | 213 end | 
| jbe/bsw@0 | 214 } | 
| jbe/bsw@0 | 215 end | 
| jbe/bsw@0 | 216 end | 
| jbe/bsw@0 | 217 } | 
| jbe/bsw@0 | 218 end | 
| jbe/bsw@0 | 219 } | 
| jbe/bsw@0 | 220 end | 
| jbe/bsw@0 | 221 } | 
| jbe/bsw@0 | 222 if prefix then | 
| jbe/bsw@0 | 223 -- ui.field.hidden is used instead of ui.hidden_field to suppress output in case of read-only mode. | 
| jbe/bsw@0 | 224 ui.field.hidden{ | 
| jbe/bsw@0 | 225 html_name = prefix .. "[len]", | 
| jbe/bsw@0 | 226 value = #records | 
| jbe/bsw@0 | 227 } | 
| jbe/bsw@0 | 228 end | 
| jbe/bsw@0 | 229 end |