@object-ui/plugin-aggrid 0.4.1 → 2.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/.turbo/turbo-build.log +99 -0
- package/CHANGELOG.md +16 -0
- package/OBJECT_AGGRID_CN.md +483 -0
- package/QUICKSTART.md +186 -0
- package/README.md +221 -1
- package/dist/AddressField-Bntpynvd.js +95 -0
- package/dist/AgGridImpl-3Mmf2qrR.js +229 -0
- package/dist/AutoNumberField-C1kBJaxh.js +8 -0
- package/dist/FileField-BDwbJvor.js +101 -0
- package/dist/FormulaField-BXNiyGoh.js +9 -0
- package/dist/GeolocationField-Df3yYcM9.js +141 -0
- package/dist/GridField-CcjQp4WM.js +29 -0
- package/dist/LocationField-BIfN5QIq.js +33 -0
- package/dist/MasterDetailField-CAEmxbIT.js +117 -0
- package/dist/ObjectAgGridImpl-EjifM4aY.js +28727 -0
- package/dist/ObjectField-BpkQpIF-.js +51 -0
- package/dist/QRCodeField-VCBewTDG.js +96 -0
- package/dist/RichTextField-CyQwSi2C.js +37 -0
- package/dist/SignatureField-Cr4tsEbj.js +96 -0
- package/dist/SummaryField-CnEJ_GZI.js +9 -0
- package/dist/UserField-DJjaVyrV.js +49 -0
- package/dist/VectorField-cPYmcKnV.js +25 -0
- package/dist/{index-B6NPAFZx.js → index-B87wd1E0.js} +301 -143
- package/dist/index.css +1 -1
- package/dist/index.js +4 -3
- package/dist/index.umd.cjs +225 -2
- package/dist/src/AgGridImpl.d.ts +5 -2
- package/dist/src/ObjectAgGridImpl.d.ts +6 -0
- package/dist/src/VirtualScrolling.d.ts +72 -0
- package/dist/src/field-renderers.d.ts +67 -0
- package/dist/src/index.d.ts +47 -2
- package/dist/src/object-aggrid.types.d.ts +74 -0
- package/dist/src/types.d.ts +48 -1
- package/package.json +11 -9
- package/src/AgGridImpl.tsx +100 -11
- package/src/ObjectAgGridImpl.tsx +501 -0
- package/src/VirtualScrolling.ts +74 -0
- package/src/field-renderers.test.tsx +383 -0
- package/src/field-renderers.tsx +224 -0
- package/src/index.test.ts +1 -1
- package/src/index.tsx +211 -2
- package/src/object-aggrid.test.ts +99 -0
- package/src/object-aggrid.types.ts +123 -0
- package/src/types.ts +57 -1
- package/vite.config.ts +13 -0
- package/dist/AgGridImpl-DKkq6v1B.js +0 -171
|
@@ -0,0 +1,383 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ObjectUI
|
|
3
|
+
* Copyright (c) 2024-present ObjectStack Inc.
|
|
4
|
+
*
|
|
5
|
+
* This source code is licensed under the MIT license found in the
|
|
6
|
+
* LICENSE file in the root directory of this source tree.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
|
10
|
+
import type { ICellRendererParams, ICellEditorParams } from 'ag-grid-community';
|
|
11
|
+
import type { FieldMetadata } from '@object-ui/types';
|
|
12
|
+
import {
|
|
13
|
+
FieldWidgetCellRenderer,
|
|
14
|
+
FieldWidgetCellEditor,
|
|
15
|
+
createFieldCellRenderer,
|
|
16
|
+
createFieldCellEditor
|
|
17
|
+
} from './field-renderers';
|
|
18
|
+
|
|
19
|
+
describe('field-renderers', () => {
|
|
20
|
+
describe('FieldWidgetCellRenderer', () => {
|
|
21
|
+
let renderer: FieldWidgetCellRenderer;
|
|
22
|
+
|
|
23
|
+
beforeEach(() => {
|
|
24
|
+
renderer = new FieldWidgetCellRenderer();
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
afterEach(() => {
|
|
28
|
+
renderer.destroy();
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it('should initialize with a field widget for supported types', () => {
|
|
32
|
+
const field: FieldMetadata = {
|
|
33
|
+
name: 'testField',
|
|
34
|
+
label: 'Test Field',
|
|
35
|
+
type: 'text',
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
const params = {
|
|
39
|
+
value: 'test value',
|
|
40
|
+
field,
|
|
41
|
+
} as ICellRendererParams & { field: FieldMetadata };
|
|
42
|
+
|
|
43
|
+
renderer.init(params);
|
|
44
|
+
|
|
45
|
+
expect(renderer.eGui).toBeDefined();
|
|
46
|
+
expect(renderer.eGui.className).toBe('field-widget-cell');
|
|
47
|
+
expect(renderer.root).toBeDefined();
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it('should initialize with fallback for unsupported types', () => {
|
|
51
|
+
const field = {
|
|
52
|
+
name: 'testField',
|
|
53
|
+
label: 'Test Field',
|
|
54
|
+
type: 'unsupported_type',
|
|
55
|
+
} as unknown as FieldMetadata;
|
|
56
|
+
|
|
57
|
+
const params = {
|
|
58
|
+
value: 'fallback value',
|
|
59
|
+
field,
|
|
60
|
+
} as ICellRendererParams & { field: FieldMetadata };
|
|
61
|
+
|
|
62
|
+
renderer.init(params);
|
|
63
|
+
|
|
64
|
+
expect(renderer.eGui).toBeDefined();
|
|
65
|
+
expect(renderer.eGui.textContent).toBe('fallback value');
|
|
66
|
+
expect(renderer.root).toBeNull();
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
it('should handle null values in fallback mode', () => {
|
|
70
|
+
const field = {
|
|
71
|
+
name: 'testField',
|
|
72
|
+
label: 'Test Field',
|
|
73
|
+
type: 'unsupported_type',
|
|
74
|
+
} as unknown as FieldMetadata;
|
|
75
|
+
|
|
76
|
+
const params = {
|
|
77
|
+
value: null,
|
|
78
|
+
field,
|
|
79
|
+
} as ICellRendererParams & { field: FieldMetadata };
|
|
80
|
+
|
|
81
|
+
renderer.init(params);
|
|
82
|
+
|
|
83
|
+
expect(renderer.eGui.textContent).toBe('');
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
it('should return the GUI element', () => {
|
|
87
|
+
const field: FieldMetadata = {
|
|
88
|
+
name: 'testField',
|
|
89
|
+
label: 'Test Field',
|
|
90
|
+
type: 'text',
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
const params = {
|
|
94
|
+
value: 'test',
|
|
95
|
+
field,
|
|
96
|
+
} as ICellRendererParams & { field: FieldMetadata };
|
|
97
|
+
|
|
98
|
+
renderer.init(params);
|
|
99
|
+
const gui = renderer.getGui();
|
|
100
|
+
|
|
101
|
+
expect(gui).toBe(renderer.eGui);
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
it('should refresh with new value for supported field type', () => {
|
|
105
|
+
const field: FieldMetadata = {
|
|
106
|
+
name: 'testField',
|
|
107
|
+
label: 'Test Field',
|
|
108
|
+
type: 'text',
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
const initParams = {
|
|
112
|
+
value: 'initial value',
|
|
113
|
+
field,
|
|
114
|
+
} as ICellRendererParams & { field: FieldMetadata };
|
|
115
|
+
|
|
116
|
+
renderer.init(initParams);
|
|
117
|
+
|
|
118
|
+
const refreshParams = {
|
|
119
|
+
value: 'updated value',
|
|
120
|
+
field,
|
|
121
|
+
} as ICellRendererParams & { field: FieldMetadata };
|
|
122
|
+
|
|
123
|
+
const result = renderer.refresh(refreshParams);
|
|
124
|
+
|
|
125
|
+
expect(result).toBe(true);
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
it('should refresh with new value for unsupported field type', () => {
|
|
129
|
+
const field = {
|
|
130
|
+
name: 'testField',
|
|
131
|
+
label: 'Test Field',
|
|
132
|
+
type: 'unsupported_type',
|
|
133
|
+
} as unknown as FieldMetadata;
|
|
134
|
+
|
|
135
|
+
const initParams = {
|
|
136
|
+
value: 'initial value',
|
|
137
|
+
field,
|
|
138
|
+
} as ICellRendererParams & { field: FieldMetadata };
|
|
139
|
+
|
|
140
|
+
renderer.init(initParams);
|
|
141
|
+
|
|
142
|
+
const refreshParams = {
|
|
143
|
+
value: 'updated value',
|
|
144
|
+
field,
|
|
145
|
+
} as ICellRendererParams & { field: FieldMetadata };
|
|
146
|
+
|
|
147
|
+
const result = renderer.refresh(refreshParams);
|
|
148
|
+
|
|
149
|
+
expect(result).toBe(true);
|
|
150
|
+
expect(renderer.eGui.textContent).toBe('updated value');
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
it('should clean up root on destroy', () => {
|
|
154
|
+
const field: FieldMetadata = {
|
|
155
|
+
name: 'testField',
|
|
156
|
+
label: 'Test Field',
|
|
157
|
+
type: 'text',
|
|
158
|
+
};
|
|
159
|
+
|
|
160
|
+
const params = {
|
|
161
|
+
value: 'test',
|
|
162
|
+
field,
|
|
163
|
+
} as ICellRendererParams & { field: FieldMetadata };
|
|
164
|
+
|
|
165
|
+
renderer.init(params);
|
|
166
|
+
|
|
167
|
+
const unmountSpy = vi.spyOn(renderer.root!, 'unmount');
|
|
168
|
+
renderer.destroy();
|
|
169
|
+
|
|
170
|
+
expect(unmountSpy).toHaveBeenCalled();
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
it('should handle destroy when root is null', () => {
|
|
174
|
+
const field = {
|
|
175
|
+
name: 'testField',
|
|
176
|
+
label: 'Test Field',
|
|
177
|
+
type: 'unsupported_type',
|
|
178
|
+
} as unknown as FieldMetadata;
|
|
179
|
+
|
|
180
|
+
const params = {
|
|
181
|
+
value: 'test',
|
|
182
|
+
field,
|
|
183
|
+
} as ICellRendererParams & { field: FieldMetadata };
|
|
184
|
+
|
|
185
|
+
renderer.init(params);
|
|
186
|
+
|
|
187
|
+
// Should not throw
|
|
188
|
+
expect(() => renderer.destroy()).not.toThrow();
|
|
189
|
+
});
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
describe('FieldWidgetCellEditor', () => {
|
|
193
|
+
let editor: FieldWidgetCellEditor;
|
|
194
|
+
|
|
195
|
+
beforeEach(() => {
|
|
196
|
+
editor = new FieldWidgetCellEditor();
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
afterEach(() => {
|
|
200
|
+
editor.destroy();
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
it('should initialize with a field widget for supported types', () => {
|
|
204
|
+
const field: FieldMetadata = {
|
|
205
|
+
name: 'testField',
|
|
206
|
+
label: 'Test Field',
|
|
207
|
+
type: 'text',
|
|
208
|
+
};
|
|
209
|
+
|
|
210
|
+
const params = {
|
|
211
|
+
value: 'initial value',
|
|
212
|
+
field,
|
|
213
|
+
} as ICellEditorParams & { field: FieldMetadata };
|
|
214
|
+
|
|
215
|
+
editor.init(params);
|
|
216
|
+
|
|
217
|
+
expect(editor.eGui).toBeDefined();
|
|
218
|
+
expect(editor.eGui.className).toBe('field-widget-editor');
|
|
219
|
+
expect(editor.root).toBeDefined();
|
|
220
|
+
expect(editor.currentValue).toBe('initial value');
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
it('should initialize with fallback input for unsupported types', () => {
|
|
224
|
+
const field = {
|
|
225
|
+
name: 'testField',
|
|
226
|
+
label: 'Test Field',
|
|
227
|
+
type: 'unsupported_type',
|
|
228
|
+
} as unknown as FieldMetadata;
|
|
229
|
+
|
|
230
|
+
const params = {
|
|
231
|
+
value: 'fallback value',
|
|
232
|
+
field,
|
|
233
|
+
} as ICellEditorParams & { field: FieldMetadata };
|
|
234
|
+
|
|
235
|
+
editor.init(params);
|
|
236
|
+
|
|
237
|
+
expect(editor.eGui).toBeDefined();
|
|
238
|
+
expect(editor.root).toBeNull();
|
|
239
|
+
const input = editor.eGui.querySelector('input');
|
|
240
|
+
expect(input).toBeDefined();
|
|
241
|
+
expect(input?.value).toBe('fallback value');
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
it('should return current value', () => {
|
|
245
|
+
const field: FieldMetadata = {
|
|
246
|
+
name: 'testField',
|
|
247
|
+
label: 'Test Field',
|
|
248
|
+
type: 'text',
|
|
249
|
+
};
|
|
250
|
+
|
|
251
|
+
const params = {
|
|
252
|
+
value: 'test value',
|
|
253
|
+
field,
|
|
254
|
+
} as ICellEditorParams & { field: FieldMetadata };
|
|
255
|
+
|
|
256
|
+
editor.init(params);
|
|
257
|
+
|
|
258
|
+
expect(editor.getValue()).toBe('test value');
|
|
259
|
+
});
|
|
260
|
+
|
|
261
|
+
it('should return the GUI element', () => {
|
|
262
|
+
const field: FieldMetadata = {
|
|
263
|
+
name: 'testField',
|
|
264
|
+
label: 'Test Field',
|
|
265
|
+
type: 'text',
|
|
266
|
+
};
|
|
267
|
+
|
|
268
|
+
const params = {
|
|
269
|
+
value: 'test',
|
|
270
|
+
field,
|
|
271
|
+
} as ICellEditorParams & { field: FieldMetadata };
|
|
272
|
+
|
|
273
|
+
editor.init(params);
|
|
274
|
+
const gui = editor.getGui();
|
|
275
|
+
|
|
276
|
+
expect(gui).toBe(editor.eGui);
|
|
277
|
+
});
|
|
278
|
+
|
|
279
|
+
it('should return true for popup editors for specific field types', () => {
|
|
280
|
+
const popupTypes = ['date', 'datetime', 'select', 'lookup', 'color'] as const;
|
|
281
|
+
|
|
282
|
+
popupTypes.forEach(type => {
|
|
283
|
+
const field: FieldMetadata = {
|
|
284
|
+
name: 'testField',
|
|
285
|
+
label: 'Test Field',
|
|
286
|
+
type,
|
|
287
|
+
};
|
|
288
|
+
|
|
289
|
+
const params = {
|
|
290
|
+
value: 'test',
|
|
291
|
+
field,
|
|
292
|
+
} as ICellEditorParams & { field: FieldMetadata };
|
|
293
|
+
|
|
294
|
+
const testEditor = new FieldWidgetCellEditor();
|
|
295
|
+
testEditor.init(params);
|
|
296
|
+
|
|
297
|
+
expect(testEditor.isPopup()).toBe(true);
|
|
298
|
+
testEditor.destroy();
|
|
299
|
+
});
|
|
300
|
+
});
|
|
301
|
+
|
|
302
|
+
it('should return false for popup editors for non-popup field types', () => {
|
|
303
|
+
const field: FieldMetadata = {
|
|
304
|
+
name: 'testField',
|
|
305
|
+
label: 'Test Field',
|
|
306
|
+
type: 'text',
|
|
307
|
+
};
|
|
308
|
+
|
|
309
|
+
const params = {
|
|
310
|
+
value: 'test',
|
|
311
|
+
field,
|
|
312
|
+
} as ICellEditorParams & { field: FieldMetadata };
|
|
313
|
+
|
|
314
|
+
editor.init(params);
|
|
315
|
+
|
|
316
|
+
expect(editor.isPopup()).toBe(false);
|
|
317
|
+
});
|
|
318
|
+
|
|
319
|
+
it('should clean up root on destroy', () => {
|
|
320
|
+
const field: FieldMetadata = {
|
|
321
|
+
name: 'testField',
|
|
322
|
+
label: 'Test Field',
|
|
323
|
+
type: 'text',
|
|
324
|
+
};
|
|
325
|
+
|
|
326
|
+
const params = {
|
|
327
|
+
value: 'test',
|
|
328
|
+
field,
|
|
329
|
+
} as ICellEditorParams & { field: FieldMetadata };
|
|
330
|
+
|
|
331
|
+
editor.init(params);
|
|
332
|
+
|
|
333
|
+
const unmountSpy = vi.spyOn(editor.root!, 'unmount');
|
|
334
|
+
editor.destroy();
|
|
335
|
+
|
|
336
|
+
expect(unmountSpy).toHaveBeenCalled();
|
|
337
|
+
});
|
|
338
|
+
});
|
|
339
|
+
|
|
340
|
+
describe('createFieldCellRenderer', () => {
|
|
341
|
+
it('should create a renderer class with field metadata', () => {
|
|
342
|
+
const field: FieldMetadata = {
|
|
343
|
+
name: 'testField',
|
|
344
|
+
label: 'Test Field',
|
|
345
|
+
type: 'text',
|
|
346
|
+
};
|
|
347
|
+
|
|
348
|
+
const RendererClass = createFieldCellRenderer(field);
|
|
349
|
+
const renderer = new RendererClass();
|
|
350
|
+
|
|
351
|
+
const params = {
|
|
352
|
+
value: 'test value',
|
|
353
|
+
} as ICellRendererParams;
|
|
354
|
+
|
|
355
|
+
renderer.init(params);
|
|
356
|
+
|
|
357
|
+
expect(renderer.eGui).toBeDefined();
|
|
358
|
+
renderer.destroy();
|
|
359
|
+
});
|
|
360
|
+
});
|
|
361
|
+
|
|
362
|
+
describe('createFieldCellEditor', () => {
|
|
363
|
+
it('should create an editor class with field metadata', () => {
|
|
364
|
+
const field: FieldMetadata = {
|
|
365
|
+
name: 'testField',
|
|
366
|
+
label: 'Test Field',
|
|
367
|
+
type: 'text',
|
|
368
|
+
};
|
|
369
|
+
|
|
370
|
+
const EditorClass = createFieldCellEditor(field);
|
|
371
|
+
const editor = new EditorClass();
|
|
372
|
+
|
|
373
|
+
const params = {
|
|
374
|
+
value: 'test value',
|
|
375
|
+
} as ICellEditorParams;
|
|
376
|
+
|
|
377
|
+
editor.init(params);
|
|
378
|
+
|
|
379
|
+
expect(editor.eGui).toBeDefined();
|
|
380
|
+
editor.destroy();
|
|
381
|
+
});
|
|
382
|
+
});
|
|
383
|
+
});
|
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ObjectUI
|
|
3
|
+
* Copyright (c) 2024-present ObjectStack Inc.
|
|
4
|
+
*
|
|
5
|
+
* This source code is licensed under the MIT license found in the
|
|
6
|
+
* LICENSE file in the root directory of this source tree.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import React from 'react';
|
|
10
|
+
import { createRoot, type Root } from 'react-dom/client';
|
|
11
|
+
import type { ICellRendererParams, ICellEditorParams } from 'ag-grid-community';
|
|
12
|
+
import type { FieldMetadata } from '@object-ui/types';
|
|
13
|
+
|
|
14
|
+
// Import field widgets
|
|
15
|
+
import {
|
|
16
|
+
TextField,
|
|
17
|
+
NumberField,
|
|
18
|
+
BooleanField,
|
|
19
|
+
SelectField,
|
|
20
|
+
DateField,
|
|
21
|
+
DateTimeField,
|
|
22
|
+
TimeField,
|
|
23
|
+
EmailField,
|
|
24
|
+
PhoneField,
|
|
25
|
+
UrlField,
|
|
26
|
+
CurrencyField,
|
|
27
|
+
PercentField,
|
|
28
|
+
PasswordField,
|
|
29
|
+
TextAreaField,
|
|
30
|
+
ColorField,
|
|
31
|
+
RatingField,
|
|
32
|
+
ImageField,
|
|
33
|
+
AvatarField,
|
|
34
|
+
LookupField,
|
|
35
|
+
SliderField,
|
|
36
|
+
CodeField,
|
|
37
|
+
} from '@object-ui/fields';
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Map field type to field widget component
|
|
41
|
+
* Defined at module level to avoid recreating on every call
|
|
42
|
+
*/
|
|
43
|
+
const widgetMap: Record<string, React.ComponentType<any>> = {
|
|
44
|
+
text: TextField,
|
|
45
|
+
textarea: TextAreaField,
|
|
46
|
+
number: NumberField,
|
|
47
|
+
currency: CurrencyField,
|
|
48
|
+
percent: PercentField,
|
|
49
|
+
boolean: BooleanField,
|
|
50
|
+
select: SelectField,
|
|
51
|
+
date: DateField,
|
|
52
|
+
datetime: DateTimeField,
|
|
53
|
+
time: TimeField,
|
|
54
|
+
email: EmailField,
|
|
55
|
+
phone: PhoneField,
|
|
56
|
+
url: UrlField,
|
|
57
|
+
password: PasswordField,
|
|
58
|
+
color: ColorField,
|
|
59
|
+
rating: RatingField,
|
|
60
|
+
image: ImageField,
|
|
61
|
+
avatar: AvatarField,
|
|
62
|
+
lookup: LookupField,
|
|
63
|
+
slider: SliderField,
|
|
64
|
+
code: CodeField,
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
function getFieldWidget(fieldType: string): React.ComponentType<any> | null {
|
|
68
|
+
return widgetMap[fieldType] || null;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* AG Grid Cell Renderer using Field Widgets (Read-only mode)
|
|
73
|
+
*/
|
|
74
|
+
export class FieldWidgetCellRenderer {
|
|
75
|
+
public eGui!: HTMLDivElement;
|
|
76
|
+
public root: Root | null = null;
|
|
77
|
+
|
|
78
|
+
init(params: ICellRendererParams & { field: FieldMetadata }) {
|
|
79
|
+
const { value, field } = params;
|
|
80
|
+
const FieldWidget = getFieldWidget(field.type);
|
|
81
|
+
|
|
82
|
+
this.eGui = document.createElement('div');
|
|
83
|
+
this.eGui.className = 'field-widget-cell';
|
|
84
|
+
|
|
85
|
+
if (FieldWidget) {
|
|
86
|
+
this.root = createRoot(this.eGui);
|
|
87
|
+
this.root.render(
|
|
88
|
+
<FieldWidget
|
|
89
|
+
value={value}
|
|
90
|
+
onChange={() => {}} // No-op for read-only mode
|
|
91
|
+
field={field}
|
|
92
|
+
readonly={true}
|
|
93
|
+
/>
|
|
94
|
+
);
|
|
95
|
+
} else {
|
|
96
|
+
// Fallback to text display
|
|
97
|
+
this.eGui.textContent = value != null ? String(value) : '';
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
getGui() {
|
|
102
|
+
return this.eGui;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
refresh(params: ICellRendererParams & { field: FieldMetadata }): boolean {
|
|
106
|
+
const { value, field } = params;
|
|
107
|
+
const FieldWidget = getFieldWidget(field.type);
|
|
108
|
+
|
|
109
|
+
if (FieldWidget && this.root) {
|
|
110
|
+
this.root.render(
|
|
111
|
+
<FieldWidget
|
|
112
|
+
value={value}
|
|
113
|
+
onChange={() => {}} // No-op for read-only mode
|
|
114
|
+
field={field}
|
|
115
|
+
readonly={true}
|
|
116
|
+
/>
|
|
117
|
+
);
|
|
118
|
+
return true;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// Fallback to text display when no FieldWidget is available
|
|
122
|
+
if (this.eGui) {
|
|
123
|
+
this.eGui.textContent = value != null ? String(value) : '';
|
|
124
|
+
return true;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
return false;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
destroy() {
|
|
131
|
+
if (this.root) {
|
|
132
|
+
this.root.unmount();
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* AG Grid Cell Editor using Field Widgets (Edit mode)
|
|
139
|
+
*/
|
|
140
|
+
export class FieldWidgetCellEditor {
|
|
141
|
+
public eGui!: HTMLDivElement;
|
|
142
|
+
public root: Root | null = null;
|
|
143
|
+
public currentValue: unknown;
|
|
144
|
+
public params!: ICellEditorParams & { field: FieldMetadata };
|
|
145
|
+
|
|
146
|
+
init(params: ICellEditorParams & { field: FieldMetadata }) {
|
|
147
|
+
this.params = params;
|
|
148
|
+
this.currentValue = params.value;
|
|
149
|
+
const { field } = params;
|
|
150
|
+
const FieldWidget = getFieldWidget(field.type);
|
|
151
|
+
|
|
152
|
+
this.eGui = document.createElement('div');
|
|
153
|
+
this.eGui.className = 'field-widget-editor';
|
|
154
|
+
|
|
155
|
+
if (FieldWidget) {
|
|
156
|
+
this.root = createRoot(this.eGui);
|
|
157
|
+
this.root.render(
|
|
158
|
+
<FieldWidget
|
|
159
|
+
value={this.currentValue}
|
|
160
|
+
onChange={(newValue: any) => {
|
|
161
|
+
this.currentValue = newValue;
|
|
162
|
+
}}
|
|
163
|
+
field={field}
|
|
164
|
+
readonly={false}
|
|
165
|
+
/>
|
|
166
|
+
);
|
|
167
|
+
} else {
|
|
168
|
+
// Fallback to input element
|
|
169
|
+
const input = document.createElement('input');
|
|
170
|
+
input.value = this.currentValue != null ? String(this.currentValue) : '';
|
|
171
|
+
input.className = 'ag-input-field-input ag-text-field-input';
|
|
172
|
+
input.addEventListener('input', (e) => {
|
|
173
|
+
this.currentValue = (e.target as HTMLInputElement).value;
|
|
174
|
+
});
|
|
175
|
+
this.eGui.appendChild(input);
|
|
176
|
+
setTimeout(() => input.focus(), 0);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
getGui() {
|
|
181
|
+
return this.eGui;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
getValue() {
|
|
185
|
+
return this.currentValue;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
destroy() {
|
|
189
|
+
if (this.root) {
|
|
190
|
+
this.root.unmount();
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
isPopup(): boolean {
|
|
195
|
+
// Return true for complex widgets that need more space
|
|
196
|
+
const popupTypes = ['date', 'datetime', 'select', 'lookup', 'color'];
|
|
197
|
+
return popupTypes.includes(this.params.field.type);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* Factory function to create cell renderer with field metadata
|
|
203
|
+
*/
|
|
204
|
+
export function createFieldCellRenderer(field: FieldMetadata) {
|
|
205
|
+
return class extends FieldWidgetCellRenderer {
|
|
206
|
+
init(params: ICellRendererParams) {
|
|
207
|
+
super.init({ ...params, field });
|
|
208
|
+
}
|
|
209
|
+
refresh(params: ICellRendererParams): boolean {
|
|
210
|
+
return super.refresh({ ...params, field });
|
|
211
|
+
}
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* Factory function to create cell editor with field metadata
|
|
217
|
+
*/
|
|
218
|
+
export function createFieldCellEditor(field: FieldMetadata) {
|
|
219
|
+
return class extends FieldWidgetCellEditor {
|
|
220
|
+
init(params: ICellEditorParams) {
|
|
221
|
+
super.init({ ...params, field });
|
|
222
|
+
}
|
|
223
|
+
};
|
|
224
|
+
}
|
package/src/index.test.ts
CHANGED
|
@@ -13,7 +13,7 @@ describe('Plugin AgGrid', () => {
|
|
|
13
13
|
// Import all renderers to register them
|
|
14
14
|
beforeAll(async () => {
|
|
15
15
|
await import('./index');
|
|
16
|
-
});
|
|
16
|
+
}, 15000); // Increase timeout to 15 seconds for async import
|
|
17
17
|
|
|
18
18
|
describe('aggrid component', () => {
|
|
19
19
|
it('should be registered in ComponentRegistry', () => {
|