pixl-xyapp 2.1.11 → 2.1.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -293,7 +293,7 @@ The library provides CSS styles and JavaScript functions for creating data table
293
293
  </table>
294
294
  ```
295
295
 
296
- In addition to the CSS, a pagination system is provided, to assist you with generating tables from a large dataset that have pagination links built-in. The function to call is `this.getPaginatedTable()` and is available in the `Page` base class. It returns the final rendered HTML for the page.
296
+ In addition to the CSS, a pagination system is provided, to assist you with generating tables from a large dataset that have pagination links built-in. The function to call is `this.getPaginatedGrid()` and is available in the `Page` base class. It returns the final rendered HTML for the page.
297
297
 
298
298
  To use it, you'll need to provide an object containing the following pieces of information:
299
299
 
@@ -322,7 +322,7 @@ Here is an example:
322
322
  { name: 'Rhubarb', color: 'Purple', size: '2ft', quantity: 190, price: '$3.99', created: 1441724876 }
323
323
  ];
324
324
 
325
- var html = this.getPaginatedTable({
325
+ var html = this.getPaginatedGrid({
326
326
  cols: cols,
327
327
  rows: rows,
328
328
  data_type: 'vegetable',
@@ -343,7 +343,7 @@ Here is an example:
343
343
  });
344
344
  ```
345
345
 
346
- So the idea here is, we have a dataset of 10 items total, but we are only showing 5 items per page. So we have an array of 5 items in `rows`, but we're specifying the `total` as 10, and `offset` as 0 (first page). Based on this, the `getPaginatedTable()` will generate the proper pagination links.
346
+ So the idea here is, we have a dataset of 10 items total, but we are only showing 5 items per page. So we have an array of 5 items in `rows`, but we're specifying the `total` as 10, and `offset` as 0 (first page). Based on this, the `getPaginatedGrid()` will generate the proper pagination links.
347
347
 
348
348
  Your callback is fired once per row, and is passed the current row (array element from `rows`), and the localized index in `idx` (starts from `0` regardless of `offset`). Your function should return an array of values which should match up with the `cols`, and each will be stuffed into a `<TD>` element.
349
349
 
package/css/base.css CHANGED
@@ -185,6 +185,9 @@ button.link {
185
185
  margin: 0;
186
186
  font: inherit;
187
187
  }
188
+ button.link.icon_pad i.mdi {
189
+ padding-right: 5px;
190
+ }
188
191
 
189
192
  a, .link {
190
193
  color: var(--link-color);
@@ -2535,6 +2538,9 @@ div.dropzone.drag > div.dz_bar {
2535
2538
  overflow: hidden;
2536
2539
  text-overflow: ellipsis;
2537
2540
  }
2541
+ .data_grid_pagination button.link {
2542
+ text-transform: uppercase;
2543
+ }
2538
2544
  .data_grid_pagination i.mdi:before {
2539
2545
  transform: scale(1.5);
2540
2546
  }
package/js/base.js CHANGED
@@ -8,6 +8,11 @@ var app = {
8
8
  secure: !!location.protocol.match(/^https/i),
9
9
  retina: (window.devicePixelRatio > 1),
10
10
  mobile: !!navigator.userAgent.match(/(iOS|iPhone|iPad|Android)/),
11
+ os: {
12
+ mac: !!navigator.userAgent.match(/(Macintosh|Mac OS X|macOS)/),
13
+ win: !!navigator.userAgent.match(/(Windows)/),
14
+ linux: !!navigator.userAgent.match(/(Linux)/)
15
+ },
11
16
  base_api_url: '/api',
12
17
  plain_text_post: false,
13
18
  prefs: {},
@@ -163,6 +168,7 @@ var app = {
163
168
  var id = this.page_manager.current_page_id;
164
169
  var page = this.page_manager.find(id);
165
170
  if (page && page.onKeyDown) page.onKeyDown(event);
171
+ else if (app.onKeyDown) app.onKeyDown(event);
166
172
  }
167
173
  else if (app.onKeyDown) app.onKeyDown(event);
168
174
  },
package/js/page.js CHANGED
@@ -493,161 +493,6 @@ window.Page = class Page {
493
493
  $(elem).closest('.form_row_range').find('input').val(value);
494
494
  }
495
495
 
496
- getPaginatedTable() {
497
- // get html for paginated table
498
- // dual-calling convention: (resp, cols, data_type, callback) or (args)
499
- var args = null;
500
- if (arguments.length == 1) {
501
- // custom args calling convention
502
- args = arguments[0];
503
-
504
- // V2 API
505
- if (!args.resp && args.rows && args.total) {
506
- args.resp = {
507
- rows: args.rows,
508
- list: { length: args.total }
509
- };
510
- }
511
- }
512
- else {
513
- // classic calling convention
514
- args = {
515
- resp: arguments[0],
516
- cols: arguments[1],
517
- data_type: arguments[2],
518
- callback: arguments[3],
519
- limit: this.args.limit,
520
- offset: this.args.offset || 0
521
- };
522
- }
523
-
524
- var resp = args.resp;
525
- var cols = args.cols;
526
- var data_type = args.data_type;
527
- var callback = args.callback;
528
- var cpl = args.pagination_link || '';
529
- var html = '';
530
-
531
- // pagination header
532
- html += '<div class="pagination">';
533
- html += '<table cellspacing="0" cellpadding="0" border="0" width="100%"><tr>';
534
-
535
- var results = {
536
- limit: args.limit,
537
- offset: args.offset || 0,
538
- total: resp.list.length
539
- };
540
-
541
- var num_pages = Math.floor( results.total / results.limit ) + 1;
542
- if (results.total % results.limit == 0) num_pages--;
543
- var current_page = Math.floor( results.offset / results.limit ) + 1;
544
-
545
- html += '<td align="left" width="33%">';
546
- html += commify(results.total) + ' ' + pluralize(data_type, results.total) + ' found';
547
- html += '</td>';
548
-
549
- html += '<td align="center" width="34%">';
550
- if (num_pages > 1) html += 'Page ' + commify(current_page) + ' of ' + commify(num_pages);
551
- else html += '&nbsp;';
552
- html += '</td>';
553
-
554
- html += '<td align="right" width="33%">';
555
-
556
- if (num_pages > 1) {
557
- // html += 'Page: ';
558
- if (current_page > 1) {
559
- if (cpl) {
560
- html += '<span class="link" onClick="'+cpl+'('+Math.floor((current_page - 2) * results.limit)+')">&laquo; Prev</span>';
561
- }
562
- else {
563
- html += '<a href="#' + this.ID + compose_query_string(merge_objects(this.args, {
564
- offset: (current_page - 2) * results.limit
565
- })) + '">&laquo; Prev</a>';
566
- }
567
- }
568
- html += '&nbsp;&nbsp;&nbsp;';
569
-
570
- var start_page = current_page - 4;
571
- var end_page = current_page + 5;
572
-
573
- if (start_page < 1) {
574
- end_page += (1 - start_page);
575
- start_page = 1;
576
- }
577
-
578
- if (end_page > num_pages) {
579
- start_page -= (end_page - num_pages);
580
- if (start_page < 1) start_page = 1;
581
- end_page = num_pages;
582
- }
583
-
584
- for (var idx = start_page; idx <= end_page; idx++) {
585
- if (idx == current_page) {
586
- html += '<b>' + commify(idx) + '</b>';
587
- }
588
- else {
589
- if (cpl) {
590
- html += '<span class="link" onClick="'+cpl+'('+Math.floor((idx - 1) * results.limit)+')">' + commify(idx) + '</span>';
591
- }
592
- else {
593
- html += '<a href="#' + this.ID + compose_query_string(merge_objects(this.args, {
594
- offset: (idx - 1) * results.limit
595
- })) + '">' + commify(idx) + '</a>';
596
- }
597
- }
598
- html += '&nbsp;';
599
- }
600
-
601
- html += '&nbsp;&nbsp;';
602
- if (current_page < num_pages) {
603
- if (cpl) {
604
- html += '<span class="link" onClick="'+cpl+'('+Math.floor((current_page + 0) * results.limit)+')">Next &raquo;</span>';
605
- }
606
- else {
607
- html += '<a href="#' + this.ID + compose_query_string(merge_objects(this.args, {
608
- offset: (current_page + 0) * results.limit
609
- })) + '">Next &raquo;</a>';
610
- }
611
- }
612
- } // more than one page
613
- else {
614
- html += 'Page 1 of 1';
615
- }
616
- html += '</td>';
617
- html += '</tr></table>';
618
- html += '</div>';
619
-
620
- html += '<div style="margin-top:5px; overflow-x:auto;">';
621
-
622
- var tattrs = args.attribs || {};
623
- if (!tattrs.class) tattrs.class = 'data_table ellip';
624
- if (!tattrs.width) tattrs.width = '100%';
625
- html += '<table ' + compose_attribs(tattrs) + '>';
626
-
627
- html += '<tr><th>' + cols.join('</th><th>').replace(/\s+/g, '&nbsp;') + '</th></tr>';
628
-
629
- for (var idx = 0, len = resp.rows.length; idx < len; idx++) {
630
- var row = resp.rows[idx];
631
- var tds = callback(row, idx);
632
- if (tds) {
633
- html += '<tr' + (tds.className ? (' class="'+tds.className+'"') : '') + '>';
634
- html += '<td>' + tds.join('</td><td>') + '</td>';
635
- html += '</tr>';
636
- }
637
- } // foreach row
638
-
639
- if (!resp.rows.length) {
640
- html += '<tr><td colspan="'+cols.length+'" align="center" style="padding-top:10px; padding-bottom:10px; font-weight:bold;">';
641
- html += 'No '+pluralize(data_type)+' found.';
642
- html += '</td></tr>';
643
- }
644
-
645
- html += '</table>';
646
- html += '</div>';
647
-
648
- return html;
649
- }
650
-
651
496
  getPaginatedGrid() {
652
497
  // get html for paginated grid
653
498
  // multi-calling convention: (resp, cols, data_type, callback), or (args, callback), or (args)
@@ -717,12 +562,13 @@ window.Page = class Page {
717
562
  // html += 'Page: ';
718
563
  if (current_page > 1) {
719
564
  if (cpl) {
720
- html += '<span class="link" onClick="'+cpl+'('+Math.floor((current_page - 2) * results.limit)+')"><i class="mdi mdi-chevron-left"></i>&nbsp;Prev</span>';
565
+ var click = cpl + '(' + Math.floor((current_page - 2) * results.limit) + ')';
566
+ html += '<button class="link" ' + (args.primary ? 'id="btn_nav_prev"' : '') + ' onClick="' + click + '"><i class="mdi mdi-chevron-left"></i>&nbsp;Prev</button>';
721
567
  }
722
568
  else {
723
569
  html += '<a href="#' + this.ID + compose_query_string(merge_objects(this.args, {
724
570
  offset: (current_page - 2) * results.limit
725
- })) + '">&laquo; Prev</a>';
571
+ })) + '" ' + (args.primary ? 'id="btn_nav_prev"' : '') + '><i class="mdi mdi-chevron-left"></i>&nbsp;Prev</a>';
726
572
  }
727
573
  }
728
574
  html += '&nbsp;&nbsp;&nbsp;';
@@ -748,7 +594,7 @@ window.Page = class Page {
748
594
  }
749
595
  else {
750
596
  if (cpl) {
751
- html += '<span class="link" onClick="'+cpl+'('+Math.floor((idx - 1) * results.limit)+')">' + commify(idx) + '</span>';
597
+ html += '<button class="link" onClick="'+cpl+'('+Math.floor((idx - 1) * results.limit)+')">' + commify(idx) + '</button>';
752
598
  }
753
599
  else {
754
600
  html += '<a href="#' + this.ID + compose_query_string(merge_objects(this.args, {
@@ -763,12 +609,13 @@ window.Page = class Page {
763
609
 
764
610
  if (current_page < num_pages) {
765
611
  if (cpl) {
766
- html += '<span class="link" onClick="'+cpl+'('+Math.floor((current_page + 0) * results.limit)+')">Next&nbsp;<i class="mdi mdi-chevron-right"></i></span>';
612
+ var click = cpl + '(' + Math.floor((current_page + 0) * results.limit) + ')';
613
+ html += '<button class="link" ' + (args.primary ? 'id="btn_nav_next"' : '') + ' onClick="' + click + '">Next&nbsp;<i class="mdi mdi-chevron-right"></i></button>';
767
614
  }
768
615
  else {
769
616
  html += '<a href="#' + this.ID + compose_query_string(merge_objects(this.args, {
770
617
  offset: (current_page + 0) * results.limit
771
- })) + '">Next &raquo;</a>';
618
+ })) + '" ' + (args.primary ? 'id="btn_nav_next"' : '') + '>Next&nbsp;<i class="mdi mdi-chevron-right"></i></a>';
772
619
  }
773
620
  }
774
621
  } // more than one page
package/js/select.js CHANGED
@@ -946,3 +946,170 @@ var TextSelect = {
946
946
  }
947
947
 
948
948
  }; // TextSelect
949
+
950
+ var KeySelect = {
951
+
952
+ init: function(sel) {
953
+ // initialize all key-selects based on selector
954
+ $(sel).each( function() {
955
+ var self = this;
956
+ var $this = $(this);
957
+ $this.css('display', 'none').attr({ 'aria-hidden': true, 'tabindex': '-1' });
958
+
959
+ var $ms = $('<div class="multiselect text" role="button" tabindex="0"></div>');
960
+ $this.after( $ms );
961
+
962
+ var redraw = function() {
963
+ // render contents of visible multiselect div
964
+ var num_sel = 0;
965
+ $ms.empty();
966
+ $ms.append('<div class="select_chevron mdi mdi-plus"></div>');
967
+
968
+ for (var idx = 0, len = self.options.length; idx < len; idx++) {
969
+ var opt = self.options[idx];
970
+ var $item = $('<div class="item"></div>').data('value', opt.value).html(
971
+ '<i class="mdi mdi-close">&nbsp;</i>' + opt.label
972
+ );
973
+ $ms.append( $item );
974
+ num_sel++;
975
+ }
976
+
977
+ if (num_sel) $ms.append( '<div class="clear"></div>' );
978
+ else $ms.append( '<div class="placeholder">' + ($this.attr('placeholder') || 'Click to add...') + '</div>' );
979
+
980
+ $ms.find('div.item > i').on('click', function(e) {
981
+ // user clicked on the 'X' -- remove this item and redraw
982
+ var $item = $(this).parent();
983
+ var value = $item.data('value');
984
+
985
+ var idx = find_object_idx( self.options, { value: value } );
986
+ self.options.remove( idx );
987
+
988
+ $this.trigger('change');
989
+ e.stopPropagation();
990
+ e.preventDefault();
991
+ return false;
992
+ });
993
+ }; // redraw
994
+
995
+ redraw();
996
+
997
+ // also trigger a redraw if the underlying hidden select changes
998
+ $this.on('change', redraw);
999
+
1000
+ // also expose redraw as a custom event that can be triggered
1001
+ $this.on('redraw', redraw);
1002
+
1003
+ // allow keyboard to open menu
1004
+ $ms.on('keydown', function(event) {
1005
+ if ((event.key == 'Enter') || (event.key == ' ')) {
1006
+ $ms.click();
1007
+ event.preventDefault();
1008
+ }
1009
+ } );
1010
+
1011
+ $ms.on('click', function() {
1012
+ // create popover dialog for adding new items
1013
+ var html = '';
1014
+ if ($ms.hasClass('disabled')) return;
1015
+
1016
+ html += '<div class="sel_dialog_label">' + ($this.attr('title') || 'Add New Item') + '</div>';
1017
+ html += '<div class="sel_dialog_search_container">';
1018
+ html += '<input type="hidden" id="fe_sel_dialog_key" value=""/>';
1019
+ html += '<input type="text" id="fe_sel_dialog_text" class="sel_dialog_search" style="border-radius:2px;" autocomplete="off" value=""/>';
1020
+ html += '<div class="sel_dialog_search_icon"><i class="mdi mdi-' + ($this.attr('icon') || 'plus') + '"></i></div>';
1021
+ html += '</div>';
1022
+
1023
+ if ($this.attr('description')) {
1024
+ html += '<div class="sel_dialog_caption">' + $this.attr('description') + '</div>';
1025
+ }
1026
+
1027
+ html += '<div class="sel_dialog_button_container">';
1028
+ html += '<div class="button" id="btn_sel_dialog_cancel">Cancel</div>';
1029
+ html += '<div class="button primary" id="btn_sel_dialog_add">' + ($this.attr('confirm') || 'Add Hot Key') + '</div>';
1030
+ html += '</div>';
1031
+
1032
+ Popover.attach( $ms, '<div style="padding:15px;">' + html + '</div>', $this.data('shrinkwrap') || false );
1033
+
1034
+ var doAdd = function() {
1035
+ app.clearError();
1036
+
1037
+ var value = $('#fe_sel_dialog_key').val();
1038
+ var label = $('#fe_sel_dialog_text').val();
1039
+
1040
+ if (!value.length || find_object(self.options, { value: value })) {
1041
+ Popover.detach();
1042
+ return;
1043
+ }
1044
+
1045
+ // add new item
1046
+ var opt = new Option( label, value );
1047
+ opt.selected = true;
1048
+ self.options[ self.options.length ] = opt;
1049
+
1050
+ Popover.detach();
1051
+ $this.trigger('change');
1052
+ }; // doAdd
1053
+
1054
+ $('#btn_sel_dialog_cancel').on('click', function() { Popover.detach(); });
1055
+ $('#btn_sel_dialog_add').on('click', function() { doAdd(); });
1056
+
1057
+ var $input = $('#fe_sel_dialog_text').focus().on('keydown', function(event) {
1058
+ // capture keydown
1059
+ // if (event.keyCode == 27) {
1060
+ // event.preventDefault();
1061
+ // event.stopPropagation();
1062
+ // Popover.detach();
1063
+ // return;
1064
+ // }
1065
+ if ((event.keyCode == 13) && this.value.length) {
1066
+ event.preventDefault();
1067
+ event.stopPropagation();
1068
+ doAdd();
1069
+ }
1070
+
1071
+ var key_id = KeySelect.getKeyID(event);
1072
+ if (!key_id) return;
1073
+
1074
+ event.preventDefault();
1075
+ event.stopPropagation();
1076
+
1077
+ $('#fe_sel_dialog_key').val( key_id );
1078
+ $('#fe_sel_dialog_text').val( KeySelect.getkeyLabel(key_id) );
1079
+ });
1080
+
1081
+ // highlight multiselect field under us
1082
+ $ms.addClass('selected');
1083
+ Popover.onDetach = function() {
1084
+ $ms.removeClass('selected').focus();
1085
+ };
1086
+ }); // click
1087
+
1088
+ }); // forach elem
1089
+ },
1090
+
1091
+ getKeyID(event) {
1092
+ // get get ID based on event
1093
+ // ignore modifiers by themselves
1094
+ if (event.key.match(/^(Shift|Control|Alt|Meta)$/)) return '';
1095
+
1096
+ var parts = [];
1097
+ if (event.shiftKey) parts.push('Shift');
1098
+ if (event.ctrlKey) parts.push('Control');
1099
+ if (event.altKey) parts.push('Alt');
1100
+ if (event.metaKey) parts.push('Meta');
1101
+ if (!parts.includes(event.code)) parts.push( event.code );
1102
+
1103
+ return parts.join('+');
1104
+ },
1105
+
1106
+ getkeyLabel(key_id, glue = '+') {
1107
+ // get formatted label based on key id
1108
+ var os = app.os;
1109
+ return key_id.split(/\+/).map( function(key) {
1110
+ if (key == 'Meta') return os.mac ? 'Command' : (os.win ? 'Windows' : 'Super');
1111
+ else return key.replace(/^(Key|Digit)/, '');
1112
+ } ).join(glue);
1113
+ }
1114
+
1115
+ }; // KeySelect
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "pixl-xyapp",
3
- "version": "2.1.11",
4
- "description": "A theme for xyOps.",
3
+ "version": "2.1.12",
4
+ "description": "Front-end web application and theme for xyOps.",
5
5
  "author": "Joseph Huckaby <jhuckaby@pixlcore.com>",
6
6
  "homepage": "https://github.com/pixlcore/pixl-xyapp",
7
7
  "license": "BSD-3-Clause",