@memberjunction/react-runtime 2.75.0 → 2.76.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 +1 -1
- package/CHANGELOG.md +13 -0
- package/dist/compiler/component-compiler.d.ts +0 -1
- package/dist/compiler/component-compiler.d.ts.map +1 -1
- package/dist/compiler/component-compiler.js +34 -25
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -2
- package/dist/runtime/index.d.ts +1 -1
- package/dist/runtime/index.d.ts.map +1 -1
- package/dist/runtime/index.js +1 -2
- package/dist/runtime/prop-builder.d.ts +1 -2
- package/dist/runtime/prop-builder.d.ts.map +1 -1
- package/dist/runtime/prop-builder.js +4 -76
- package/dist/types/index.d.ts +1 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/utilities/core-libraries.d.ts +5 -0
- package/dist/utilities/core-libraries.d.ts.map +1 -0
- package/dist/utilities/core-libraries.js +52 -0
- package/dist/utilities/library-loader.d.ts +2 -2
- package/dist/utilities/library-loader.d.ts.map +1 -1
- package/dist/utilities/library-loader.js +27 -16
- package/package.json +4 -4
- package/samples/entities-1.js +493 -0
- package/src/compiler/component-compiler.ts +64 -35
- package/src/index.ts +0 -1
- package/src/runtime/index.ts +0 -1
- package/src/runtime/prop-builder.ts +5 -112
- package/src/types/index.ts +2 -0
- package/src/utilities/core-libraries.ts +61 -0
- package/src/utilities/library-loader.ts +45 -26
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -0,0 +1,493 @@
|
|
|
1
|
+
function EntityFieldSummaryTable({ data, utilities, styles, components, onStateChanged, filters, sortBy, sortDirection }) {
|
|
2
|
+
const { TableComponent, FiltersPanel } = components;
|
|
3
|
+
|
|
4
|
+
const handleSort = (column, direction) => {
|
|
5
|
+
onStateChanged?.({ sortBy: column, sortDirection: direction });
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
const handleFiltersChange = (updatedFilters) => {
|
|
9
|
+
onStateChanged?.({ filters: updatedFilters });
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
const columns = [
|
|
13
|
+
{
|
|
14
|
+
key: 'EntityName',
|
|
15
|
+
title: 'Entity Name',
|
|
16
|
+
dataIndex: 'EntityName',
|
|
17
|
+
width: 200,
|
|
18
|
+
ellipsis: true,
|
|
19
|
+
sortable: true,
|
|
20
|
+
render: (text) => (
|
|
21
|
+
<div style={{
|
|
22
|
+
maxWidth: '200px',
|
|
23
|
+
whiteSpace: 'nowrap',
|
|
24
|
+
overflow: 'hidden',
|
|
25
|
+
textOverflow: 'ellipsis',
|
|
26
|
+
cursor: 'pointer',
|
|
27
|
+
color: styles.colors.primary
|
|
28
|
+
}} title={text}>
|
|
29
|
+
{text}
|
|
30
|
+
</div>
|
|
31
|
+
)
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
key: 'EntityDescription',
|
|
35
|
+
title: 'Description',
|
|
36
|
+
dataIndex: 'EntityDescription',
|
|
37
|
+
width: 300,
|
|
38
|
+
ellipsis: true,
|
|
39
|
+
sortable: false,
|
|
40
|
+
render: (text) => (
|
|
41
|
+
<div style={{
|
|
42
|
+
maxWidth: '300px',
|
|
43
|
+
whiteSpace: 'nowrap',
|
|
44
|
+
overflow: 'hidden',
|
|
45
|
+
textOverflow: 'ellipsis',
|
|
46
|
+
color: styles.colors.textSecondary
|
|
47
|
+
}} title={text}>
|
|
48
|
+
{text || 'No description'}
|
|
49
|
+
</div>
|
|
50
|
+
)
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
key: 'TotalFields',
|
|
54
|
+
title: 'Total Fields',
|
|
55
|
+
dataIndex: 'TotalFields',
|
|
56
|
+
width: 100,
|
|
57
|
+
sortable: true,
|
|
58
|
+
align: 'right',
|
|
59
|
+
render: (value) => (
|
|
60
|
+
<span style={{
|
|
61
|
+
fontWeight: styles.typography.fontWeight?.medium || '500',
|
|
62
|
+
color: styles.colors.text
|
|
63
|
+
}}>
|
|
64
|
+
{value || 0}
|
|
65
|
+
</span>
|
|
66
|
+
)
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
key: 'TextFields',
|
|
70
|
+
title: 'Text',
|
|
71
|
+
dataIndex: 'TextFields',
|
|
72
|
+
width: 80,
|
|
73
|
+
sortable: true,
|
|
74
|
+
align: 'right',
|
|
75
|
+
render: (value) => (
|
|
76
|
+
<span style={{ color: styles.colors.info }}>
|
|
77
|
+
{value || 0}
|
|
78
|
+
</span>
|
|
79
|
+
)
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
key: 'NumberFields',
|
|
83
|
+
title: 'Number',
|
|
84
|
+
dataIndex: 'NumberFields',
|
|
85
|
+
width: 80,
|
|
86
|
+
sortable: true,
|
|
87
|
+
align: 'right',
|
|
88
|
+
render: (value) => (
|
|
89
|
+
<span style={{ color: styles.colors.success }}>
|
|
90
|
+
{value || 0}
|
|
91
|
+
</span>
|
|
92
|
+
)
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
key: 'DateFields',
|
|
96
|
+
title: 'Date',
|
|
97
|
+
dataIndex: 'DateFields',
|
|
98
|
+
width: 80,
|
|
99
|
+
sortable: true,
|
|
100
|
+
align: 'right',
|
|
101
|
+
render: (value) => (
|
|
102
|
+
<span style={{ color: styles.colors.warning }}>
|
|
103
|
+
{value || 0}
|
|
104
|
+
</span>
|
|
105
|
+
)
|
|
106
|
+
},
|
|
107
|
+
{
|
|
108
|
+
key: 'BooleanFields',
|
|
109
|
+
title: 'Boolean',
|
|
110
|
+
dataIndex: 'BooleanFields',
|
|
111
|
+
width: 80,
|
|
112
|
+
sortable: true,
|
|
113
|
+
align: 'right',
|
|
114
|
+
render: (value) => (
|
|
115
|
+
<span style={{ color: styles.colors.error }}>
|
|
116
|
+
{value || 0}
|
|
117
|
+
</span>
|
|
118
|
+
)
|
|
119
|
+
},
|
|
120
|
+
{
|
|
121
|
+
key: 'GuidFields',
|
|
122
|
+
title: 'GUID',
|
|
123
|
+
dataIndex: 'GuidFields',
|
|
124
|
+
width: 80,
|
|
125
|
+
sortable: true,
|
|
126
|
+
align: 'right',
|
|
127
|
+
render: (value) => (
|
|
128
|
+
<span style={{ color: styles.colors.textSecondary }}>
|
|
129
|
+
{value || 0}
|
|
130
|
+
</span>
|
|
131
|
+
)
|
|
132
|
+
},
|
|
133
|
+
{
|
|
134
|
+
key: 'BinaryFields',
|
|
135
|
+
title: 'Binary',
|
|
136
|
+
dataIndex: 'BinaryFields',
|
|
137
|
+
width: 80,
|
|
138
|
+
sortable: true,
|
|
139
|
+
align: 'right',
|
|
140
|
+
render: (value) => (
|
|
141
|
+
<span style={{ color: styles.colors.textTertiary }}>
|
|
142
|
+
{value || 0}
|
|
143
|
+
</span>
|
|
144
|
+
)
|
|
145
|
+
},
|
|
146
|
+
{
|
|
147
|
+
key: 'EntityRowCount',
|
|
148
|
+
title: 'Rows',
|
|
149
|
+
dataIndex: 'EntityRowCount',
|
|
150
|
+
width: 100,
|
|
151
|
+
sortable: true,
|
|
152
|
+
align: 'right',
|
|
153
|
+
render: (value) => (
|
|
154
|
+
<span style={{ fontSize: styles.typography.fontSize.sm }}>
|
|
155
|
+
{value?.toLocaleString() || '0'}
|
|
156
|
+
</span>
|
|
157
|
+
)
|
|
158
|
+
},
|
|
159
|
+
{
|
|
160
|
+
key: 'EntityStatus',
|
|
161
|
+
title: 'Status',
|
|
162
|
+
dataIndex: 'EntityStatus',
|
|
163
|
+
width: 100,
|
|
164
|
+
sortable: true,
|
|
165
|
+
render: (value) => (
|
|
166
|
+
<span style={{
|
|
167
|
+
padding: '4px 8px',
|
|
168
|
+
borderRadius: styles.borders.radius,
|
|
169
|
+
fontSize: styles.typography.fontSize.xs,
|
|
170
|
+
backgroundColor: value === 'Active' ? styles.colors.successLight :
|
|
171
|
+
value === 'Deprecated' ? styles.colors.warningLight :
|
|
172
|
+
styles.colors.errorLight,
|
|
173
|
+
color: value === 'Active' ? styles.colors.success :
|
|
174
|
+
value === 'Deprecated' ? styles.colors.warning :
|
|
175
|
+
styles.colors.error
|
|
176
|
+
}}>
|
|
177
|
+
{value}
|
|
178
|
+
</span>
|
|
179
|
+
)
|
|
180
|
+
},
|
|
181
|
+
{
|
|
182
|
+
key: 'LastFieldUpdate',
|
|
183
|
+
title: 'Last Updated',
|
|
184
|
+
dataIndex: 'LastFieldUpdate',
|
|
185
|
+
width: 150,
|
|
186
|
+
sortable: true,
|
|
187
|
+
render: (value) => (
|
|
188
|
+
<div title={value ? new Date(value).toLocaleString() : ''}>
|
|
189
|
+
<span style={{
|
|
190
|
+
fontSize: styles.typography.fontSize.sm,
|
|
191
|
+
color: styles.colors.textSecondary
|
|
192
|
+
}}>
|
|
193
|
+
{value ? new Date(value).toLocaleDateString() : 'Never'}
|
|
194
|
+
</span>
|
|
195
|
+
</div>
|
|
196
|
+
)
|
|
197
|
+
}
|
|
198
|
+
];
|
|
199
|
+
|
|
200
|
+
const containerStyle = {
|
|
201
|
+
display: 'flex',
|
|
202
|
+
flexDirection: 'column',
|
|
203
|
+
height: '100%',
|
|
204
|
+
backgroundColor: styles.colors.background,
|
|
205
|
+
fontFamily: styles.typography.fontFamily
|
|
206
|
+
};
|
|
207
|
+
|
|
208
|
+
const filterContainerStyle = {
|
|
209
|
+
padding: styles.spacing.md,
|
|
210
|
+
borderBottom: `1px solid ${styles.colors.border}`,
|
|
211
|
+
backgroundColor: styles.colors.surface
|
|
212
|
+
};
|
|
213
|
+
|
|
214
|
+
const tableContainerStyle = {
|
|
215
|
+
flex: 1,
|
|
216
|
+
overflow: 'auto',
|
|
217
|
+
padding: styles.spacing.md
|
|
218
|
+
};
|
|
219
|
+
|
|
220
|
+
return (
|
|
221
|
+
<div style={containerStyle}>
|
|
222
|
+
<div style={filterContainerStyle}>
|
|
223
|
+
<FiltersPanel
|
|
224
|
+
searchTerm={filters?.searchTerm || ''}
|
|
225
|
+
minFields={filters?.minFields || 0}
|
|
226
|
+
maxFields={filters?.maxFields || 9999}
|
|
227
|
+
selectedStatuses={filters?.selectedStatuses || ['Active']}
|
|
228
|
+
typeFilters={filters?.typeFilters || {}}
|
|
229
|
+
onFiltersChange={handleFiltersChange}
|
|
230
|
+
styles={styles}
|
|
231
|
+
utilities={utilities}
|
|
232
|
+
components={components}
|
|
233
|
+
/>
|
|
234
|
+
</div>
|
|
235
|
+
<div style={tableContainerStyle}>
|
|
236
|
+
<TableComponent
|
|
237
|
+
columns={columns}
|
|
238
|
+
data={data || []}
|
|
239
|
+
sortColumn={sortBy}
|
|
240
|
+
sortDirection={sortDirection}
|
|
241
|
+
onSort={handleSort}
|
|
242
|
+
styles={styles}
|
|
243
|
+
utilities={utilities}
|
|
244
|
+
components={components}
|
|
245
|
+
/>
|
|
246
|
+
</div>
|
|
247
|
+
</div>
|
|
248
|
+
);
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
/*******************************************************
|
|
252
|
+
TableComponent
|
|
253
|
+
undefined
|
|
254
|
+
*******************************************************/
|
|
255
|
+
function TableComponent({ columns = [], data = [], sortColumn, sortDirection, styles = {}, callbacks = {}, ...userState }) {
|
|
256
|
+
const updateState = (newState) => {
|
|
257
|
+
callbacks?.UpdateUserState?.({ ...userState, ...newState });
|
|
258
|
+
};
|
|
259
|
+
|
|
260
|
+
const handleSort = (columnName) => {
|
|
261
|
+
const newDirection = sortColumn === columnName && sortDirection === 'asc' ? 'desc' : 'asc';
|
|
262
|
+
updateState({ sortColumn: columnName, sortDirection: newDirection });
|
|
263
|
+
};
|
|
264
|
+
|
|
265
|
+
const containerStyle = {
|
|
266
|
+
height: '100%',
|
|
267
|
+
overflow: 'auto',
|
|
268
|
+
backgroundColor: styles.colors?.background || '#ffffff',
|
|
269
|
+
border: `1px solid ${styles.colors?.border || '#e0e0e0'}`,
|
|
270
|
+
borderRadius: styles.borders?.radius || '4px',
|
|
271
|
+
fontFamily: styles.typography?.fontFamily || 'Arial, sans-serif'
|
|
272
|
+
};
|
|
273
|
+
|
|
274
|
+
const tableStyle = {
|
|
275
|
+
width: '100%',
|
|
276
|
+
borderCollapse: 'collapse',
|
|
277
|
+
fontSize: styles.typography?.fontSize?.md || '14px'
|
|
278
|
+
};
|
|
279
|
+
|
|
280
|
+
const headerStyle = {
|
|
281
|
+
backgroundColor: styles.colors?.surface || '#f5f5f5',
|
|
282
|
+
borderBottom: `1px solid ${styles.colors?.border || '#e0e0e0'}`,
|
|
283
|
+
padding: styles.spacing?.md || '12px',
|
|
284
|
+
textAlign: 'left',
|
|
285
|
+
fontWeight: styles.typography?.fontWeight?.semibold || '600',
|
|
286
|
+
color: styles.colors?.text || '#333333',
|
|
287
|
+
cursor: 'pointer',
|
|
288
|
+
userSelect: 'none'
|
|
289
|
+
};
|
|
290
|
+
|
|
291
|
+
const cellStyle = {
|
|
292
|
+
padding: styles.spacing?.md || '12px',
|
|
293
|
+
borderBottom: `1px solid ${styles.colors?.borderLight || styles.colors?.border || '#e0e0e0'}`,
|
|
294
|
+
color: styles.colors?.textSecondary || '#666666'
|
|
295
|
+
};
|
|
296
|
+
|
|
297
|
+
const sortIconStyle = {
|
|
298
|
+
marginLeft: styles.spacing?.xs || '4px',
|
|
299
|
+
fontSize: styles.typography?.fontSize?.sm || '12px'
|
|
300
|
+
};
|
|
301
|
+
|
|
302
|
+
return (
|
|
303
|
+
<div style={containerStyle}>
|
|
304
|
+
<table style={tableStyle}>
|
|
305
|
+
<thead>
|
|
306
|
+
<tr>
|
|
307
|
+
{columns.map(column => (
|
|
308
|
+
<th
|
|
309
|
+
key={column.field}
|
|
310
|
+
style={{
|
|
311
|
+
...headerStyle,
|
|
312
|
+
...(sortColumn === column.field
|
|
313
|
+
? { backgroundColor: styles.colors?.primaryLight || styles.colors?.primary || '#e3f2fd' }
|
|
314
|
+
: {})
|
|
315
|
+
}}
|
|
316
|
+
onClick={() => handleSort(column.field)}
|
|
317
|
+
>
|
|
318
|
+
{column.title}
|
|
319
|
+
{sortColumn === column.field && (
|
|
320
|
+
<span style={sortIconStyle}>
|
|
321
|
+
{sortDirection === 'asc' ? '↑' : '↓'}
|
|
322
|
+
</span>
|
|
323
|
+
)}
|
|
324
|
+
</th>
|
|
325
|
+
))}
|
|
326
|
+
</tr>
|
|
327
|
+
</thead>
|
|
328
|
+
<tbody>
|
|
329
|
+
{data.map((row, index) => (
|
|
330
|
+
<tr key={index}>
|
|
331
|
+
{columns.map(column => (
|
|
332
|
+
<td
|
|
333
|
+
key={`${index}-${column.field}`}
|
|
334
|
+
style={cellStyle}
|
|
335
|
+
>
|
|
336
|
+
{row[column.field] || '-'}
|
|
337
|
+
</td>
|
|
338
|
+
))}
|
|
339
|
+
</tr>
|
|
340
|
+
))}
|
|
341
|
+
</tbody>
|
|
342
|
+
</table>
|
|
343
|
+
</div>
|
|
344
|
+
);
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
/*******************************************************
|
|
348
|
+
FiltersPanel
|
|
349
|
+
undefined
|
|
350
|
+
*******************************************************/
|
|
351
|
+
function FiltersPanel({ data, utilities, styles, components, searchTerm, minFields, maxFields, selectedStatuses, typeFilters, onStateChanged }) {
|
|
352
|
+
const handleSearchChange = (e) => {
|
|
353
|
+
onStateChanged?.({ searchTerm: e.target.value });
|
|
354
|
+
};
|
|
355
|
+
|
|
356
|
+
const handleMinFieldsChange = (e) => {
|
|
357
|
+
const value = parseInt(e.target.value) || 0;
|
|
358
|
+
onStateChanged?.({ minFields: value });
|
|
359
|
+
};
|
|
360
|
+
|
|
361
|
+
const handleMaxFieldsChange = (e) => {
|
|
362
|
+
const value = parseInt(e.target.value) || 0;
|
|
363
|
+
onStateChanged?.({ maxFields: value });
|
|
364
|
+
};
|
|
365
|
+
|
|
366
|
+
const handleStatusToggle = (status) => {
|
|
367
|
+
const updatedStatuses = selectedStatuses.includes(status)
|
|
368
|
+
? selectedStatuses.filter(s => s !== status)
|
|
369
|
+
: [...selectedStatuses, status];
|
|
370
|
+
onStateChanged?.({ selectedStatuses: updatedStatuses });
|
|
371
|
+
};
|
|
372
|
+
|
|
373
|
+
const handleTypeChange = (typeName, value) => {
|
|
374
|
+
const updatedTypeFilters = { ...typeFilters, [typeName]: value };
|
|
375
|
+
onStateChanged?.({ typeFilters: updatedTypeFilters });
|
|
376
|
+
};
|
|
377
|
+
|
|
378
|
+
const containerStyle = {
|
|
379
|
+
padding: styles.spacing.md,
|
|
380
|
+
backgroundColor: styles.colors.surface,
|
|
381
|
+
border: `1px solid ${styles.colors.border}`,
|
|
382
|
+
borderRadius: styles.borders.radius,
|
|
383
|
+
height: '100%',
|
|
384
|
+
overflow: 'auto'
|
|
385
|
+
};
|
|
386
|
+
|
|
387
|
+
const inputStyle = {
|
|
388
|
+
width: '100%',
|
|
389
|
+
padding: styles.spacing.sm,
|
|
390
|
+
border: `1px solid ${styles.colors.border}`,
|
|
391
|
+
borderRadius: styles.borders.radius,
|
|
392
|
+
fontSize: styles.typography.fontSize.sm,
|
|
393
|
+
marginBottom: styles.spacing.sm
|
|
394
|
+
};
|
|
395
|
+
|
|
396
|
+
const labelStyle = {
|
|
397
|
+
display: 'block',
|
|
398
|
+
marginBottom: styles.spacing.xs,
|
|
399
|
+
fontSize: styles.typography.fontSize.sm,
|
|
400
|
+
color: styles.colors.textSecondary,
|
|
401
|
+
fontWeight: styles.typography.fontWeight?.medium || '500'
|
|
402
|
+
};
|
|
403
|
+
|
|
404
|
+
const statusButtonStyle = (isActive) => ({
|
|
405
|
+
padding: `${styles.spacing.xs} ${styles.spacing.sm}`,
|
|
406
|
+
margin: `0 ${styles.spacing.xs} ${styles.spacing.xs} 0`,
|
|
407
|
+
border: `1px solid ${isActive ? styles.colors.primary : styles.colors.border}`,
|
|
408
|
+
borderRadius: styles.borders.radius,
|
|
409
|
+
backgroundColor: isActive ? styles.colors.primary : 'transparent',
|
|
410
|
+
color: isActive ? styles.colors.textInverse || styles.colors.background : styles.colors.text,
|
|
411
|
+
cursor: 'pointer',
|
|
412
|
+
fontSize: styles.typography.fontSize.xs
|
|
413
|
+
});
|
|
414
|
+
|
|
415
|
+
const numberInputStyle = {
|
|
416
|
+
width: '80px',
|
|
417
|
+
padding: styles.spacing.xs,
|
|
418
|
+
border: `1px solid ${styles.colors.border}`,
|
|
419
|
+
borderRadius: styles.borders.radius,
|
|
420
|
+
fontSize: styles.typography.fontSize.sm,
|
|
421
|
+
margin: `0 ${styles.spacing.sm}`
|
|
422
|
+
};
|
|
423
|
+
|
|
424
|
+
return (
|
|
425
|
+
<div style={containerStyle}>
|
|
426
|
+
<div style={{ marginBottom: styles.spacing.lg }}>
|
|
427
|
+
<label style={labelStyle}>Entity Name Search</label>
|
|
428
|
+
<input
|
|
429
|
+
type="text"
|
|
430
|
+
value={searchTerm || ''}
|
|
431
|
+
onChange={handleSearchChange}
|
|
432
|
+
placeholder="Search entities..."
|
|
433
|
+
style={inputStyle}
|
|
434
|
+
/>
|
|
435
|
+
</div>
|
|
436
|
+
|
|
437
|
+
<div style={{ marginBottom: styles.spacing.lg }}>
|
|
438
|
+
<label style={labelStyle}>Field Count Range</label>
|
|
439
|
+
<div style={{ display: 'flex', alignItems: 'center', gap: styles.spacing.sm }}>
|
|
440
|
+
<span>Min:</span>
|
|
441
|
+
<input
|
|
442
|
+
type="number"
|
|
443
|
+
value={minFields || 0}
|
|
444
|
+
onChange={handleMinFieldsChange}
|
|
445
|
+
min="0"
|
|
446
|
+
style={numberInputStyle}
|
|
447
|
+
/>
|
|
448
|
+
<span>Max:</span>
|
|
449
|
+
<input
|
|
450
|
+
type="number"
|
|
451
|
+
value={maxFields || 0}
|
|
452
|
+
onChange={handleMaxFieldsChange}
|
|
453
|
+
min="0"
|
|
454
|
+
style={numberInputStyle}
|
|
455
|
+
/>
|
|
456
|
+
</div>
|
|
457
|
+
</div>
|
|
458
|
+
|
|
459
|
+
<div style={{ marginBottom: styles.spacing.lg }}>
|
|
460
|
+
<label style={labelStyle}>Status Filters</label>
|
|
461
|
+
<div>
|
|
462
|
+
{['Active', 'Deprecated', 'Disabled'].map(status => (
|
|
463
|
+
<button
|
|
464
|
+
key={status}
|
|
465
|
+
onClick={() => handleStatusToggle(status)}
|
|
466
|
+
style={statusButtonStyle(selectedStatuses.includes(status))}
|
|
467
|
+
>
|
|
468
|
+
{status}
|
|
469
|
+
</button>
|
|
470
|
+
))}
|
|
471
|
+
</div>
|
|
472
|
+
</div>
|
|
473
|
+
|
|
474
|
+
<div>
|
|
475
|
+
<label style={labelStyle}>Type Distribution Threshold</label>
|
|
476
|
+
{Object.entries(typeFilters || {}).map(([typeName, currentValue]) => (
|
|
477
|
+
<div key={typeName} style={{ marginBottom: styles.spacing.sm }}>
|
|
478
|
+
<label style={{ ...labelStyle, fontSize: styles.typography.fontSize.xs }}>
|
|
479
|
+
{typeName}:
|
|
480
|
+
</label>
|
|
481
|
+
<input
|
|
482
|
+
type="number"
|
|
483
|
+
value={currentValue || 0}
|
|
484
|
+
onChange={(e) => handleTypeChange(typeName, parseInt(e.target.value) || 0)}
|
|
485
|
+
min="0"
|
|
486
|
+
style={numberInputStyle}
|
|
487
|
+
/>
|
|
488
|
+
</div>
|
|
489
|
+
))}
|
|
490
|
+
</div>
|
|
491
|
+
</div>
|
|
492
|
+
);
|
|
493
|
+
}
|
|
@@ -161,7 +161,11 @@ export class ComponentCompiler {
|
|
|
161
161
|
*/
|
|
162
162
|
private wrapComponentCode(componentCode: string, componentName: string): string {
|
|
163
163
|
return `
|
|
164
|
-
function createComponent(
|
|
164
|
+
function createComponent(
|
|
165
|
+
React, ReactDOM,
|
|
166
|
+
useState, useEffect, useCallback, useMemo, useRef, useContext, useReducer, useLayoutEffect,
|
|
167
|
+
libraries, styles, console
|
|
168
|
+
) {
|
|
165
169
|
${componentCode}
|
|
166
170
|
|
|
167
171
|
// Ensure the component exists
|
|
@@ -193,19 +197,17 @@ export class ComponentCompiler {
|
|
|
193
197
|
*/
|
|
194
198
|
private createComponentFactory(transpiledCode: string, componentName: string): Function {
|
|
195
199
|
try {
|
|
196
|
-
// Create the factory function
|
|
200
|
+
// Create the factory function with all React hooks
|
|
197
201
|
const factoryCreator = new Function(
|
|
198
|
-
'React', 'ReactDOM',
|
|
199
|
-
'
|
|
202
|
+
'React', 'ReactDOM',
|
|
203
|
+
'useState', 'useEffect', 'useCallback', 'useMemo', 'useRef', 'useContext', 'useReducer', 'useLayoutEffect',
|
|
204
|
+
'libraries', 'styles', 'console',
|
|
200
205
|
`${transpiledCode}; return createComponent;`
|
|
201
206
|
);
|
|
202
207
|
|
|
203
208
|
// Return a function that executes the factory with runtime context
|
|
204
209
|
return (context: RuntimeContext, styles: any = {}) => {
|
|
205
210
|
const { React, ReactDOM, libraries = {} } = context;
|
|
206
|
-
|
|
207
|
-
// Create state updater utility
|
|
208
|
-
const createStateUpdater = this.createStateUpdaterUtility();
|
|
209
211
|
|
|
210
212
|
// Execute the factory creator to get the createComponent function
|
|
211
213
|
const createComponentFn = factoryCreator(
|
|
@@ -214,7 +216,11 @@ export class ComponentCompiler {
|
|
|
214
216
|
React.useState,
|
|
215
217
|
React.useEffect,
|
|
216
218
|
React.useCallback,
|
|
217
|
-
|
|
219
|
+
React.useMemo,
|
|
220
|
+
React.useRef,
|
|
221
|
+
React.useContext,
|
|
222
|
+
React.useReducer,
|
|
223
|
+
React.useLayoutEffect,
|
|
218
224
|
libraries,
|
|
219
225
|
styles,
|
|
220
226
|
console
|
|
@@ -227,7 +233,11 @@ export class ComponentCompiler {
|
|
|
227
233
|
React.useState,
|
|
228
234
|
React.useEffect,
|
|
229
235
|
React.useCallback,
|
|
230
|
-
|
|
236
|
+
React.useMemo,
|
|
237
|
+
React.useRef,
|
|
238
|
+
React.useContext,
|
|
239
|
+
React.useReducer,
|
|
240
|
+
React.useLayoutEffect,
|
|
231
241
|
libraries,
|
|
232
242
|
styles,
|
|
233
243
|
console
|
|
@@ -238,28 +248,6 @@ export class ComponentCompiler {
|
|
|
238
248
|
}
|
|
239
249
|
}
|
|
240
250
|
|
|
241
|
-
/**
|
|
242
|
-
* Creates the state updater utility function for nested components
|
|
243
|
-
* @returns State updater function
|
|
244
|
-
*/
|
|
245
|
-
private createStateUpdaterUtility(): Function {
|
|
246
|
-
return (statePath: string, parentStateUpdater: Function) => {
|
|
247
|
-
return (componentStateUpdate: any) => {
|
|
248
|
-
if (!statePath) {
|
|
249
|
-
// Root component - pass through directly
|
|
250
|
-
parentStateUpdater(componentStateUpdate);
|
|
251
|
-
} else {
|
|
252
|
-
// Sub-component - bubble up with path context
|
|
253
|
-
const pathParts = statePath.split('.');
|
|
254
|
-
const componentKey = pathParts[pathParts.length - 1];
|
|
255
|
-
|
|
256
|
-
parentStateUpdater({
|
|
257
|
-
[componentKey]: componentStateUpdate
|
|
258
|
-
});
|
|
259
|
-
}
|
|
260
|
-
};
|
|
261
|
-
};
|
|
262
|
-
}
|
|
263
251
|
|
|
264
252
|
/**
|
|
265
253
|
* Validates compilation options
|
|
@@ -267,21 +255,62 @@ export class ComponentCompiler {
|
|
|
267
255
|
* @throws Error if validation fails
|
|
268
256
|
*/
|
|
269
257
|
private validateCompileOptions(options: CompileOptions): void {
|
|
258
|
+
// Check if options object exists
|
|
259
|
+
if (!options) {
|
|
260
|
+
throw new Error(
|
|
261
|
+
'Component compilation failed: No options provided.\n' +
|
|
262
|
+
'Expected an object with componentName and componentCode properties.\n' +
|
|
263
|
+
'Example: { componentName: "MyComponent", componentCode: "function MyComponent() { ... }" }'
|
|
264
|
+
);
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
// Check component name
|
|
270
268
|
if (!options.componentName) {
|
|
271
|
-
|
|
269
|
+
const providedKeys = Object.keys(options).join(', ');
|
|
270
|
+
throw new Error(
|
|
271
|
+
'Component compilation failed: Component name is required.\n' +
|
|
272
|
+
`Received options with keys: [${providedKeys}]\n` +
|
|
273
|
+
'Please ensure your component spec includes a "name" property.\n' +
|
|
274
|
+
'Example: { name: "MyComponent", code: "..." }'
|
|
275
|
+
);
|
|
272
276
|
}
|
|
273
277
|
|
|
278
|
+
// Check component code
|
|
274
279
|
if (!options.componentCode) {
|
|
275
|
-
throw new Error(
|
|
280
|
+
throw new Error(
|
|
281
|
+
`Component compilation failed: Component code is required for "${options.componentName}".\n` +
|
|
282
|
+
'Please ensure your component spec includes a "code" property with the component source code.\n' +
|
|
283
|
+
'Example: { name: "MyComponent", code: "function MyComponent() { return <div>Hello</div>; }" }'
|
|
284
|
+
);
|
|
276
285
|
}
|
|
277
286
|
|
|
287
|
+
// Check code type
|
|
278
288
|
if (typeof options.componentCode !== 'string') {
|
|
279
|
-
|
|
289
|
+
const actualType = typeof options.componentCode;
|
|
290
|
+
throw new Error(
|
|
291
|
+
`Component compilation failed: Component code must be a string for "${options.componentName}".\n` +
|
|
292
|
+
`Received type: ${actualType}\n` +
|
|
293
|
+
`Received value: ${JSON.stringify(options.componentCode).substring(0, 100)}...\n` +
|
|
294
|
+
'Please ensure the code property contains a string of JavaScript/JSX code.'
|
|
295
|
+
);
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
// Check if code is empty or whitespace only
|
|
299
|
+
if (options.componentCode.trim().length === 0) {
|
|
300
|
+
throw new Error(
|
|
301
|
+
`Component compilation failed: Component code is empty for "${options.componentName}".\n` +
|
|
302
|
+
'The code property must contain valid JavaScript/JSX code defining a React component.'
|
|
303
|
+
);
|
|
280
304
|
}
|
|
281
305
|
|
|
282
306
|
// Basic syntax check
|
|
283
307
|
if (!options.componentCode.includes(options.componentName)) {
|
|
284
|
-
throw new Error(
|
|
308
|
+
throw new Error(
|
|
309
|
+
`Component compilation failed: Component code must define a component named "${options.componentName}".\n` +
|
|
310
|
+
'The function/component name in the code must match the componentName property.\n' +
|
|
311
|
+
`Expected to find: function ${options.componentName}(...) or const ${options.componentName} = ...\n` +
|
|
312
|
+
'Code preview: ' + options.componentCode.substring(0, 200) + '...'
|
|
313
|
+
);
|
|
285
314
|
}
|
|
286
315
|
}
|
|
287
316
|
|
package/src/index.ts
CHANGED