x4js 1.4.2

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.
Files changed (176) hide show
  1. package/lib/application.d.ts +95 -0
  2. package/lib/application.js +137 -0
  3. package/lib/base64.d.ts +31 -0
  4. package/lib/base64.js +135 -0
  5. package/lib/base_component.d.ts +64 -0
  6. package/lib/base_component.js +77 -0
  7. package/lib/button.d.ts +145 -0
  8. package/lib/button.js +235 -0
  9. package/lib/calendar.d.ts +77 -0
  10. package/lib/calendar.js +236 -0
  11. package/lib/canvas.d.ts +88 -0
  12. package/lib/canvas.js +354 -0
  13. package/lib/cardview.d.ts +83 -0
  14. package/lib/cardview.js +152 -0
  15. package/lib/checkbox.d.ts +72 -0
  16. package/lib/checkbox.js +126 -0
  17. package/lib/color.d.ts +144 -0
  18. package/lib/color.js +584 -0
  19. package/lib/colorpicker.d.ts +98 -0
  20. package/lib/colorpicker.js +1457 -0
  21. package/lib/combobox.d.ts +97 -0
  22. package/lib/combobox.js +246 -0
  23. package/lib/component.d.ts +572 -0
  24. package/lib/component.js +1712 -0
  25. package/lib/datastore.d.ts +392 -0
  26. package/lib/datastore.js +986 -0
  27. package/lib/dialog.d.ts +171 -0
  28. package/lib/dialog.js +468 -0
  29. package/lib/dom_events.d.ts +284 -0
  30. package/lib/dom_events.js +13 -0
  31. package/lib/drag_manager.d.ts +26 -0
  32. package/lib/drag_manager.js +118 -0
  33. package/lib/drawtext.d.ts +43 -0
  34. package/lib/drawtext.js +261 -0
  35. package/lib/fileupload.d.ts +60 -0
  36. package/lib/fileupload.js +158 -0
  37. package/lib/form.d.ts +122 -0
  38. package/lib/form.js +293 -0
  39. package/lib/formatters.d.ts +31 -0
  40. package/lib/formatters.js +75 -0
  41. package/lib/gridview.d.ts +171 -0
  42. package/lib/gridview.js +786 -0
  43. package/lib/hosts/host.d.ts +44 -0
  44. package/lib/hosts/host.js +69 -0
  45. package/lib/i18n.d.ts +67 -0
  46. package/lib/i18n.js +169 -0
  47. package/lib/icon.d.ts +56 -0
  48. package/lib/icon.js +173 -0
  49. package/lib/image.d.ts +51 -0
  50. package/lib/image.js +149 -0
  51. package/lib/index.js +1 -0
  52. package/lib/input.d.ts +86 -0
  53. package/lib/input.js +172 -0
  54. package/lib/label.d.ts +54 -0
  55. package/lib/label.js +86 -0
  56. package/lib/layout.d.ts +77 -0
  57. package/lib/layout.js +261 -0
  58. package/lib/link.d.ts +46 -0
  59. package/lib/link.js +55 -0
  60. package/lib/listview.d.ts +173 -0
  61. package/lib/listview.js +532 -0
  62. package/lib/md5.d.ts +56 -0
  63. package/lib/md5.js +397 -0
  64. package/lib/menu.d.ts +122 -0
  65. package/lib/menu.js +276 -0
  66. package/lib/messagebox.d.ts +64 -0
  67. package/lib/messagebox.js +141 -0
  68. package/lib/panel.d.ts +42 -0
  69. package/lib/panel.js +61 -0
  70. package/lib/popup.d.ts +71 -0
  71. package/lib/popup.js +373 -0
  72. package/lib/property_editor.d.ts +67 -0
  73. package/lib/property_editor.js +247 -0
  74. package/lib/radiobtn.d.ts +68 -0
  75. package/lib/radiobtn.js +131 -0
  76. package/lib/rating.d.ts +49 -0
  77. package/lib/rating.js +93 -0
  78. package/lib/request.d.ts +48 -0
  79. package/lib/request.js +220 -0
  80. package/lib/router.d.ts +13 -0
  81. package/lib/router.js +27 -0
  82. package/lib/settings.d.ts +33 -0
  83. package/lib/settings.js +63 -0
  84. package/lib/sidebarview.d.ts +44 -0
  85. package/lib/sidebarview.js +73 -0
  86. package/lib/smartedit.d.ts +103 -0
  87. package/lib/smartedit.js +381 -0
  88. package/lib/spreadsheet.d.ts +214 -0
  89. package/lib/spreadsheet.js +1073 -0
  90. package/lib/styles.d.ts +81 -0
  91. package/lib/styles.js +262 -0
  92. package/lib/svgcomponent.d.ts +165 -0
  93. package/lib/svgcomponent.js +350 -0
  94. package/lib/tabbar.d.ts +41 -0
  95. package/lib/tabbar.js +66 -0
  96. package/lib/tabview.d.ts +45 -0
  97. package/lib/tabview.js +79 -0
  98. package/lib/textarea.d.ts +59 -0
  99. package/lib/textarea.js +119 -0
  100. package/lib/textedit.d.ts +118 -0
  101. package/lib/textedit.js +406 -0
  102. package/lib/texthiliter.d.ts +56 -0
  103. package/lib/texthiliter.js +219 -0
  104. package/lib/toaster.d.ts +38 -0
  105. package/lib/toaster.js +58 -0
  106. package/lib/tools.d.ts +382 -0
  107. package/lib/tools.js +1096 -0
  108. package/lib/tooltips.d.ts +42 -0
  109. package/lib/tooltips.js +148 -0
  110. package/lib/treeview.d.ts +128 -0
  111. package/lib/treeview.js +490 -0
  112. package/lib/x4_events.d.ts +253 -0
  113. package/lib/x4_events.js +363 -0
  114. package/package.json +21 -0
  115. package/src/README.md +2 -0
  116. package/src/application.ts +191 -0
  117. package/src/base64.ts +162 -0
  118. package/src/base_component.ts +118 -0
  119. package/src/button.ts +327 -0
  120. package/src/calendar.ts +312 -0
  121. package/src/canvas.ts +501 -0
  122. package/src/cardview.ts +220 -0
  123. package/src/checkbox.ts +178 -0
  124. package/src/color.ts +748 -0
  125. package/src/colorpicker.ts +1618 -0
  126. package/src/combobox.ts +348 -0
  127. package/src/component.ts +2330 -0
  128. package/src/datastore.ts +1318 -0
  129. package/src/dialog.ts +631 -0
  130. package/src/dom_events.ts +297 -0
  131. package/src/drag_manager.ts +168 -0
  132. package/src/drawtext.ts +342 -0
  133. package/src/fileupload.ts +208 -0
  134. package/src/form.ts +362 -0
  135. package/src/formatters.ts +96 -0
  136. package/src/gridview.ts +1051 -0
  137. package/src/hosts/electron.ts +161 -0
  138. package/src/hosts/host.ts +100 -0
  139. package/src/hosts/nwjs.ts +141 -0
  140. package/src/hosts/nwjs_types.ts +339 -0
  141. package/src/i18n.ts +205 -0
  142. package/src/icon.ts +237 -0
  143. package/src/image.ts +198 -0
  144. package/src/input.ts +236 -0
  145. package/src/label.ts +124 -0
  146. package/src/layout.ts +366 -0
  147. package/src/link.ts +82 -0
  148. package/src/listview.ts +749 -0
  149. package/src/md5.ts +432 -0
  150. package/src/menu.ts +394 -0
  151. package/src/messagebox.ts +199 -0
  152. package/src/panel.ts +81 -0
  153. package/src/popup.ts +488 -0
  154. package/src/property_editor.ts +333 -0
  155. package/src/radiobtn.ts +190 -0
  156. package/src/rating.ts +131 -0
  157. package/src/request.ts +296 -0
  158. package/src/router.ts +43 -0
  159. package/src/settings.ts +75 -0
  160. package/src/sidebarview.ts +97 -0
  161. package/src/smartedit.ts +532 -0
  162. package/src/spreadsheet.ts +1423 -0
  163. package/src/styles.ts +332 -0
  164. package/src/svgcomponent.ts +440 -0
  165. package/src/tabbar.ts +105 -0
  166. package/src/tabview.ts +106 -0
  167. package/src/textarea.ts +183 -0
  168. package/src/textedit.ts +535 -0
  169. package/src/texthiliter.ts +284 -0
  170. package/src/toaster.ts +76 -0
  171. package/src/tools.ts +1391 -0
  172. package/src/tooltips.ts +185 -0
  173. package/src/treeview.ts +670 -0
  174. package/src/x4.less +1940 -0
  175. package/src/x4_events.ts +558 -0
  176. package/tsconfig.json +14 -0
@@ -0,0 +1,786 @@
1
+ /**
2
+ * ___ ___ __
3
+ * \ \/ / / _
4
+ * \ / /_| |_
5
+ * / \____ _|
6
+ * /__/\__\ |_|
7
+ *
8
+ * @file gridview.ts
9
+ * @author Etienne Cochard
10
+ * @license
11
+ * Copyright (c) 2019-2021 R-libre ingenierie
12
+ *
13
+ * This program is free software; you can redistribute it and/or modify
14
+ * it under the terms of the GNU General Public License as published by
15
+ * the Free Software Foundation; either version 3 of the License, or
16
+ * (at your option) any later version.
17
+ *
18
+ * This program is distributed in the hope that it will be useful,
19
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
20
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21
+ * GNU General Public License for more details.
22
+ *
23
+ * You should have received a copy of the GNU General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
24
+ **/
25
+ const T_UPDATE = Symbol('update');
26
+ /**
27
+ * todo: sizable column
28
+ * todo: button in a column
29
+ */
30
+ import { HLayout, VLayout } from './layout';
31
+ import { Component, EvDblClick, flyWrap, html, SizerOverlay } from './component';
32
+ import { Label } from './label';
33
+ import { _tr } from './i18n';
34
+ import { downloadData } from './tools';
35
+ import { DataStore } from './datastore';
36
+ import { EvContextMenu, EvSelectionChange, BasicEvent } from "./x4_events";
37
+ export function EvGridCheck(rec, chk) {
38
+ return BasicEvent({ rec, chk });
39
+ }
40
+ /**
41
+ * gridview class
42
+ */
43
+ export class GridView extends VLayout {
44
+ m_dataview;
45
+ m_data_cx;
46
+ m_columns;
47
+ m_view_el;
48
+ m_container;
49
+ m_header;
50
+ m_footer;
51
+ m_empty_msg;
52
+ m_empty_text;
53
+ m_selection;
54
+ m_itemHeight;
55
+ m_topIndex;
56
+ m_visible_rows; // shown elements
57
+ m_hasMarks;
58
+ m_marks; // checked elements
59
+ m_recycler;
60
+ m_rowClassifier;
61
+ constructor(props) {
62
+ super(props);
63
+ this.m_columns = props.columns;
64
+ this.m_hasMarks = props.hasMarks ?? false;
65
+ this.m_marks = new Set();
66
+ // prepend the checkable column
67
+ if (this.m_hasMarks) {
68
+ this.m_columns.unshift({
69
+ id: 'id',
70
+ title: '',
71
+ width: 30,
72
+ renderer: (e) => this._renderCheck(e)
73
+ });
74
+ }
75
+ this.setAttribute('tabindex', 0);
76
+ this.m_topIndex = 0;
77
+ this.m_itemHeight = 0;
78
+ this.m_recycler = [];
79
+ this.m_rowClassifier = props.calcRowClass;
80
+ this.m_empty_text = props.empty_text ?? _tr.global.empty_list;
81
+ //this.setDomEvent('create', this._handleCreate, this);
82
+ this.setDomEvent('click', (e) => this._itemClick(e));
83
+ this.setDomEvent('dblclick', (e) => this._itemDblClick(e));
84
+ this.setDomEvent('contextmenu', (e) => this._itemMenu(e));
85
+ this.setDomEvent('keydown', (e) => this._handleKey(e));
86
+ this.setStore(props.store);
87
+ }
88
+ componentCreated() {
89
+ this._updateScroll(true);
90
+ }
91
+ /**
92
+ *
93
+ */
94
+ _moveSel(sens, select = true) {
95
+ let sel = this.m_selection;
96
+ let scrolltype = null;
97
+ if (sel === undefined) {
98
+ sel = this.m_dataview.getByIndex(0).getID();
99
+ }
100
+ else {
101
+ let index = this.m_dataview.indexOfId(this.m_selection);
102
+ if (sens == 1) {
103
+ index++;
104
+ }
105
+ else if (sens == -1) {
106
+ index--;
107
+ }
108
+ else if (sens == 2) {
109
+ index += this.m_visible_rows.length - 1;
110
+ }
111
+ else if (sens == -2) {
112
+ index -= this.m_visible_rows.length - 1;
113
+ }
114
+ if (sens < 0) {
115
+ scrolltype = 'start';
116
+ }
117
+ else {
118
+ scrolltype = 'end';
119
+ }
120
+ if (index < 0) {
121
+ index = 0;
122
+ }
123
+ else if (index >= this.m_dataview.count) {
124
+ index = this.m_dataview.count - 1;
125
+ }
126
+ sel = this.m_dataview.getByIndex(index).getID();
127
+ }
128
+ if (this.m_selection != sel && select) {
129
+ this._selectItem(sel, null, scrolltype);
130
+ }
131
+ return sel;
132
+ }
133
+ /**
134
+ *
135
+ */
136
+ _handleKey(event) {
137
+ //debugger;
138
+ if (!this.m_dataview || this.m_dataview.count == 0) {
139
+ return;
140
+ }
141
+ switch (event.key) {
142
+ case 'ArrowDown':
143
+ case 'Down': {
144
+ this._moveSel(1);
145
+ break;
146
+ }
147
+ case 'ArrowUp':
148
+ case 'Up': {
149
+ this._moveSel(-1);
150
+ break;
151
+ }
152
+ case 'PageUp': {
153
+ this._moveSel(-2);
154
+ break;
155
+ }
156
+ case 'PageDown': {
157
+ this._moveSel(2);
158
+ break;
159
+ }
160
+ }
161
+ }
162
+ /**
163
+ *
164
+ */
165
+ getNextSel(sens) {
166
+ return this._moveSel(sens, false);
167
+ }
168
+ _scrollIntoView(id, sens) {
169
+ let itm = this._findItem(id);
170
+ if (itm) {
171
+ itm.scrollIntoView({
172
+ block: 'center' //<ScrollLogicalPosition>sens ?? 'nearest'
173
+ });
174
+ }
175
+ else {
176
+ this.m_topIndex = this.m_dataview.indexOfId(id);
177
+ this.m_view_el.dom.scrollTop = this.m_topIndex * this.m_itemHeight;
178
+ this._buildItems();
179
+ this._scrollIntoView(id);
180
+ }
181
+ }
182
+ /**
183
+ * change the list of item displayed
184
+ * @param items - new array of items
185
+ */
186
+ setStore(store) {
187
+ this.m_selection = undefined;
188
+ if (store instanceof DataStore) {
189
+ this.m_dataview = store.createView();
190
+ }
191
+ else {
192
+ this.m_dataview = store;
193
+ }
194
+ if (this.m_hasMarks) {
195
+ this.clearMarks();
196
+ }
197
+ // unlink previous observer
198
+ if (this.m_data_cx) {
199
+ this.m_data_cx.dispose();
200
+ }
201
+ if (this.m_dataview) {
202
+ this.m_data_cx = this.m_dataview.on('view_change', (ev) => {
203
+ if (ev.action == 'change') {
204
+ this.m_selection = undefined;
205
+ }
206
+ this._updateScroll(true);
207
+ });
208
+ //this.update( );
209
+ this._updateScroll(true);
210
+ }
211
+ }
212
+ getView() {
213
+ return this.m_dataview;
214
+ }
215
+ /**
216
+ * return the current selection (row id) or null
217
+ */
218
+ getSelection() {
219
+ return this.m_selection;
220
+ }
221
+ getSelRec() {
222
+ if (this.m_selection) {
223
+ return this.m_dataview.getById(this.m_selection);
224
+ }
225
+ return null;
226
+ }
227
+ setSelection(recId) {
228
+ this._selectItem(recId, null, 'center');
229
+ }
230
+ /** @ignore */
231
+ render() {
232
+ this.m_recycler = [];
233
+ this.m_container = new Component({
234
+ cls: 'content',
235
+ });
236
+ this.m_empty_msg = new Label({
237
+ cls: 'empty-msg',
238
+ text: ''
239
+ });
240
+ this.m_view_el = new Component({
241
+ cls: '@scroll-view',
242
+ flex: 1,
243
+ dom_events: {
244
+ sizechange: () => this._updateScroll(true),
245
+ scroll: () => this._updateScroll(false)
246
+ },
247
+ content: this.m_container
248
+ });
249
+ let cols = this.m_columns.map((col, index) => {
250
+ let cls = '@cell';
251
+ if (col.cls) {
252
+ cls += ' ' + col.cls;
253
+ }
254
+ let comp = new Component({
255
+ cls,
256
+ content: new Component({
257
+ tag: 'span',
258
+ content: col.title
259
+ }),
260
+ flex: col.flex,
261
+ sizable: 'right',
262
+ style: {
263
+ width: col.width
264
+ },
265
+ dom_events: {
266
+ click: (ev) => {
267
+ let t = flyWrap(ev.target);
268
+ if (!t.hasClass('@sizer-overlay')) { // avoid sizer click
269
+ this._sortCol(col);
270
+ ev.preventDefault();
271
+ }
272
+ }
273
+ }
274
+ });
275
+ const resizeCol = (ev) => {
276
+ this._on_col_resize(index, ev.size.width);
277
+ if (this.m_footer) {
278
+ let col = Component.getElement(this.m_footer.dom.childNodes[index]);
279
+ if (col) {
280
+ col.setStyleValue('width', ev.size.width);
281
+ }
282
+ }
283
+ };
284
+ new SizerOverlay({
285
+ target: comp,
286
+ sens: 'right',
287
+ events: { resize: (e) => resizeCol(e) }
288
+ });
289
+ col.$col = comp;
290
+ return comp;
291
+ });
292
+ // compute full width
293
+ let full_width = 0;
294
+ this.m_columns.forEach((col) => {
295
+ full_width += col.width ?? 0;
296
+ });
297
+ this.m_header = new HLayout({
298
+ cls: '@header',
299
+ content: cols,
300
+ style: {
301
+ minWidth: full_width
302
+ }
303
+ });
304
+ if (this.m_props.hasFooter) {
305
+ let foots = this.m_columns.map((col, index) => {
306
+ let cls = '@cell';
307
+ if (col.align) {
308
+ cls += ' ' + col.align;
309
+ }
310
+ if (col.cls) {
311
+ cls += ' ' + col.cls;
312
+ }
313
+ let comp = new Component({
314
+ cls,
315
+ data: { col: index },
316
+ flex: col.flex,
317
+ style: {
318
+ width: col.width
319
+ }
320
+ });
321
+ return comp;
322
+ });
323
+ this.m_footer = new HLayout({
324
+ cls: '@footer',
325
+ content: foots,
326
+ style: {
327
+ minWidth: full_width
328
+ }
329
+ });
330
+ }
331
+ else {
332
+ this.m_footer = null;
333
+ }
334
+ this.setContent([
335
+ this.m_header,
336
+ this.m_view_el,
337
+ this.m_footer,
338
+ this.m_empty_msg,
339
+ ]);
340
+ }
341
+ _on_col_resize(col, width) {
342
+ this.m_columns[col].width = width;
343
+ this.m_columns[col].flex = undefined;
344
+ this._updateScroll(true);
345
+ }
346
+ /**
347
+ *
348
+ */
349
+ _sortCol(col) {
350
+ if (col.sortable === false) {
351
+ return;
352
+ }
353
+ this.m_columns.forEach((c) => {
354
+ if (c !== col) {
355
+ c.$sorted = false;
356
+ c.$col.removeClass('sort desc');
357
+ }
358
+ });
359
+ const $col = col;
360
+ if ($col.$sorted) {
361
+ $col.$sens = $col.$sens ? 0 : 1;
362
+ $col.$col.setClass('desc', $col.$sens);
363
+ }
364
+ else {
365
+ $col.$sens = 0;
366
+ $col.$sorted = true;
367
+ $col.$col.addClass('sort');
368
+ $col.$col.removeClass('desc');
369
+ }
370
+ if (this.m_dataview) {
371
+ this.m_dataview.sort([
372
+ { field: col.id, ascending: $col.$sens ? false : true }
373
+ ]);
374
+ }
375
+ }
376
+ /**
377
+ *
378
+ */
379
+ _computeItemHeight() {
380
+ let gr = document.createElement('div');
381
+ gr.classList.add('x-row');
382
+ let gv = document.createElement('div');
383
+ gv.classList.add('x-grid-view');
384
+ gv.style.position = 'absolute';
385
+ gv.style.top = '-1000px';
386
+ gv.appendChild(gr);
387
+ this.dom.appendChild(gv);
388
+ let rc = gr.getBoundingClientRect();
389
+ this.dom.removeChild(gv);
390
+ this.m_itemHeight = rc.height;
391
+ }
392
+ _createRow(props) {
393
+ let row;
394
+ if (this.m_recycler.length) {
395
+ row = this.m_recycler.pop();
396
+ row.clearClasses();
397
+ row.addClass(props.cls);
398
+ row.setContent(props.content);
399
+ row.setStyle(props.style);
400
+ for (let n in props.data) {
401
+ row.setData(n, props.data[n]);
402
+ }
403
+ }
404
+ else {
405
+ row = new HLayout(props);
406
+ }
407
+ if (!row.dom) {
408
+ this.m_container.appendChild(row);
409
+ }
410
+ return row;
411
+ }
412
+ _buildItems(canOpt = true) {
413
+ let rc = this.getBoundingRect();
414
+ let rh = this.m_header.getBoundingRect();
415
+ let height = rc.height - rh.height + this.m_itemHeight;
416
+ if (this.m_itemHeight == 0) {
417
+ this._computeItemHeight();
418
+ }
419
+ let top = this.m_topIndex * this.m_itemHeight;
420
+ let y = 0;
421
+ let cidx = 0;
422
+ let index = this.m_topIndex;
423
+ let count = this.m_dataview ? this.m_dataview.count : 0;
424
+ let full_width = 0;
425
+ let even = this.m_topIndex & 1 ? true : false;
426
+ // compute full width
427
+ this.m_columns.forEach((col) => {
428
+ full_width += col.width ?? 0;
429
+ });
430
+ // if items height make scroll visible, update header width
431
+ if (((count + 1) * this.m_itemHeight) >= height) {
432
+ let w = Component.getScrollbarSize();
433
+ this.m_header.setStyleValue("paddingRight", w);
434
+ this.m_footer?.setStyleValue("paddingRight", w);
435
+ }
436
+ else {
437
+ this.m_header.setStyleValue("paddingRight", 0);
438
+ this.m_footer?.setStyleValue("paddingRight", 0);
439
+ }
440
+ // passe 0 - all created cells are moved to the recycler
441
+ if (this.m_visible_rows) {
442
+ this.m_visible_rows.forEach((c) => {
443
+ this.m_recycler.push(c);
444
+ });
445
+ }
446
+ this.m_visible_rows = [];
447
+ let limit = 100;
448
+ while (y < height && index < count && --limit > 0) {
449
+ let rec = this.m_dataview.getByIndex(index);
450
+ let rowid = rec.getID();
451
+ let crow = canOpt ? this.m_recycler.findIndex((r) => r.getData('row-id') == rowid) : -1;
452
+ if (crow >= 0) {
453
+ let rrow = this.m_recycler.splice(crow, 1)[0];
454
+ rrow.setStyle({
455
+ top: y + top,
456
+ minWidth: full_width,
457
+ });
458
+ if (this.m_hasMarks) {
459
+ rrow.setClass('@marked', this.m_marks.has(rowid));
460
+ }
461
+ rrow.removeClass('@hidden');
462
+ rrow.setClass('@selected', this.m_selection === rowid);
463
+ this.m_visible_rows[cidx] = rrow;
464
+ }
465
+ else {
466
+ let cols = this.m_columns.map(col => {
467
+ let cls = '@cell';
468
+ if (col.align) {
469
+ cls += ' ' + col.align;
470
+ }
471
+ if (col.cls) {
472
+ cls += ' ' + col.cls;
473
+ }
474
+ let cell;
475
+ if (col.renderer) {
476
+ cell = col.renderer(rec);
477
+ if (cell) {
478
+ cell.addClass(cls);
479
+ cell.setStyleValue('width', col.width);
480
+ if (col.flex !== undefined) {
481
+ cell.addClass('@flex');
482
+ cell.setStyleValue('flex', col.flex);
483
+ }
484
+ }
485
+ }
486
+ else {
487
+ let fmt = col.formatter;
488
+ let text;
489
+ if (fmt && fmt instanceof Function) {
490
+ text = fmt(rec.getRaw(col.id), rec);
491
+ }
492
+ else {
493
+ text = rec.getField(col.id);
494
+ }
495
+ cell = new Component({
496
+ cls,
497
+ width: col.width,
498
+ content: html `<span>${text}</span>`,
499
+ flex: col.flex
500
+ });
501
+ }
502
+ return cell;
503
+ });
504
+ let cls = '@row @hlayout center';
505
+ if (this.m_hasMarks) {
506
+ if (this.m_marks.has(rowid)) {
507
+ cls += ' @marked';
508
+ }
509
+ }
510
+ if (this.m_selection === rowid) {
511
+ cls += ' @selected';
512
+ }
513
+ let row = this.m_visible_rows[cidx] = this._createRow({
514
+ cls,
515
+ content: cols,
516
+ style: {
517
+ top: y + top,
518
+ minWidth: full_width,
519
+ },
520
+ data: {
521
+ 'row-id': rowid,
522
+ 'row-idx': index
523
+ }
524
+ });
525
+ row.addClass(even ? 'even' : 'odd');
526
+ even = !even;
527
+ if (this.m_rowClassifier) {
528
+ this.m_rowClassifier(rec, row);
529
+ }
530
+ }
531
+ y += this.m_itemHeight;
532
+ index++;
533
+ cidx++;
534
+ }
535
+ // if some cells are still in cache, hide them
536
+ this.m_recycler.forEach((c) => {
537
+ c.addClass('@hidden');
538
+ });
539
+ //this.m_container.setContent(<ComponentContent>this.m_visible_rows);
540
+ let show = !count;
541
+ let msg = (this.m_empty_text instanceof Function) ? this.m_empty_text() : this.m_empty_text;
542
+ this.m_empty_msg.text = msg;
543
+ if (show && msg.length == 0) {
544
+ show = false;
545
+ }
546
+ this.m_empty_msg.show(show);
547
+ if (full_width < rc.width) {
548
+ this.m_header.setStyleValue('width', null);
549
+ this.m_footer?.setStyleValue('width', null);
550
+ this.m_container.setStyle({
551
+ height: count * this.m_itemHeight,
552
+ width: null
553
+ });
554
+ }
555
+ else {
556
+ this.m_header.setStyleValue('width', full_width);
557
+ this.m_footer?.setStyleValue('width', full_width);
558
+ this.m_container.setStyle({
559
+ height: count * this.m_itemHeight,
560
+ width: full_width
561
+ });
562
+ }
563
+ }
564
+ /**
565
+ *
566
+ */
567
+ _updateScroll(forceUpdate) {
568
+ if (!this.m_view_el || !this.m_view_el.dom) {
569
+ return;
570
+ }
571
+ const update = () => {
572
+ let newTop = Math.floor(this.m_view_el.dom.scrollTop / (this.m_itemHeight || 1));
573
+ if (newTop != this.m_topIndex || forceUpdate) {
574
+ this.m_topIndex = newTop;
575
+ this._buildItems(!forceUpdate);
576
+ }
577
+ let newLeft = this.m_view_el.dom.scrollLeft;
578
+ this.m_header.setStyleValue('left', -newLeft);
579
+ this.m_footer?.setStyleValue('left', -newLeft);
580
+ };
581
+ if (forceUpdate) {
582
+ this.singleShot(update, 10);
583
+ }
584
+ else {
585
+ update();
586
+ }
587
+ }
588
+ /** @ignore */
589
+ _rowFromTarget(dom) {
590
+ let self = this.dom;
591
+ while (dom && dom != self) {
592
+ let itm = Component.getElement(dom);
593
+ if (itm) {
594
+ let id = itm.getData('row-id');
595
+ if (id !== undefined) {
596
+ return { id, itm };
597
+ }
598
+ }
599
+ dom = dom.parentElement;
600
+ }
601
+ return undefined;
602
+ }
603
+ _itemClick(e) {
604
+ let hit = this._rowFromTarget(e.target);
605
+ if (hit) {
606
+ this._selectItem(hit.id, hit.itm);
607
+ }
608
+ else {
609
+ this._selectItem(undefined, undefined);
610
+ }
611
+ }
612
+ _itemDblClick(e) {
613
+ let hit = this._rowFromTarget(e.target);
614
+ if (hit) {
615
+ this._selectItem(hit.id, hit.itm);
616
+ let rec = this.m_dataview.getById(hit.id);
617
+ this.emit('dblClick', EvDblClick(rec));
618
+ if (this.m_hasMarks) {
619
+ this._toggleMark(rec);
620
+ }
621
+ }
622
+ }
623
+ /** @ignore */
624
+ _itemMenu(e) {
625
+ let dom = e.target, self = this.dom;
626
+ while (dom && dom != self) {
627
+ let itm = Component.getElement(dom), id = itm?.getData('row-id');
628
+ if (id !== undefined) {
629
+ this._selectItem(id, itm);
630
+ let idx = itm.getData('row-idx');
631
+ let rec = this.m_dataview.getByIndex(idx);
632
+ this._showItemContextMenu(e, rec);
633
+ e.preventDefault();
634
+ return;
635
+ }
636
+ dom = dom.parentElement;
637
+ }
638
+ }
639
+ /**
640
+ *
641
+ */
642
+ _findItem(id) {
643
+ for (let i = 0; i < this.m_visible_rows.length; i++) {
644
+ let itm = this.m_visible_rows[i];
645
+ if (itm.getData('row-id') === id) {
646
+ return itm;
647
+ }
648
+ }
649
+ return null;
650
+ }
651
+ /**
652
+ * @ignore
653
+ * called when an item is selected by mouse
654
+ */
655
+ _selectItem(item, dom_item, scrollIntoView) {
656
+ if (this.m_selection !== undefined) {
657
+ let old = this._findItem(this.m_selection);
658
+ if (old) {
659
+ old.removeClass('@selected');
660
+ }
661
+ }
662
+ this.m_selection = item;
663
+ if (item) {
664
+ if (scrollIntoView) {
665
+ this._scrollIntoView(item, scrollIntoView);
666
+ }
667
+ if (!dom_item) {
668
+ dom_item = this._findItem(item);
669
+ }
670
+ if (dom_item) {
671
+ dom_item.addClass('@selected');
672
+ }
673
+ let rec = this.m_dataview.getById(item);
674
+ this.emit('selectionChange', EvSelectionChange(rec));
675
+ }
676
+ else {
677
+ this.emit('selectionChange', EvSelectionChange(null));
678
+ }
679
+ }
680
+ /**
681
+ *
682
+ */
683
+ _showItemContextMenu(event, item) {
684
+ this.emit('contextMenu', EvContextMenu(event, item));
685
+ }
686
+ /**
687
+ *
688
+ */
689
+ clearSelection() {
690
+ this._selectItem(null, null);
691
+ }
692
+ /**
693
+ * todo: moveto datastore
694
+ */
695
+ exportData(filename) {
696
+ let data = '';
697
+ const fsep = '\t';
698
+ const lsep = '\r\n';
699
+ let rec = '';
700
+ this.m_columns.map((col) => {
701
+ if (rec.length) {
702
+ rec += fsep;
703
+ }
704
+ rec += col.title;
705
+ });
706
+ data += rec + lsep;
707
+ let count = this.m_dataview.count;
708
+ for (let i = 0; i < count; i++) {
709
+ let record = this.m_dataview.getByIndex(i);
710
+ rec = '';
711
+ let cols = this.m_columns.map((col) => {
712
+ let text = record.getField(col.id);
713
+ let fmt = col.formatter;
714
+ if (fmt && fmt instanceof Function) {
715
+ text = fmt(text, record);
716
+ }
717
+ if (rec.length > 0) {
718
+ rec += fsep;
719
+ }
720
+ rec += text;
721
+ });
722
+ data += rec + lsep;
723
+ }
724
+ //todo: review that
725
+ data = data.replace(/[àâä]/gm, 'a');
726
+ data = data.replace(/[éèê]/gm, 'e');
727
+ data = data.replace(/[îï]/gm, 'i');
728
+ data = data.replace(/[ûüù]/gm, 'u');
729
+ data = data.replace(/ /gm, ' '); // non breaking space
730
+ downloadData(data, 'text/csv', filename);
731
+ }
732
+ set empty_text(text) {
733
+ this.m_empty_msg.text = text;
734
+ }
735
+ _renderCheck(rec) {
736
+ let cls = '';
737
+ if (this.m_marks.has(rec.getID())) {
738
+ cls = ' checked';
739
+ }
740
+ return new Component({ cls: '@grid-checkbox' + cls });
741
+ }
742
+ _toggleMark(rec) {
743
+ let id = rec.getID();
744
+ let chk = false;
745
+ if (this.m_marks.has(id)) {
746
+ this.m_marks.delete(id);
747
+ }
748
+ else {
749
+ this.m_marks.add(id);
750
+ chk = true;
751
+ }
752
+ this.emit('gridCheck', EvGridCheck(rec, chk));
753
+ this._buildItems(false);
754
+ }
755
+ getMarks() {
756
+ let ids = [];
757
+ for (const v of this.m_marks.values()) {
758
+ ids.push(v);
759
+ }
760
+ return ids;
761
+ }
762
+ clearMarks() {
763
+ if (this.m_marks.size) {
764
+ this.m_marks = new Set();
765
+ this._buildItems(false);
766
+ }
767
+ }
768
+ setFooterData(rec) {
769
+ if (!this.m_footer) {
770
+ return;
771
+ }
772
+ this.m_footer.enumChilds((c) => {
773
+ let cid = c.getData('col');
774
+ let col = this.m_columns[cid];
775
+ let fmt = col.formatter;
776
+ let text;
777
+ if (fmt && fmt instanceof Function) {
778
+ text = fmt(rec[col.id], rec);
779
+ }
780
+ else {
781
+ text = rec[col.id];
782
+ }
783
+ c.setContent(text, false);
784
+ });
785
+ }
786
+ }