x4js 2.0.12 → 2.0.13

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 (108) hide show
  1. package/README.md +15 -15
  2. package/lib/README.txt +15 -15
  3. package/lib/cjs/x4.css +1 -1
  4. package/lib/cjs/x4.js +1 -1
  5. package/lib/esm/x4.css +1 -1
  6. package/lib/esm/x4.mjs +1 -1
  7. package/lib/src/components/base.scss +25 -26
  8. package/lib/src/components/boxes/boxes.module.scss +37 -37
  9. package/lib/src/components/boxes/boxes.ts +129 -125
  10. package/lib/src/components/breadcrumb/breadcrumb.scss +28 -0
  11. package/lib/src/components/breadcrumb/breadcrumb.ts +84 -0
  12. package/lib/src/components/breadcrumb/chevron-right.svg +1 -0
  13. package/lib/src/components/btngroup/btngroup.module.scss +28 -28
  14. package/lib/src/components/btngroup/btngroup.ts +119 -101
  15. package/lib/src/components/button/button.module.scss +154 -153
  16. package/lib/src/components/button/button.ts +117 -117
  17. package/lib/src/components/calendar/calendar.module.scss +162 -162
  18. package/lib/src/components/calendar/calendar.ts +326 -325
  19. package/lib/src/components/checkbox/check.svg +3 -3
  20. package/lib/src/components/checkbox/checkbox.module.scss +141 -141
  21. package/lib/src/components/checkbox/checkbox.ts +125 -124
  22. package/lib/src/components/colorinput/colorinput.module.scss +64 -64
  23. package/lib/src/components/colorinput/colorinput.ts +90 -87
  24. package/lib/src/components/colorpicker/colorpicker.module.scss +132 -132
  25. package/lib/src/components/colorpicker/colorpicker.ts +481 -476
  26. package/lib/src/components/combobox/combobox.module.scss +123 -120
  27. package/lib/src/components/combobox/combobox.ts +192 -190
  28. package/lib/src/components/combobox/updown.svg +3 -3
  29. package/lib/src/components/components.ts +34 -0
  30. package/lib/src/components/dialog/dialog.module.scss +71 -71
  31. package/lib/src/components/dialog/dialog.ts +94 -92
  32. package/lib/src/components/form/form.module.scss +34 -34
  33. package/lib/src/components/form/form.ts +41 -36
  34. package/lib/src/components/grid/datastore.ts +1298 -0
  35. package/lib/src/components/grid/gridview.ts +1108 -0
  36. package/lib/src/components/grid/memdb.ts +325 -0
  37. package/lib/src/components/header/header.module.scss +39 -39
  38. package/lib/src/components/header/header.ts +129 -123
  39. package/lib/src/components/icon/icon.module.scss +29 -29
  40. package/lib/src/components/icon/icon.ts +136 -134
  41. package/lib/src/components/image/image.module.scss +20 -20
  42. package/lib/src/components/image/image.ts +68 -66
  43. package/lib/src/components/input/input.module.scss +69 -69
  44. package/lib/src/components/input/input.ts +275 -274
  45. package/lib/src/components/label/label.module.scss +58 -52
  46. package/lib/src/components/label/label.ts +64 -55
  47. package/lib/src/components/link/link.ts +78 -0
  48. package/lib/src/components/listbox/listbox.module.scss +103 -103
  49. package/lib/src/components/listbox/listbox.ts +431 -427
  50. package/lib/src/components/menu/menu.module.scss +107 -107
  51. package/lib/src/components/menu/menu.ts +171 -168
  52. package/lib/src/components/messages/messages.module.scss +48 -47
  53. package/lib/src/components/messages/messages.ts +68 -63
  54. package/lib/src/components/normalize.scss +386 -386
  55. package/lib/src/components/notification/notification.module.scss +81 -81
  56. package/lib/src/components/notification/notification.ts +109 -108
  57. package/lib/src/components/panel/panel.module.scss +47 -47
  58. package/lib/src/components/panel/panel.ts +57 -56
  59. package/lib/src/components/popup/popup.module.scss +43 -43
  60. package/lib/src/components/popup/popup.ts +396 -395
  61. package/lib/src/components/progress/progress.module.scss +56 -56
  62. package/lib/src/components/progress/progress.ts +43 -42
  63. package/lib/src/components/rating/rating.module.scss +22 -22
  64. package/lib/src/components/rating/rating.ts +131 -125
  65. package/lib/src/components/shared.scss +90 -76
  66. package/lib/src/components/sizers/sizer.module.scss +89 -89
  67. package/lib/src/components/sizers/sizer.ts +123 -119
  68. package/lib/src/components/slider/slider.module.scss +70 -70
  69. package/lib/src/components/slider/slider.ts +147 -142
  70. package/lib/src/components/switch/switch.module.scss +126 -126
  71. package/lib/src/components/switch/switch.ts +61 -55
  72. package/lib/src/components/tabs/tabs.module.scss +46 -46
  73. package/lib/src/components/tabs/tabs.ts +168 -157
  74. package/lib/src/components/textarea/textarea.module.scss +59 -59
  75. package/lib/src/components/textarea/textarea.ts +60 -54
  76. package/lib/src/components/textedit/textedit.module.scss +113 -113
  77. package/lib/src/components/textedit/textedit.ts +83 -82
  78. package/lib/src/components/themes.scss +81 -77
  79. package/lib/src/components/tooltips/tooltips.scss +50 -50
  80. package/lib/src/components/tooltips/tooltips.ts +103 -102
  81. package/lib/src/components/treeview/treeview.module.scss +115 -115
  82. package/lib/src/components/treeview/treeview.ts +410 -403
  83. package/lib/src/components/viewport/viewport.module.scss +24 -24
  84. package/lib/src/components/viewport/viewport.ts +41 -38
  85. package/lib/src/core/component.ts +1002 -979
  86. package/lib/src/core/core_application.ts +44 -0
  87. package/lib/src/core/core_colors.ts +249 -249
  88. package/lib/src/core/core_dom.ts +471 -471
  89. package/lib/src/core/core_dragdrop.ts +200 -200
  90. package/lib/src/core/core_element.ts +97 -97
  91. package/lib/src/core/core_events.ts +149 -149
  92. package/lib/src/core/core_i18n.ts +377 -377
  93. package/lib/src/core/core_router.ts +221 -221
  94. package/lib/src/core/core_styles.ts +214 -214
  95. package/lib/src/core/core_svg.ts +550 -550
  96. package/lib/src/core/core_tools.ts +688 -673
  97. package/lib/src/demo/assets/radio.svg +3 -3
  98. package/lib/src/demo/index.html +11 -11
  99. package/lib/src/demo/main.scss +21 -21
  100. package/lib/src/demo/main.tsx +323 -323
  101. package/lib/src/types/scss.d.ts +4 -4
  102. package/lib/src/types/x4react.d.ts +8 -8
  103. package/lib/src/x4.scss +18 -18
  104. package/lib/src/x4.ts +31 -62
  105. package/lib/styles/x4.css +1 -1
  106. package/lib/types/x4js.d.ts +100 -49
  107. package/package.json +2 -3
  108. package/src/x4.ts +31 -62
@@ -0,0 +1,325 @@
1
+ import { EventSource } from '@core/core_events.js';
2
+
3
+ type RecordID = any; // Define RecordID as any
4
+ type FieldType = string | number | boolean | Date;
5
+
6
+ // Define a type for field definitions with additional parameters
7
+ type FieldDef =
8
+ | { type: 'string'; minLength?: number; maxLength?: number; format?: 'email'; required?: boolean }
9
+ | { type: 'number'; min?: number; max?: number; required?: boolean }
10
+ | { type: 'boolean'; required?: boolean };
11
+
12
+ // Define a type for records
13
+ type Record = {
14
+ id: RecordID;
15
+ [key: string]: FieldType;
16
+ };
17
+
18
+ // Model class
19
+ class Model {
20
+ private structure: { [key: string]: FieldDef };
21
+
22
+ constructor(structure: { [key: string]: FieldDef }) {
23
+ this.structure = { ...structure };
24
+ }
25
+
26
+ // Validate data based on the model
27
+ validate(data: { [key: string]: any }): void {
28
+ for (const key in this.structure) {
29
+ const field = this.structure[key];
30
+ const value = data[key];
31
+
32
+ if (field.required && value === undefined) {
33
+ throw new Error(`Field ${key} is required`);
34
+ }
35
+
36
+ if ( value !== undefined && typeof value !== field.type) {
37
+ throw new Error(`Field ${key} has an incorrect type`);
38
+ }
39
+
40
+ if (field.type === 'string' && value !== undefined) {
41
+ if (field.minLength !== undefined && value.length < field.minLength) {
42
+ throw new Error(`Field ${key} is shorter than the minimum length of ${field.minLength}`);
43
+ }
44
+ if (field.maxLength !== undefined && value.length > field.maxLength) {
45
+ throw new Error(`Field ${key} is longer than the maximum length of ${field.maxLength}`);
46
+ }
47
+ if (field.format === 'email' && !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value)) {
48
+ throw new Error(`Field ${key} is not a valid email`);
49
+ }
50
+ }
51
+
52
+ if (field.type === 'number' && value !== undefined) {
53
+ if (field.min !== undefined && value < field.min) {
54
+ throw new Error(`Field ${key} is less than the minimum value of ${field.min}`);
55
+ }
56
+ if (field.max !== undefined && value > field.max) {
57
+ throw new Error(`Field ${key} is greater than the maximum value of ${field.max}`);
58
+ }
59
+ }
60
+ }
61
+ }
62
+
63
+ // Get the structure of the model
64
+ getStructure(): { [key: string]: FieldDef } {
65
+ return this.structure;
66
+ }
67
+
68
+ // Create a record based on the model
69
+ createRecord(data: { [key: string]: any }, copy: boolean): Record {
70
+ this.validate(data);
71
+
72
+ const recordData = copy ? { ...data } : data;
73
+
74
+ return recordData as Record;
75
+ }
76
+
77
+ // Get the value of a specific field in a record
78
+ getFieldValue(record: Record, fieldName: string): FieldType | undefined {
79
+ if (fieldName in this.structure) {
80
+ return record[fieldName];
81
+ }
82
+ throw new Error(`Field ${fieldName} does not exist in the model`);
83
+ }
84
+ }
85
+
86
+ // MemoryDatabase class
87
+ class MemoryDatabase extends EventSource {
88
+ private records: Record[] = [];
89
+ private model: Model;
90
+
91
+ constructor(model: Model) {
92
+ super();
93
+ this.model = model;
94
+ }
95
+
96
+ // Add a new record to the records array
97
+ addRecord(record: Record): void {
98
+ this.records.push(record);
99
+ this.emit('recordAdded', record);
100
+ }
101
+
102
+ // Get the count of records
103
+ getCount(): number {
104
+ return this.records.length;
105
+ }
106
+
107
+ // Get a record by its index
108
+ getRecordByIndex(index: number): Record | null {
109
+ return this.records[index] || null;
110
+ }
111
+
112
+ // Get a record by its ID
113
+ getRecordById(id: RecordID): Record | null {
114
+ return this.records.find(record => record.id === id) || null;
115
+ }
116
+
117
+ // Get all records
118
+ getAllRecords(): Record[] {
119
+ return this.records;
120
+ }
121
+
122
+ // Remove a record by its ID
123
+ removeRecordById(id: RecordID): void {
124
+ const index = this.records.findIndex(record => record.id === id);
125
+ if (index !== -1) {
126
+ const removedRecord = this.records.splice(index, 1)[0];
127
+ this.emit('recordRemoved', removedRecord);
128
+ }
129
+ }
130
+
131
+ // Remove a record by its index
132
+ removeRecordByIndex(index: number): void {
133
+ if (index >= 0 && index < this.records.length) {
134
+ const removedRecord = this.records.splice(index, 1)[0];
135
+ this.emit('recordRemoved', removedRecord);
136
+ }
137
+ }
138
+ }
139
+
140
+ // DataView class
141
+ class DataView {
142
+ private database: MemoryDatabase;
143
+ private view: Int32Array = new Int32Array(0);
144
+ private currentFilter: { field: string, valueOrPattern: any } | null = null;
145
+ private currentSort: { field: string, ascending: boolean } | null = null;
146
+
147
+ constructor(database: MemoryDatabase) {
148
+ this.database = database;
149
+ this.database.on('recordAdded', () => this.handleDatabaseChange());
150
+ }
151
+
152
+ // Initialize the view with all record indexes
153
+ initializeView(): void {
154
+ const recordCount = this.database.getCount();
155
+ this.view = Int32Array.from({ length: recordCount }, (_, i) => i);
156
+ }
157
+
158
+ // Sort the view by a specified field
159
+ sortView(field: string, ascending: boolean = true): void {
160
+ this.currentSort = { field, ascending };
161
+ this.applySort();
162
+ }
163
+
164
+ private applySort(): void {
165
+ if (this.currentSort) {
166
+ const { field, ascending } = this.currentSort;
167
+ this.view = Int32Array.from(this.view).sort((a, b) => {
168
+ const recordA = this.database.getRecordByIndex(a);
169
+ const recordB = this.database.getRecordByIndex(b);
170
+
171
+ if (recordA && recordB) {
172
+ const valueA = recordA[field];
173
+ const valueB = recordB[field];
174
+
175
+ if (valueA instanceof Date && valueB instanceof Date) {
176
+ return ascending ? valueA.getTime() - valueB.getTime() : valueB.getTime() - valueA.getTime();
177
+ }
178
+
179
+ if (valueA < valueB) {
180
+ return ascending ? -1 : 1;
181
+ }
182
+ if (valueA > valueB) {
183
+ return ascending ? 1 : -1;
184
+ }
185
+ }
186
+ return 0;
187
+ });
188
+ }
189
+ }
190
+
191
+ // Filter the view by a specified field and value or regex pattern
192
+ filterView(field: string, valueOrPattern: any): void {
193
+ this.currentFilter = { field, valueOrPattern };
194
+ this.applyFilter();
195
+ }
196
+
197
+ private applyFilter(): void {
198
+ if (this.currentFilter) {
199
+ const { field, valueOrPattern } = this.currentFilter;
200
+ const records = this.database.getAllRecords();
201
+ if (valueOrPattern instanceof RegExp) {
202
+ this.view = Int32Array.from(
203
+ records
204
+ .map((record, index) => ({ record, index }))
205
+ .filter(({ record }) => valueOrPattern.test(record[field] as string))
206
+ .map(({ index }) => index)
207
+ );
208
+ } else {
209
+ this.view = Int32Array.from(
210
+ records
211
+ .map((record, index) => ({ record, index }))
212
+ .filter(({ record }) => record[field] === valueOrPattern)
213
+ .map(({ index }) => index)
214
+ );
215
+ }
216
+ }
217
+ }
218
+
219
+ // Handle database change event
220
+ private handleDatabaseChange(): void {
221
+ this.initializeView();
222
+ if (this.currentFilter) {
223
+ this.applyFilter();
224
+ }
225
+ if (this.currentSort) {
226
+ this.applySort();
227
+ }
228
+ }
229
+
230
+ // Advanced filtering similar to LokiJS
231
+ advancedFilterView(filter: (record: Record) => boolean): void {
232
+ const records = this.database.getAllRecords();
233
+ this.view = Int32Array.from(
234
+ records
235
+ .map((record, index) => ({ record, index }))
236
+ .filter(({ record }) => filter(record))
237
+ .map(({ index }) => index)
238
+ );
239
+ }
240
+
241
+ // Clear all filters and reset the view to include all records
242
+ clearFilter(): void {
243
+ this.currentFilter = null;
244
+ this.initializeView();
245
+ if (this.currentSort) {
246
+ this.applySort();
247
+ }
248
+ }
249
+
250
+ // Get the records in the view
251
+ getViewRecords(): Record[] {
252
+ return Array.from(this.view).map(index => this.database.getRecordByIndex(index)).filter(record => record !== null) as Record[];
253
+ }
254
+
255
+ // Get the number of records in the view
256
+ getRecordCount(): number {
257
+ return this.view.length;
258
+ }
259
+
260
+ // Get a record by its index in the view
261
+ getRecordByIndex(index: number): Record | null {
262
+ if (index >= 0 && index < this.view.length) {
263
+ return this.database.getRecordByIndex(this.view[index]);
264
+ }
265
+ return null;
266
+ }
267
+
268
+ // Get a record by its ID in the view
269
+ getRecordById(id: RecordID): Record | null {
270
+ const recordIndex = this.database.getAllRecords().findIndex(record => record.id === id);
271
+ if (recordIndex !== -1 && this.view.includes(recordIndex)) {
272
+ return this.database.getRecordByIndex(recordIndex);
273
+ }
274
+ return null;
275
+ }
276
+ }
277
+
278
+ // RemoteDataLoader class
279
+ class RemoteDataLoader {
280
+ private database: MemoryDatabase;
281
+ private url: string;
282
+
283
+ constructor(database: MemoryDatabase, url: string) {
284
+ this.database = database;
285
+ this.url = url;
286
+ }
287
+
288
+ // Fetch records from the given URL and add them to the database
289
+ async fetchRecords(): Promise<void> {
290
+ try {
291
+ const response = await fetch(this.url);
292
+ const data = await response.json();
293
+
294
+ if (!Array.isArray(data)) {
295
+ throw new Error('Response is not an array');
296
+ }
297
+
298
+ data.forEach(item => {
299
+ const record = this.database.model.createRecord(item, true);
300
+ this.database.addRecord(record);
301
+ });
302
+
303
+ this.database.emit('dataLoaded');
304
+ } catch (error) {
305
+ console.error('Error fetching records:', error);
306
+ }
307
+ }
308
+ }
309
+
310
+ // Example usage
311
+ const userModel = new Model({
312
+ name: { type: 'string', minLength: 3, maxLength: 50, required: true },
313
+ age: { type: 'number', min: 0, max: 120, required: true },
314
+ isActive: { type: 'boolean', required: true },
315
+ email: { type: 'string', format: 'email', required: true }
316
+ });
317
+
318
+ const db = new MemoryDatabase(userModel);
319
+ const remoteDataLoader = new RemoteDataLoader(db, 'https://api.example.com/users');
320
+
321
+ remoteDataLoader.fetchRecords().then(() => {
322
+ const dataView = new DataView(db);
323
+ dataView.initializeView();
324
+ console.log(dataView.getViewRecords());
325
+ });
@@ -1,40 +1,40 @@
1
- @use "../shared.scss";
2
-
3
- :root {
4
- --header-background-hover: rgba(100,100,100,0.1);
5
- --header-sizer-hover: var( --border-hover );
6
- }
7
-
8
- .x4header {
9
- @extend .flex;
10
- width: 100%;
11
-
12
- overflow: hidden;
13
- min-height: 2em;
14
- border-bottom: 1px solid var( --border );
15
-
16
-
17
- .cell {
18
- border-bottom: 1px solid transparent;
19
- overflow: hidden;
20
- min-width: 3rem;
21
-
22
- transition: border-color 0.5s ease;
23
- padding: 4px;
24
-
25
- span {
26
- white-space: nowrap;
27
- text-overflow: ellipsis;
28
- overflow: hidden;
29
- }
30
-
31
- &:hover {
32
- background-color: var( --header-background-hover );
33
- }
34
-
35
- .x4csizer:hover {
36
- border-right: 1px solid var( --header-sizer-hover );
37
- }
38
- }
39
-
1
+ @use "../shared.scss";
2
+
3
+ :root {
4
+ --header-background-hover: rgba(100,100,100,0.1);
5
+ --header-sizer-hover: var( --border-hover );
6
+ }
7
+
8
+ .x4header {
9
+ @extend .flex;
10
+ width: 100%;
11
+
12
+ overflow: hidden;
13
+ min-height: 2em;
14
+ border-bottom: 1px solid var( --border );
15
+
16
+
17
+ .cell {
18
+ border-bottom: 1px solid transparent;
19
+ overflow: hidden;
20
+ min-width: 3rem;
21
+
22
+ transition: border-color 0.5s ease;
23
+ padding: 4px;
24
+
25
+ span {
26
+ white-space: nowrap;
27
+ text-overflow: ellipsis;
28
+ overflow: hidden;
29
+ }
30
+
31
+ &:hover {
32
+ background-color: var( --header-background-hover );
33
+ }
34
+
35
+ .x4csizer:hover {
36
+ border-right: 1px solid var( --header-sizer-hover );
37
+ }
38
+ }
39
+
40
40
  }
@@ -1,124 +1,130 @@
1
- import { Component, ComponentProps } from '../../core/component.js';
2
- import { HBox } from '../boxes/boxes.js';
3
- import { Label } from '../label/label.js';
4
- import { CSizer } from '../sizers/sizer.js';
5
-
6
- import "./header.module.scss"
7
-
8
- interface HeaderItem {
9
- name: string;
10
- title: string;
11
- iconId?: string;
12
- width?: number; // <0 for flex
13
- }
14
-
15
- interface HeaderProps extends Omit<ComponentProps,"content"> {
16
- items: HeaderItem[]
17
- }
18
-
19
- export class Header extends HBox<HeaderProps> {
20
-
21
- private _els: Component[];
22
- private _vwp: Component;
23
-
24
- constructor( props: HeaderProps ) {
25
- super( props );
26
-
27
- this._els = props.items?.map( x => {
28
- const cell = new Label( { cls: "cell", text: x.title, icon: x.iconId } );
29
- const sizer = new CSizer( "right" );
30
-
31
- if( x.width>0 ) {
32
- cell.setStyleValue( "width", x.width+'px' );
33
- cell.setInternalData( "width", x.width );
34
- }
35
- else if( x.width<0 ) {
36
- cell.setInternalData( "flex", -x.width );
37
- }
38
- else {
39
- cell.setInternalData( "width", 0 );
40
- }
41
-
42
- sizer.addDOMEvent( "dblclick", ( e: MouseEvent ) => {
43
- cell.setInternalData( "flex", 1 );
44
- this._calc_sizes( );
45
- })
46
-
47
- sizer.on( "resize", ( ev ) => {
48
- //cell.setStyleValue( "flexGrow", "0" );
49
- cell.setInternalData("flex",0);
50
- cell.setInternalData("width",ev.size);
51
- this._calc_sizes( );
52
- });
53
-
54
- cell.appendContent( sizer );
55
- cell.setInternalData( "data", x );
56
-
57
- return cell;
58
- });
59
-
60
- this.addDOMEvent( "resized", ( ) => this._on_resize() );
61
- this.addDOMEvent( "created", ( ) => this._calc_sizes( ) );
62
-
63
- this._vwp = new HBox( { content: this._els } );
64
- this.setContent( this._vwp );
65
- }
66
-
67
- private _calc_sizes( ) {
68
-
69
- let count = 0;
70
- let filled = 0;
71
-
72
- this._els.forEach( c => {
73
- const flex = c.getInternalData( "flex" );
74
- if( flex ) {
75
- count += flex;
76
- }
77
- else {
78
- let width = c.getInternalData( "width" );
79
- if( width==0 ) {
80
- const rc = c.getBoundingRect( );
81
- width = Math.ceil( rc.width )+2;
82
- c.setInternalData( "width", width );
83
- }
84
-
85
- filled += width;
86
- }
87
- } );
88
-
89
- const rc = this.getBoundingRect( );
90
-
91
- let rest = (rc.width-filled);
92
- const unit = Math.ceil( rest/count );
93
-
94
- console.log( "filled", filled );
95
- console.log( "count", count );
96
- console.log( "rest", rest );
97
- console.log( "unit", unit );
98
-
99
- let fullw = 0;
100
- this._els.forEach( c => {
101
- let width = 0;
102
-
103
- const flex = c.getInternalData( "flex" );
104
- if( flex ) {
105
- width = Math.min( unit*flex, rest );
106
- rest -= width;
107
- }
108
- else {
109
- width = c.getInternalData( "width" );
110
- }
111
-
112
- c.setWidth( width );
113
- fullw += width;
114
- } );
115
-
116
- this._vwp.setWidth( fullw );
117
- }
118
-
119
- private _on_resize( ) {
120
- this._calc_sizes( );
121
- }
122
-
123
-
1
+ import { class_ns } from '@core/core_tools.js';
2
+ import { Component, ComponentProps } from '../../core/component.js';
3
+ import { HBox } from '../boxes/boxes.js';
4
+ import { Label } from '../label/label.js';
5
+ import { CSizer } from '../sizers/sizer.js';
6
+
7
+ import "./header.module.scss"
8
+
9
+ interface HeaderItem {
10
+ name: string;
11
+ title: string;
12
+ iconId?: string;
13
+ width?: number; // <0 for flex
14
+ }
15
+
16
+ interface HeaderProps extends Omit<ComponentProps,"content"> {
17
+ items: HeaderItem[]
18
+ }
19
+
20
+ /**
21
+ *
22
+ */
23
+
24
+ @class_ns( "x4" )
25
+ export class Header extends HBox<HeaderProps> {
26
+
27
+ private _els: Component[];
28
+ private _vwp: Component;
29
+
30
+ constructor( props: HeaderProps ) {
31
+ super( props );
32
+
33
+ this._els = props.items?.map( x => {
34
+ const cell = new Label( { cls: "cell", text: x.title, icon: x.iconId } );
35
+ const sizer = new CSizer( "right" );
36
+
37
+ if( x.width>0 ) {
38
+ cell.setStyleValue( "width", x.width+'px' );
39
+ cell.setInternalData( "width", x.width );
40
+ }
41
+ else if( x.width<0 ) {
42
+ cell.setInternalData( "flex", -x.width );
43
+ }
44
+ else {
45
+ cell.setInternalData( "width", 0 );
46
+ }
47
+
48
+ sizer.addDOMEvent( "dblclick", ( e: MouseEvent ) => {
49
+ cell.setInternalData( "flex", 1 );
50
+ this._calc_sizes( );
51
+ })
52
+
53
+ sizer.on( "resize", ( ev ) => {
54
+ //cell.setStyleValue( "flexGrow", "0" );
55
+ cell.setInternalData("flex",0);
56
+ cell.setInternalData("width",ev.size);
57
+ this._calc_sizes( );
58
+ });
59
+
60
+ cell.appendContent( sizer );
61
+ cell.setInternalData( "data", x );
62
+
63
+ return cell;
64
+ });
65
+
66
+ this.addDOMEvent( "resized", ( ) => this._on_resize() );
67
+ this.addDOMEvent( "created", ( ) => this._calc_sizes( ) );
68
+
69
+ this._vwp = new HBox( { content: this._els } );
70
+ this.setContent( this._vwp );
71
+ }
72
+
73
+ private _calc_sizes( ) {
74
+
75
+ let count = 0;
76
+ let filled = 0;
77
+
78
+ this._els.forEach( c => {
79
+ const flex = c.getInternalData( "flex" );
80
+ if( flex ) {
81
+ count += flex;
82
+ }
83
+ else {
84
+ let width = c.getInternalData( "width" );
85
+ if( width==0 ) {
86
+ const rc = c.getBoundingRect( );
87
+ width = Math.ceil( rc.width )+2;
88
+ c.setInternalData( "width", width );
89
+ }
90
+
91
+ filled += width;
92
+ }
93
+ } );
94
+
95
+ const rc = this.getBoundingRect( );
96
+
97
+ let rest = (rc.width-filled);
98
+ const unit = Math.ceil( rest/count );
99
+
100
+ console.log( "filled", filled );
101
+ console.log( "count", count );
102
+ console.log( "rest", rest );
103
+ console.log( "unit", unit );
104
+
105
+ let fullw = 0;
106
+ this._els.forEach( c => {
107
+ let width = 0;
108
+
109
+ const flex = c.getInternalData( "flex" );
110
+ if( flex ) {
111
+ width = Math.min( unit*flex, rest );
112
+ rest -= width;
113
+ }
114
+ else {
115
+ width = c.getInternalData( "width" );
116
+ }
117
+
118
+ c.setWidth( width );
119
+ fullw += width;
120
+ } );
121
+
122
+ this._vwp.setWidth( fullw );
123
+ }
124
+
125
+ private _on_resize( ) {
126
+ this._calc_sizes( );
127
+ }
128
+
129
+
124
130
  }