ezfw-core 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/components/EzBaseComponent.ts +648 -0
- package/components/EzComponent.ts +89 -0
- package/components/EzInput.module.scss +183 -0
- package/components/EzInput.ts +104 -0
- package/components/EzLabel.ts +22 -0
- package/components/EzOutlet.ts +181 -0
- package/components/HtmlWrapper.ts +305 -0
- package/components/avatar/EzAvatar.module.scss +200 -0
- package/components/avatar/EzAvatar.ts +130 -0
- package/components/badge/EzBadge.module.scss +202 -0
- package/components/badge/EzBadge.ts +77 -0
- package/components/button/EzButton.module.scss +402 -0
- package/components/button/EzButton.ts +175 -0
- package/components/button/EzButtonGroup.ts +48 -0
- package/components/card/EzCard.module.scss +71 -0
- package/components/card/EzCard.ts +120 -0
- package/components/chart/EzBarChart.ts +47 -0
- package/components/chart/EzChart.module.scss +14 -0
- package/components/chart/EzChart.ts +279 -0
- package/components/chart/EzDoughnutChart.ts +47 -0
- package/components/chart/EzLineChart.ts +53 -0
- package/components/checkbox/EzCheckbox.module.scss +145 -0
- package/components/checkbox/EzCheckbox.ts +115 -0
- package/components/dataview/EzDataView.module.scss +115 -0
- package/components/dataview/EzDataView.ts +355 -0
- package/components/dataview/modes/EzDataViewCards.ts +322 -0
- package/components/dataview/modes/EzDataViewGrid.ts +76 -0
- package/components/datepicker/EzDatePicker.module.scss +348 -0
- package/components/datepicker/EzDatePicker.ts +519 -0
- package/components/dialog/EzDialog.module.scss +180 -0
- package/components/dropdown/EzDropdown.module.scss +107 -0
- package/components/dropdown/EzDropdown.ts +235 -0
- package/components/feed/EzActivityFeed.module.scss +90 -0
- package/components/feed/EzActivityFeed.ts +78 -0
- package/components/form/EzForm.ts +364 -0
- package/components/form/EzValidators.test.js +421 -0
- package/components/form/EzValidators.ts +202 -0
- package/components/grid/EzGrid.scss +88 -0
- package/components/grid/EzGrid.ts +1085 -0
- package/components/grid/EzGridContainer.ts +104 -0
- package/components/grid/body/EzGridBody.scss +283 -0
- package/components/grid/body/EzGridBody.ts +549 -0
- package/components/grid/body/EzGridCell.ts +211 -0
- package/components/grid/body/EzGridRow.ts +196 -0
- package/components/grid/filter/EzGridFilters.scss +78 -0
- package/components/grid/filter/EzGridFilters.ts +285 -0
- package/components/grid/footer/EzGridFooter.scss +136 -0
- package/components/grid/footer/EzGridFooter.ts +448 -0
- package/components/grid/header/EzGridHeader.scss +199 -0
- package/components/grid/header/EzGridHeader.ts +430 -0
- package/components/grid/query/EzGridQuery.ts +81 -0
- package/components/grid/state/EzGridColumns.ts +155 -0
- package/components/grid/state/EzGridController.ts +470 -0
- package/components/grid/state/EzGridLifecycle.ts +136 -0
- package/components/grid/state/EzGridNormalizers.test.js +273 -0
- package/components/grid/state/EzGridNormalizers.ts +162 -0
- package/components/grid/state/EzGridParts.ts +233 -0
- package/components/grid/state/EzGridPersistence.ts +140 -0
- package/components/grid/state/EzGridRemote.test.js +573 -0
- package/components/grid/state/EzGridRemote.ts +335 -0
- package/components/grid/state/EzGridSelection.ts +231 -0
- package/components/grid/state/EzGridSort.ts +286 -0
- package/components/grid/title/EzGridActionBar.ts +98 -0
- package/components/grid/title/EzGridTitle.ts +114 -0
- package/components/grid/title/EzGridTitleBar.scss +65 -0
- package/components/grid/title/EzGridTitleBar.ts +87 -0
- package/components/grid/types.ts +607 -0
- package/components/panel/EzPanel.module.scss +133 -0
- package/components/panel/EzPanel.ts +147 -0
- package/components/radio/EzRadio.module.scss +190 -0
- package/components/radio/EzRadio.ts +149 -0
- package/components/select/EzSelect.module.scss +153 -0
- package/components/select/EzSelect.ts +238 -0
- package/components/skeleton/EzSkeleton.module.scss +95 -0
- package/components/skeleton/EzSkeleton.ts +70 -0
- package/components/store/EzStore.ts +344 -0
- package/components/switch/EzSwitch.module.scss +164 -0
- package/components/switch/EzSwitch.ts +117 -0
- package/components/tabs/EzTabPanel.module.scss +181 -0
- package/components/tabs/EzTabPanel.ts +402 -0
- package/components/textarea/EzTextarea.module.scss +131 -0
- package/components/textarea/EzTextarea.ts +161 -0
- package/components/timepicker/EzTimePicker.module.scss +282 -0
- package/components/timepicker/EzTimePicker.ts +540 -0
- package/components/toast/EzToast.module.scss +291 -0
- package/components/tooltip/EzTooltip.module.scss +124 -0
- package/components/tooltip/EzTooltip.ts +153 -0
- package/core/EzComponentTypes.ts +693 -0
- package/core/EzError.ts +63 -0
- package/core/EzModel.ts +268 -0
- package/core/EzTypes.ts +328 -0
- package/core/eventBus.ts +284 -0
- package/core/ez.ts +617 -0
- package/core/loader.ts +725 -0
- package/core/renderer.ts +1010 -0
- package/core/router.ts +490 -0
- package/core/services.ts +124 -0
- package/core/state.ts +142 -0
- package/core/utils.ts +81 -0
- package/package.json +51 -0
- package/services/RouteUI.js +17 -0
- package/services/crypto.js +64 -0
- package/services/dialog.js +222 -0
- package/services/fetchApi.js +63 -0
- package/services/firebase.js +30 -0
- package/services/toast.js +214 -0
- package/template/doc/EzDocs.js +15 -0
- package/template/doc/EzDocs.module.scss +627 -0
- package/template/doc/EzDocsController.js +164 -0
- package/template/doc/data/activityfeed/EzActivityFeedDoc.js +42 -0
- package/template/doc/data/avatar/EzAvatarDoc.js +71 -0
- package/template/doc/data/badge/EzBadgeDoc.js +92 -0
- package/template/doc/data/button/EzButtonDoc.js +77 -0
- package/template/doc/data/buttongroup/EzButtonGroupDoc.js +102 -0
- package/template/doc/data/card/EzCardDoc.js +39 -0
- package/template/doc/data/chart/EzChartDoc.js +60 -0
- package/template/doc/data/checkbox/EzCheckboxDoc.js +67 -0
- package/template/doc/data/component/EzComponentDoc.js +34 -0
- package/template/doc/data/cssmodules/CSSModulesDoc.js +70 -0
- package/template/doc/data/datepicker/EzDatePickerDoc.js +126 -0
- package/template/doc/data/dialog/EzDialogDoc.js +217 -0
- package/template/doc/data/dropdown/EzDropdownDoc.js +178 -0
- package/template/doc/data/form/EzFormDoc.js +90 -0
- package/template/doc/data/grid/EzGridDoc.js +99 -0
- package/template/doc/data/input/EzInputDoc.js +92 -0
- package/template/doc/data/label/EzLabelDoc.js +40 -0
- package/template/doc/data/model/EzModelDoc.js +53 -0
- package/template/doc/data/outlet/EzOutletDoc.js +63 -0
- package/template/doc/data/panel/EzPanelDoc.js +214 -0
- package/template/doc/data/radio/EzRadioDoc.js +174 -0
- package/template/doc/data/router/EzRouterDoc.js +75 -0
- package/template/doc/data/select/EzSelectDoc.js +37 -0
- package/template/doc/data/skeleton/EzSkeletonDoc.js +149 -0
- package/template/doc/data/switch/EzSwitchDoc.js +82 -0
- package/template/doc/data/tabpanel/EzTabPanelDoc.js +44 -0
- package/template/doc/data/textarea/EzTextareaDoc.js +131 -0
- package/template/doc/data/timepicker/EzTimePickerDoc.js +107 -0
- package/template/doc/data/tooltip/EzTooltipDoc.js +193 -0
- package/template/doc/data/validators/EzValidatorsDoc.js +37 -0
- package/template/doc/sidebar/EzDocsSidebar.js +32 -0
- package/template/doc/sidebar/category/EzDocsCategory.js +33 -0
- package/template/doc/sidebar/item/EzDocsComponentItem.js +24 -0
- package/template/doc/viewer/EzDocsViewer.js +18 -0
- package/template/doc/viewer/codepanel/EzDocsCodePanel.js +51 -0
- package/template/doc/viewer/content/EzDocsContent.js +315 -0
- package/template/doc/viewer/header/EzDocsViewerHeader.js +46 -0
- package/template/doc/viewer/showcase/EzDocsShowcase.js +59 -0
- package/template/doc/viewer/showcase/EzDocsShowcaseSection.js +25 -0
- package/template/doc/viewer/showcase/EzDocsVariantItem.js +29 -0
- package/template/doc/welcome/EzDocsWelcome.js +48 -0
- package/themes/ez-theme.scss +179 -0
- package/themes/nature-fresh.scss +169 -0
- package/types/global.d.ts +21 -0
- package/utils/cssModules.js +81 -0
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
// ==========================================================
|
|
2
|
+
// EzGridNormalizers - Unit Tests
|
|
3
|
+
// ==========================================================
|
|
4
|
+
// Para correr: npm test
|
|
5
|
+
// Para correr una vez: npm run test:run
|
|
6
|
+
// Para correr solo este archivo: npm test EzGridNormalizers
|
|
7
|
+
// ==========================================================
|
|
8
|
+
|
|
9
|
+
import { describe, it, expect } from 'vitest';
|
|
10
|
+
import { EzGridNormalizers } from './EzGridNormalizers.js';
|
|
11
|
+
|
|
12
|
+
// ==========================================================
|
|
13
|
+
// normalizeHeader
|
|
14
|
+
// ==========================================================
|
|
15
|
+
|
|
16
|
+
describe('EzGridNormalizers.normalizeHeader', () => {
|
|
17
|
+
|
|
18
|
+
it('should return false when header is false', () => {
|
|
19
|
+
const result = EzGridNormalizers.normalizeHeader(false);
|
|
20
|
+
expect(result).toBe(false);
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
it('should return default config when header is true', () => {
|
|
24
|
+
const result = EzGridNormalizers.normalizeHeader(true);
|
|
25
|
+
expect(result).toEqual({ align: 'left' });
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
it('should return default config when header is null/undefined', () => {
|
|
29
|
+
expect(EzGridNormalizers.normalizeHeader(null)).toEqual({ align: 'left' });
|
|
30
|
+
expect(EzGridNormalizers.normalizeHeader(undefined)).toEqual({ align: 'left' });
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it('should preserve custom align from config', () => {
|
|
34
|
+
const result = EzGridNormalizers.normalizeHeader({ align: 'center' });
|
|
35
|
+
expect(result.align).toBe('center');
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it('should preserve all header properties', () => {
|
|
39
|
+
const result = EzGridNormalizers.normalizeHeader({
|
|
40
|
+
align: 'right',
|
|
41
|
+
cls: 'my-header',
|
|
42
|
+
height: 50,
|
|
43
|
+
visible: false
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
expect(result).toEqual({
|
|
47
|
+
align: 'right',
|
|
48
|
+
cls: 'my-header',
|
|
49
|
+
height: 50,
|
|
50
|
+
visible: false
|
|
51
|
+
});
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
// ==========================================================
|
|
57
|
+
// normalizeColumns
|
|
58
|
+
// ==========================================================
|
|
59
|
+
|
|
60
|
+
describe('EzGridNormalizers.normalizeColumns', () => {
|
|
61
|
+
|
|
62
|
+
it('should normalize basic columns', () => {
|
|
63
|
+
const columns = [
|
|
64
|
+
{ index: 'name', text: 'Name' },
|
|
65
|
+
{ index: 'email', text: 'Email' }
|
|
66
|
+
];
|
|
67
|
+
|
|
68
|
+
const result = EzGridNormalizers.normalizeColumns(columns);
|
|
69
|
+
|
|
70
|
+
expect(result).toHaveLength(2);
|
|
71
|
+
expect(result[0].index).toBe('name');
|
|
72
|
+
expect(result[0].text).toBe('Name');
|
|
73
|
+
expect(result[0].flex).toBe(1); // default flex
|
|
74
|
+
expect(result[0].sortable).toBe(true); // default sortable
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it('should set default flex to 1', () => {
|
|
78
|
+
const columns = [{ index: 'name' }];
|
|
79
|
+
const result = EzGridNormalizers.normalizeColumns(columns);
|
|
80
|
+
|
|
81
|
+
expect(result[0].flex).toBe(1);
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
it('should preserve custom flex value', () => {
|
|
85
|
+
const columns = [{ index: 'name', flex: 3 }];
|
|
86
|
+
const result = EzGridNormalizers.normalizeColumns(columns);
|
|
87
|
+
|
|
88
|
+
expect(result[0].flex).toBe(3);
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
it('should preserve fixed width', () => {
|
|
92
|
+
const columns = [{ index: 'name', width: 200 }];
|
|
93
|
+
const result = EzGridNormalizers.normalizeColumns(columns);
|
|
94
|
+
|
|
95
|
+
expect(result[0].width).toBe(200);
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
it('should handle actions column', () => {
|
|
99
|
+
const columns = [
|
|
100
|
+
{ index: 'name' },
|
|
101
|
+
{ type: 'actions', actions: [{ icon: '✏️' }] }
|
|
102
|
+
];
|
|
103
|
+
|
|
104
|
+
const result = EzGridNormalizers.normalizeColumns(columns);
|
|
105
|
+
|
|
106
|
+
expect(result[1].type).toBe('actions');
|
|
107
|
+
expect(result[1].width).toBe(96); // default actions width
|
|
108
|
+
expect(result[1].actions).toHaveLength(1);
|
|
109
|
+
expect(result[1].header.align).toBe('center');
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
it('should use custom width for actions column', () => {
|
|
113
|
+
const columns = [
|
|
114
|
+
{ type: 'actions', width: 150, actions: [] }
|
|
115
|
+
];
|
|
116
|
+
|
|
117
|
+
const result = EzGridNormalizers.normalizeColumns(columns);
|
|
118
|
+
|
|
119
|
+
expect(result[0].width).toBe(150);
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
// ----- Selection Column -----
|
|
123
|
+
|
|
124
|
+
it('should NOT add selection column when mode is single', () => {
|
|
125
|
+
const columns = [{ index: 'name' }];
|
|
126
|
+
const result = EzGridNormalizers.normalizeColumns(columns, {
|
|
127
|
+
selectionMode: 'single'
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
expect(result).toHaveLength(1);
|
|
131
|
+
expect(result[0].type).not.toBe('selection');
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
it('should auto-add selection column when mode is multi', () => {
|
|
135
|
+
const columns = [{ index: 'name' }];
|
|
136
|
+
const result = EzGridNormalizers.normalizeColumns(columns, {
|
|
137
|
+
selectionMode: 'multi'
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
expect(result).toHaveLength(2);
|
|
141
|
+
expect(result[0].type).toBe('selection');
|
|
142
|
+
expect(result[0].width).toBe(36); // default width
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
it('should use custom selection width', () => {
|
|
146
|
+
const columns = [{ index: 'name' }];
|
|
147
|
+
const result = EzGridNormalizers.normalizeColumns(columns, {
|
|
148
|
+
selectionMode: 'multi',
|
|
149
|
+
selectionWidth: 50
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
expect(result[0].type).toBe('selection');
|
|
153
|
+
expect(result[0].width).toBe(50);
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
it('should filter out manually added selection columns', () => {
|
|
157
|
+
const columns = [
|
|
158
|
+
{ type: 'selection', width: 40 }, // manual - should be ignored
|
|
159
|
+
{ index: 'name' }
|
|
160
|
+
];
|
|
161
|
+
|
|
162
|
+
const result = EzGridNormalizers.normalizeColumns(columns, {
|
|
163
|
+
selectionMode: 'multi',
|
|
164
|
+
selectionWidth: 36
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
// Should have 2: auto-added selection + name
|
|
168
|
+
expect(result).toHaveLength(2);
|
|
169
|
+
expect(result[0].type).toBe('selection');
|
|
170
|
+
expect(result[0].width).toBe(36); // uses selectionWidth, not manual 40
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
// ----- Wrap -----
|
|
174
|
+
|
|
175
|
+
it('should normalize wrap: true to 3 lines', () => {
|
|
176
|
+
const columns = [{ index: 'description', wrap: true }];
|
|
177
|
+
const result = EzGridNormalizers.normalizeColumns(columns);
|
|
178
|
+
|
|
179
|
+
expect(result[0].wrap).toBe(3);
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
it('should preserve numeric wrap value', () => {
|
|
183
|
+
const columns = [{ index: 'description', wrap: 5 }];
|
|
184
|
+
const result = EzGridNormalizers.normalizeColumns(columns);
|
|
185
|
+
|
|
186
|
+
expect(result[0].wrap).toBe(5);
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
it('should set wrap to false when not specified', () => {
|
|
190
|
+
const columns = [{ index: 'name' }];
|
|
191
|
+
const result = EzGridNormalizers.normalizeColumns(columns);
|
|
192
|
+
|
|
193
|
+
expect(result[0].wrap).toBe(false);
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
// ----- Sortable -----
|
|
197
|
+
|
|
198
|
+
it('should be sortable by default', () => {
|
|
199
|
+
const columns = [{ index: 'name' }];
|
|
200
|
+
const result = EzGridNormalizers.normalizeColumns(columns);
|
|
201
|
+
|
|
202
|
+
expect(result[0].sortable).toBe(true);
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
it('should disable sort when sort: false', () => {
|
|
206
|
+
const columns = [{ index: 'name', sort: false }];
|
|
207
|
+
const result = EzGridNormalizers.normalizeColumns(columns);
|
|
208
|
+
|
|
209
|
+
expect(result[0].sortable).toBe(false);
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
// ----- Header Align -----
|
|
213
|
+
|
|
214
|
+
it('should use column header align', () => {
|
|
215
|
+
const columns = [{ index: 'price', header: { align: 'right' } }];
|
|
216
|
+
const result = EzGridNormalizers.normalizeColumns(columns);
|
|
217
|
+
|
|
218
|
+
expect(result[0].header.align).toBe('right');
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
it('should use global headerAlign as fallback', () => {
|
|
222
|
+
const columns = [{ index: 'name' }];
|
|
223
|
+
const result = EzGridNormalizers.normalizeColumns(columns, {
|
|
224
|
+
headerAlign: 'center'
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
expect(result[0].header.align).toBe('center');
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
it('should default to left align', () => {
|
|
231
|
+
const columns = [{ index: 'name' }];
|
|
232
|
+
const result = EzGridNormalizers.normalizeColumns(columns);
|
|
233
|
+
|
|
234
|
+
expect(result[0].header.align).toBe('left');
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
// ==========================================================
|
|
240
|
+
// normalizeRowKey
|
|
241
|
+
// ==========================================================
|
|
242
|
+
|
|
243
|
+
describe('EzGridNormalizers.normalizeRowKey', () => {
|
|
244
|
+
|
|
245
|
+
it('should return function as-is', () => {
|
|
246
|
+
const fn = (row) => row.customId;
|
|
247
|
+
const result = EzGridNormalizers.normalizeRowKey(fn);
|
|
248
|
+
|
|
249
|
+
expect(result).toBe(fn);
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
it('should convert string to accessor function', () => {
|
|
253
|
+
const result = EzGridNormalizers.normalizeRowKey('userId');
|
|
254
|
+
|
|
255
|
+
expect(typeof result).toBe('function');
|
|
256
|
+
expect(result({ userId: 123 })).toBe(123);
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
it('should default to id field', () => {
|
|
260
|
+
const result = EzGridNormalizers.normalizeRowKey(undefined);
|
|
261
|
+
|
|
262
|
+
expect(typeof result).toBe('function');
|
|
263
|
+
expect(result({ id: 456 })).toBe(456);
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
it('should handle null row gracefully', () => {
|
|
267
|
+
const result = EzGridNormalizers.normalizeRowKey('id');
|
|
268
|
+
|
|
269
|
+
expect(result(null)).toBe(undefined);
|
|
270
|
+
expect(result(undefined)).toBe(undefined);
|
|
271
|
+
});
|
|
272
|
+
|
|
273
|
+
});
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
// EzGrid/state/EzGridNormalizers.ts
|
|
2
|
+
|
|
3
|
+
import type {
|
|
4
|
+
HeaderAlign,
|
|
5
|
+
HeaderConfig,
|
|
6
|
+
ColumnConfig,
|
|
7
|
+
NormalizedColumn,
|
|
8
|
+
SelectionMode,
|
|
9
|
+
RowData,
|
|
10
|
+
RowKeyFn
|
|
11
|
+
} from '../types.js';
|
|
12
|
+
|
|
13
|
+
export interface NormalizeColumnsOptions {
|
|
14
|
+
selectionMode?: SelectionMode;
|
|
15
|
+
selectionWidth?: number;
|
|
16
|
+
headerAlign?: HeaderAlign;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// Re-export types for backwards compatibility
|
|
20
|
+
export type {
|
|
21
|
+
HeaderAlign,
|
|
22
|
+
HeaderConfig,
|
|
23
|
+
ColumnConfig,
|
|
24
|
+
NormalizedColumn
|
|
25
|
+
} from '../types.js';
|
|
26
|
+
|
|
27
|
+
export type RowKeyFunction = RowKeyFn;
|
|
28
|
+
|
|
29
|
+
export class EzGridNormalizers {
|
|
30
|
+
|
|
31
|
+
// ==========================================================
|
|
32
|
+
// Header Normalization
|
|
33
|
+
// ==========================================================
|
|
34
|
+
|
|
35
|
+
static normalizeHeader(header: boolean | HeaderConfig | null | undefined): HeaderConfig | false {
|
|
36
|
+
if (header === false) return false;
|
|
37
|
+
|
|
38
|
+
if (header === true || header == null) {
|
|
39
|
+
return {
|
|
40
|
+
align: 'left'
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if (typeof header === 'object') {
|
|
45
|
+
return {
|
|
46
|
+
align: header.align ?? 'left',
|
|
47
|
+
cls: header.cls,
|
|
48
|
+
height: header.height,
|
|
49
|
+
visible: header.visible
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return { align: 'left' };
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// ==========================================================
|
|
57
|
+
// Columns Normalization
|
|
58
|
+
// ==========================================================
|
|
59
|
+
|
|
60
|
+
static normalizeColumns(columns: ColumnConfig[], options: NormalizeColumnsOptions = {}): NormalizedColumn[] {
|
|
61
|
+
const { selectionMode, selectionWidth = 36, headerAlign } = options;
|
|
62
|
+
|
|
63
|
+
// Filter out manually added selection columns (we'll add it automatically)
|
|
64
|
+
const filteredColumns = columns.filter(col => col.type !== 'selection');
|
|
65
|
+
|
|
66
|
+
const normalizedColumns: NormalizedColumn[] = filteredColumns.map((col, index) => {
|
|
67
|
+
if (col.type === 'actions') {
|
|
68
|
+
return {
|
|
69
|
+
type: 'actions',
|
|
70
|
+
text: '',
|
|
71
|
+
flex: 0,
|
|
72
|
+
render: null,
|
|
73
|
+
sortable: false,
|
|
74
|
+
filter: null,
|
|
75
|
+
width: col.width ?? 96,
|
|
76
|
+
actions: Array.isArray(col.actions) ? col.actions : [],
|
|
77
|
+
header: {
|
|
78
|
+
align: 'center' as HeaderAlign
|
|
79
|
+
},
|
|
80
|
+
wrap: false,
|
|
81
|
+
resizable: false,
|
|
82
|
+
reorderable: false,
|
|
83
|
+
summary: null,
|
|
84
|
+
_id: `actions-${index}`
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Normalize filter to FilterConfig | null
|
|
89
|
+
let normalizedFilter: { operator?: string; itemRender?: unknown } | null = null;
|
|
90
|
+
if (col.filter === true) {
|
|
91
|
+
normalizedFilter = {};
|
|
92
|
+
} else if (typeof col.filter === 'string') {
|
|
93
|
+
normalizedFilter = { operator: col.filter };
|
|
94
|
+
} else if (col.filter && typeof col.filter === 'object') {
|
|
95
|
+
normalizedFilter = col.filter;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// Normalize summary to SummaryConfig | null
|
|
99
|
+
let normalizedSummary: { type: 'count' | 'sum' | 'avg' | 'average' | 'min' | 'max' } | null = null;
|
|
100
|
+
if (typeof col.summary === 'string') {
|
|
101
|
+
normalizedSummary = { type: col.summary as 'count' | 'sum' | 'avg' | 'average' | 'min' | 'max' };
|
|
102
|
+
} else if (col.summary && typeof col.summary === 'object') {
|
|
103
|
+
normalizedSummary = col.summary;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
return {
|
|
107
|
+
index: col.index,
|
|
108
|
+
type: col.type,
|
|
109
|
+
text: col.text ?? '',
|
|
110
|
+
flex: col.flex ?? 1,
|
|
111
|
+
render: typeof col.render === 'function' ? col.render : null,
|
|
112
|
+
header: {
|
|
113
|
+
align: col.header?.align ?? headerAlign ?? 'left'
|
|
114
|
+
},
|
|
115
|
+
width: col.width,
|
|
116
|
+
sortable: col.sort !== false,
|
|
117
|
+
filter: normalizedFilter,
|
|
118
|
+
wrap: col.wrap === true ? 3 : (typeof col.wrap === 'number' ? col.wrap : false),
|
|
119
|
+
resizable: col.resizable !== false,
|
|
120
|
+
reorderable: col.reorderable !== false,
|
|
121
|
+
summary: normalizedSummary,
|
|
122
|
+
_id: col.index || `col-${index}`
|
|
123
|
+
};
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
// Auto-add selection column for multi mode
|
|
127
|
+
if (selectionMode === 'multi') {
|
|
128
|
+
normalizedColumns.unshift({
|
|
129
|
+
type: 'selection',
|
|
130
|
+
text: '',
|
|
131
|
+
flex: 0,
|
|
132
|
+
render: null,
|
|
133
|
+
sortable: false,
|
|
134
|
+
filter: null,
|
|
135
|
+
width: selectionWidth,
|
|
136
|
+
header: { align: 'center' },
|
|
137
|
+
wrap: false,
|
|
138
|
+
resizable: false,
|
|
139
|
+
reorderable: false,
|
|
140
|
+
summary: null,
|
|
141
|
+
_id: 'selection'
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
return normalizedColumns;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// ==========================================================
|
|
149
|
+
// RowKey Normalization
|
|
150
|
+
// ==========================================================
|
|
151
|
+
|
|
152
|
+
static normalizeRowKey(rowKey: string | RowKeyFunction | undefined): RowKeyFunction {
|
|
153
|
+
if (typeof rowKey === 'function') return rowKey;
|
|
154
|
+
|
|
155
|
+
if (typeof rowKey === 'string') {
|
|
156
|
+
return (row: any) => row?.[rowKey];
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// fallback to 'id' field
|
|
160
|
+
return (row: any) => row?.id;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
// EzGrid/state/EzGridParts.ts
|
|
2
|
+
|
|
3
|
+
import type {
|
|
4
|
+
NormalizedColumn,
|
|
5
|
+
EzGridController,
|
|
6
|
+
EzGridSelectionRef,
|
|
7
|
+
RowData,
|
|
8
|
+
RowKeyFn,
|
|
9
|
+
RowCallback,
|
|
10
|
+
RowContextMenuCallback,
|
|
11
|
+
TitleBarContext,
|
|
12
|
+
ToolConfig,
|
|
13
|
+
EzGridConfig,
|
|
14
|
+
HeaderConfig,
|
|
15
|
+
FilterMode,
|
|
16
|
+
EzGridHeaderRef,
|
|
17
|
+
EzGridFiltersRef,
|
|
18
|
+
EzGridBodyRef,
|
|
19
|
+
EzGridFooterRef,
|
|
20
|
+
EzGridRemoteRef
|
|
21
|
+
} from '../types.js';
|
|
22
|
+
import type { EzGridSelection } from './EzGridSelection.js';
|
|
23
|
+
|
|
24
|
+
export interface ComponentConfig {
|
|
25
|
+
eztype: string;
|
|
26
|
+
grid?: EzGridPartsRef;
|
|
27
|
+
_ezAfterRender?: (el: HTMLElement, instance: unknown) => void;
|
|
28
|
+
[key: string]: unknown;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export interface EzGridPartsRef {
|
|
32
|
+
config: {
|
|
33
|
+
titleBar?: (ctx: TitleBarContext) => ComponentConfig | null;
|
|
34
|
+
title?: string | ((ctx: TitleBarContext) => unknown);
|
|
35
|
+
tools?: ToolConfig[] | ((ctx: TitleBarContext) => ToolConfig[]);
|
|
36
|
+
bind?: { data?: string; title?: string };
|
|
37
|
+
id?: string;
|
|
38
|
+
controller?: string;
|
|
39
|
+
overRules?: string;
|
|
40
|
+
data?: RowData[];
|
|
41
|
+
rowHeight?: number;
|
|
42
|
+
};
|
|
43
|
+
controller: EzGridController | null;
|
|
44
|
+
header: HeaderConfig | false;
|
|
45
|
+
selection: EzGridSelection;
|
|
46
|
+
rowKey: RowKeyFn;
|
|
47
|
+
headerInstance?: EzGridHeaderRef;
|
|
48
|
+
_filtersInstance?: EzGridFiltersRef;
|
|
49
|
+
_bodyInstance?: EzGridBodyRef;
|
|
50
|
+
_footerInstance?: EzGridFooterRef;
|
|
51
|
+
_remote?: EzGridRemoteRef;
|
|
52
|
+
_rowClickHandler?: RowCallback | null;
|
|
53
|
+
_rowDoubleClickHandler?: RowCallback | null;
|
|
54
|
+
_rowContextMenuHandler?: RowContextMenuCallback | null;
|
|
55
|
+
getSelection: () => string[];
|
|
56
|
+
isSelected: (row: RowData) => boolean;
|
|
57
|
+
getVisibleColumns: () => NormalizedColumn[];
|
|
58
|
+
isRemoteMode: () => boolean;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Re-export for backwards compatibility
|
|
62
|
+
export type { TitleBarContext } from '../types.js';
|
|
63
|
+
|
|
64
|
+
export class EzGridParts {
|
|
65
|
+
grid: EzGridPartsRef;
|
|
66
|
+
|
|
67
|
+
constructor(grid: EzGridPartsRef) {
|
|
68
|
+
this.grid = grid;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// ==========================================================
|
|
72
|
+
// Build All Parts
|
|
73
|
+
// ==========================================================
|
|
74
|
+
|
|
75
|
+
build(): ComponentConfig[] {
|
|
76
|
+
const items: ComponentConfig[] = [];
|
|
77
|
+
|
|
78
|
+
items.push(...this._buildTitleBar());
|
|
79
|
+
items.push(...this._buildHeader());
|
|
80
|
+
items.push(...this._buildFilters());
|
|
81
|
+
items.push(...this._buildBody());
|
|
82
|
+
items.push(...this._buildFooter());
|
|
83
|
+
|
|
84
|
+
return items;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// ==========================================================
|
|
88
|
+
// TitleBar
|
|
89
|
+
// ==========================================================
|
|
90
|
+
|
|
91
|
+
private _buildTitleBar(): ComponentConfig[] {
|
|
92
|
+
const grid = this.grid;
|
|
93
|
+
const config = grid.config;
|
|
94
|
+
|
|
95
|
+
const ctx = {
|
|
96
|
+
grid: grid as any,
|
|
97
|
+
controller: grid.controller,
|
|
98
|
+
selection: {
|
|
99
|
+
keys: grid.getSelection(),
|
|
100
|
+
rows: grid.controller?.state?.data?.filter(row =>
|
|
101
|
+
grid.isSelected(row)
|
|
102
|
+
) ?? [],
|
|
103
|
+
count: grid.getSelection().length
|
|
104
|
+
},
|
|
105
|
+
state: grid.controller?.state ?? null,
|
|
106
|
+
bind: config.bind ?? null,
|
|
107
|
+
config: config as any
|
|
108
|
+
} as TitleBarContext;
|
|
109
|
+
|
|
110
|
+
// Full override of titlebar
|
|
111
|
+
if (typeof config.titleBar === 'function') {
|
|
112
|
+
const res = config.titleBar(ctx);
|
|
113
|
+
return res ? [res] : [];
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// Default titlebar with partial overrides
|
|
117
|
+
if (config.title || config.tools || config.bind?.title) {
|
|
118
|
+
return [{
|
|
119
|
+
eztype: 'EzGridTitleBar',
|
|
120
|
+
title: config.title,
|
|
121
|
+
tools: config.tools,
|
|
122
|
+
bind: config.bind,
|
|
123
|
+
grid: grid,
|
|
124
|
+
titleOverride: typeof config.title === 'function'
|
|
125
|
+
? config.title
|
|
126
|
+
: null,
|
|
127
|
+
toolsOverride: typeof config.tools === 'function'
|
|
128
|
+
? config.tools
|
|
129
|
+
: null,
|
|
130
|
+
ctx
|
|
131
|
+
}];
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
return [];
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// ==========================================================
|
|
138
|
+
// Header
|
|
139
|
+
// ==========================================================
|
|
140
|
+
|
|
141
|
+
private _buildHeader(): ComponentConfig[] {
|
|
142
|
+
const grid = this.grid;
|
|
143
|
+
|
|
144
|
+
if (grid.header === false) return [];
|
|
145
|
+
|
|
146
|
+
return [{
|
|
147
|
+
eztype: 'EzGridHeader',
|
|
148
|
+
header: grid.header,
|
|
149
|
+
columns: () => grid.getVisibleColumns(),
|
|
150
|
+
grid: grid,
|
|
151
|
+
_ezAfterRender: (el: HTMLElement, instance: any) => {
|
|
152
|
+
grid.headerInstance = instance;
|
|
153
|
+
}
|
|
154
|
+
}];
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// ==========================================================
|
|
158
|
+
// Filters
|
|
159
|
+
// ==========================================================
|
|
160
|
+
|
|
161
|
+
private _buildFilters(): ComponentConfig[] {
|
|
162
|
+
const grid = this.grid;
|
|
163
|
+
|
|
164
|
+
// Get filter mode from remote config (only applies to remote mode)
|
|
165
|
+
const filterMode = grid._remote?.config?.filter ?? 'onInput';
|
|
166
|
+
|
|
167
|
+
return [{
|
|
168
|
+
eztype: 'EzGridFilters',
|
|
169
|
+
grid: grid,
|
|
170
|
+
columns: () => grid.getVisibleColumns(),
|
|
171
|
+
filterMode: grid.isRemoteMode() ? filterMode : 'onInput',
|
|
172
|
+
_ezAfterRender: (el: HTMLElement, instance: any) => {
|
|
173
|
+
grid._filtersInstance = instance;
|
|
174
|
+
}
|
|
175
|
+
}];
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// ==========================================================
|
|
179
|
+
// Body
|
|
180
|
+
// ==========================================================
|
|
181
|
+
|
|
182
|
+
private _buildBody(): ComponentConfig[] {
|
|
183
|
+
const grid = this.grid;
|
|
184
|
+
const config = grid.config;
|
|
185
|
+
|
|
186
|
+
return [{
|
|
187
|
+
eztype: 'EzGridBody',
|
|
188
|
+
grid: grid,
|
|
189
|
+
controller: grid.controller,
|
|
190
|
+
_ezAfterRender: (el: HTMLElement, instance: any) => {
|
|
191
|
+
grid._bodyInstance = instance;
|
|
192
|
+
},
|
|
193
|
+
overRules: config.overRules,
|
|
194
|
+
columns: () => grid.getVisibleColumns(),
|
|
195
|
+
rowKey: grid.rowKey,
|
|
196
|
+
selection: grid.selection,
|
|
197
|
+
bind: config.bind,
|
|
198
|
+
data: config.data,
|
|
199
|
+
rowHeight: config.rowHeight || null,
|
|
200
|
+
// Use resolved handlers from listenTo (supports string callbacks)
|
|
201
|
+
onRowClick: grid._rowClickHandler || null,
|
|
202
|
+
onRowDoubleClick: grid._rowDoubleClickHandler || null,
|
|
203
|
+
onRowContextMenu: grid._rowContextMenuHandler || null,
|
|
204
|
+
flex: 1
|
|
205
|
+
}];
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
// ==========================================================
|
|
209
|
+
// Footer
|
|
210
|
+
// ==========================================================
|
|
211
|
+
|
|
212
|
+
private _buildFooter(): ComponentConfig[] {
|
|
213
|
+
const grid = this.grid;
|
|
214
|
+
|
|
215
|
+
return [{
|
|
216
|
+
eztype: 'EzGridFooter',
|
|
217
|
+
grid: grid,
|
|
218
|
+
_ezAfterRender: (el: HTMLElement, instance: any) => {
|
|
219
|
+
grid._footerInstance = instance;
|
|
220
|
+
}
|
|
221
|
+
}];
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
// ==========================================================
|
|
225
|
+
// Cleanup Config
|
|
226
|
+
// ==========================================================
|
|
227
|
+
|
|
228
|
+
cleanupConfig(): void {
|
|
229
|
+
delete (this.grid.config as any).title;
|
|
230
|
+
delete (this.grid.config as any).tools;
|
|
231
|
+
delete (this.grid.config as any).columns;
|
|
232
|
+
}
|
|
233
|
+
}
|