x4js 2.0.12 → 2.0.14

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 (284) hide show
  1. package/.vscode/launch.json +14 -0
  2. package/README.md +20 -15
  3. package/{lib/src/demo → demo}/assets/radio.svg +3 -3
  4. package/{lib/src/demo → demo}/index.html +11 -11
  5. package/{lib/src/demo → demo}/main.scss +23 -21
  6. package/{lib/src/demo/main.tsx → demo/main.ts} +324 -323
  7. package/demo/package.json +26 -0
  8. package/demo/scss.d.ts +4 -0
  9. package/demo/svg.d.ts +1 -0
  10. package/demo/tsconfig.json +14 -0
  11. package/lib/README.txt +20 -15
  12. package/lib/cjs/x4.css +1 -1
  13. package/lib/cjs/x4.js +2 -1
  14. package/lib/esm/x4.css +1 -1
  15. package/lib/esm/x4.mjs +2 -1
  16. package/lib/src/components/base.scss +25 -26
  17. package/lib/src/components/boxes/boxes.module.scss +54 -37
  18. package/lib/src/components/boxes/boxes.ts +278 -125
  19. package/lib/src/components/breadcrumb/breadcrumb.scss +56 -0
  20. package/lib/src/components/breadcrumb/breadcrumb.ts +93 -0
  21. package/lib/src/components/breadcrumb/chevron-right.svg +1 -0
  22. package/lib/src/components/btngroup/btngroup.module.scss +40 -28
  23. package/lib/src/components/btngroup/btngroup.ts +152 -101
  24. package/lib/src/components/button/button.module.scss +172 -153
  25. package/lib/src/components/button/button.ts +185 -117
  26. package/lib/src/components/calendar/calendar.module.scss +162 -162
  27. package/lib/src/components/calendar/calendar.ts +326 -325
  28. package/lib/src/components/canvas/canvas.module.scss +25 -0
  29. package/lib/src/components/canvas/canvas.ts +189 -0
  30. package/lib/src/components/canvas/canvas_ex.ts +269 -0
  31. package/lib/src/components/checkbox/check.svg +3 -3
  32. package/lib/src/components/checkbox/checkbox.module.scss +141 -141
  33. package/lib/src/components/checkbox/checkbox.ts +139 -124
  34. package/lib/src/components/colorinput/colorinput.module.scss +64 -64
  35. package/lib/src/components/colorinput/colorinput.ts +90 -87
  36. package/lib/src/components/colorpicker/colorpicker.module.scss +132 -132
  37. package/lib/src/components/colorpicker/colorpicker.ts +481 -476
  38. package/lib/src/components/combobox/combobox.module.scss +132 -120
  39. package/lib/src/components/combobox/combobox.ts +275 -190
  40. package/lib/src/components/combobox/updown.svg +3 -3
  41. package/lib/src/components/components.ts +41 -0
  42. package/lib/src/components/dialog/dialog.module.scss +105 -71
  43. package/lib/src/components/dialog/dialog.ts +212 -92
  44. package/lib/src/components/filedrop/cloud-arrow-up.svg +1 -0
  45. package/lib/src/components/filedrop/filedrop.module.scss +70 -0
  46. package/lib/src/components/filedrop/filedrop.ts +131 -0
  47. package/lib/src/components/form/form.module.scss +38 -34
  48. package/lib/src/components/form/form.ts +172 -36
  49. package/lib/src/components/gridview/arrow-down-light.svg +1 -0
  50. package/lib/src/components/gridview/arrow-up-light.svg +1 -0
  51. package/lib/src/components/gridview/gridview.module.scss +324 -0
  52. package/lib/src/components/gridview/gridview.ts +1175 -0
  53. package/lib/src/components/header/header.module.scss +39 -39
  54. package/lib/src/components/header/header.ts +129 -123
  55. package/lib/src/components/icon/icon.module.scss +30 -30
  56. package/lib/src/components/icon/icon.ts +139 -134
  57. package/lib/src/components/image/image.module.scss +27 -20
  58. package/lib/src/components/image/image.ts +168 -67
  59. package/lib/src/components/input/input.module.scss +74 -69
  60. package/lib/src/components/input/input.ts +398 -274
  61. package/lib/src/components/keyboard/arrow-up.svg +1 -0
  62. package/lib/src/components/keyboard/delete-left.svg +1 -0
  63. package/lib/src/components/keyboard/eye-slash.svg +1 -0
  64. package/lib/src/components/keyboard/keyboard.module.scss +134 -0
  65. package/lib/src/components/keyboard/keyboard.ts +525 -0
  66. package/lib/src/components/label/label.module.scss +76 -52
  67. package/lib/src/components/label/label.ts +97 -55
  68. package/lib/src/components/link/link.ts +81 -0
  69. package/lib/src/components/listbox/listbox.module.scss +161 -103
  70. package/lib/src/components/listbox/listbox.ts +539 -427
  71. package/lib/src/components/menu/menu.module.scss +116 -107
  72. package/lib/src/components/menu/menu.ts +174 -168
  73. package/lib/src/components/messages/messages.module.scss +92 -47
  74. package/lib/src/components/messages/messages.ts +215 -64
  75. package/lib/src/components/messages/pen-field.svg +1 -0
  76. package/lib/src/components/normalize.scss +391 -386
  77. package/lib/src/components/notification/notification.module.scss +83 -81
  78. package/lib/src/components/notification/notification.ts +107 -108
  79. package/lib/src/components/panel/panel.module.scss +59 -47
  80. package/lib/src/components/panel/panel.ts +57 -56
  81. package/lib/src/components/popup/popup.module.scss +45 -43
  82. package/lib/src/components/popup/popup.ts +440 -395
  83. package/lib/src/components/progress/progress.module.scss +56 -56
  84. package/lib/src/components/progress/progress.ts +43 -42
  85. package/lib/src/components/propgrid/folder-closed.svg +1 -0
  86. package/lib/src/components/propgrid/folder-open.svg +1 -0
  87. package/lib/src/components/propgrid/progrid.module.scss +108 -0
  88. package/lib/src/components/propgrid/propgrid.ts +271 -0
  89. package/lib/src/components/propgrid/updown.svg +4 -0
  90. package/lib/src/components/radio/radio.module.scss +147 -0
  91. package/lib/src/components/radio/radio.svg +4 -0
  92. package/lib/src/components/radio/radio.ts +142 -0
  93. package/lib/src/components/rating/rating.module.scss +22 -22
  94. package/lib/src/components/rating/rating.ts +131 -125
  95. package/lib/src/components/select/select.module.scss +9 -0
  96. package/lib/src/components/select/select.ts +134 -0
  97. package/lib/src/components/shared.scss +137 -76
  98. package/lib/src/components/sizers/sizer.module.scss +89 -89
  99. package/lib/src/components/sizers/sizer.ts +130 -119
  100. package/lib/src/components/slider/slider.module.scss +117 -70
  101. package/lib/src/components/slider/slider.ts +197 -142
  102. package/lib/src/components/switch/switch.module.scss +126 -126
  103. package/lib/src/components/switch/switch.ts +61 -55
  104. package/lib/src/components/tabs/tabs.module.scss +45 -46
  105. package/lib/src/components/tabs/tabs.ts +199 -157
  106. package/lib/src/components/textarea/textarea.module.scss +63 -59
  107. package/lib/src/components/textarea/textarea.ts +125 -54
  108. package/lib/src/components/textedit/textedit.module.scss +115 -113
  109. package/lib/src/components/textedit/textedit.ts +110 -82
  110. package/lib/src/components/themes.scss +88 -77
  111. package/lib/src/components/tickline/tickline.module.scss +26 -0
  112. package/lib/src/components/tickline/tickline.ts +82 -0
  113. package/lib/src/components/tooltips/comments-question.svg +1 -0
  114. package/lib/src/components/tooltips/tooltips.scss +71 -50
  115. package/lib/src/components/tooltips/tooltips.ts +108 -102
  116. package/lib/src/components/treeview/treeview.module.scss +184 -115
  117. package/lib/src/components/treeview/treeview.ts +445 -403
  118. package/lib/src/components/viewport/viewport.module.scss +31 -24
  119. package/lib/src/components/viewport/viewport.ts +41 -38
  120. package/lib/src/core/component.ts +1072 -979
  121. package/lib/src/core/core_application.ts +264 -0
  122. package/lib/src/core/core_colors.ts +249 -249
  123. package/lib/src/core/core_data.ts +1309 -0
  124. package/lib/src/core/core_dom.ts +471 -471
  125. package/lib/src/core/core_dragdrop.ts +200 -200
  126. package/lib/src/core/core_element.ts +109 -97
  127. package/lib/src/core/core_events.ts +177 -149
  128. package/lib/src/core/core_i18n.ts +393 -377
  129. package/lib/src/core/core_react.ts +79 -0
  130. package/lib/src/core/core_router.ts +237 -221
  131. package/lib/src/core/core_styles.ts +214 -214
  132. package/lib/src/core/core_svg.ts +711 -550
  133. package/lib/src/core/core_tools.ts +906 -673
  134. package/lib/src/types/scss.d.ts +4 -4
  135. package/lib/src/types/x4react.d.ts +8 -8
  136. package/lib/src/x4.scss +18 -18
  137. package/lib/src/x4.ts +31 -62
  138. package/lib/src/x4tsx.d.ts +25 -0
  139. package/lib/styles/x4.css +1 -1
  140. package/lib/types/x4js.d.ts +853 -127
  141. package/package.json +5 -6
  142. package/scripts/build.mjs +378 -0
  143. package/scripts/prepack.mjs +346 -0
  144. package/src/components/base.scss +25 -0
  145. package/src/components/boxes/boxes.module.scss +54 -0
  146. package/src/components/boxes/boxes.ts +278 -0
  147. package/src/components/breadcrumb/breadcrumb.scss +56 -0
  148. package/src/components/breadcrumb/breadcrumb.ts +93 -0
  149. package/src/components/breadcrumb/chevron-right.svg +1 -0
  150. package/src/components/btngroup/btngroup.module.scss +41 -0
  151. package/src/components/btngroup/btngroup.ts +153 -0
  152. package/src/components/button/button.module.scss +173 -0
  153. package/src/components/button/button.ts +185 -0
  154. package/src/components/calendar/calendar-check-sharp-light.svg +1 -0
  155. package/src/components/calendar/calendar.module.scss +163 -0
  156. package/src/components/calendar/calendar.ts +327 -0
  157. package/src/components/calendar/chevron-left-sharp-light.svg +1 -0
  158. package/src/components/calendar/chevron-right-sharp-light.svg +1 -0
  159. package/src/components/canvas/canvas.module.scss +25 -0
  160. package/src/components/canvas/canvas.ts +189 -0
  161. package/src/components/canvas/canvas_ex.ts +269 -0
  162. package/src/components/checkbox/check.svg +4 -0
  163. package/src/components/checkbox/checkbox.module.scss +142 -0
  164. package/src/components/checkbox/checkbox.ts +140 -0
  165. package/src/components/colorinput/colorinput.module.scss +65 -0
  166. package/src/components/colorinput/colorinput.ts +91 -0
  167. package/src/components/colorinput/crosshairs-simple-sharp-light.svg +1 -0
  168. package/src/components/colorpicker/colorpicker.module.scss +133 -0
  169. package/src/components/colorpicker/colorpicker.ts +482 -0
  170. package/src/components/combobox/combobox.module.scss +133 -0
  171. package/src/components/combobox/combobox.ts +275 -0
  172. package/src/components/combobox/updown.svg +4 -0
  173. package/src/components/components.ts +41 -0
  174. package/src/components/dialog/dialog.module.scss +105 -0
  175. package/src/components/dialog/dialog.ts +212 -0
  176. package/src/components/dialog/xmark-sharp-light.svg +1 -0
  177. package/src/components/filedrop/cloud-arrow-up.svg +1 -0
  178. package/src/components/filedrop/filedrop.module.scss +70 -0
  179. package/src/components/filedrop/filedrop.ts +131 -0
  180. package/src/components/form/form.module.scss +38 -0
  181. package/src/components/form/form.ts +172 -0
  182. package/src/components/gridview/arrow-down-light.svg +1 -0
  183. package/src/components/gridview/arrow-up-light.svg +1 -0
  184. package/src/components/gridview/gridview.module.scss +324 -0
  185. package/src/components/gridview/gridview.ts +1175 -0
  186. package/src/components/header/header.module.scss +40 -0
  187. package/src/components/header/header.ts +130 -0
  188. package/src/components/icon/icon.module.scss +30 -0
  189. package/src/components/icon/icon.ts +139 -0
  190. package/src/components/image/image.module.scss +28 -0
  191. package/src/components/image/image.ts +168 -0
  192. package/src/components/input/input.module.scss +74 -0
  193. package/src/components/input/input.ts +398 -0
  194. package/src/components/keyboard/arrow-up.svg +1 -0
  195. package/src/components/keyboard/delete-left.svg +1 -0
  196. package/src/components/keyboard/eye-slash.svg +1 -0
  197. package/src/components/keyboard/keyboard.module.scss +134 -0
  198. package/src/components/keyboard/keyboard.ts +525 -0
  199. package/src/components/label/label.module.scss +76 -0
  200. package/src/components/label/label.ts +97 -0
  201. package/src/components/link/link.ts +81 -0
  202. package/src/components/listbox/listbox.module.scss +161 -0
  203. package/src/components/listbox/listbox.ts +539 -0
  204. package/src/components/menu/caret-right-solid.svg +1 -0
  205. package/src/components/menu/menu.module.scss +117 -0
  206. package/src/components/menu/menu.ts +174 -0
  207. package/src/components/messages/circle-exclamation.svg +1 -0
  208. package/src/components/messages/messages.module.scss +92 -0
  209. package/src/components/messages/messages.ts +215 -0
  210. package/src/components/messages/pen-field.svg +1 -0
  211. package/src/components/normalize.scss +391 -0
  212. package/src/components/notification/circle-check-solid.svg +1 -0
  213. package/src/components/notification/circle-exclamation-solid.svg +1 -0
  214. package/src/components/notification/circle-notch-light.svg +1 -0
  215. package/src/components/notification/notification.module.scss +84 -0
  216. package/src/components/notification/notification.ts +107 -0
  217. package/src/components/notification/xmark-sharp-light.svg +1 -0
  218. package/src/components/panel/panel.module.scss +60 -0
  219. package/src/components/panel/panel.ts +58 -0
  220. package/src/components/popup/popup.module.scss +45 -0
  221. package/src/components/popup/popup.ts +440 -0
  222. package/src/components/progress/progress.module.scss +57 -0
  223. package/src/components/progress/progress.ts +44 -0
  224. package/src/components/propgrid/folder-closed.svg +1 -0
  225. package/src/components/propgrid/folder-open.svg +1 -0
  226. package/src/components/propgrid/progrid.module.scss +108 -0
  227. package/src/components/propgrid/propgrid.ts +271 -0
  228. package/src/components/propgrid/updown.svg +4 -0
  229. package/src/components/radio/radio.module.scss +147 -0
  230. package/src/components/radio/radio.svg +4 -0
  231. package/src/components/radio/radio.ts +142 -0
  232. package/src/components/rating/rating.module.scss +23 -0
  233. package/src/components/rating/rating.ts +131 -0
  234. package/src/components/rating/star-sharp-light.svg +1 -0
  235. package/src/components/rating/star-sharp-solid.svg +1 -0
  236. package/src/components/select/select.module.scss +9 -0
  237. package/src/components/select/select.ts +134 -0
  238. package/src/components/shared.scss +137 -0
  239. package/src/components/sizers/sizer.module.scss +90 -0
  240. package/src/components/sizers/sizer.ts +131 -0
  241. package/src/components/slider/slider.module.scss +118 -0
  242. package/src/components/slider/slider.ts +198 -0
  243. package/src/components/switch/switch.module.scss +127 -0
  244. package/src/components/switch/switch.ts +62 -0
  245. package/src/components/tabs/tabs.module.scss +45 -0
  246. package/src/components/tabs/tabs.ts +199 -0
  247. package/src/components/textarea/textarea.module.scss +63 -0
  248. package/src/components/textarea/textarea.ts +125 -0
  249. package/src/components/textedit/textedit.module.scss +116 -0
  250. package/src/components/textedit/textedit.ts +110 -0
  251. package/src/components/themes.scss +88 -0
  252. package/src/components/tickline/tickline.module.scss +26 -0
  253. package/src/components/tickline/tickline.ts +82 -0
  254. package/src/components/tooltips/circle-info-sharp-light.svg +1 -0
  255. package/src/components/tooltips/comments-question.svg +1 -0
  256. package/src/components/tooltips/tooltips.scss +72 -0
  257. package/src/components/tooltips/tooltips.ts +109 -0
  258. package/src/components/treeview/chevron-down-light.svg +1 -0
  259. package/src/components/treeview/treeview.module.scss +185 -0
  260. package/src/components/treeview/treeview.ts +445 -0
  261. package/src/components/viewport/viewport.module.scss +32 -0
  262. package/src/components/viewport/viewport.ts +41 -0
  263. package/src/core/component.ts +1072 -0
  264. package/src/core/core_application.ts +264 -0
  265. package/src/core/core_colors.ts +250 -0
  266. package/src/core/core_data.ts +1309 -0
  267. package/src/core/core_dom.ts +471 -0
  268. package/src/core/core_dragdrop.ts +201 -0
  269. package/src/core/core_element.ts +110 -0
  270. package/src/core/core_events.ts +177 -0
  271. package/src/core/core_i18n.ts +393 -0
  272. package/src/core/core_react.ts +79 -0
  273. package/src/core/core_router.ts +237 -0
  274. package/src/core/core_styles.ts +214 -0
  275. package/src/core/core_svg.ts +711 -0
  276. package/src/core/core_tools.ts +906 -0
  277. package/src/types/scss.d.ts +4 -0
  278. package/src/types/svg.d.ts +1 -0
  279. package/src/types/x4react.d.ts +9 -0
  280. package/src/x4.scss +19 -0
  281. package/src/x4.ts +31 -62
  282. package/src/x4tsx.d.ts +25 -0
  283. package/tsconfig.json +14 -0
  284. /package/{lib/src/demo → demo}/assets/house-light.svg +0 -0
@@ -0,0 +1,1175 @@
1
+ /**
2
+ * ___ ___ __
3
+ * \ \/ / / _
4
+ * \ / /_| |_
5
+ * / \____ _|
6
+ * /__/\__\ |_|
7
+ *
8
+ * @file gridview.ts
9
+ * @author Etienne Cochard
10
+ *
11
+ * @copyright (c) 2024 R-libre ingenierie
12
+ *
13
+ * Use of this source code is governed by an MIT-style license
14
+ * that can be found in the LICENSE file or at https://opensource.org/licenses/MIT.
15
+ **/
16
+
17
+
18
+ import { Component, ComponentContent, ComponentEvents, ComponentProps, EvClick, EvContextMenu, EvDblClick, EvSelectionChange, componentFromDOM } from '../../core/component';
19
+ import { class_ns, isNumber, isString, setWaitCursor } from '../../core/core_tools';
20
+ import { DataModel, DataStore, DataView, DataRecord, DataFieldValue, EvViewChange } from '../../core/core_data';
21
+ import { EventCallback } from '../../core/core_events';
22
+ import { kbNav } from '../../core/core_tools';
23
+
24
+ import { Icon } from '../icon/icon';
25
+ import { Image } from '../image/image'
26
+ import { Box } from '../boxes/boxes';
27
+ import { CSizer } from '../sizers/sizer'
28
+ import { Viewport } from '../viewport/viewport';
29
+ import { SimpleText } from '../label/label';
30
+
31
+ import check_icon from "../checkbox/check.svg";
32
+ import "./gridview.module.scss"
33
+
34
+ export type CellRenderer = (rec: DataRecord) => Component;
35
+ export type CellClassifier = (data: any, rec: DataRecord, col: string ) => string; // return the cell computed class
36
+
37
+ type ColType = "number" | "money" | "checkbox" | "date" | "string" | "image" | "percent" | "icon";
38
+
39
+ const SCROLL_LIMIT = 200;
40
+
41
+
42
+ /**
43
+ *
44
+ */
45
+
46
+ interface GridColumn {
47
+ id: any;
48
+ title: string;
49
+ width: number;
50
+ fixed?: boolean;
51
+ flex?: number;
52
+ align?: 'left' | 'center' | 'right';
53
+ header_align?: 'left' | 'center' | 'right';
54
+ renderer?: CellRenderer; // for "renderer" type
55
+ formatter?: (input: any) => string; // for "custom" type
56
+ type?: ColType;
57
+ cls?: string;
58
+ sortable?: boolean;
59
+ footer_val?: string;
60
+ classifier?: CellClassifier;
61
+ }
62
+
63
+ interface GridColumnEx extends GridColumn {
64
+ sens?: "up" | "dn";
65
+ }
66
+
67
+ export interface GridviewEvents extends ComponentEvents {
68
+ click?: EvClick;
69
+ dblClick?: EvDblClick;
70
+ contextMenu?: EvContextMenu;
71
+ selectionChange?: EvSelectionChange;
72
+ }
73
+
74
+ export interface GridviewProps extends ComponentProps {
75
+ footer?: boolean;
76
+ store: DataStore;
77
+ columns: GridColumn[];
78
+
79
+ click?: EventCallback<EvClick>;
80
+ dblClick?: EventCallback<EvDblClick>;
81
+ contextMenu?: EventCallback<EvContextMenu>;
82
+ selectionChange?: EventCallback<EvSelectionChange>;
83
+ }
84
+
85
+ /**
86
+ * we can handle
87
+ * 4_095 cols and (1_048_575-1)/2 rows (this is a chrome limitation max pixels of scrollbars )
88
+ */
89
+
90
+ /**
91
+ *
92
+ */
93
+
94
+ @class_ns("x4")
95
+ export class Gridview<P extends GridviewProps = GridviewProps, E extends GridviewEvents = GridviewEvents> extends Component<P,E> {
96
+
97
+ private _dataview: DataView;
98
+ private _datamodel: DataModel;
99
+
100
+ private _columns: GridColumnEx[];
101
+
102
+ private _lock: number;
103
+ private _dirty: number;
104
+
105
+ private _row_height: number;
106
+
107
+ private _left: number;
108
+ private _top: number;
109
+
110
+ private _body: Component;
111
+ private _viewport: Component;
112
+
113
+ private _fheader: Box; // fixed col header
114
+ private _hheader: Box; // col header
115
+ private _vheader: Box; // vertical row header
116
+ private _ffooter: Box; // fixed footer
117
+ private _footer: Box; // footer
118
+
119
+ private _vis_rows: Map<number, { h: Component, r: Component }>;
120
+ private _start: number;
121
+ private _end: number;
122
+
123
+ private _selection: Set<number>;
124
+ private _num_fmt = new Intl.NumberFormat('fr-FR');
125
+ private _mny_fmt = new Intl.NumberFormat('fr-FR', { style: 'currency', currency: 'EUR' });
126
+ private _dte_fmt = new Intl.DateTimeFormat('fr-FR', {});
127
+
128
+ private _has_fixed: boolean;
129
+ private _has_footer: boolean;
130
+
131
+ constructor(props: P) {
132
+ super(props);
133
+
134
+ this._lock = 0;
135
+ this._dirty = 0;
136
+
137
+ this._row_height = 32;
138
+
139
+ this._left = 0;
140
+ this._top = 0;
141
+
142
+ this._vis_rows = new Map();
143
+ this._selection = new Set();
144
+ this._has_fixed = false;
145
+ this._has_footer = props.footer;
146
+
147
+ this._columns = props.columns.map(x => x);
148
+
149
+ this.mapPropEvents( props, "click", "dblClick", "contextMenu", "selectionChange" );
150
+
151
+ this.lock(true);
152
+ this.setAttribute("tabindex", 0);
153
+
154
+ this.addDOMEvent("created", () => {
155
+ this._init();
156
+ this._dirty = 1;
157
+ this.lock(false);
158
+ });
159
+
160
+ this.addDOMEvent("resized", () => {
161
+ this._updateFlexs( );
162
+ this._computeFullSize( );
163
+ this._update( true );
164
+ });
165
+
166
+ this.addDOMEvent( "keydown", (e) => {
167
+ this._on_key( e );
168
+ })
169
+
170
+ if (props.store) {
171
+ this.setStore(props.store);
172
+ }
173
+
174
+ }
175
+
176
+ /**
177
+ *
178
+ */
179
+
180
+ private _on_key( ev: KeyboardEvent ) {
181
+ if( this.isDisabled() ) {
182
+ return;
183
+ }
184
+
185
+ switch( ev.key ) {
186
+ case "ArrowDown": {
187
+ this.navigate( kbNav.next );
188
+ break;
189
+ }
190
+
191
+ case "ArrowUp": {
192
+ this.navigate( kbNav.prev );
193
+ break;
194
+ }
195
+
196
+ case "Home": {
197
+ this.navigate( kbNav.first );
198
+ break;
199
+ }
200
+
201
+ case "End": {
202
+ this.navigate( kbNav.last );
203
+ break;
204
+ }
205
+
206
+ case "PageDown": {
207
+ this.navigate( kbNav.pgdn );
208
+ break;
209
+ }
210
+
211
+ case "PageUp": {
212
+ this.navigate( kbNav.pgup );
213
+ break;
214
+ }
215
+
216
+ default:
217
+ return;
218
+ }
219
+
220
+ ev.preventDefault( );
221
+ ev.stopPropagation( );
222
+ }
223
+
224
+ /**
225
+ *
226
+ */
227
+
228
+ navigate( sens: kbNav ) {
229
+ if( !this._selection.size ) {
230
+ if( sens==kbNav.next || sens==kbNav.pgdn ) {
231
+ sens = kbNav.first;
232
+ }
233
+ else {
234
+ sens = kbNav.last;
235
+ }
236
+ }
237
+
238
+ if( sens==kbNav.first || sens==kbNav.last ) {
239
+ let nel = sens==kbNav.first ? 0 : this._dataview.getCount()-1;
240
+ this._clearSelection();
241
+ this._addSelection( nel );
242
+ this._scrollToIndex( nel );
243
+ return true;
244
+ }
245
+ else if( sens==kbNav.prev || sens==kbNav.next ) {
246
+ const fsel = this._selection.values().next().value;
247
+ let nel = sens==kbNav.next ? fsel+1 : fsel-1;
248
+ if( nel>=0 && nel<this._dataview.getCount() ) {
249
+ this._clearSelection();
250
+ this._addSelection( nel );
251
+ this._scrollToIndex( nel );
252
+ return true;
253
+ }
254
+ }
255
+ else if( sens==kbNav.pgdn || sens==kbNav.pgup ) {
256
+ const pgh = this._vis_rows.size;
257
+
258
+ const fsel = this._selection.values().next().value;
259
+
260
+ let sby = sens==kbNav.pgdn ? pgh : -pgh;
261
+ let nel = fsel+sby;
262
+
263
+ if( nel<0 ) {
264
+ nel = 0;
265
+ }
266
+ else if( nel>=this._dataview.getCount() ) {
267
+ nel = this._dataview.getCount()-1;
268
+ }
269
+
270
+ if( nel!=fsel ) {
271
+ this._clearSelection();
272
+ this._addSelection( nel );
273
+
274
+ if (this._dataview.getCount() < SCROLL_LIMIT) {
275
+ sby *= this._row_height;
276
+ }
277
+
278
+ this._viewport.dom.scrollBy( 0, sby );
279
+
280
+ return true;
281
+ }
282
+ }
283
+
284
+ return false;
285
+ }
286
+
287
+ /**
288
+ *
289
+ */
290
+
291
+ private _scrollToIndex( index: number, block = 'nearest' ) {
292
+
293
+ // is it already visible ?
294
+ let rows = this.queryAll(`.row[data-row="${index}"]`);
295
+ if (rows.length) {
296
+ rows.forEach( row => {
297
+ row.scrollIntoView({ block: block as any } );
298
+ } );
299
+ }
300
+ // nope, refill
301
+ else {
302
+ let top = index;
303
+ if (this._dataview.getCount() < SCROLL_LIMIT) {
304
+ top *= this._row_height;
305
+ }
306
+
307
+ this._viewport.dom.scrollTo( 0, top );
308
+ }
309
+ }
310
+
311
+ /**
312
+ *
313
+ */
314
+
315
+ setStore(store: DataStore) {
316
+
317
+ const on_change = (ev: EvViewChange) => {
318
+ if( !this._viewport ) {
319
+ // not created
320
+ return;
321
+ }
322
+
323
+ if (ev.change_type == 'change') {
324
+ this._selection.clear();
325
+ }
326
+
327
+ this._updateFlexs( );
328
+ this._computeFullSize();
329
+ this._update(true);
330
+ }
331
+
332
+ // unlink previous observer
333
+ if (this._dataview) {
334
+ this._dataview.off('view_change', on_change);
335
+ }
336
+
337
+ if (store) {
338
+ this._dataview = new DataView({ store: store });
339
+ this._datamodel = store.getModel();
340
+ this._dataview.on('view_change', on_change);
341
+ }
342
+ else {
343
+ this._dataview = null;
344
+ this._datamodel = null;
345
+ }
346
+ }
347
+
348
+ getView( ): DataView {
349
+ return this._dataview;
350
+ }
351
+
352
+ /**
353
+ *
354
+ */
355
+
356
+ lock(lock: boolean) {
357
+ if (lock) {
358
+ this._lock++;
359
+ }
360
+ else {
361
+ if (--this._lock == 0 && this._dirty) {
362
+ this._update( true );
363
+ }
364
+ }
365
+ }
366
+
367
+ private _getColCount() {
368
+ return this._columns.length;
369
+ }
370
+
371
+ private _getCol(index: number) {
372
+ return this._columns[index];
373
+ }
374
+
375
+ /**
376
+ *
377
+ */
378
+
379
+ private _buildColHeader(fixed: boolean) {
380
+ // row header
381
+ const els: Component[] = [];
382
+
383
+ const count = this._getColCount();
384
+ for (let col = 0; col < count; col++) {
385
+ const cdata = this._getCol(col);
386
+ if ((!!cdata.fixed) != fixed) {
387
+ continue;
388
+ }
389
+
390
+ const sizer = new CSizer("right");
391
+
392
+ sizer.on("stop", ( ) => {
393
+ this._updateFlexs( );
394
+ })
395
+
396
+ sizer.on("resize", (ev) => {
397
+ cdata.width = ev.size;
398
+ cdata.flex = 0;
399
+
400
+ const cols = this.queryAll(`[data-col="${col}"]`)
401
+ cols.forEach(c => {
402
+ c.setStyleValue("width", ev.size + "px");
403
+ });
404
+
405
+ const rh = header.getBoundingRect();
406
+
407
+ if (!fixed) {
408
+ this._body.setStyleValue("width", rh.width + "px");
409
+ }
410
+ else {
411
+ this.setStyleVariable("--fixed-width", rh.width + "px");
412
+ }
413
+ })
414
+
415
+ const cell = new Component({
416
+ cls: `cell`,
417
+ attrs: { "data-col": col },
418
+ style: { width: cdata.width ? cdata.width + "px" : undefined },
419
+ content: [
420
+ new SimpleText({ text: cdata.title, align: cdata.header_align ?? "left" }),
421
+ new Component({ cls: "sorter" }),
422
+ sizer
423
+ ]
424
+ });
425
+
426
+ cell.addDOMEvent("touchend", () => {
427
+ const last = cell.getInternalData("touchend");
428
+ const now = Date.now();
429
+ const delta = last ? now - last : 0;
430
+ if (delta > 30 && delta < 300) {
431
+ this._sortCol(col);
432
+ }
433
+ else {
434
+ cell.setInternalData("touchend", now);
435
+ }
436
+ })
437
+
438
+ cell.addDOMEvent("dblclick", () => {
439
+ this._sortCol(col);
440
+ });
441
+
442
+ els.push(cell);
443
+ }
444
+
445
+ if (fixed && els.length == 0) {
446
+ return null;
447
+ }
448
+
449
+ const header = new Box({ cls: "col-header", content: els });
450
+ header.setClass("fixed", fixed);
451
+
452
+ return header;
453
+ }
454
+
455
+ /**
456
+ *
457
+ */
458
+
459
+ private _buildColFooter(fixed: boolean) {
460
+ // row header
461
+ const els: Component[] = [];
462
+
463
+ const count = this._getColCount();
464
+ for (let col = 0; col < count; col++) {
465
+ const cdata = this._getCol(col);
466
+ if ((!!cdata.fixed) != fixed) {
467
+ continue;
468
+ }
469
+
470
+ const cell = new Component({
471
+ cls: `cell`,
472
+ attrs: { "data-col": col },
473
+ style: { width: cdata.width ? cdata.width + "px" : undefined },
474
+ content: [
475
+ new SimpleText({ text: cdata.footer_val }),
476
+ ]
477
+ });
478
+
479
+ cell.addDOMEvent("dblclick", () => {
480
+ this._sortCol(col);
481
+ });
482
+
483
+ els.push(cell);
484
+ }
485
+
486
+ if (fixed && els.length == 0) {
487
+ return null;
488
+ }
489
+
490
+ const header = new Box({ cls: "col-footer", content: els });
491
+ header.setClass("fixed", fixed);
492
+
493
+ return header;
494
+ }
495
+
496
+ /**
497
+ *
498
+ */
499
+
500
+ private _sortCol(col: number, ascending?: boolean ) {
501
+
502
+ setWaitCursor(true);
503
+
504
+ // to allow cursor
505
+ this.setTimeout("sort", 50, () => {
506
+ let asc = true;
507
+
508
+ // already sorted ?
509
+ const scol = this.query(`.col-header [data-col="${col}"`);
510
+
511
+ if( ascending===undefined ) {
512
+ if (scol.hasClass("sorted")) {
513
+ if (scol.hasClass("desc")) {
514
+ asc = true;
515
+ }
516
+ else {
517
+ asc = false;
518
+ }
519
+ }
520
+ else {
521
+ const sorted = this.queryAll(".sorted");
522
+ sorted.forEach(x => x.removeClass("sorted asc desc"));
523
+ }
524
+ }
525
+ else {
526
+ asc = ascending;
527
+ }
528
+
529
+ scol.setClass("sorted");
530
+ scol.setClass("desc", !asc);
531
+
532
+ const cdata = this._getCol(col);
533
+
534
+ let num = false;
535
+ switch (cdata.type) {
536
+ case "checkbox":
537
+ case "money":
538
+ case "number":
539
+ case "percent": {
540
+ num = true;
541
+ }
542
+ }
543
+
544
+ this._dataview.sort([{
545
+ field: cdata.id,
546
+ ascending: asc,
547
+ numeric: num
548
+ }]);
549
+
550
+ this._update(true);
551
+
552
+ setWaitCursor(false);
553
+ });
554
+ }
555
+
556
+ /**
557
+ *
558
+ */
559
+
560
+ sortCol( colIdx: any, ascending: boolean ) {
561
+
562
+ const idx = this._columns.findIndex( x => x.id === colIdx );
563
+ if( idx>=0 ) {
564
+ this._sortCol( idx, ascending );
565
+ }
566
+ }
567
+
568
+ /**
569
+ *
570
+ */
571
+
572
+ private _renderCell(rec: DataRecord, column: GridColumnEx, extra_cls: string[] ): ComponentContent {
573
+
574
+ const col = column.id;
575
+ const type = column.type;
576
+
577
+ let data = this._datamodel.getRaw(col, rec);
578
+ if (data === undefined || data === null) {
579
+ return null;
580
+ }
581
+
582
+ let cls = "";
583
+ if( column.classifier ) {
584
+ extra_cls.push( column.classifier( data, rec, col ) );
585
+ }
586
+
587
+ if (data instanceof Function) {
588
+ return data(rec, col);
589
+ }
590
+
591
+ if( column.formatter ) {
592
+ return column.formatter( data );
593
+ }
594
+
595
+ switch (type) {
596
+ case "checkbox": {
597
+ if (data) {
598
+ return new Icon({ cls: "cell-check" + cls, iconId: check_icon });
599
+ }
600
+
601
+ return undefined;
602
+ }
603
+
604
+ case "image": {
605
+ if (isString(data)) {
606
+ return new Image({ cls, src: data, fit: "scale-down" });
607
+ }
608
+
609
+ return undefined;
610
+ }
611
+
612
+ case "number": {
613
+ if (!isNumber(data)) {
614
+ return "NaN";
615
+ }
616
+
617
+ data = this._num_fmt.format(data as number);
618
+ break;
619
+ }
620
+
621
+ case "money": {
622
+ if (!isNumber(data)) {
623
+ return "NaN";
624
+ }
625
+
626
+ data = this._mny_fmt.format(data as number);
627
+ break;
628
+ }
629
+
630
+ case "percent": {
631
+ return new Box({
632
+ cls: "percent" + cls,
633
+ content: new Component({ cls: "bar", width: data + "%" })
634
+ });
635
+ }
636
+
637
+ case "icon": {
638
+ return new Icon({ cls, iconId: data + "" });
639
+ }
640
+
641
+ case "date": {
642
+ data = this._dte_fmt.format(data as Date);
643
+ break;
644
+ }
645
+
646
+ default: {
647
+ data = data + "";
648
+ break;
649
+ }
650
+ }
651
+
652
+ return new Component({ tag: "span", cls, content: data });
653
+ }
654
+
655
+ /**
656
+ *
657
+ */
658
+
659
+ private _buildRow(rowid: number, rec: DataRecord, top: number) {
660
+
661
+ const els: Component[] = [];
662
+ const count = this._getColCount();
663
+
664
+ for (let col = 0; col < count; col++) {
665
+ const cdata = this._getCol(col);
666
+ if (cdata.fixed) {
667
+ continue;
668
+ }
669
+
670
+ const extra: string[] = []
671
+ const content = this._renderCell(rec, cdata, extra );
672
+
673
+ let align = "start";
674
+ switch (cdata.align) {
675
+ default: align = "start"; break;
676
+ case "center": align = "center"; break;
677
+ case "right": align = "end"; break;
678
+ }
679
+
680
+ const el = new Component({
681
+ cls: "cell",
682
+ style: { width: cdata?.width ? cdata.width + "px" : undefined, justifyContent: align },
683
+ content
684
+ });
685
+
686
+ if( extra.length ) {
687
+ el.addClass( extra.join(' ') );
688
+ }
689
+
690
+ if (cdata.type) {
691
+ el.addClass(cdata.type);
692
+ }
693
+
694
+ el.setData("col", col + "");
695
+ el.setData("row", rowid + "")
696
+
697
+ els.push(el);
698
+ }
699
+
700
+ const rowel = new Box({ cls: "row", style: { top: top.toFixed(2) + "px" }, content: els });
701
+ rowel.setData("row", rowid + "");
702
+
703
+ if (this._selection.has(rowid)) {
704
+ rowel.addClass("selected");
705
+ }
706
+
707
+ return rowel;
708
+ }
709
+
710
+ /**
711
+ *
712
+ */
713
+
714
+ private _buildRowHeader(rowid: number, rec: DataRecord, top: number) {
715
+
716
+ const cols: Component[] = [];
717
+ const count = this._getColCount();
718
+
719
+ for (let col = 0; col < count; col++) {
720
+ const cdata = this._getCol(col);
721
+ if (!cdata?.fixed) {
722
+ continue;
723
+ }
724
+
725
+ const content = this._renderCell(rec, cdata.id, [cdata.type] );
726
+
727
+ let align = "start";
728
+ switch (cdata.align) {
729
+ default: align = "start"; break;
730
+ case "center": align = "center"; break;
731
+ case "right": align = "end"; break;
732
+ }
733
+
734
+ const el = new Component({
735
+ cls: "cell",
736
+ style: { width: cdata?.width ? cdata.width + "px" : undefined, justifyContent: align },
737
+ content
738
+ });
739
+
740
+ if (cdata.type) {
741
+ el.addClass(cdata.type);
742
+ }
743
+
744
+ el.setData("col", col + "");
745
+ el.setData("row", rowid + "")
746
+
747
+ cols.push(el);
748
+ }
749
+
750
+ const rowel = new Box({ cls: "row", style: { top: top + "px" }, content: cols });
751
+ rowel.setData("row", rowid + "");
752
+
753
+ if (this._selection.has(rowid)) {
754
+ rowel.addClass("selected");
755
+ }
756
+
757
+ return rowel;
758
+ }
759
+
760
+ /**
761
+ *
762
+ */
763
+
764
+ private _updateFlexs( ) {
765
+ let maxw = 0;
766
+ let flexc = 0;
767
+
768
+ const ccount = this._getColCount();
769
+
770
+ for (let x = 0; x < ccount; x++) {
771
+ const cdata = this._getCol(x);
772
+
773
+ if( !cdata.fixed && cdata.flex ) {
774
+ flexc += cdata.flex;
775
+ }
776
+ else {
777
+ maxw += cdata.width;
778
+ }
779
+ }
780
+
781
+ if( flexc ) {
782
+ const width = this._viewport.dom.clientWidth;
783
+ const delta = width - maxw;
784
+ const fw = delta / flexc;
785
+
786
+ for (let col = 0; col < ccount; col++) {
787
+ const cdata = this._getCol(col);
788
+ if( !cdata.fixed && cdata.flex ) {
789
+ cdata.width = Math.max( cdata.flex*fw, 32 );
790
+
791
+ const cols = this.queryAll(`[data-col="${col}"]`)
792
+ cols.forEach(c => {
793
+ c.setStyleValue("width", cdata.width + "px");
794
+ });
795
+ }
796
+ }
797
+ }
798
+ }
799
+
800
+ /**
801
+ *
802
+ */
803
+
804
+ private _computeFullSize() {
805
+
806
+ let maxw = 0;
807
+ let maxfw = 0;
808
+
809
+ const ccount = this._getColCount();
810
+
811
+ for (let x = 0; x < ccount; x++) {
812
+ const cdata = this._getCol(x);
813
+ let w = 0;
814
+
815
+ if (cdata.fixed) {
816
+ this._has_fixed = true;
817
+ }
818
+
819
+ if ( cdata.width) {
820
+ w += cdata.width;
821
+ }
822
+
823
+ if (cdata.fixed) {
824
+ maxfw += w;
825
+ }
826
+ else {
827
+ maxw += w;
828
+ }
829
+ }
830
+
831
+ const maxr = this._dataview.getCount();
832
+ let maxh = maxr;
833
+
834
+ if (maxr < SCROLL_LIMIT) {
835
+ maxh *= this._row_height;
836
+ }
837
+ else {
838
+ const height = this._body.dom.parentElement.clientHeight;
839
+ const npage = height / this._row_height;
840
+ maxh = maxr - Math.floor(npage) + npage * this._row_height;
841
+ }
842
+
843
+ this.setStyleVariable("--fixed-width", maxfw + "px");
844
+ this._body.setStyleValue("height", maxh + "px");
845
+ this._body.setStyleValue("width", maxw + "px");
846
+ this._vheader.setStyleValue("height", maxh + "px");
847
+ }
848
+
849
+ /**
850
+ *
851
+ */
852
+
853
+ private _init() {
854
+ this._body = new Component({ cls: "body" });
855
+
856
+ this._viewport = new Viewport({ content: this._body });
857
+
858
+ if (!this._has_footer) {
859
+ this.setStyleVariable("--footer-height", "0");
860
+ }
861
+
862
+ // SCROLL
863
+ this._viewport.addDOMEvent( "scroll", (ev) => {
864
+ // sync horz & vert elements
865
+ this._left = this._viewport.dom.scrollLeft;
866
+ this.setStyleVariable("--left", -this._left + "px");
867
+
868
+ this._top = this._viewport.dom.scrollTop;
869
+ this.setStyleVariable("--top", -this._top + "px");
870
+
871
+ //this.setTimeout( "update", 0, ( ) => this._update( ) );
872
+ this._update()
873
+ });
874
+
875
+ // WHEEL
876
+ this.addDOMEvent("wheel", (ev: WheelEvent) => {
877
+ if (ev.deltaY && this._dataview.getCount() >= SCROLL_LIMIT) {
878
+ this._viewport.dom.scrollBy(0, ev.deltaY < 0 ? -1 : 1);
879
+ ev.stopPropagation();
880
+ ev.preventDefault();
881
+ }
882
+
883
+ if (this._has_fixed && ev.deltaY) {
884
+ // wheel on fixed part
885
+ // fixed part do not have scrollbar, so we need to handle it by hand
886
+ let t = ev.target as Node;
887
+ while (t != this.dom) {
888
+ if (t == this._vheader.dom) {
889
+ this._viewport.dom.scrollBy(0, ev.deltaY < 0 ? -this._row_height : this._row_height);
890
+ ev.stopPropagation();
891
+ ev.preventDefault();
892
+ break;
893
+ }
894
+
895
+ t = t.parentNode;
896
+ }
897
+ }
898
+ })
899
+
900
+ const targetRow = ( e: MouseEvent ) => {
901
+ let el = Component.parentElement(e.target as HTMLElement, Component);
902
+ while (el && !el.hasClass("row")) {
903
+ el = el.parentElement();
904
+ }
905
+
906
+ if (el) {
907
+ return el.getIntData("row");
908
+ }
909
+
910
+ return undefined;
911
+ }
912
+
913
+
914
+ // CLICK
915
+ this.addDOMEvent("click", (e) => {
916
+ const row = targetRow( e );
917
+ if (row!==undefined ) {
918
+ //TODO: multiselection
919
+ if( !this._selection.has(row) ) {
920
+ this._clearSelection();
921
+ this._addSelection(row);
922
+ }
923
+ }
924
+ });
925
+
926
+ // DBLCLICK
927
+ this.addDOMEvent("dblclick", (e) => {
928
+ const row = targetRow( e );
929
+ if (row!==undefined ) {
930
+ //TODO: multiselection
931
+ if( !this._selection.has(row) ) {
932
+ this._clearSelection();
933
+ this._addSelection(row);
934
+ }
935
+
936
+ this._on_dblclk( e, row );
937
+
938
+ const rec = this._dataview.getByIndex( row );
939
+ this.fire( "dblClick", { context: rec } );
940
+ }
941
+ });
942
+
943
+ // CONTEXT
944
+ this.addDOMEvent("contextmenu", (e) => {
945
+ const row = targetRow( e );
946
+ if (row!==undefined ) {
947
+ //TODO: multiselection
948
+ if( !this._selection.has(row) ) {
949
+ this._clearSelection();
950
+ this._addSelection(row);
951
+ }
952
+
953
+ const rec = this._dataview.getByIndex( row );
954
+ this.fire( "contextMenu", { uievent: e, context: rec } );
955
+ }
956
+
957
+ e.preventDefault( );
958
+ e.stopPropagation( );
959
+ });
960
+
961
+ // MOUSE OVER
962
+ this.addDOMEvent("mouseover", (e) => {
963
+
964
+ if (!this._has_fixed) {
965
+ return;
966
+ }
967
+
968
+ let el = Component.parentElement(e.target as HTMLElement, Component);
969
+ while (el && !el.hasClass("row")) {
970
+ el = el.parentElement();
971
+ }
972
+
973
+ if (el) {
974
+ const data = el.getData("row");
975
+
976
+ this.queryAll(".hover").forEach(x => x.removeClass("hover"));
977
+
978
+ if (data) {
979
+ const rows = this.queryAll(`.row[data-row="${data}"]`);
980
+ rows.forEach(x => x.addClass("hover"));
981
+ }
982
+ }
983
+ });
984
+
985
+ // MOUSE LEAVE
986
+ this.addDOMEvent("mouseleave", (e) => {
987
+
988
+ if (!this._has_fixed) {
989
+ return;
990
+ }
991
+
992
+ this.queryAll(".hover").forEach(x => x.removeClass("hover"));
993
+ });
994
+
995
+ this._updateFlexs( );
996
+
997
+ this._fheader = this._buildColHeader(true);
998
+ this._hheader = this._buildColHeader(false);
999
+ this._vheader = new Box({ cls: "row-header" })
1000
+
1001
+ if (this._has_footer) {
1002
+ this._ffooter = this._buildColFooter(true);
1003
+ this._footer = this._buildColFooter(false);
1004
+ }
1005
+
1006
+ this.setContent([this._viewport, this._fheader, this._hheader, this._ffooter, this._footer, this._vheader]);
1007
+
1008
+ // compute misc variables
1009
+ {
1010
+ const rh = this.getStyleVariable("--row-height");
1011
+ this._row_height = parseInt(rh);
1012
+ }
1013
+
1014
+ this._computeFullSize();
1015
+ }
1016
+
1017
+ /**
1018
+ *
1019
+ */
1020
+
1021
+ protected _on_dblclk( e: UIEvent, row: number ) {
1022
+
1023
+ }
1024
+
1025
+ /**
1026
+ *
1027
+ */
1028
+
1029
+ private _update(force = false) {
1030
+
1031
+ if (!this._lock) {
1032
+ const rc = this.getBoundingRect();
1033
+
1034
+ // rows
1035
+ const rowc = this._dataview.getCount();
1036
+ const mul = rowc < SCROLL_LIMIT ? this._row_height : 1;
1037
+
1038
+ const start = Math.floor(this._top / mul);
1039
+ const end = start + Math.ceil(rc.height / this._row_height);
1040
+ const hasFixed = this._has_fixed;
1041
+
1042
+ if (this._start != start || this._end != end || force) {
1043
+
1044
+ const rows: Component[] = [];
1045
+ const headers: Component[] = [];
1046
+
1047
+ if (force) {
1048
+ this._vis_rows.clear();
1049
+ }
1050
+
1051
+ let newvis: typeof this._vis_rows = new Map();
1052
+
1053
+ let y = start * mul;
1054
+
1055
+ for (let row = start; row < end && row < rowc; row++, y += this._row_height) {
1056
+
1057
+ let el = this._vis_rows.get(row);
1058
+ const rec = this._dataview.getByIndex(row);
1059
+
1060
+ if (hasFixed) {
1061
+ if (!el) {
1062
+ el = {
1063
+ h: this._buildRowHeader(row, rec, y),
1064
+ r: this._buildRow(row, rec, y),
1065
+ };
1066
+ }
1067
+ else {
1068
+ el.h.setStyleValue("top", y + "px");
1069
+ el.r.setStyleValue("top", y + "px");
1070
+ }
1071
+
1072
+ headers.push(el.h);
1073
+ }
1074
+ else {
1075
+ if (!el) {
1076
+ el = { h: null, r: this._buildRow(row, rec, y), };
1077
+ }
1078
+ else {
1079
+ el.r.setStyleValue("top", y + "px");
1080
+ }
1081
+ }
1082
+
1083
+ rows.push(el.r);
1084
+ newvis.set(row, el);
1085
+ }
1086
+
1087
+ if (hasFixed) {
1088
+ headers.push(new Component({ cls: "cell-out", style: { top: y + "px" } }));
1089
+ }
1090
+
1091
+ this._vis_rows = newvis;
1092
+ this._start = start;
1093
+ this._end = end;
1094
+
1095
+ this._body.setContent(rows);
1096
+
1097
+ if (hasFixed) {
1098
+ this._vheader.removeClass("@hidden");
1099
+ this._vheader.setContent(headers);
1100
+ }
1101
+ else {
1102
+ this._vheader.addClass("@hidden");
1103
+ }
1104
+ }
1105
+ }
1106
+ }
1107
+ /**
1108
+ *
1109
+ */
1110
+
1111
+ private _clearSelection() {
1112
+ for (const ref of this._selection.keys()) {
1113
+ const els = this.queryAll(`.row[data-row="${ref}"]`)
1114
+ els.forEach(el => {
1115
+ el.removeClass("selected");
1116
+ })
1117
+ }
1118
+
1119
+ this._selection.clear();
1120
+ }
1121
+
1122
+ /**
1123
+ *
1124
+ */
1125
+
1126
+ private _addSelection(rowid: number) {
1127
+ this._selection.add(rowid)
1128
+
1129
+ const els = this.queryAll(`.row[data-row="${rowid}"]`)
1130
+ els.forEach(el => {
1131
+ el.addClass("selected");
1132
+ });
1133
+
1134
+ const rec = this._dataview.getByIndex( rowid );
1135
+ this.fire("selectionChange", { selection: rec, empty: false } );
1136
+ }
1137
+
1138
+ /**
1139
+ *
1140
+ */
1141
+
1142
+ getSelection( ) {
1143
+ if( this._selection.size==0 ) {
1144
+ return null;
1145
+ }
1146
+
1147
+ const ids = [...this._selection.values() ];
1148
+ return ids.map( id => this._dataview.getByIndex(id) );
1149
+ }
1150
+
1151
+ /**
1152
+ *
1153
+ */
1154
+
1155
+ getFirstSel( ) {
1156
+ if( this._selection.size==0 ) {
1157
+ return null;
1158
+ }
1159
+
1160
+ const id = this._selection.values().next().value;
1161
+ return this._dataview.getByIndex( id );
1162
+ }
1163
+
1164
+ /**
1165
+ *
1166
+ */
1167
+
1168
+ selectItem( id: any ) {
1169
+ const index = this._dataview.indexOfId( id );
1170
+ if( index>=0 ) {
1171
+ this._addSelection( index );
1172
+ }
1173
+ }
1174
+ }
1175
+