@firecms/core 3.0.0-alpha.81 → 3.0.0-alpha.83
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/dist/components/EntityCollectionTable/EntityCollectionTableProps.d.ts +4 -3
- package/dist/components/EntityCollectionTable/column_utils.d.ts +2 -2
- package/dist/components/EntityCollectionTable/fields/TableReferenceField.d.ts +5 -1
- package/dist/components/EntityCollectionView/EntityCollectionView.d.ts +2 -2
- package/dist/components/EntityCollectionView/EntityCollectionViewActions.d.ts +1 -1
- package/dist/components/VirtualTable/VirtualTableProps.d.ts +1 -1
- package/dist/contexts/AnalyticsContext.d.ts +3 -0
- package/dist/contexts/CustomizationControllerContext.d.ts +3 -0
- package/dist/core/field_configs.d.ts +1 -1
- package/dist/hooks/index.d.ts +1 -0
- package/dist/hooks/useAnalyticsController.d.ts +5 -0
- package/dist/hooks/useCustomizationController.d.ts +11 -0
- package/dist/hooks/useFireCMSContext.d.ts +0 -1
- package/dist/index.es.js +4878 -4856
- package/dist/index.es.js.map +1 -1
- package/dist/index.umd.js +5 -5
- package/dist/index.umd.js.map +1 -1
- package/dist/internal/useBuildCustomizationController.d.ts +2 -0
- package/dist/types/analytics_controller.d.ts +7 -0
- package/dist/types/collections.d.ts +3 -0
- package/dist/types/customization_controller.d.ts +47 -0
- package/dist/types/entity_callbacks.d.ts +17 -3
- package/dist/types/firecms_context.d.ts +6 -46
- package/dist/types/index.d.ts +2 -0
- package/dist/util/icons.d.ts +4 -1
- package/dist/util/useTraceUpdate.d.ts +1 -1
- package/package.json +3 -3
- package/src/components/DeleteEntityDialog.tsx +9 -3
- package/src/components/EntityCollectionTable/EntityCollectionRowActions.tsx +1 -1
- package/src/components/EntityCollectionTable/EntityCollectionTable.tsx +33 -8
- package/src/components/EntityCollectionTable/EntityCollectionTableProps.tsx +5 -5
- package/src/components/EntityCollectionTable/PropertyTableCell.tsx +1 -1
- package/src/components/EntityCollectionTable/SimpleEntityCollectionTable.tsx +0 -2
- package/src/components/EntityCollectionTable/column_utils.tsx +3 -3
- package/src/components/EntityCollectionTable/fields/TableReferenceField.tsx +125 -122
- package/src/components/EntityCollectionTable/filters/DateTimeFilterField.tsx +2 -2
- package/src/components/EntityCollectionTable/internal/default_entity_actions.tsx +3 -3
- package/src/components/EntityCollectionTable/internal/popup_field/PopupFormField.tsx +4 -3
- package/src/components/EntityCollectionView/EntityCollectionView.tsx +77 -48
- package/src/components/EntityCollectionView/EntityCollectionViewActions.tsx +5 -3
- package/src/components/EntityPreview.tsx +11 -9
- package/src/components/HomePage/DefaultHomePage.tsx +9 -8
- package/src/components/HomePage/NavigationCollectionCard.tsx +6 -5
- package/src/components/ReferenceSelectionInner.tsx +38 -36
- package/src/components/VirtualTable/VirtualTable.tsx +23 -20
- package/src/components/VirtualTable/VirtualTableCell.tsx +27 -9
- package/src/components/VirtualTable/VirtualTableHeader.tsx +1 -1
- package/src/components/VirtualTable/VirtualTableProps.tsx +2 -2
- package/src/components/VirtualTable/fields/VirtualTableDateField.tsx +2 -2
- package/src/contexts/AnalyticsContext.tsx +4 -0
- package/src/contexts/CustomizationControllerContext.tsx +4 -0
- package/src/core/Drawer.tsx +7 -6
- package/src/core/FireCMS.tsx +39 -29
- package/src/form/EntityForm.tsx +16 -5
- package/src/form/PropertyFieldBinding.tsx +8 -10
- package/src/form/components/CustomIdField.tsx +7 -5
- package/src/form/field_bindings/ArrayCustomShapedFieldBinding.tsx +1 -2
- package/src/form/field_bindings/DateTimeFieldBinding.tsx +2 -2
- package/src/form/field_bindings/KeyValueFieldBinding.tsx +4 -6
- package/src/hooks/data/save.ts +8 -3
- package/src/hooks/index.tsx +1 -0
- package/src/hooks/useAnalyticsController.tsx +8 -0
- package/src/hooks/useCustomizationController.tsx +14 -0
- package/src/hooks/useFireCMSContext.tsx +8 -7
- package/src/internal/EntityView.tsx +4 -2
- package/src/internal/useBuildCustomizationController.tsx +5 -0
- package/src/internal/useBuildSideDialogsController.tsx +0 -1
- package/src/preview/PropertyPreview.tsx +7 -4
- package/src/preview/components/DatePreview.tsx +4 -5
- package/src/preview/components/ReferencePreview.tsx +17 -8
- package/src/preview/property_previews/ArrayOfMapsPreview.tsx +3 -3
- package/src/preview/property_previews/ArrayOfReferencesPreview.tsx +3 -3
- package/src/preview/property_previews/ArrayOfStorageComponentsPreview.tsx +3 -3
- package/src/preview/property_previews/ArrayOfStringsPreview.tsx +4 -4
- package/src/preview/property_previews/ArrayOneOfPreview.tsx +3 -3
- package/src/preview/property_previews/ArrayPropertyPreview.tsx +3 -3
- package/src/types/analytics_controller.tsx +10 -0
- package/src/types/collections.ts +3 -0
- package/src/types/customization_controller.tsx +58 -0
- package/src/types/entity_callbacks.ts +22 -3
- package/src/types/firecms_context.tsx +6 -51
- package/src/types/index.ts +2 -0
- package/src/util/icons.tsx +22 -19
- package/src/util/useTraceUpdate.tsx +5 -5
|
@@ -38,6 +38,7 @@ import { ReferencePreview } from "../../preview";
|
|
|
38
38
|
import {
|
|
39
39
|
saveEntityWithCallbacks,
|
|
40
40
|
useAuthController,
|
|
41
|
+
useCustomizationController,
|
|
41
42
|
useDataSource,
|
|
42
43
|
useFireCMSContext,
|
|
43
44
|
useLargeLayout,
|
|
@@ -46,7 +47,9 @@ import {
|
|
|
46
47
|
} from "../../hooks";
|
|
47
48
|
import { useUserConfigurationPersistence } from "../../hooks/useUserConfigurationPersistence";
|
|
48
49
|
import { EntityCollectionViewActions } from "./EntityCollectionViewActions";
|
|
49
|
-
import {
|
|
50
|
+
import {
|
|
51
|
+
useDataSourceEntityCollectionTableController
|
|
52
|
+
} from "../EntityCollectionTable/useDataSourceEntityCollectionTableController";
|
|
50
53
|
import {
|
|
51
54
|
AddIcon,
|
|
52
55
|
Button,
|
|
@@ -72,6 +75,7 @@ import {
|
|
|
72
75
|
editEntityAction
|
|
73
76
|
} from "../EntityCollectionTable/internal/default_entity_actions";
|
|
74
77
|
import { DeleteEntityDialog } from "../DeleteEntityDialog";
|
|
78
|
+
import { useAnalyticsController } from "../../hooks/useAnalyticsController";
|
|
75
79
|
|
|
76
80
|
const COLLECTION_GROUP_PARENT_ID = "collectionGroupParent";
|
|
77
81
|
|
|
@@ -124,6 +128,8 @@ export const EntityCollectionView = React.memo(
|
|
|
124
128
|
const sideEntityController = useSideEntityController();
|
|
125
129
|
const authController = useAuthController();
|
|
126
130
|
const userConfigPersistence = useUserConfigurationPersistence();
|
|
131
|
+
const analyticsController = useAnalyticsController();
|
|
132
|
+
const customizationController = useCustomizationController();
|
|
127
133
|
const context = useFireCMSContext();
|
|
128
134
|
|
|
129
135
|
const containerRef = React.useRef<HTMLDivElement>(null);
|
|
@@ -133,6 +139,11 @@ export const EntityCollectionView = React.memo(
|
|
|
133
139
|
return (userOverride ? mergeDeep(collectionProp, userOverride) : collectionProp) as EntityCollection<M>;
|
|
134
140
|
}, [collectionProp, fullPath, userConfigPersistence?.getCollectionConfig]);
|
|
135
141
|
|
|
142
|
+
const collectionRef = React.useRef(collection);
|
|
143
|
+
useEffect(() => {
|
|
144
|
+
collectionRef.current = collection;
|
|
145
|
+
}, [collection]);
|
|
146
|
+
|
|
136
147
|
const canCreateEntities = canCreateEntity(collection, authController, fullPathToCollectionSegments(fullPath), null);
|
|
137
148
|
const [selectedNavigationEntity, setSelectedNavigationEntity] = useState<Entity<M> | undefined>(undefined);
|
|
138
149
|
const [deleteEntityClicked, setDeleteEntityClicked] = React.useState<Entity<M> | Entity<M>[] | undefined>(undefined);
|
|
@@ -152,11 +163,12 @@ export const EntityCollectionView = React.memo(
|
|
|
152
163
|
}, [selectedNavigationEntity]);
|
|
153
164
|
|
|
154
165
|
const checkInlineEditing = useCallback((entity?: Entity<any>): boolean => {
|
|
166
|
+
const collection = collectionRef.current;
|
|
155
167
|
if (!canEditEntity(collection, authController, fullPathToCollectionSegments(fullPath), entity ?? null)) {
|
|
156
168
|
return false;
|
|
157
169
|
}
|
|
158
170
|
return collection.inlineEditing === undefined || collection.inlineEditing;
|
|
159
|
-
}, [
|
|
171
|
+
}, [authController, fullPath]);
|
|
160
172
|
|
|
161
173
|
const selectionEnabled = collection.selectionEnabled === undefined || collection.selectionEnabled;
|
|
162
174
|
const hoverRow = !checkInlineEditing();
|
|
@@ -178,7 +190,7 @@ export const EntityCollectionView = React.memo(
|
|
|
178
190
|
|
|
179
191
|
const tableController = useDataSourceEntityCollectionTableController<M>({
|
|
180
192
|
fullPath,
|
|
181
|
-
collection,
|
|
193
|
+
collection: collectionRef.current,
|
|
182
194
|
entitiesDisplayedFirst: [],
|
|
183
195
|
lastDeleteTimestamp
|
|
184
196
|
});
|
|
@@ -191,8 +203,9 @@ export const EntityCollectionView = React.memo(
|
|
|
191
203
|
}, [tableController.setPopupCell]);
|
|
192
204
|
|
|
193
205
|
const onEntityClick = useCallback((clickedEntity: Entity<M>) => {
|
|
206
|
+
const collection = collectionRef.current;
|
|
194
207
|
setSelectedNavigationEntity(clickedEntity);
|
|
195
|
-
|
|
208
|
+
analyticsController.onAnalyticsEvent?.("edit_entity_clicked", {
|
|
196
209
|
path: clickedEntity.path,
|
|
197
210
|
entityId: clickedEntity.id
|
|
198
211
|
});
|
|
@@ -203,10 +216,12 @@ export const EntityCollectionView = React.memo(
|
|
|
203
216
|
updateUrl: true,
|
|
204
217
|
onClose: unselectNavigatedEntity
|
|
205
218
|
});
|
|
206
|
-
}, [
|
|
219
|
+
}, [unselectNavigatedEntity]);
|
|
207
220
|
|
|
208
221
|
const onNewClick = useCallback(() => {
|
|
209
|
-
|
|
222
|
+
|
|
223
|
+
const collection = collectionRef.current;
|
|
224
|
+
analyticsController.onAnalyticsEvent?.("new_entity_click", {
|
|
210
225
|
path: fullPath
|
|
211
226
|
});
|
|
212
227
|
sideEntityController.open({
|
|
@@ -215,38 +230,31 @@ export const EntityCollectionView = React.memo(
|
|
|
215
230
|
updateUrl: true,
|
|
216
231
|
onClose: unselectNavigatedEntity
|
|
217
232
|
});
|
|
218
|
-
}, [fullPath
|
|
219
|
-
|
|
220
|
-
const onSingleDeleteClick = useCallback((selectedEntity: Entity<any>) => {
|
|
221
|
-
context.onAnalyticsEvent?.("single_delete_dialog_open", {
|
|
222
|
-
path: fullPath
|
|
223
|
-
});
|
|
224
|
-
setDeleteEntityClicked(selectedEntity);
|
|
225
|
-
}, [context, fullPath]);
|
|
233
|
+
}, [fullPath]);
|
|
226
234
|
|
|
227
|
-
const onMultipleDeleteClick =
|
|
228
|
-
|
|
235
|
+
const onMultipleDeleteClick = () => {
|
|
236
|
+
analyticsController.onAnalyticsEvent?.("multiple_delete_dialog_open", {
|
|
229
237
|
path: fullPath
|
|
230
238
|
});
|
|
231
239
|
setDeleteEntityClicked(selectedEntities);
|
|
232
|
-
}
|
|
240
|
+
};
|
|
233
241
|
|
|
234
|
-
const internalOnEntityDelete =
|
|
235
|
-
|
|
242
|
+
const internalOnEntityDelete = (_path: string, entity: Entity<M>) => {
|
|
243
|
+
analyticsController.onAnalyticsEvent?.("single_entity_deleted", {
|
|
236
244
|
path: fullPath
|
|
237
245
|
});
|
|
238
246
|
setSelectedEntities((selectedEntities) => selectedEntities.filter((e) => e.id !== entity.id));
|
|
239
247
|
setLastDeleteTimestamp(Date.now());
|
|
240
|
-
}
|
|
248
|
+
};
|
|
241
249
|
|
|
242
|
-
const internalOnMultipleEntitiesDelete =
|
|
243
|
-
|
|
250
|
+
const internalOnMultipleEntitiesDelete = (_path: string, entities: Entity<M>[]) => {
|
|
251
|
+
analyticsController.onAnalyticsEvent?.("multiple_entities_deleted", {
|
|
244
252
|
path: fullPath
|
|
245
253
|
});
|
|
246
254
|
setSelectedEntities([]);
|
|
247
255
|
setDeleteEntityClicked(undefined);
|
|
248
256
|
setLastDeleteTimestamp(Date.now());
|
|
249
|
-
}
|
|
257
|
+
};
|
|
250
258
|
|
|
251
259
|
let AddColumnComponent: React.ComponentType<{
|
|
252
260
|
fullPath: string,
|
|
@@ -255,8 +263,8 @@ export const EntityCollectionView = React.memo(
|
|
|
255
263
|
}> | undefined
|
|
256
264
|
|
|
257
265
|
// we are only using the first plugin that implements this
|
|
258
|
-
if (
|
|
259
|
-
AddColumnComponent =
|
|
266
|
+
if (customizationController?.plugins) {
|
|
267
|
+
AddColumnComponent = customizationController.plugins.find(plugin => plugin.collectionView?.AddColumnComponent)?.collectionView?.AddColumnComponent;
|
|
260
268
|
}
|
|
261
269
|
|
|
262
270
|
const onCollectionModifiedForUser = useCallback((path: string, partialCollection: PartialEntityCollection<M>) => {
|
|
@@ -271,11 +279,13 @@ export const EntityCollectionView = React.memo(
|
|
|
271
279
|
width,
|
|
272
280
|
key
|
|
273
281
|
}: OnColumnResizeParams) => {
|
|
282
|
+
|
|
283
|
+
const collection = collectionRef.current;
|
|
274
284
|
// Only for property columns
|
|
275
285
|
if (!getPropertyInPath(collection.properties, key)) return;
|
|
276
286
|
const localCollection = buildPropertyWidthOverwrite(key, width);
|
|
277
287
|
onCollectionModifiedForUser(fullPath, localCollection);
|
|
278
|
-
}, [
|
|
288
|
+
}, [onCollectionModifiedForUser, fullPath]);
|
|
279
289
|
|
|
280
290
|
const onSizeChanged = useCallback((size: CollectionSize) => {
|
|
281
291
|
if (userConfigPersistence)
|
|
@@ -334,7 +344,7 @@ export const EntityCollectionView = React.memo(
|
|
|
334
344
|
const resolvedCollection = useMemo(() => resolveCollection<M>({
|
|
335
345
|
collection,
|
|
336
346
|
path: fullPath,
|
|
337
|
-
fields:
|
|
347
|
+
fields: customizationController.propertyConfigs
|
|
338
348
|
}), [collection, fullPath]);
|
|
339
349
|
|
|
340
350
|
const getPropertyFor = useCallback(({
|
|
@@ -357,9 +367,9 @@ export const EntityCollectionView = React.memo(
|
|
|
357
367
|
propertyValue,
|
|
358
368
|
values: entity.values,
|
|
359
369
|
entityId: entity.id,
|
|
360
|
-
fields:
|
|
370
|
+
fields: customizationController.propertyConfigs
|
|
361
371
|
});
|
|
362
|
-
}, [collection.properties,
|
|
372
|
+
}, [collection.properties, customizationController.propertyConfigs, fullPath, resolvedCollection.properties]);
|
|
363
373
|
|
|
364
374
|
const displayedColumnIds = useColumnIds(resolvedCollection, true);
|
|
365
375
|
|
|
@@ -448,7 +458,7 @@ export const EntityCollectionView = React.memo(
|
|
|
448
458
|
const uncollapsedActions = entityActions.filter(a => a.collapsed === false);
|
|
449
459
|
const actionsWidth = uncollapsedActions.length * (largeLayout ? 40 : 30);
|
|
450
460
|
return (largeLayout ? (80 + actionsWidth) : (70 + actionsWidth)) + (collapsedActions.length > 0 ? (largeLayout ? 40 : 30) : 0);
|
|
451
|
-
}, [largeLayout
|
|
461
|
+
}, [largeLayout]);
|
|
452
462
|
|
|
453
463
|
const tableRowActionsBuilder = useCallback(({
|
|
454
464
|
entity,
|
|
@@ -500,8 +510,7 @@ export const EntityCollectionView = React.memo(
|
|
|
500
510
|
setPopOverOpen(true);
|
|
501
511
|
e.stopPropagation();
|
|
502
512
|
}
|
|
503
|
-
: undefined}
|
|
504
|
-
>
|
|
513
|
+
: undefined}>
|
|
505
514
|
{`${collection.name}`}
|
|
506
515
|
</Typography>
|
|
507
516
|
|
|
@@ -522,19 +531,20 @@ export const EntityCollectionView = React.memo(
|
|
|
522
531
|
|
|
523
532
|
</Popover>;
|
|
524
533
|
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
534
|
+
const buildAdditionalHeaderWidget = useCallback(({
|
|
535
|
+
property,
|
|
536
|
+
propertyKey,
|
|
537
|
+
onHover
|
|
538
|
+
}: {
|
|
530
539
|
property: ResolvedProperty,
|
|
531
540
|
propertyKey: string,
|
|
532
541
|
onHover: boolean
|
|
533
|
-
}) {
|
|
534
|
-
|
|
542
|
+
}) => {
|
|
543
|
+
const collection = collectionRef.current;
|
|
544
|
+
if (!customizationController.plugins)
|
|
535
545
|
return null;
|
|
536
546
|
return <>
|
|
537
|
-
{
|
|
547
|
+
{customizationController.plugins.filter(plugin => plugin.collectionView?.HeaderAction)
|
|
538
548
|
.map((plugin, i) => {
|
|
539
549
|
const HeaderAction = plugin.collectionView!.HeaderAction!;
|
|
540
550
|
return <HeaderAction
|
|
@@ -547,7 +557,7 @@ export const EntityCollectionView = React.memo(
|
|
|
547
557
|
parentCollectionIds={parentCollectionIds ?? []}/>;
|
|
548
558
|
})}
|
|
549
559
|
</>;
|
|
550
|
-
}
|
|
560
|
+
}, [customizationController.plugins, fullPath, parentCollectionIds]);
|
|
551
561
|
|
|
552
562
|
const addColumnComponentInternal = AddColumnComponent
|
|
553
563
|
? function () {
|
|
@@ -562,13 +572,13 @@ export const EntityCollectionView = React.memo(
|
|
|
562
572
|
const [textSearchInitialised, setTextSearchInitialised] = useState<boolean>(false);
|
|
563
573
|
let onTextSearchClick: (() => void) | undefined;
|
|
564
574
|
let textSearchEnabled = Boolean(collection.textSearchEnabled);
|
|
565
|
-
if (
|
|
566
|
-
const addTextSearchClickListener =
|
|
575
|
+
if (customizationController?.plugins) {
|
|
576
|
+
const addTextSearchClickListener = customizationController.plugins?.find(p => Boolean(p.collectionView?.onTextSearchClick));
|
|
567
577
|
|
|
568
578
|
onTextSearchClick = addTextSearchClickListener
|
|
569
579
|
? () => {
|
|
570
580
|
setTextSearchLoading(true);
|
|
571
|
-
Promise.all(
|
|
581
|
+
Promise.all(customizationController.plugins?.map(p => {
|
|
572
582
|
if (p.collectionView?.onTextSearchClick)
|
|
573
583
|
return p.collectionView.onTextSearchClick({ context, path: resolvedFullPath, collection, parentCollectionIds });
|
|
574
584
|
return Promise.resolve(true);
|
|
@@ -580,7 +590,7 @@ export const EntityCollectionView = React.memo(
|
|
|
580
590
|
}
|
|
581
591
|
: undefined;
|
|
582
592
|
|
|
583
|
-
|
|
593
|
+
customizationController.plugins?.forEach(p => {
|
|
584
594
|
if (!textSearchEnabled)
|
|
585
595
|
if (p.collectionView?.showTextSearchBar) {
|
|
586
596
|
textSearchEnabled = p.collectionView.showTextSearchBar({ context, path: resolvedFullPath, collection, parentCollectionIds });
|
|
@@ -675,17 +685,36 @@ export const EntityCollectionView = React.memo(
|
|
|
675
685
|
|
|
676
686
|
</div>
|
|
677
687
|
);
|
|
678
|
-
},
|
|
679
|
-
|
|
680
|
-
|
|
688
|
+
}, (a, b) => {
|
|
689
|
+
return equal(a.fullPath, b.fullPath) &&
|
|
690
|
+
equal(a.parentCollectionIds, b.parentCollectionIds) &&
|
|
691
|
+
equal(a.isSubCollection, b.isSubCollection) &&
|
|
692
|
+
equal(a.className, b.className) &&
|
|
693
|
+
equal(a.properties, b.properties) &&
|
|
694
|
+
equal(a.hideIdFromCollection, b.hideIdFromCollection) &&
|
|
695
|
+
equal(a.inlineEditing, b.inlineEditing) &&
|
|
696
|
+
equal(a.selectionEnabled, b.selectionEnabled) &&
|
|
697
|
+
equal(a.selectionController, b.selectionController) &&
|
|
698
|
+
equal(a.Actions, b.Actions) &&
|
|
699
|
+
equal(a.defaultSize, b.defaultSize) &&
|
|
700
|
+
equal(a.textSearchEnabled, b.textSearchEnabled) &&
|
|
701
|
+
equal(a.additionalFields, b.additionalFields) &&
|
|
702
|
+
equal(a.forceFilter, b.forceFilter);
|
|
703
|
+
}) as React.FunctionComponent<EntityCollectionViewProps<any>>
|
|
704
|
+
|
|
705
|
+
export function useSelectionController<M extends Record<string, any> = any>(
|
|
706
|
+
onSelectionChange?: (entity: Entity<M>, selected: boolean) => void
|
|
707
|
+
): SelectionController<M> {
|
|
681
708
|
|
|
682
709
|
const [selectedEntities, setSelectedEntities] = useState<Entity<M>[]>([]);
|
|
683
710
|
|
|
684
711
|
const toggleEntitySelection = useCallback((entity: Entity<M>) => {
|
|
685
712
|
let newValue;
|
|
686
713
|
if (selectedEntities.map(e => e.id).includes(entity.id)) {
|
|
714
|
+
onSelectionChange?.(entity, false);
|
|
687
715
|
newValue = selectedEntities.filter((item: Entity<M>) => item.id !== entity.id);
|
|
688
716
|
} else {
|
|
717
|
+
onSelectionChange?.(entity, true);
|
|
689
718
|
newValue = [...selectedEntities, entity];
|
|
690
719
|
}
|
|
691
720
|
setSelectedEntities(newValue);
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
|
|
3
3
|
import { canCreateEntity, canDeleteEntity, fullPathToCollectionSegments } from "../../util";
|
|
4
|
-
import { useAuthController, useFireCMSContext, useLargeLayout } from "../../hooks";
|
|
5
|
-
import { CollectionActionsProps, EntityCollection,
|
|
4
|
+
import { useAuthController, useCustomizationController, useFireCMSContext, useLargeLayout } from "../../hooks";
|
|
5
|
+
import { CollectionActionsProps, EntityCollection, EntityTableController, SelectionController } from "../../types";
|
|
6
6
|
import { AddIcon, Button, DeleteIcon, IconButton, Tooltip } from "@firecms/ui";
|
|
7
7
|
import { toArray } from "../../util/arrays";
|
|
8
8
|
import { ErrorBoundary } from "../ErrorBoundary";
|
|
@@ -34,7 +34,9 @@ export function EntityCollectionViewActions<M extends Record<string, any>>({
|
|
|
34
34
|
}: EntityCollectionViewActionsProps<M>) {
|
|
35
35
|
|
|
36
36
|
const context = useFireCMSContext();
|
|
37
|
-
|
|
37
|
+
|
|
38
|
+
const customizationController = useCustomizationController();
|
|
39
|
+
const plugins = customizationController.plugins ?? [];
|
|
38
40
|
|
|
39
41
|
const authController = useAuthController();
|
|
40
42
|
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import React, { useMemo } from "react";
|
|
2
2
|
import { PropertyPreview } from "../preview";
|
|
3
|
-
import { Entity, EntityCollection,
|
|
3
|
+
import { Entity, EntityCollection, ResolvedEntityCollection, ResolvedProperties } from "../types";
|
|
4
4
|
import { resolveCollection } from "../util";
|
|
5
5
|
import { useFireCMSContext } from "../hooks";
|
|
6
6
|
import { cn, defaultBorderMixin, IconButton, OpenInNewIcon } from "@firecms/ui";
|
|
7
|
+
import { CustomizationController } from "../types/customization_controller";
|
|
8
|
+
import { useCustomizationController } from "../hooks/useCustomizationController";
|
|
7
9
|
|
|
8
10
|
/**
|
|
9
11
|
* @group Components
|
|
@@ -23,17 +25,15 @@ export function EntityPreview<M extends Record<string, any>>(
|
|
|
23
25
|
className
|
|
24
26
|
}: EntityPreviewProps<M>) {
|
|
25
27
|
|
|
26
|
-
const
|
|
28
|
+
const customizationController: CustomizationController = useCustomizationController();
|
|
27
29
|
const resolvedCollection: ResolvedEntityCollection<M> = useMemo(() => resolveCollection<M>({
|
|
28
30
|
collection,
|
|
29
31
|
path,
|
|
30
32
|
entityId: entity.id,
|
|
31
33
|
values: entity.values,
|
|
32
|
-
fields:
|
|
34
|
+
fields: customizationController.propertyConfigs
|
|
33
35
|
}), [collection, path, entity]);
|
|
34
36
|
|
|
35
|
-
const appConfig: FireCMSContext | undefined = useFireCMSContext();
|
|
36
|
-
|
|
37
37
|
const properties: ResolvedProperties = resolvedCollection.properties;
|
|
38
38
|
|
|
39
39
|
return (
|
|
@@ -43,10 +43,11 @@ export function EntityPreview<M extends Record<string, any>>(
|
|
|
43
43
|
<div className="flex items-center w-1/4">
|
|
44
44
|
<span className="pl-2 text-sm text-gray-600">Id</span>
|
|
45
45
|
</div>
|
|
46
|
-
<div
|
|
46
|
+
<div
|
|
47
|
+
className="flex-grow p-2 ml-2 w-3/4 text-gray-900 dark:text-white min-h-[56px] flex items-center">
|
|
47
48
|
<span className="flex-grow mr-2">{entity.id}</span>
|
|
48
|
-
{
|
|
49
|
-
<a href={
|
|
49
|
+
{customizationController?.entityLinkBuilder &&
|
|
50
|
+
<a href={customizationController.entityLinkBuilder({ entity })}
|
|
50
51
|
rel="noopener noreferrer"
|
|
51
52
|
target="_blank">
|
|
52
53
|
<IconButton>
|
|
@@ -66,7 +67,8 @@ export function EntityPreview<M extends Record<string, any>>(
|
|
|
66
67
|
<div className="flex items-center w-1/4">
|
|
67
68
|
<span className="pl-2 text-sm text-gray-600">{property.name}</span>
|
|
68
69
|
</div>
|
|
69
|
-
<div
|
|
70
|
+
<div
|
|
71
|
+
className="flex-grow p-2 ml-2 w-3/4 text-gray-900 dark:text-white min-h-[56px] flex items-center">
|
|
70
72
|
<PropertyPreview
|
|
71
73
|
propertyKey={key}
|
|
72
74
|
value={value}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import React, { useCallback, useEffect, useState } from "react";
|
|
2
2
|
|
|
3
|
-
import { useFireCMSContext, useNavigationController } from "../../hooks";
|
|
3
|
+
import { useCustomizationController, useFireCMSContext, useNavigationController } from "../../hooks";
|
|
4
4
|
import { PluginGenericProps, PluginHomePageAdditionalCardsProps } from "../../types";
|
|
5
5
|
|
|
6
6
|
import { toArray } from "../../util/arrays";
|
|
@@ -46,6 +46,7 @@ export function DefaultHomePage({
|
|
|
46
46
|
}) {
|
|
47
47
|
|
|
48
48
|
const context = useFireCMSContext();
|
|
49
|
+
const customizationController = useCustomizationController();
|
|
49
50
|
const navigationController = useNavigationController();
|
|
50
51
|
|
|
51
52
|
if (!navigationController.topLevelNavigation)
|
|
@@ -90,12 +91,12 @@ export function DefaultHomePage({
|
|
|
90
91
|
let additionalPluginChildrenStart: React.ReactNode | undefined;
|
|
91
92
|
let additionalPluginChildrenEnd: React.ReactNode | undefined;
|
|
92
93
|
let additionalPluginSections: React.ReactNode | undefined;
|
|
93
|
-
if (
|
|
94
|
+
if (customizationController.plugins) {
|
|
94
95
|
const sectionProps: PluginGenericProps = {
|
|
95
96
|
context
|
|
96
97
|
};
|
|
97
98
|
additionalPluginSections = <>
|
|
98
|
-
{
|
|
99
|
+
{customizationController.plugins.filter(plugin => plugin.homePage?.includeSection)
|
|
99
100
|
.map((plugin, i) => {
|
|
100
101
|
const section = plugin.homePage!.includeSection!(sectionProps)
|
|
101
102
|
return (
|
|
@@ -108,14 +109,14 @@ export function DefaultHomePage({
|
|
|
108
109
|
})}
|
|
109
110
|
</>;
|
|
110
111
|
additionalPluginChildrenStart = <div className={"flex flex-col gap-2"}>
|
|
111
|
-
{
|
|
112
|
+
{customizationController.plugins.filter(plugin => plugin.homePage?.additionalChildrenStart)
|
|
112
113
|
.map((plugin, i) => {
|
|
113
114
|
return <div key={`plugin_children_start_${i}`}>{plugin.homePage!.additionalChildrenStart}</div>;
|
|
114
115
|
})}
|
|
115
116
|
</div>;
|
|
116
117
|
|
|
117
118
|
additionalPluginChildrenEnd = <div className={"flex flex-col gap-2"}>
|
|
118
|
-
{
|
|
119
|
+
{customizationController.plugins.filter(plugin => plugin.homePage?.additionalChildrenEnd)
|
|
119
120
|
.map((plugin, i) => {
|
|
120
121
|
return <div key={`plugin_children_start_${i}`}>{plugin.homePage!.additionalChildrenEnd}</div>;
|
|
121
122
|
})}
|
|
@@ -152,8 +153,8 @@ export function DefaultHomePage({
|
|
|
152
153
|
context
|
|
153
154
|
};
|
|
154
155
|
|
|
155
|
-
if (
|
|
156
|
-
|
|
156
|
+
if (customizationController.plugins) {
|
|
157
|
+
customizationController.plugins.forEach(plugin => {
|
|
157
158
|
if (plugin.homePage?.AdditionalCards) {
|
|
158
159
|
AdditionalCards.push(...toArray(plugin.homePage?.AdditionalCards));
|
|
159
160
|
}
|
|
@@ -181,7 +182,7 @@ export function DefaultHomePage({
|
|
|
181
182
|
: entry.type === "view"
|
|
182
183
|
? "home_navigate_to_view"
|
|
183
184
|
: "unmapped_event";
|
|
184
|
-
context.onAnalyticsEvent?.(event, { path: entry.path });
|
|
185
|
+
context.analyticsController?.onAnalyticsEvent?.(event, { path: entry.path });
|
|
185
186
|
}}
|
|
186
187
|
/>
|
|
187
188
|
</div>
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { useNavigate } from "react-router-dom";
|
|
2
2
|
|
|
3
|
-
import { useFireCMSContext } from "../../hooks";
|
|
3
|
+
import { useCustomizationController, useFireCMSContext } from "../../hooks";
|
|
4
4
|
import { PluginHomePageActionsProps, TopNavigationEntry } from "../../types";
|
|
5
|
-
import {
|
|
5
|
+
import { IconForView } from "../../util";
|
|
6
6
|
import { useUserConfigurationPersistence } from "../../hooks/useUserConfigurationPersistence";
|
|
7
7
|
import { ArrowForwardIcon, Card, cn, IconButton, Markdown, StarBorderIcon, StarIcon, Typography } from "@firecms/ui";
|
|
8
8
|
|
|
@@ -32,22 +32,23 @@ export function NavigationCollectionCard({
|
|
|
32
32
|
}) {
|
|
33
33
|
|
|
34
34
|
const userConfigurationPersistence = useUserConfigurationPersistence();
|
|
35
|
-
const collectionIcon =
|
|
35
|
+
const collectionIcon = <IconForView collectionOrView={collection ?? view}/>;
|
|
36
36
|
|
|
37
37
|
const navigate = useNavigate();
|
|
38
38
|
const context = useFireCMSContext();
|
|
39
|
+
const customizationController = useCustomizationController();
|
|
39
40
|
|
|
40
41
|
const favourite = (userConfigurationPersistence?.favouritePaths ?? []).includes(path);
|
|
41
42
|
|
|
42
43
|
let actions: React.ReactNode | undefined;
|
|
43
|
-
if (
|
|
44
|
+
if (customizationController.plugins && collection) {
|
|
44
45
|
const actionProps: PluginHomePageActionsProps = {
|
|
45
46
|
path,
|
|
46
47
|
collection,
|
|
47
48
|
context
|
|
48
49
|
};
|
|
49
50
|
actions = <>
|
|
50
|
-
{
|
|
51
|
+
{customizationController.plugins.map((plugin, i) => (
|
|
51
52
|
plugin.homePage?.CollectionActions
|
|
52
53
|
? <plugin.homePage.CollectionActions
|
|
53
54
|
key={`actions_${i}`}
|
|
@@ -5,8 +5,8 @@ import { EntityCollectionTable } from "./EntityCollectionTable";
|
|
|
5
5
|
import { EntityCollectionRowActions } from "./EntityCollectionTable/EntityCollectionRowActions";
|
|
6
6
|
import {
|
|
7
7
|
useAuthController,
|
|
8
|
+
useCustomizationController,
|
|
8
9
|
useDataSource,
|
|
9
|
-
useFireCMSContext,
|
|
10
10
|
useLargeLayout,
|
|
11
11
|
useNavigationController,
|
|
12
12
|
useSideEntityController
|
|
@@ -15,9 +15,12 @@ import { ErrorView } from "./ErrorView";
|
|
|
15
15
|
import { AddIcon, Button, DialogActions, Typography } from "@firecms/ui";
|
|
16
16
|
import { canCreateEntity, fullPathToCollectionSegments, resolveCollection } from "../util";
|
|
17
17
|
import { useSelectionController } from "./EntityCollectionView/EntityCollectionView";
|
|
18
|
-
import {
|
|
18
|
+
import {
|
|
19
|
+
useDataSourceEntityCollectionTableController
|
|
20
|
+
} from "./EntityCollectionTable/useDataSourceEntityCollectionTableController";
|
|
19
21
|
import { useColumnIds } from "./EntityCollectionView/useColumnsIds";
|
|
20
22
|
import { useSideDialogContext } from "../core";
|
|
23
|
+
import { useAnalyticsController } from "../hooks/useAnalyticsController";
|
|
21
24
|
|
|
22
25
|
/**
|
|
23
26
|
* @group Components
|
|
@@ -101,7 +104,8 @@ export function ReferenceSelectionInner<M extends Record<string, any>>(
|
|
|
101
104
|
const sideDialogContext = useSideDialogContext();
|
|
102
105
|
const sideEntityController = useSideEntityController();
|
|
103
106
|
const navigation = useNavigationController();
|
|
104
|
-
const
|
|
107
|
+
const analyticsController = useAnalyticsController();
|
|
108
|
+
const customizationController = useCustomizationController();
|
|
105
109
|
|
|
106
110
|
const fullPath = navigation.resolveAliasesFrom(pathInput);
|
|
107
111
|
|
|
@@ -109,7 +113,31 @@ export function ReferenceSelectionInner<M extends Record<string, any>>(
|
|
|
109
113
|
|
|
110
114
|
const [entitiesDisplayedFirst, setEntitiesDisplayedFirst] = useState<Entity<any>[]>([]);
|
|
111
115
|
|
|
112
|
-
const
|
|
116
|
+
const toggleEntitySelection = (entity: Entity<any>) => {
|
|
117
|
+
let newValue;
|
|
118
|
+
const selectedEntities = selectionController.selectedEntities;
|
|
119
|
+
|
|
120
|
+
analyticsController.onAnalyticsEvent?.("reference_selection_toggle", {
|
|
121
|
+
path: fullPath,
|
|
122
|
+
entityId: entity.id
|
|
123
|
+
});
|
|
124
|
+
if (selectedEntities) {
|
|
125
|
+
|
|
126
|
+
if (selectedEntities.map((e) => e.id).indexOf(entity.id) > -1) {
|
|
127
|
+
newValue = selectedEntities.filter((item: Entity<any>) => item.id !== entity.id);
|
|
128
|
+
} else {
|
|
129
|
+
if (maxSelection && selectedEntities.length >= maxSelection)
|
|
130
|
+
return;
|
|
131
|
+
newValue = [...selectedEntities, entity];
|
|
132
|
+
}
|
|
133
|
+
selectionController.setSelectedEntities(newValue);
|
|
134
|
+
|
|
135
|
+
if (onMultipleEntitiesSelected)
|
|
136
|
+
onMultipleEntitiesSelected(newValue);
|
|
137
|
+
}
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
const selectionController = useSelectionController(toggleEntitySelection);
|
|
113
141
|
|
|
114
142
|
/**
|
|
115
143
|
* Fetch initially selected ids
|
|
@@ -142,7 +170,7 @@ export function ReferenceSelectionInner<M extends Record<string, any>>(
|
|
|
142
170
|
}, [dataSource, fullPath, selectedEntityIdsProp, collection, selectionController.setSelectedEntities]);
|
|
143
171
|
|
|
144
172
|
const onClear = () => {
|
|
145
|
-
|
|
173
|
+
analyticsController.onAnalyticsEvent?.("reference_selection_clear", {
|
|
146
174
|
path: fullPath
|
|
147
175
|
});
|
|
148
176
|
selectionController.setSelectedEntities([]);
|
|
@@ -153,36 +181,9 @@ export function ReferenceSelectionInner<M extends Record<string, any>>(
|
|
|
153
181
|
}
|
|
154
182
|
};
|
|
155
183
|
|
|
156
|
-
const toggleEntitySelection = (entity: Entity<any>) => {
|
|
157
|
-
console.debug("ReferenceSelectionInner toggleEntitySelection", entity);
|
|
158
|
-
let newValue;
|
|
159
|
-
const selectedEntities = selectionController.selectedEntities;
|
|
160
|
-
|
|
161
|
-
context.onAnalyticsEvent?.("reference_selection_toggle", {
|
|
162
|
-
path: fullPath,
|
|
163
|
-
entityId: entity.id
|
|
164
|
-
});
|
|
165
|
-
if (selectedEntities) {
|
|
166
|
-
|
|
167
|
-
if (selectedEntities.map((e) => e.id).indexOf(entity.id) > -1) {
|
|
168
|
-
newValue = selectedEntities.filter((item: Entity<any>) => item.id !== entity.id);
|
|
169
|
-
} else {
|
|
170
|
-
if (maxSelection && selectedEntities.length >= maxSelection)
|
|
171
|
-
return;
|
|
172
|
-
newValue = [...selectedEntities, entity];
|
|
173
|
-
}
|
|
174
|
-
selectionController.setSelectedEntities(newValue);
|
|
175
|
-
|
|
176
|
-
if (onMultipleEntitiesSelected)
|
|
177
|
-
onMultipleEntitiesSelected(newValue);
|
|
178
|
-
}
|
|
179
|
-
};
|
|
180
|
-
|
|
181
184
|
const onEntityClick = (entity: Entity<any>) => {
|
|
182
|
-
console.debug("ReferenceSelectionInner onEntityClick", entity);
|
|
183
|
-
|
|
184
185
|
if (!multiselect && onSingleEntitySelected) {
|
|
185
|
-
|
|
186
|
+
analyticsController.onAnalyticsEvent?.("reference_selected_single", {
|
|
186
187
|
path: fullPath,
|
|
187
188
|
entityId: entity.id
|
|
188
189
|
});
|
|
@@ -195,7 +196,7 @@ export function ReferenceSelectionInner<M extends Record<string, any>>(
|
|
|
195
196
|
|
|
196
197
|
// create a new entity from within the reference dialog
|
|
197
198
|
const onNewClick = () => {
|
|
198
|
-
|
|
199
|
+
analyticsController.onAnalyticsEvent?.("reference_selection_new_entity", {
|
|
199
200
|
path: fullPath
|
|
200
201
|
});
|
|
201
202
|
sideEntityController.open({
|
|
@@ -251,8 +252,8 @@ export function ReferenceSelectionInner<M extends Record<string, any>>(
|
|
|
251
252
|
collection: collection,
|
|
252
253
|
path: fullPath,
|
|
253
254
|
values: {},
|
|
254
|
-
fields:
|
|
255
|
-
}), [collection,
|
|
255
|
+
fields: customizationController.propertyConfigs
|
|
256
|
+
}), [collection, customizationController.propertyConfigs, fullPath]);
|
|
256
257
|
|
|
257
258
|
// eslint-disable-next-line react-hooks/rules-of-hooks
|
|
258
259
|
const displayedColumnIds = useColumnIds(resolvedCollection, false);
|
|
@@ -272,6 +273,7 @@ export function ReferenceSelectionInner<M extends Record<string, any>>(
|
|
|
272
273
|
<div className="flex-grow">
|
|
273
274
|
{entitiesDisplayedFirst &&
|
|
274
275
|
<EntityCollectionTable
|
|
276
|
+
debugKey={"reference_selection_table"}
|
|
275
277
|
displayedColumnIds={displayedColumnIds}
|
|
276
278
|
onEntityClick={onEntityClick}
|
|
277
279
|
tableController={tableController}
|