x4js 1.6.4 → 2.0.1

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 (282) hide show
  1. package/README.md +3 -14
  2. package/lib/README.txt +3 -14
  3. package/lib/src/assets/house-light.svg +1 -0
  4. package/lib/src/assets/radio.svg +4 -0
  5. package/lib/src/components/base.scss +26 -0
  6. package/lib/src/components/boxes/boxes.module.scss +37 -0
  7. package/lib/src/components/boxes/boxes.ts +125 -0
  8. package/lib/src/components/btngroup/btngroup.module.scss +29 -0
  9. package/lib/src/components/btngroup/btngroup.ts +106 -0
  10. package/lib/src/components/button/button.module.scss +154 -0
  11. package/lib/src/components/button/button.ts +117 -0
  12. package/lib/src/components/calendar/calendar-check-sharp-light.svg +1 -0
  13. package/lib/src/components/calendar/calendar.module.scss +163 -0
  14. package/lib/src/{calendar.ts → components/calendar/calendar.ts} +81 -83
  15. package/lib/src/components/calendar/chevron-left-sharp-light.svg +1 -0
  16. package/lib/src/components/calendar/chevron-right-sharp-light.svg +1 -0
  17. package/lib/src/components/checkbox/check.svg +4 -0
  18. package/lib/src/components/checkbox/checkbox.module.scss +142 -0
  19. package/lib/src/components/checkbox/checkbox.ts +125 -0
  20. package/lib/src/components/colorinput/colorinput.module.scss +65 -0
  21. package/lib/src/components/colorinput/colorinput.ts +88 -0
  22. package/lib/src/components/colorinput/crosshairs-simple-sharp-light.svg +1 -0
  23. package/lib/src/components/colorpicker/colorpicker.module.scss +133 -0
  24. package/lib/src/components/colorpicker/colorpicker.ts +477 -0
  25. package/lib/src/components/combobox/combobox.module.scss +121 -0
  26. package/lib/src/components/combobox/combobox.ts +190 -0
  27. package/lib/src/components/combobox/updown.svg +4 -0
  28. package/lib/src/components/dialog/dialog.module.scss +71 -0
  29. package/lib/src/components/dialog/dialog.ts +91 -0
  30. package/lib/src/components/dialog/xmark-sharp-light.svg +1 -0
  31. package/lib/src/components/form/form.module.scss +34 -0
  32. package/lib/src/components/form/form.ts +36 -0
  33. package/lib/src/components/header/header.module.scss +40 -0
  34. package/lib/src/components/header/header.ts +124 -0
  35. package/lib/src/components/icon/icon.module.scss +30 -0
  36. package/lib/src/components/icon/icon.ts +134 -0
  37. package/lib/src/components/image/image.module.scss +21 -0
  38. package/lib/src/components/image/image.ts +67 -0
  39. package/lib/src/components/input/input.module.scss +69 -0
  40. package/lib/src/components/input/input.ts +274 -0
  41. package/lib/src/components/label/label.module.scss +52 -0
  42. package/lib/src/components/label/label.ts +55 -0
  43. package/lib/src/components/listbox/listbox.module.scss +103 -0
  44. package/lib/src/components/listbox/listbox.ts +427 -0
  45. package/lib/src/components/menu/caret-right-solid.svg +1 -0
  46. package/lib/src/components/menu/menu.module.scss +108 -0
  47. package/lib/src/components/menu/menu.ts +168 -0
  48. package/lib/src/components/messages/circle-exclamation.svg +1 -0
  49. package/lib/src/components/messages/messages.module.scss +47 -0
  50. package/lib/src/components/messages/messages.ts +64 -0
  51. package/lib/src/components/normalize.scss +386 -0
  52. package/lib/src/components/notification/circle-check-solid.svg +1 -0
  53. package/lib/src/components/notification/circle-exclamation-solid.svg +1 -0
  54. package/lib/src/components/notification/circle-notch-light.svg +1 -0
  55. package/lib/src/components/notification/notification.module.scss +82 -0
  56. package/lib/src/components/notification/notification.ts +108 -0
  57. package/lib/src/components/notification/xmark-sharp-light.svg +1 -0
  58. package/lib/src/components/panel/panel.module.scss +48 -0
  59. package/lib/src/components/panel/panel.ts +57 -0
  60. package/lib/src/components/popup/popup.module.scss +43 -0
  61. package/lib/src/components/popup/popup.ts +395 -0
  62. package/lib/src/components/progress/progress.module.scss +57 -0
  63. package/lib/src/components/progress/progress.ts +43 -0
  64. package/lib/src/components/rating/rating.module.scss +23 -0
  65. package/lib/src/components/rating/rating.ts +125 -0
  66. package/lib/src/components/rating/star-sharp-light.svg +1 -0
  67. package/lib/src/components/rating/star-sharp-solid.svg +1 -0
  68. package/lib/src/components/shared.scss +76 -0
  69. package/lib/src/components/sizers/sizer.module.scss +90 -0
  70. package/lib/src/components/sizers/sizer.ts +120 -0
  71. package/lib/src/components/slider/slider.module.scss +71 -0
  72. package/lib/src/components/slider/slider.ts +143 -0
  73. package/lib/src/components/switch/switch.module.scss +127 -0
  74. package/lib/src/components/switch/switch.ts +56 -0
  75. package/lib/src/components/tabs/tabs.module.scss +46 -0
  76. package/lib/src/components/tabs/tabs.ts +157 -0
  77. package/lib/src/components/textarea/textarea.module.scss +59 -0
  78. package/lib/src/components/textarea/textarea.ts +54 -0
  79. package/lib/src/components/textedit/textedit.module.scss +114 -0
  80. package/lib/src/components/textedit/textedit.ts +82 -0
  81. package/lib/src/components/themes.scss +77 -0
  82. package/lib/src/components/tooltips/circle-info-sharp-light.svg +1 -0
  83. package/lib/src/components/tooltips/tooltips.scss +51 -0
  84. package/lib/src/components/tooltips/tooltips.ts +103 -0
  85. package/lib/src/components/treeview/chevron-down-light.svg +1 -0
  86. package/lib/src/components/treeview/treeview.module.scss +116 -0
  87. package/lib/src/components/treeview/treeview.ts +403 -0
  88. package/lib/src/components/viewport/viewport.module.scss +25 -0
  89. package/lib/src/components/viewport/viewport.ts +38 -0
  90. package/lib/src/core/component.ts +979 -0
  91. package/lib/src/core/core_colors.ts +250 -0
  92. package/lib/src/{dom_events.ts → core/core_dom.ts} +195 -39
  93. package/lib/src/{drag_manager.ts → core/core_dragdrop.ts} +29 -44
  94. package/lib/src/core/core_element.ts +98 -0
  95. package/lib/src/core/core_events.ts +149 -0
  96. package/lib/src/{i18n.ts → core/core_i18n.ts} +43 -42
  97. package/lib/src/{router.ts → core/core_router.ts} +27 -40
  98. package/lib/src/core/core_styles.ts +215 -0
  99. package/lib/src/core/core_svg.ts +550 -0
  100. package/lib/src/core/core_tools.ts +673 -0
  101. package/lib/src/main.scss +21 -0
  102. package/lib/src/main.tsx +323 -0
  103. package/lib/src/x4.scss +19 -0
  104. package/lib/types/x4.d.ts +2624 -0
  105. package/package.json +67 -59
  106. package/scripts/build.mjs +351 -0
  107. package/scripts/prepack.mjs +15 -0
  108. package/src/assets/house-light.svg +1 -0
  109. package/src/assets/radio.svg +4 -0
  110. package/src/components/base.scss +26 -0
  111. package/src/components/boxes/boxes.module.scss +37 -0
  112. package/src/components/boxes/boxes.ts +125 -0
  113. package/src/components/btngroup/btngroup.module.scss +29 -0
  114. package/src/components/btngroup/btngroup.ts +106 -0
  115. package/src/components/button/button.module.scss +154 -0
  116. package/src/components/button/button.ts +117 -0
  117. package/src/components/calendar/calendar-check-sharp-light.svg +1 -0
  118. package/src/components/calendar/calendar.module.scss +163 -0
  119. package/src/components/calendar/calendar.ts +326 -0
  120. package/src/components/calendar/chevron-left-sharp-light.svg +1 -0
  121. package/src/components/calendar/chevron-right-sharp-light.svg +1 -0
  122. package/src/components/checkbox/check.svg +4 -0
  123. package/src/components/checkbox/checkbox.module.scss +142 -0
  124. package/src/components/checkbox/checkbox.ts +125 -0
  125. package/src/components/colorinput/colorinput.module.scss +65 -0
  126. package/src/components/colorinput/colorinput.ts +88 -0
  127. package/src/components/colorinput/crosshairs-simple-sharp-light.svg +1 -0
  128. package/src/components/colorpicker/colorpicker.module.scss +133 -0
  129. package/src/components/colorpicker/colorpicker.ts +477 -0
  130. package/src/components/combobox/combobox.module.scss +121 -0
  131. package/src/components/combobox/combobox.ts +190 -0
  132. package/src/components/combobox/updown.svg +4 -0
  133. package/src/components/dialog/dialog.module.scss +71 -0
  134. package/src/components/dialog/dialog.ts +91 -0
  135. package/src/components/dialog/xmark-sharp-light.svg +1 -0
  136. package/src/components/form/form.module.scss +34 -0
  137. package/src/components/form/form.ts +36 -0
  138. package/src/components/header/header.module.scss +40 -0
  139. package/src/components/header/header.ts +124 -0
  140. package/src/components/icon/icon.module.scss +30 -0
  141. package/src/components/icon/icon.ts +134 -0
  142. package/src/components/image/image.module.scss +21 -0
  143. package/src/components/image/image.ts +67 -0
  144. package/src/components/input/input.module.scss +69 -0
  145. package/src/components/input/input.ts +274 -0
  146. package/src/components/label/label.module.scss +52 -0
  147. package/src/components/label/label.ts +55 -0
  148. package/src/components/listbox/listbox.module.scss +103 -0
  149. package/src/components/listbox/listbox.ts +427 -0
  150. package/src/components/menu/caret-right-solid.svg +1 -0
  151. package/src/components/menu/menu.module.scss +108 -0
  152. package/src/components/menu/menu.ts +168 -0
  153. package/src/components/messages/circle-exclamation.svg +1 -0
  154. package/src/components/messages/messages.module.scss +47 -0
  155. package/src/components/messages/messages.ts +64 -0
  156. package/src/components/normalize.scss +386 -0
  157. package/src/components/notification/circle-check-solid.svg +1 -0
  158. package/src/components/notification/circle-exclamation-solid.svg +1 -0
  159. package/src/components/notification/circle-notch-light.svg +1 -0
  160. package/src/components/notification/notification.module.scss +82 -0
  161. package/src/components/notification/notification.ts +108 -0
  162. package/src/components/notification/xmark-sharp-light.svg +1 -0
  163. package/src/components/panel/panel.module.scss +48 -0
  164. package/src/components/panel/panel.ts +57 -0
  165. package/src/components/popup/popup.module.scss +43 -0
  166. package/src/components/popup/popup.ts +395 -0
  167. package/src/components/progress/progress.module.scss +57 -0
  168. package/src/components/progress/progress.ts +43 -0
  169. package/src/components/rating/rating.module.scss +23 -0
  170. package/src/components/rating/rating.ts +125 -0
  171. package/src/components/rating/star-sharp-light.svg +1 -0
  172. package/src/components/rating/star-sharp-solid.svg +1 -0
  173. package/src/components/shared.scss +76 -0
  174. package/src/components/sizers/sizer.module.scss +90 -0
  175. package/src/components/sizers/sizer.ts +120 -0
  176. package/src/components/slider/slider.module.scss +71 -0
  177. package/src/components/slider/slider.ts +143 -0
  178. package/src/components/switch/switch.module.scss +127 -0
  179. package/src/components/switch/switch.ts +56 -0
  180. package/src/components/tabs/tabs.module.scss +46 -0
  181. package/src/components/tabs/tabs.ts +157 -0
  182. package/src/components/textarea/textarea.module.scss +59 -0
  183. package/src/components/textarea/textarea.ts +54 -0
  184. package/src/components/textedit/textedit.module.scss +114 -0
  185. package/src/components/textedit/textedit.ts +82 -0
  186. package/src/components/themes.scss +77 -0
  187. package/src/components/tooltips/circle-info-sharp-light.svg +1 -0
  188. package/src/components/tooltips/tooltips.scss +51 -0
  189. package/src/components/tooltips/tooltips.ts +103 -0
  190. package/src/components/treeview/chevron-down-light.svg +1 -0
  191. package/src/components/treeview/treeview.module.scss +116 -0
  192. package/src/components/treeview/treeview.ts +403 -0
  193. package/src/components/viewport/viewport.module.scss +25 -0
  194. package/src/components/viewport/viewport.ts +38 -0
  195. package/src/core/component.ts +979 -0
  196. package/src/core/core_colors.ts +250 -0
  197. package/src/core/core_dom.ts +471 -0
  198. package/src/core/core_dragdrop.ts +201 -0
  199. package/src/core/core_element.ts +98 -0
  200. package/src/core/core_events.ts +149 -0
  201. package/src/core/core_i18n.ts +377 -0
  202. package/src/core/core_router.ts +221 -0
  203. package/src/core/core_styles.ts +215 -0
  204. package/src/core/core_svg.ts +550 -0
  205. package/src/core/core_tools.ts +673 -0
  206. package/src/main.scss +21 -0
  207. package/src/main.tsx +323 -0
  208. package/src/x4.scss +19 -0
  209. package/tsconfig.json +14 -0
  210. package/types/scss.d.ts +4 -0
  211. package/types/svg.d.ts +4 -0
  212. package/types/x4react.d.ts +9 -0
  213. package/lib/changelog.txt +0 -23
  214. package/lib/cjs/x4js.js +0 -39
  215. package/lib/cjs/x4js.js.map +0 -7
  216. package/lib/esm/x4js.mjs +0 -15972
  217. package/lib/esm/x4js.mjs.map +0 -7
  218. package/lib/licence.md +0 -21
  219. package/lib/src/MIT-license.md +0 -14
  220. package/lib/src/action.ts +0 -88
  221. package/lib/src/alpha.jpg +0 -0
  222. package/lib/src/app_sockets.ts +0 -81
  223. package/lib/src/application.ts +0 -262
  224. package/lib/src/autocomplete.ts +0 -232
  225. package/lib/src/base64.ts +0 -166
  226. package/lib/src/base_component.ts +0 -152
  227. package/lib/src/button.ts +0 -355
  228. package/lib/src/canvas.ts +0 -510
  229. package/lib/src/cardview.ts +0 -228
  230. package/lib/src/checkbox.ts +0 -188
  231. package/lib/src/color.ts +0 -752
  232. package/lib/src/colorpicker.ts +0 -1649
  233. package/lib/src/combobox.ts +0 -512
  234. package/lib/src/component.ts +0 -2367
  235. package/lib/src/copyright.txt +0 -27
  236. package/lib/src/datastore.ts +0 -1302
  237. package/lib/src/dialog.ts +0 -656
  238. package/lib/src/drawtext.ts +0 -355
  239. package/lib/src/fileupload.ts +0 -213
  240. package/lib/src/form.ts +0 -413
  241. package/lib/src/formatters.ts +0 -105
  242. package/lib/src/gridview.ts +0 -1185
  243. package/lib/src/icon.ts +0 -362
  244. package/lib/src/image.ts +0 -225
  245. package/lib/src/index.ts +0 -89
  246. package/lib/src/input.ts +0 -297
  247. package/lib/src/label.ts +0 -153
  248. package/lib/src/layout.ts +0 -442
  249. package/lib/src/link.ts +0 -86
  250. package/lib/src/listview.ts +0 -765
  251. package/lib/src/md5.ts +0 -438
  252. package/lib/src/menu.ts +0 -425
  253. package/lib/src/messagebox.ts +0 -224
  254. package/lib/src/panel.ts +0 -86
  255. package/lib/src/popup.ts +0 -494
  256. package/lib/src/property_editor.ts +0 -337
  257. package/lib/src/radiobtn.ts +0 -197
  258. package/lib/src/rating.ts +0 -135
  259. package/lib/src/request.ts +0 -300
  260. package/lib/src/settings.ts +0 -77
  261. package/lib/src/sidebarview.ts +0 -108
  262. package/lib/src/spreadsheet.ts +0 -1449
  263. package/lib/src/styles.ts +0 -343
  264. package/lib/src/svgcomponent.ts +0 -592
  265. package/lib/src/tabbar.ts +0 -151
  266. package/lib/src/tabview.ts +0 -110
  267. package/lib/src/textarea.ts +0 -235
  268. package/lib/src/textedit.ts +0 -533
  269. package/lib/src/toaster.ts +0 -80
  270. package/lib/src/tools.ts +0 -1473
  271. package/lib/src/tooltips.ts +0 -191
  272. package/lib/src/treeview.ts +0 -716
  273. package/lib/src/version.ts +0 -30
  274. package/lib/src/x4.less +0 -2242
  275. package/lib/src/x4dom.ts +0 -57
  276. package/lib/src/x4events.ts +0 -585
  277. package/lib/src/x4js.ts +0 -89
  278. package/lib/src/x4react.ts +0 -90
  279. package/lib/styles/x4.css +0 -1785
  280. package/lib/styles/x4.less +0 -2242
  281. package/lib/types/x4js.d.ts +0 -6728
  282. package/license.md +0 -21
@@ -1,1302 +0,0 @@
1
- /**
2
- * ___ ___ __
3
- * \ \/ / / _
4
- * \ / /_| |_
5
- * / \____ _|
6
- * /__/\__\ |_|
7
- *
8
- * @file datastore.ts
9
- * @author Etienne Cochard
10
- *
11
- * Copyright (c) 2019-2023 R-libre ingenierie
12
- *
13
- * Permission is hereby granted, free of charge, to any person obtaining a copy
14
- * of this software and associated documentation files (the "Software"), to deal
15
- * in the Software without restriction, including without limitation the rights
16
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
17
- * of the Software, and to permit persons to whom the Software is furnished to do so,
18
- * subject to the following conditions:
19
- * The above copyright notice and this permission notice shall be included in all copies
20
- * or substantial portions of the Software.
21
- *
22
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
23
- * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
24
- * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
25
- * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
26
- * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
27
- * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
28
- **/
29
-
30
- import { ajaxRequest } from './request';
31
- import { isArray, isString } from './tools';
32
- import { BasicEvent, EvChange, EventSource, EventMap, MapEvents } from './x4events';
33
- import { BaseComponent, BaseComponentEventMap, BaseComponentProps } from './base_component';
34
-
35
- export type ChangeCallback = (type: string, id?: any) => void;
36
- export type CalcCallback = () => string;
37
-
38
- export type FieldType = 'string' | 'int' | 'float' | 'date' | 'bool' | 'array' | 'object' | 'any' | 'calc';
39
- export type DataIndex = Uint32Array;
40
-
41
- export interface EvDataChange extends BasicEvent {
42
- type: string;
43
- id: any;
44
- }
45
-
46
- export function EvDataChange( type: 'create' | 'update' | 'delete' | 'data' | 'change', id?: any ) {
47
- return BasicEvent<EvDataChange>( { type, id } );
48
- }
49
-
50
-
51
-
52
-
53
- /**
54
- * fields definition
55
- * field with index=0 is record id
56
- */
57
-
58
- export interface MetaData {
59
- type?: FieldType;
60
- prec?: number;
61
- required?: boolean;
62
- calc?: (rec: Record) => any;
63
- model?: Record; // in case of array of subtypes, the model
64
- }
65
-
66
- export interface FieldInfo extends MetaData {
67
- name: string;
68
- }
69
-
70
- /**
71
- *
72
- */
73
-
74
- class MetaInfos {
75
- name: string;
76
- id: string; // field name holding 'id' record info
77
- fields: FieldInfo[]; // field list
78
-
79
- constructor( name: string ) {
80
- this.name = name;
81
- this.id = undefined;
82
- this.fields = [];
83
- }
84
- }
85
-
86
- const metaFields = Symbol( 'metaField' );
87
-
88
- function _getMetas( obj: object, create = true ) : MetaInfos {
89
-
90
- let ctor = obj.constructor;
91
- let mfld = ctor.hasOwnProperty(metaFields) ? ctor[metaFields] : undefined;
92
-
93
- if( mfld===undefined ) {
94
- if( !create ) {
95
- console.assert( mfld!==undefined );
96
- }
97
-
98
- // construct our metas
99
- mfld = new MetaInfos( ctor.name );
100
-
101
- // merge with parent class metas
102
- let pctor = Object.getPrototypeOf(ctor);
103
- if( pctor!=Record ) {
104
- let pmetas = pctor[metaFields];
105
- mfld.fields = [...pmetas.fields, ...mfld.fields ]
106
-
107
- console.assert( mfld.id===undefined, 'cannot define mutiple record id' );
108
- if( !mfld.id ) {
109
- mfld.id = pmetas.id;
110
- }
111
- }
112
-
113
- obj.constructor[metaFields] = mfld;
114
- }
115
-
116
- return mfld;
117
- }
118
-
119
- export namespace data {
120
-
121
- /**
122
- * define a record id
123
- * @example
124
- * \@data_id()
125
- * id: string; // this field is the record id
126
- **/
127
-
128
- export function id( ) {
129
- return ( ownerCls, fldName ) => {
130
- let metas = _getMetas( ownerCls );
131
- metas.fields.push( {
132
- name: fldName,
133
- type: 'any',
134
- required: true,
135
- });
136
-
137
- metas.id = fldName;
138
- }
139
- }
140
-
141
- /**
142
- * @ignore
143
- */
144
-
145
- export function field( data: MetaData ) {
146
-
147
- return ( ownerCls, fldName ) => {
148
- let metas = _getMetas( ownerCls );
149
- metas.fields.push( {
150
- name: fldName,
151
- ...data
152
- } );
153
- }
154
- }
155
-
156
- /**
157
- * following member is a string field
158
- * @example
159
- * \@data_string()
160
- * my_field: string; // this field will be seen as a string
161
- */
162
-
163
- export function string( props?: MetaData ) {
164
- return field( { ...props, type: 'string' } );
165
- }
166
-
167
- /**
168
- * following member is an integer field
169
- * @example
170
- * \@data_string()
171
- * my_field: number; // this field will be seen as an integer
172
- */
173
-
174
- export function int( props?: MetaData ) {
175
- return field( { ...props, type: 'int' } );
176
- }
177
-
178
- /**
179
- * following member is a float field
180
- * @example
181
- * \@data_float()
182
- * my_field: number; // this field will be seen as a float
183
- */
184
-
185
- export function float( props?: MetaData ) {
186
- return field( { ...props, type: 'float' } );
187
- }
188
-
189
- /**
190
- * following member is a boolean field
191
- * @example
192
- * \@data_bool()
193
- * my_field: boolean; // this field will be seen as a boolean
194
- */
195
-
196
- export function bool( props?: MetaData ) {
197
- return field( { ...props, type: 'bool' } );
198
- }
199
-
200
- /**
201
- * following member is a date field
202
- * @example
203
- * \@data_date()
204
- * my_field: date; // this field will be seen as a date
205
- */
206
-
207
- export function date( props?: MetaData ) {
208
- return field( { ...props, type: 'date' } );
209
- }
210
-
211
- /**
212
- * following member is a calculated field
213
- * @example
214
- * \@data_calc( )
215
- * get my_field(): string => {
216
- * return 'hello';
217
- * };
218
- */
219
-
220
- export function calc( props?: MetaData ) {
221
- return field( { ...props, type: 'calc'} )
222
- }
223
-
224
-
225
-
226
- /**
227
- *
228
- */
229
-
230
- interface RecordConstructor {
231
- new ( data?: any, id?: any ): Record;
232
- }
233
-
234
- /**
235
- * following member is a record array
236
- * @example
237
- * \@data_array( )
238
- * my_field(): TypedRecord[];
239
- */
240
-
241
- export function array( ctor: RecordConstructor, props?: MetaData ) {
242
- return data.field( { ...props, type: 'array', model: ctor ? new ctor() : null } )
243
- }
244
-
245
- }
246
-
247
-
248
-
249
-
250
- /**
251
- * record model
252
- */
253
-
254
- export class Record {
255
-
256
- constructor( data?: any, id?: any ) {
257
-
258
- if( data!==undefined ) {
259
- this.unSerialize( data, id );
260
- }
261
- }
262
-
263
- clone( source?: any ) {
264
- let rec = new (this.constructor as any)( );
265
- if( source ) {
266
- rec.unSerialize( source );
267
- }
268
- return rec;
269
- }
270
-
271
- /**
272
- * get the record unique identifier
273
- * by default the return value is the first field
274
- * @return unique identifier
275
- */
276
-
277
- getID(): any {
278
- let metas = _getMetas( this, false );
279
- return this[metas.id];
280
- }
281
-
282
- /**
283
- * MUST IMPLEMENT
284
- * @returns fields descriptors
285
- */
286
-
287
- getFields(): FieldInfo[] {
288
- let metas = _getMetas( this, false );
289
- return metas.fields;
290
- }
291
-
292
- /**
293
- *
294
- */
295
-
296
- validate( ) : Error[] {
297
-
298
- let errs: Error[] = null;
299
-
300
- let fields = this.getFields( );
301
- fields.forEach( (fi) => {
302
- if( fi.required && !this.getField(fi.name) ) {
303
- if( errs ) {
304
- errs = [];
305
- }
306
-
307
- errs.push( new Error( `field ${fi.name} is required.` ) );
308
- }
309
- })
310
-
311
- return errs;
312
- }
313
-
314
- //mapAnyFields() {
315
- // this.getFields = ( ) => {
316
- // return Object.keys( this ).map( (name) => {
317
- // return <FieldInfo>{ name };
318
- // });
319
- // }
320
- //}
321
-
322
- getFieldIndex( name: string ) : number {
323
- let fields = this.getFields( );
324
- return fields.findIndex( (fd) => fd.name == name );
325
- }
326
-
327
- /**
328
- * default serializer
329
- * @returns an object with known record values
330
- */
331
-
332
- serialize(): any {
333
- let rec = {};
334
-
335
- this.getFields().forEach((f) => {
336
- if( f.calc === undefined ) {
337
- rec[f.name] = rec[f.name];
338
- }
339
- });
340
-
341
- return rec;
342
- }
343
-
344
- /**
345
- * default unserializer
346
- * @param data - input data
347
- * @returns a new Record
348
- */
349
-
350
- unSerialize(data: any, id?: any) : Record {
351
-
352
- let fields = this.getFields();
353
-
354
- fields.forEach( (sf) => {
355
- let value = data[sf.name];
356
- if (value !== undefined) {
357
- this[sf.name] = this._convertField( sf, value );
358
- }
359
- });
360
-
361
- if( id!==undefined ) {
362
- this[fields[0].name] = id;
363
- }
364
- else {
365
- console.assert( this.getID()!==undefined ); // store do not have ID field
366
- }
367
-
368
- return this;
369
- }
370
-
371
- /**
372
- * field conversion
373
- * @param field - field descriptor
374
- * @param input - value to convert
375
- * @returns the field value in it's original form
376
- */
377
-
378
- protected _convertField( field: FieldInfo, input: any ) : any {
379
-
380
- //TODO: boolean
381
-
382
- switch( field.type ) {
383
- case 'float': {
384
- let ffv: number = typeof (input) === 'number' ? input : parseFloat(input);
385
-
386
- if (field.prec !== undefined) {
387
- let mul = Math.pow(10, field.prec);
388
- ffv = Math.round(ffv * mul) / mul;
389
- }
390
-
391
- return ffv;
392
- }
393
-
394
- case 'int': {
395
- return typeof (input) === 'number' ? input : parseInt(input);
396
- }
397
-
398
- case 'date': {
399
- return isString(input) ? new Date(input) : input;
400
- }
401
-
402
- case 'array': {
403
- let result = [];
404
-
405
- if( field.model ) {
406
- input.forEach( ( v ) => {
407
- result.push( field.model.clone( v ) );
408
- })
409
-
410
- return result;
411
- }
412
- break;
413
- }
414
- }
415
-
416
- return input;
417
- }
418
-
419
- /**
420
- * get raw value of a field
421
- * @param name - field name or field index
422
- */
423
-
424
- getRaw( name: string | number ) : any {
425
-
426
- let idx;
427
- let fields = this.getFields( );
428
-
429
- if( typeof(name) === 'string' ) {
430
- idx = fields.findIndex( fi => fi.name == name );
431
- if( idx < 0 ) {
432
- console.assert( false, 'unknown field: '+name);
433
- return undefined;
434
- }
435
- }
436
- else if( name<fields.length ) {
437
- if( name<0 ) {
438
- return undefined
439
- }
440
-
441
- idx = name;
442
- }
443
- else {
444
- console.assert( false, 'bad field name: '+name);
445
- return undefined;
446
- }
447
-
448
- let fld = fields[idx];
449
- if( fld.calc!==undefined ) {
450
- return fld.calc( this );
451
- }
452
-
453
- return this[fld.name];
454
- }
455
-
456
- /**
457
- *
458
- * @param name
459
- * @param data
460
- */
461
-
462
- setRaw( name: string, data: string ) {
463
- this[name] = data;
464
- }
465
-
466
- /**
467
- * get field value (as string)
468
- * @param name - field name
469
- * @example
470
- * let value = record.get('field1');
471
- */
472
-
473
- getField( name: string ): string {
474
- let v = this.getRaw( name );
475
- return (v===undefined || v===null) ? '' : ''+v;
476
- }
477
-
478
- /**
479
- * set field value
480
- * @param name - field name
481
- * @param value - value to set
482
- * @example
483
- * record.set( 'field1', 7 );
484
- */
485
-
486
- setField(name: string, value: any) {
487
- let fields = this.getFields( );
488
- let idx = fields.findIndex( fi => fi.name == name );
489
-
490
- if( idx < 0 ) {
491
- console.assert( false, 'unknown field: '+name);
492
- return;
493
- }
494
-
495
- let fld = fields[idx];
496
- if( fld.calc!==undefined ) {
497
- console.assert( false, 'cannot set calc field: '+name);
498
- return;
499
- }
500
-
501
- this.setRaw( fld.name, value );
502
- }
503
- }
504
-
505
- /**
506
- * by default, the field id is rhe first member or the record
507
- */
508
-
509
- export class AutoRecord extends Record {
510
-
511
- private m_data;
512
- private m_fid: string;
513
-
514
- constructor( data: any ) {
515
- super( );
516
-
517
- this.m_data = data;
518
- }
519
-
520
- getID( ) {
521
- if( !this.m_fid ) {
522
- let fnames = Object.keys( this.m_data );
523
- this.m_fid = fnames[0];
524
- }
525
-
526
- return this.m_data[this.m_fid];
527
- }
528
-
529
- getFields( ) : FieldInfo[] {
530
- let fnames = Object.keys( this.m_data );
531
- let fields: FieldInfo[] = fnames.map( (n) => {
532
- return {
533
- name: n
534
- };
535
- })
536
-
537
- return fields;
538
- }
539
-
540
- getRaw( name: string ) : string {
541
- return this.m_data[name];
542
- }
543
-
544
- setRaw( name: string, data: string ) {
545
- this.m_data[name] = data;
546
- }
547
-
548
- clone( data ) {
549
- return new AutoRecord( {...data} );
550
- }
551
- }
552
-
553
- /**
554
- *
555
- */
556
-
557
- interface DataEventMap extends BaseComponentEventMap {
558
- change?: EvChange;
559
- }
560
-
561
- type DataSolver = ( data: any ) => Record[];
562
-
563
- export interface DataProxyProps extends BaseComponentProps<DataEventMap> {
564
- url: string;
565
- params?: string[];
566
- solver?: DataSolver;
567
- }
568
-
569
- export class DataProxy extends BaseComponent<DataProxyProps,DataEventMap> {
570
-
571
- constructor( props: DataProxyProps ) {
572
- super( props );
573
- }
574
-
575
- async load( url?: string ) {
576
- if( url ) {
577
- this.m_props.url = url;
578
- }
579
- else {
580
- url = this.m_props.url;
581
- }
582
-
583
- if( this.m_props.params ) {
584
- url += '?' + this.m_props.params.join( '&' );
585
- }
586
-
587
- const r = await fetch( url );
588
- if( r.ok ) {
589
- const raw = await r.json( );
590
-
591
- let json = raw;
592
- if( this.m_props.solver ) {
593
- json = this.m_props.solver( json );
594
- }
595
-
596
- this.emit( 'change', EvChange(json,raw) );
597
- }
598
- }
599
- }
600
-
601
-
602
- /**
603
- *
604
- */
605
-
606
- interface DataStoreProps<T extends Record> extends BaseComponentProps<DataStoreEventMap> {
607
- model: T;
608
- data?: T[];
609
- url?: string;
610
- autoload?: false;
611
- }
612
-
613
-
614
- interface DataStoreEventMap extends EventMap {
615
- data_change: EvDataChange;
616
- }
617
-
618
-
619
-
620
- /**
621
- *
622
- */
623
-
624
- export class DataStore<T extends Record = Record> extends EventSource<DataStoreEventMap> {
625
-
626
- protected m_model: T;
627
- protected m_fields: FieldInfo[];
628
- protected m_records: T[];
629
-
630
- protected m_proxy: DataProxy;
631
- protected m_rec_index: DataIndex;
632
-
633
- constructor(props: DataStoreProps<T> ) {
634
- super( );
635
-
636
- this.m_fields = undefined;
637
- this.m_records = [];
638
- this.m_rec_index = null;
639
- this.m_model = props.model;
640
- this.m_fields = props.model.getFields();
641
-
642
- if (props.data) {
643
- this.setRawData( props.data );
644
- }
645
- else if( props.url ) {
646
- this.m_proxy = new DataProxy( {
647
- url: props.url,
648
- solver: props.solver,
649
- events: { change: (ev) => { this.setData( ev.value ); } }
650
- });
651
-
652
- if( props.autoload!=false ) {
653
- this.m_proxy.load( );
654
- }
655
- }
656
- }
657
-
658
- /**
659
- *
660
- * @param records
661
- */
662
-
663
- async load( url?: string ) {
664
- return this.m_proxy.load( url );
665
- }
666
-
667
- async reload( ) {
668
- return this.m_proxy.load( );
669
- }
670
-
671
- /**
672
- * convert raw objects to real records from model
673
- * @param records
674
- */
675
-
676
- public setData( records: any[] ) {
677
-
678
- let realRecords: T[] = [];
679
-
680
- records.forEach( (rec) => {
681
- realRecords.push( this.m_model.clone(rec) );
682
- });
683
-
684
- this.setRawData( realRecords );
685
- }
686
-
687
- /**
688
- * just set the records
689
- * @param records - must be of the same type as model
690
- */
691
-
692
- public setRawData(records: T[]) {
693
-
694
- this.m_records = records;
695
- this._rebuildIndex( );
696
- this.emit( 'data_change', EvDataChange( 'change' ) );
697
- }
698
-
699
-
700
- private _rebuildIndex( ) {
701
- this.m_rec_index = null; // null to signal that we have to run on records instead of index
702
- this.m_rec_index = this.createIndex( null ); // prepare index (remove deleted)
703
- this.m_rec_index = this.sortIndex( this.m_rec_index, null ); // sort by id
704
- }
705
-
706
- /**
707
- *
708
- */
709
-
710
- public update( rec: T ) {
711
- let id = rec.getID();
712
- let index = this.indexOfId(id);
713
- if (index < 0) {
714
- return false;
715
- }
716
-
717
- this.m_records[this.m_rec_index[index]] = rec;
718
- this.emit( 'data_change', EvDataChange( 'update', id ) );
719
- return true;
720
- }
721
-
722
- /**
723
- *
724
- * @param data
725
- */
726
-
727
- public append( rec: T | any ) {
728
-
729
- if( !(rec instanceof Record) ) {
730
- let nrec = this.m_model.clone( );
731
- rec = nrec.unSerialize( rec );
732
- }
733
-
734
- console.assert( rec.getID() );
735
-
736
- this.m_records.push( rec );
737
- this._rebuildIndex( );
738
- this.emit( 'data_change', EvDataChange( 'create', rec.getID() ) );
739
- }
740
-
741
- /**
742
- *
743
- */
744
-
745
- getMaxId( ) {
746
- let maxID = undefined;
747
- this.m_records.forEach( (r) => {
748
- let rid = r.getID( );
749
- if( maxID===undefined || maxID<rid ) {
750
- maxID = rid;
751
- }
752
- });
753
-
754
- return maxID;
755
- }
756
-
757
- /**
758
- *
759
- * @param id
760
- */
761
-
762
- public delete(id: any): boolean {
763
-
764
- let idx = this.indexOfId( id );
765
- if( idx<0 ) {
766
- return false;
767
- }
768
-
769
- idx = this.m_rec_index[idx];
770
-
771
- // mark as deleted
772
- this.m_records.splice( idx, 1 );
773
- this._rebuildIndex( );
774
- this.emit( 'data_change', EvDataChange( 'delete', id ) );
775
- return true;
776
- }
777
-
778
- /**
779
- * return the number of records
780
- */
781
-
782
- get count( ) : number {
783
- return this.m_rec_index ? this.m_rec_index.length : this.m_records.length;
784
- }
785
-
786
- /**
787
- * return the fields
788
- */
789
-
790
- get fields( ) : FieldInfo [] {
791
- return this.m_fields;
792
- }
793
-
794
- /**
795
- * find the index of the element with the given id
796
- */
797
-
798
- public indexOfId(id: any ): number {
799
-
800
- //if( this.count<10 ) {
801
- // this.forEach( (rec) => rec.getID() == id );
802
- //}
803
-
804
- for( let lim = this.count, base = 0; lim != 0; lim >>= 1 ) {
805
-
806
- let p = base + (lim >> 1); // int conversion
807
- let idx = this.m_rec_index[p];
808
- let rid = this.m_records[idx].getID( );
809
-
810
- if( rid==id ) {
811
- return p;
812
- }
813
-
814
- if( rid<id ) {
815
- base = p+1;
816
- lim--;
817
- }
818
- }
819
-
820
- return -1;
821
- }
822
-
823
- /**
824
- * return the record by it's id
825
- * @returns record or null
826
- */
827
-
828
- public getById(id: any): T {
829
- let idx = this.indexOfId( id );
830
- if( idx<0 ) {
831
- return null;
832
- }
833
-
834
- idx = this.m_rec_index[idx];
835
- return this.m_records[idx];
836
- }
837
-
838
- /**
839
- * return a record by it's index
840
- * @returns record or null
841
- */
842
-
843
- public getByIndex( index: number ): T {
844
- let idx = this.m_rec_index[index];
845
- return this._getRecord( idx );
846
- }
847
-
848
- private _getRecord( index: number ) : T {
849
- return this.m_records[index] ?? null;
850
- }
851
-
852
- public moveTo( other: DataStore<T> ) {
853
- other.setRawData( this.m_records );
854
- }
855
-
856
- /**
857
- * create a new view on the DataStore
858
- * @param opts
859
- */
860
-
861
- createView( opts?: DataViewProps<T> ) : DataView<T> {
862
- let eopts = { ...opts, store: this };
863
- return new DataView( eopts );
864
- }
865
-
866
- /**
867
- *
868
- */
869
-
870
- createIndex( filter: FilterInfo ) : DataIndex {
871
-
872
- if( filter && filter.op==='empty-result' ) {
873
- return new Uint32Array(0);
874
- }
875
-
876
- let index = new Uint32Array( this.m_records.length );
877
- let iidx = 0;
878
-
879
- if( !filter ) {
880
- // reset filter
881
- this.forEach( (rec, idx) => {
882
- index[iidx++] = idx;
883
- } );
884
- }
885
- else {
886
- if( typeof(filter.op)==='function' ) {
887
-
888
- let fn = <FilterFunc>filter.op;
889
-
890
- // scan all records and append only interesting ones
891
- this.forEach( (rec, idx) => {
892
-
893
- // skip deleted
894
- if( !rec ) {
895
- return;
896
- }
897
-
898
- if( fn(rec) ) {
899
- index[iidx++] = idx;
900
- }
901
- } );
902
- }
903
- else {
904
- let filterFld = this.m_model.getFieldIndex( filter.field ); // field index to filter on
905
- if( filterFld<0 ) {
906
- // unknown filter field, nothing inside
907
- console.assert( false, 'unknown field name in filter' )
908
- return new Uint32Array(0);
909
- }
910
-
911
- let filterValue = filter.value;
912
- if( isString(filterValue) && !filter.caseSensitive ) {
913
- filterValue = filterValue.toUpperCase( );
914
- }
915
-
916
- function _lt( recval: string ) : boolean {
917
- return recval < filterValue;
918
- }
919
-
920
- function _le( recval: string ) : boolean {
921
- return recval <= filterValue;
922
- }
923
-
924
- function _eq( recval: string ) : boolean {
925
- return recval == filterValue;
926
- }
927
-
928
- function _neq( recval: string ) : boolean {
929
- return recval != filterValue;
930
- }
931
-
932
- function _ge( recval: string ) : boolean {
933
- return recval >= filterValue;
934
- }
935
-
936
- function _gt( recval: string ) : boolean {
937
- return recval > filterValue;
938
- }
939
-
940
- function _re( recval: string ) : boolean {
941
- filterRe.lastIndex = -1;
942
- return filterRe.test( recval );
943
- }
944
-
945
- let filterFn; // filter fn
946
- let filterRe; // if fielter is regexp
947
- if( filterValue instanceof RegExp ) {
948
- filterRe = filterValue;
949
- filterFn = _re;
950
- }
951
- else {
952
- switch( filter.op ) {
953
- case '<': { filterFn = _lt; break; }
954
- case '<=': { filterFn = _le; break; }
955
- case '=': { filterFn = _eq; break; }
956
- case '>=': { filterFn = _ge; break; }
957
- case '>': { filterFn = _gt; break; }
958
- case '<>': { filterFn = _neq; break; }
959
- }
960
- }
961
-
962
- // scan all records and append only interesting ones
963
- this.forEach( (rec, idx) => {
964
-
965
- // skip deleted
966
- if( !rec ) {
967
- return;
968
- }
969
-
970
- let field = rec.getRaw( filterFld );
971
- if( field===null || field===undefined ) {
972
- field = '';
973
- }
974
- else {
975
- field = ''+field;
976
- if( !filter.caseSensitive ) {
977
- field = field.toUpperCase( );
978
- }
979
- }
980
-
981
- let keep = filterFn( field );
982
- if( keep ) {
983
- index[iidx++] = idx;
984
- };
985
- });
986
- }
987
- }
988
-
989
- return index.slice( 0, iidx );
990
- }
991
-
992
- sortIndex( index: DataIndex, sort: SortProp[] ) {
993
-
994
- interface sort_info {
995
- fidx: number,
996
- asc: boolean
997
- }
998
-
999
- let bads = 0; // unknown fields
1000
- let fidxs: sort_info[] = []; // fields indexes
1001
-
1002
- // if no fields are given, reset sort by id
1003
- if ( sort===null ) {
1004
- fidxs.push( { fidx: 0, asc: true } );
1005
- }
1006
- else {
1007
- fidxs = sort.map( (si) => {
1008
-
1009
- let fi = this.m_model.getFieldIndex( si.field );
1010
- if (fi == -1) {
1011
- console.assert( false, 'unknown field name in sort' )
1012
- bads++;
1013
- }
1014
-
1015
- return { fidx: fi, asc: si.ascending };
1016
- });
1017
- }
1018
-
1019
- // unknown field or nothing to sort on ??
1020
- if( bads || fidxs.length==0 ) {
1021
- return index;
1022
- }
1023
-
1024
- // sort only by one field : optimize it
1025
- if( fidxs.length==1 ) {
1026
-
1027
- let field = fidxs[0].fidx;
1028
- index.sort( ( ia, ib ) => {
1029
-
1030
- let va = this.getByIndex(ia).getRaw( field ) ?? '';
1031
- let vb = this.getByIndex(ib).getRaw( field ) ?? '';
1032
- if (va > vb) { return 1; }
1033
- if (va < vb) { return -1; }
1034
- return 0;
1035
- } );
1036
-
1037
- // just reverse if
1038
- if( !fidxs[0].asc ) {
1039
- index.reverse( );
1040
- }
1041
- }
1042
- else {
1043
- index.sort( ( ia, ib ) => {
1044
-
1045
- for( let fi=0; fi<fidxs.length; fi++ ) {
1046
-
1047
- let fidx = fidxs[fi].fidx;
1048
- let mul = fidxs[fi].asc ? 1 : -1;
1049
-
1050
- let va = this.getByIndex(ia).getRaw( fidx ) ?? '';
1051
- let vb = this.getByIndex(ib).getRaw( fidx ) ?? '';
1052
- if (va > vb) { return mul; }
1053
- if (va < vb) { return -mul; }
1054
- }
1055
-
1056
- return 0;
1057
- } );
1058
- }
1059
-
1060
- return index
1061
- }
1062
-
1063
- /**
1064
- *
1065
- */
1066
-
1067
- forEach( cb: ( rec:T, index: number ) => any ) {
1068
-
1069
- if( this.m_rec_index ) {
1070
- this.m_rec_index.some( (ri,index) => {
1071
- if( cb( this.m_records[ri], index ) ) {
1072
- return index;
1073
- }
1074
- });
1075
- }
1076
- else {
1077
- this.m_records.some( ( rec, index ) => {
1078
- if( rec ) {
1079
- if( cb( rec, index ) ) {
1080
- return index;
1081
- }
1082
- }
1083
- } );
1084
- }
1085
- }
1086
-
1087
- export( ) {
1088
- return this.m_records;
1089
- }
1090
-
1091
- changed( ) {
1092
- this.emit( 'data_change', EvDataChange('change') );
1093
- }
1094
- }
1095
-
1096
-
1097
- // :: VIEWS ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
1098
-
1099
- export interface EvViewChange extends BasicEvent {
1100
- action: string;
1101
- }
1102
-
1103
- export function EvViewChange( action: 'filter' | 'sort' | 'change' ) {
1104
- return BasicEvent<EvViewChange>( { action } );
1105
- }
1106
-
1107
- interface DataViewEventMap extends BaseComponentEventMap {
1108
- view_change: EvViewChange;
1109
- }
1110
-
1111
- interface DataViewProps<T extends Record> extends BaseComponentProps<DataViewEventMap> {
1112
- store?: DataStore<T>;
1113
- filter?: FilterInfo;
1114
- order?: string | SortProp[] | SortProp;
1115
- }
1116
-
1117
- export type FilterFunc = ( rec: Record ) => boolean;
1118
-
1119
- export interface FilterInfo {
1120
- op: '<' | '<=' | '=' | '>=' | '>' | '<>' | 'empty-result' | FilterFunc, // emptydb mean return an empty result always
1121
- field?: string;
1122
- value?: string | RegExp; // if regexp then operator is =
1123
- caseSensitive?: boolean;
1124
- }
1125
-
1126
-
1127
- export interface SortProp {
1128
- field: string; //
1129
- ascending: boolean; //
1130
- }
1131
-
1132
-
1133
-
1134
- /**
1135
- * Dataview allow different views of the DataStore.
1136
- * You can sort the columns & filter data
1137
- * You can have multiple views for a single DataStore
1138
- */
1139
-
1140
- export class DataView<T extends Record = Record> extends BaseComponent<DataViewProps<T>,DataViewEventMap>
1141
- {
1142
- protected m_index: DataIndex;
1143
- protected m_store: DataStore<T>;
1144
-
1145
- protected m_sort: SortProp[];
1146
- protected m_filter: FilterInfo;
1147
-
1148
- constructor( props: DataViewProps<T> ) {
1149
- super( props );
1150
-
1151
- this.m_store = props.store;
1152
- this.m_index = null;
1153
- this.m_filter = null;
1154
- this.m_sort = null;
1155
-
1156
- this.filter( props.filter );
1157
-
1158
- if( props.order ) {
1159
- if( isString(props.order) ) {
1160
- this.sort( [ { field: props.order, ascending: true } ] );
1161
- }
1162
- else if( isArray(props.order) ) {
1163
- this.sort( props.order );
1164
- }
1165
- else {
1166
- this.sort( [props.order] );
1167
- }
1168
- }
1169
- else {
1170
- this.sort( null );
1171
- }
1172
-
1173
- this.m_store.on( 'data_change', ( e ) => this._storeChange(e) );
1174
- }
1175
-
1176
- private _storeChange( ev: EvDataChange ) {
1177
-
1178
- this._filter( this.m_filter, ev.type!='change' );
1179
- this._sort( this.m_sort, ev.type!='change' );
1180
-
1181
- this.emit( 'view_change', EvViewChange( 'change' ) );
1182
- }
1183
-
1184
- /**
1185
- *
1186
- * @param filter
1187
- */
1188
-
1189
- public filter( filter?: FilterInfo ) : number {
1190
-
1191
- this.m_index = null; // null to signal that we have to run on records instead of index
1192
- return this._filter( filter, true );
1193
- }
1194
-
1195
- private _filter( filter: FilterInfo, notify: boolean) : number {
1196
-
1197
- this.m_index = this.m_store.createIndex( filter );
1198
- this.m_filter = filter;
1199
-
1200
- // need to sort again:
1201
- if( this.m_sort ) {
1202
- this.sort( this.m_sort );
1203
- }
1204
-
1205
- if( notify ) {
1206
- this.emit( 'view_change', EvViewChange( 'filter' ) );
1207
- }
1208
-
1209
- return this.m_index.length;
1210
- }
1211
-
1212
- /**
1213
- *
1214
- * @param columns
1215
- * @param ascending
1216
- */
1217
-
1218
- public sort( props: SortProp[] ) {
1219
- this._sort( props, true );
1220
- }
1221
-
1222
- private _sort( props: SortProp[], notify: boolean ) {
1223
- this.m_index = this.m_store.sortIndex( this.m_index, props );
1224
- this.m_sort = props;
1225
-
1226
- if( notify ) {
1227
- this.emit( 'view_change', EvViewChange( 'sort' ) );
1228
- }
1229
- }
1230
-
1231
- /**
1232
- *
1233
- */
1234
-
1235
- get store ( ) {
1236
- return this.m_store;
1237
- }
1238
-
1239
- /**
1240
- *
1241
- */
1242
-
1243
- public get count() {
1244
- return this.m_index.length;
1245
- }
1246
-
1247
- /**
1248
- *
1249
- * @param id
1250
- */
1251
-
1252
- public indexOfId(id): number {
1253
- let ridx = this.m_store.indexOfId( id );
1254
- return this.m_index.findIndex( (rid) => rid === ridx );
1255
- }
1256
-
1257
- /**
1258
- *
1259
- * @param index
1260
- */
1261
-
1262
- public getByIndex(index: number): T {
1263
-
1264
- if (index >= 0 && index < this.m_index.length) {
1265
- let rid = this.m_index[index];
1266
- return this.m_store.getByIndex( rid );
1267
- }
1268
-
1269
- return null;
1270
- }
1271
-
1272
- /**
1273
- *
1274
- * @param id
1275
- */
1276
-
1277
- public getById( id: any): T {
1278
- return this.m_store.getById( id );
1279
- }
1280
-
1281
- changed( ) {
1282
- this.emit( 'view_change', EvViewChange('change') );
1283
- }
1284
-
1285
- /**
1286
- *
1287
- */
1288
-
1289
- forEach( cb: ( rec:T, index: number ) => any ) {
1290
- this.m_index.some( ( index ) => {
1291
- let rec = this.m_store.getByIndex( index );
1292
- if( rec ) {
1293
- if( cb( rec, index ) ) {
1294
- return index;
1295
- }
1296
- }
1297
- } );
1298
- }
1299
- }
1300
-
1301
-
1302
-