formhandler.template.html 16.4 KB
Newer Older
S Anand's avatar
S Anand committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14
<!--
Each "var ..." template is embedded into formhandler.js as a minified HTML string variable.
This uses rollup-plugin-htmlparts.js: our custom rollup plugin.

Each template receives these variables:

- data: JSON data from FormHandler
- meta: Meta HTTP headers from FormHandler
- options: Options passed to $().formhandler() (including defaults)
- args: URL query parameters used to retrieve data
-->

<!-- This is the root template that renders all other components on this page -->
<!-- var template_ -->
15 16
<div class="position-relative">
  <div class="formhandler">
17
    <div class="note"></div>
18 19
    <div class="formhandler-table-header d-flex justify-content-between mb-2">
      <div class="d-flex flex-wrap">
20 21 22 23 24
        <div class="edit"></div>
        <div class="add"></div>
        <div class="count"></div>
        <div class="page"></div>
        <div class="size"></div>
25 26 27 28
      </div>
      <div class="d-flex">
        <div class="filters"></div>
        <div class="export"></div>
S Anand's avatar
S Anand committed
29
      </div>
30
    </div>
S Anand's avatar
S Anand committed
31
      <div class="<%- (options.table == 'grid') ? 'table_grid' : 'table' %>"></div>
32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52
  </div>
  <div class="loader pos-cc d-none">
    <div class="fa fa-spinner fa-spin fa-3x fa-fw"></div>
    <span class="sr-only">Loading...</span>
  </div>
</div>
<div class="modal formhandler-table-modal" id="fh-modal-<%- idcount %>" tabindex="-1" role="dialog" aria-labelledby="fh-label-<%- idcount %>"
  aria-hidden="true">
  <div class="modal-dialog modal-sm" role="document">
    <div class="modal-content">
      <form class="formhandler-table-modal-form modal-body">
        <label id="fh-label-<%- idcount %>" for="formhandler-table-modal-value">Value</label>
        <p>
          <input class="form-control" name="filter_input">
        </p>
        <div>
          <button type="button" class="btn btn-sm btn-secondary mr-1" data-dismiss="modal">Cancel</button>
          <button type="submit" class="btn btn-sm btn-primary mr-1">Apply filter</button>
          <a class="btn btn-sm btn-danger remove-action urlfilter" data-dismiss="modal" data-target="#" href="#">Remove filter</button>
        </div>
      </form>
53 54 55
    </div><!-- .modal-content -->
  </div><!-- .modal-dialog -->
</div><!-- .modal -->
S Anand's avatar
S Anand committed
56 57 58
<!-- end -->

<!-- var template_table -->
59 60 61 62
<%
  var filtered_cols = args['_c'] && args['_c'].length != options.columns.length ?
                      options.columns.filter(function(col) { return args['_c'].indexOf('-' + col.name) < 0 }) :
                      options.columns
63 64
  var cols = options.columns.length ? filtered_cols : meta.columns;
  cols = cols.filter(function(col) { return col.hide !== true})
65 66
  var form_id = idcount
%>
67

S Anand's avatar
S Anand committed
68 69
<table class="table table-sm table-striped">
  <thead>
70
    <% _.each(cols, function(colinfo) {
71 72 73
        col_defaults(colinfo, data)
        var menu_item = false
        var col_id = idcount++
74 75
        var qsort = parse('?')
        var isSorted = _.includes(args['_sort'], colinfo.name) ? {op: '', cls: 'table-primary'} : _.includes(args['_sort'], '-' + colinfo.name) ? {op: '-', cls: 'table-danger'} : {}
76
      %>
77
      <th class="<%- isSorted.cls %>" data-col="<%- colinfo.name %>">
S Anand's avatar
S Anand committed
78
        <div class="dropdown">
79
          <a href="#" class="dropdown-toggle text-nowrap" id="fh-dd-<%- col_id %>" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
S Anand's avatar
S Anand committed
80 81
            <%- colinfo.title || colinfo.name %>
          </a>
82 83
          <div class="dropdown-menu" aria-labelledby="fh-dd-<%- col_id %>">
            <% _.each(colinfo.sort, function(title, op) { menu_item = true
84 85 86 87 88
              qsort = qsort.update({_sort: args['_sort'] || []})
              if (!_.isEmpty(isSorted))
                qsort = qsort.update({_sort: [colinfo.name, '-' + colinfo.name]}, 'del')
              var active = _.includes(args['_sort'], op + colinfo.name) %>
              <a class="dropdown-item urlfilter <%- active ? 'active': '' %>" href="<%- qsort.update({_sort: [op + colinfo.name]}, active ? 'del': 'add').toString() %>">
89 90
                <%- title %>
              </a>
91 92 93
            <% }) %>
            <% if (menu_item) { %>
              <div class="dropdown-divider"></div>
S Anand's avatar
S Anand committed
94
            <% menu_item = false } %>
95 96 97 98 99 100 101 102 103 104
            <% _.each(colinfo.filters, function(title, op) { menu_item = true %>
              <a class="dropdown-item <%= colinfo.name + op in args ? 'active' : '' %>" href="#" data-op="<%- op %>" data-toggle="modal" data-target="#fh-modal-<%- form_id %>">
                <%- title %>
              </a>
            <% }) %>
            <% if (menu_item) {
              menu_item = false %>
              <div class="dropdown-divider"></div>
            <% } %>
            <% if (colinfo.hideable) { %>
105
              <a class="dropdown-item urlfilter" href="?_c=-<%- encodeURIComponent(colinfo.name) %>" data-mode="add">Hide</a>
106 107 108
            <% } %>
          </div><!-- .dropdown-menu -->
        </div><!-- .dropdown -->
S Anand's avatar
S Anand committed
109
      </th>
110
    <% }) %>
S Anand's avatar
S Anand committed
111 112
  </thead>
  <tbody>
113 114
    <% if (isAdd) { %>
      <tr class="new-row">
115 116
        <% _.each(cols, function(colinfo) {
          if (!colinfo.template) { %>
117 118 119
            <td data-key="<%- colinfo.name %>">
              <% var isEditable = colinfo.editable === undefined ? true : colinfo.editable %>
              <%= _.template(templates['template_editable'])({isEditable: isEditable, val: undefined}) %>
120
            </td>
121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146
          <% } else { %>
            <td></td>
          <% } %>
        <% }) %>
      </tr>
    <% } %>
    <% if (options.rowTemplate) { %>
      <% _.each(data, function(row, rowIndex) { %>
        <%= typeof options.rowTemplate == 'function' ? options.rowTemplate({row: row, data: data, index: rowIndex}) : _.template(options.rowTemplate)({row: row, data: data, index: rowIndex}) %>
      <% }) %>
    <% } else {%>
    <% _.each(data, function(row, rowIndex) { %>
      <tr data-val="<%- JSON.stringify(row) %>" data-row="<%- rowIndex %>">
        <% _.each(cols, function(colinfo) { %>
          <% var fmt = typeof(colinfo.format),
            val = row[colinfo.name],
            isEditable = colinfo.editable === undefined ? true : colinfo.editable,
            disp = fmt == "function" ?
              colinfo.format({name: colinfo.name, value: val, index: rowIndex, row: row, data:data }) :
            fmt === "string" && colinfo.type === "number" ?
              numeral(val).format(colinfo.format) :
            fmt === "string" && colinfo.type === "date" ?
              moment(val).format(colinfo.format):
              val,
            col_link
          %>
S Anand's avatar
S Anand committed
147
          <% if (!isEdit && 'link' in colinfo)  var col_link = typeof colinfo.link == 'function' ? colinfo.link({name: colinfo.name, value: val, format: disp, index: rowIndex, row: row, data: data}) : _.template(colinfo.link)({name: colinfo.name, value: val, format: disp, index: rowIndex, row: row, data: data}) %>
148 149 150
          <% if (colinfo.template) { %>
            <%= typeof colinfo.template == 'function' ? colinfo.template({name: colinfo.name, value: val, format: disp, link: col_link, index: rowIndex, row: row, data: data}) : _.template(colinfo.template)(({name: colinfo.name, value: val, format: disp, link: col_link, index: rowIndex, row: row, data: data})) %>
          <% } else if (col_link) { %>
S Anand's avatar
S Anand committed
151

152
              <td>
S Anand's avatar
S Anand committed
153
                <a href="<%- col_link %>" target="_blank">
154 155 156
                  <%= disp %>
                </a>
              </td>
S Anand's avatar
S Anand committed
157 158

          <% } else if (isEdit && isEditable) { %>
159 160 161
            <td data-key="<%- colinfo.name %>">
              <%= _.template(templates['template_editable'])({isEditable: isEditable, val: val}) %>
            </td>
S Anand's avatar
S Anand committed
162
          <% } else { %>
163
              <td>
164
                <a class="urlfilter" href="?<%- encodeURIComponent(colinfo.name) %>=<%- encodeURIComponent(val) %>&amp;_offset=">
165 166 167
                  <%= disp %>
                </a>
              </td>
S Anand's avatar
S Anand committed
168 169
          <% } %>
        <% }) %>
170
        </tr>
171 172
      <% }) %>
    <% } %>
S Anand's avatar
S Anand committed
173 174 175 176
  </tbody>
</table>
<!-- end -->

177 178 179 180 181 182 183 184 185
<!-- var template_editable -->
<% if (isEditable.input == 'select') { %>

  <select
      <% for (key in isEditable.attrs) { %>
        <%= key + '="' + isEditable.attrs[key] + '"' %>
      <% } %>
      class="form-control form-control-sm"
    >
186
    <option value="" disabled selected>-- select --</option>
187
    <% _.each(isEditable.options, function(item) { %>
188
      <option <%- val !== undefined && val === item ? 'selected': null %> value="<%- item %>">
189 190 191 192 193 194 195 196 197
        <%- item %>
      </option>
    <% }) %>
  </select>


<% } else if (isEditable.input == 'radio') { %>

  <% _.each(isEditable.options, function(item) { %>
198
    <input type="radio" <%- val !== undefined && val === item ? 'checked': null %> value="<%- item %>"
199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215
      <% for (key in isEditable.attrs) { %>
        <%= key + '="' + isEditable.attrs[key] + '"' %>
      <% } %>
      class="form-control form-control-sm"
    />
    <%- item %> <br>
  <% }) %>

<% } else { %>
  <input type="<%- isEditable.input || 'text' %>" value="<%- val %>"
    <% for (key in isEditable.attrs) { %>
      <%= key + '="' + isEditable.attrs[key] + '"' %>
    <% } %>
    class="form-control form-control-sm"
  />

<% } %>
216 217 218 219 220
<% if (isEditable.validationMessage) { %>
  <div class="invalid-feedback">
    <%- isEditable.validationMessage %>
  </div>
<% } %>
221 222
<!-- end -->

S Anand's avatar
S Anand committed
223 224
<!-- var template_page -->
<% var page = 1 + Math.floor(meta.offset / meta.limit),
225 226
      last_page = 'count' in meta ? Math.floor((meta.count + meta.limit - 1) / meta.limit) : meta.rows < meta.limit ? page : null,
      lo = Math.max(page - 2, 1),
227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246
      hi = last_page !== null ? Math.min(last_page, page + 2) : page + 2 %>
<ul class="pagination pagination-sm mr-2">
  <li class="page-item <%- page <= 1 ? 'disabled' : '' %>">
    <a class="page-link" href="?_offset=<%- meta.offset - meta.limit %>">Previous</a>
  </li>
  <% if (lo > 1) { %>
    <li class="page-item">
      <a class="page-link" href="?_offset=">1</a>
    </li>
    <% if (lo > 2) { %>
      <li class="page-item disabled">
        <a class="page-link" href="#">...</a>
      </li>
    <% } %>
  <% } %>
  <% _.each(_.range(lo, hi + 1), function(pg) { %>
    <li class="page-item <%- pg == page ? 'active' : '' %>">
      <a class="page-link" href="?_offset=<%- meta.limit * (pg - 1) || '' %>">
        <%- pg %>
      </a>
S Anand's avatar
S Anand committed
247
    </li>
248 249 250 251 252 253 254 255
  <% }) %>
  <% if ('count' in meta) { %>
    <% if (hi + 1 < last_page) { %>
      <li class="page-item disabled">
        <a class="page-link" href="#">...</a>
      </li>
    <% } %>
    <% if (hi < last_page || lo > hi) { %>
S Anand's avatar
S Anand committed
256
      <li class="page-item">
257 258 259
        <a class="page-link" href="?_offset=<%- meta.limit * (last_page - 1) %>">
          <%- last_page %>
        </a>
S Anand's avatar
S Anand committed
260
      </li>
261 262 263 264 265 266 267
    <% } %>
  <% } %>
  <li class="page-item <%- (last_page === null) || (page < last_page) ? '' : 'disabled' %>">
    <a class="page-link" href="?_offset=<%- meta.offset + meta.limit %>">Next</a>
  </li>
</ul>
<!-- end -->
S Anand's avatar
S Anand committed
268

269 270 271 272 273 274 275 276 277 278 279 280 281
<!-- var template_size -->
<% if (meta.limit) { %>
  <div class="btn-group btn-group-sm mr-2" role="group">
    <button id="formhandler-size-<%- idcount++ %>" type="button" class="btn btn-light btn-sm dropdown-toggle" data-toggle="dropdown"
      aria-haspopup="true" aria-expanded="false">
      <%- meta.limit %> rows
    </button>
    <div class="dropdown-menu" aria-labelledby="formhandler-size-<%- idcount %>">
      <% _.each(options.sizeValues, function(size) { %>
        <a class="dropdown-item <%- meta.limit == size ? 'active' : '' %> urlfilter" href="?_limit=<%- size %>">
          <%- size %>
        </a>
      <% }) %>
282
    </div>
283 284 285
  </div>
<% } %>
<!-- end -->
286

287 288 289 290 291 292 293
<!-- var template_count -->
<% if ('count' in meta) { %>
  <span class="btn btn-sm btn-light mr-2">
    <%- meta.count %> rows
  </span>
<% } %>
<!-- end -->
S Anand's avatar
S Anand committed
294

295
<!-- var template_edit -->
S Anand's avatar
S Anand committed
296
<button type="submit" class="btn btn-success mr-2 btn-sm edit-btn">
297 298 299
  Edit
</button>
<!-- end -->
300

301 302 303 304 305
<!-- var template_add -->
<button type="button" class="btn btn-success mr-2 btn-sm add-btn">
  Add
</button>
<!-- end -->
306

307 308 309 310 311 312 313 314 315 316 317 318 319 320 321
<!-- var template_export -->
<div class="btn-group btn-group-sm" role="group">
  <button id="formhandler-export-<%- idcount++ %>" type="button" class="btn btn-light btn-sm dropdown-toggle" data-toggle="dropdown"
    aria-haspopup="true" aria-expanded="false">
    Export as
  </button>
  <div class="dropdown-menu dropdown-menu-right" aria-labelledby="formhandler-export-<%- idcount %>">
    <% _.each(options.exportFormats, function(label, key) { %>
      <a class="dropdown-item" href="<%- parse(options.src).update(args).update({_format: key}) %>">
        <%- label %>
      </a>
    <% }) %>
  </div>
</div>
<!-- end -->
S Anand's avatar
S Anand committed
322

323 324 325 326 327 328 329
<!-- var template_filters -->
<div class="p-1"><%
  var qparts = parse('?')
  _.each(args['_c'], function(col_name) {
    qparts.update({_c: col_name}, 'add')
    var hide_col = col_name[0] == '-'
    var display_name = hide_col ? col_name.slice(1) : col_name %>
330
    <a href="?_c=<%- encodeURIComponent(col_name) %>" data-mode="del" class="badge badge-pill <%- hide_col ? 'badge-dark' : 'badge-danger' %> urlfilter"
331 332 333 334 335 336 337 338 339
      title="<%- hide_col ? 'Show' : 'Hide' %> column <%- display_name %>">
      <%- display_name %>
    </a>
  <% })
  _.each(args, function(list_values, key) {
    if (key.charAt(0) !== '_' && key !== 'c') {
      _.each(args[key], function(col_name) {
        var update = {}
        update[key] = col_name
340
        qparts.update(update, 'add') %>
341
        <a href="?<%- encodeURIComponent(key) %>=<%- encodeURIComponent(col_name) %>" data-mode="del" class="badge badge-pill badge-dark urlfilter" title="Clear <%- key %> filter">
342 343 344 345 346 347 348 349 350 351 352
          <%- key %> = <%- col_name %>
        </a>
      <% })
    }
  })
  qparts = qparts.toString()
  if (qparts && qparts != '?') { %>
    <a href="?<%- qparts.slice(1) %>" class="badge badge-pill badge-danger urlfilter" data-mode="del" title="Clear all filters">×</a>
  <% } %>
</div>
<!-- end -->
353

354 355 356 357 358 359 360 361
<!-- var template_error -->
<div class="alert alert-warning alert-dismissible" role="alert">
  <%- message %>
  <button type="button" class="close" data-dismiss="alert" aria-label="Close">
    <span aria-hidden="true">&times;</span>
  </button>
</div>
<!-- end -->
S Anand's avatar
S Anand committed
362

363 364 365 366 367 368 369
<!-- var template_table_grid -->
<%
  var filtered_cols = args['_c'] && args['_c'].length != options.columns.length ?
                      options.columns.filter(function(col) { return args['_c'].indexOf('-' + col.name) < 0 }) :
                      options.columns
  var cols = options.columns.length ? filtered_cols : meta.columns
  var form_id = idcount, img
370 371
  if (options.rowTemplate) {
    _.each(data, function(row, rowIndex) { %>
372
    <%= typeof options.rowTemplate == 'function' ? options.rowTemplate({row: row, index: rowIndex, data: data}) : _.template(options.rowTemplate)({row: row, data: data, index: rowIndex}) %>
373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398
  <% })
} else { %>
  <div class="formhandler-grid row">
    <% _.each(data, function(row, rowIndex) { %>
      <div class="col-sm-3 <%= options.classes || 'formhandler-grid-cell d-inline-block p-3 box-shadow' %>">
        <div class="thumbnail">
          <% img = options.icon ? ((typeof(options.icon) == 'function' ? options.icon({row: row, data: data, index: rowIndex}) : options.icon)) : 'fa fa-home' %>
          <% if (img.indexOf('fa ') >= 0) { %>
            <i class="<%= img %>"></i>
          <% } else { %>
            <img class="img img-responsive" src="<%= img %>"/>
          <% } %>
          <div class="caption">
            <% _.each(cols, function(colinfo) {
              var fmt = typeof(colinfo.format),
                  val = row[colinfo.name],
                  disp = (fmt == "function" ? colinfo.format({index: rowIndex, name: colinfo.name, value: val, row: row, data:data }) :
                          fmt === "string" && colinfo.type === "number" ? numeral(val).format(colinfo.format) :
                          fmt === "string" && colinfo.type === "date" ? moment(val).format(colinfo.format) :
                          val) %>
              <div>
                <strong><%= colinfo.name %></strong>:
                <% if ('link' in colinfo) {
                    var col_link = typeof colinfo.link == 'function' ? colinfo.link({row: row, value: val, index: rowIndex, name: colinfo.name, data: data, format: disp}) : _.template(colinfo.link)({row: row, value: val, index: rowIndex, name: colinfo.name, data: data, format: disp}) %>
                    <a href="<%- col_link %>" target="_blank"><%= disp %></a>
                <% } else { %>
399
                  <a class="urlfilter" href="?<%- encodeURIComponent(colinfo.name) %>=<%- encodeURIComponent(val) %>&amp;_offset=">
400 401
                    <%= disp %>
                  </a>
402 403 404 405 406 407 408 409
                <% } %>
              </div>
            <% }) %>
          </div><!-- .caption -->
        </div><!-- .thumbnail -->
      </div><!-- .col-sm-3 -->
    <% }) %>
  </div><!-- .formhandler-grid -->
410 411
<% } %>
<!-- end -->