@firecms/core 3.0.0-beta.8 → 3.0.0-beta.9
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/app/Drawer.d.ts +1 -1
- package/dist/app/Scaffold.d.ts +1 -1
- package/dist/components/ArrayContainer.d.ts +2 -1
- package/dist/components/CircularProgressCenter.d.ts +1 -1
- package/dist/components/EntityCollectionTable/EntityCollectionRowActions.d.ts +0 -1
- package/dist/components/EntityCollectionTable/fields/TableReferenceField.d.ts +1 -1
- package/dist/components/EntityCollectionTable/internal/popup_field/PopupFormField.d.ts +1 -1
- package/dist/components/EntityCollectionView/EntityCollectionView.d.ts +1 -1
- package/dist/components/EntityPreview.d.ts +1 -1
- package/dist/components/ErrorView.d.ts +1 -1
- package/dist/components/HomePage/DefaultHomePage.d.ts +1 -1
- package/dist/components/HomePage/NavigationCardBinding.d.ts +1 -1
- package/dist/components/PropertyIdCopyTooltip.d.ts +8 -0
- package/dist/components/VirtualTable/VirtualTableProps.d.ts +10 -6
- package/dist/components/common/useColumnsIds.d.ts +2 -1
- package/dist/components/index.d.ts +1 -0
- package/dist/core/DefaultAppBar.d.ts +1 -1
- package/dist/core/DefaultDrawer.d.ts +1 -1
- package/dist/core/DrawerNavigationItem.d.ts +2 -1
- package/dist/core/EntityEditView.d.ts +3 -3
- package/dist/core/FireCMS.d.ts +1 -1
- package/dist/core/NavigationRoutes.d.ts +1 -1
- package/dist/form/PropertyFieldBinding.d.ts +1 -1
- package/dist/form/components/FormikArrayContainer.d.ts +2 -1
- package/dist/form/components/LabelWithIcon.d.ts +1 -1
- package/dist/form/components/LabelWithIconAndTooltip.d.ts +15 -0
- package/dist/form/components/index.d.ts +1 -0
- package/dist/form/field_bindings/ArrayCustomShapedFieldBinding.d.ts +1 -1
- package/dist/form/field_bindings/ArrayOfReferencesFieldBinding.d.ts +1 -1
- package/dist/form/field_bindings/BlockFieldBinding.d.ts +1 -1
- package/dist/form/field_bindings/KeyValueFieldBinding.d.ts +1 -1
- package/dist/form/field_bindings/MapFieldBinding.d.ts +1 -1
- package/dist/form/field_bindings/MarkdownEditorFieldBinding.d.ts +11 -0
- package/dist/form/field_bindings/{MultiSelectBinding.d.ts → MultiSelectFieldBinding.d.ts} +1 -1
- package/dist/form/field_bindings/ReadOnlyFieldBinding.d.ts +1 -1
- package/dist/form/field_bindings/RepeatFieldBinding.d.ts +1 -1
- package/dist/form/field_bindings/SelectFieldBinding.d.ts +1 -1
- package/dist/form/field_bindings/StorageUploadFieldBinding.d.ts +1 -1
- package/dist/form/field_bindings/TextFieldBinding.d.ts +1 -1
- package/dist/form/index.d.ts +3 -3
- package/dist/form/useClearRestoreValue.d.ts +2 -2
- package/dist/hooks/useProjectLog.d.ts +2 -2
- package/dist/index.es.js +14127 -11014
- package/dist/index.es.js.map +1 -1
- package/dist/index.umd.js +19987 -587
- package/dist/index.umd.js.map +1 -1
- package/dist/preview/PropertyPreviewProps.d.ts +1 -1
- package/dist/preview/components/EnumValuesChip.d.ts +1 -1
- package/dist/preview/components/ReferencePreview.d.ts +1 -1
- package/dist/types/auth.d.ts +1 -1
- package/dist/types/collections.d.ts +12 -3
- package/dist/types/datasource.d.ts +35 -22
- package/dist/types/entities.d.ts +1 -0
- package/dist/types/entity_overrides.d.ts +2 -2
- package/dist/types/fields.d.ts +20 -15
- package/dist/types/navigation.d.ts +1 -0
- package/dist/types/properties.d.ts +18 -18
- package/dist/types/side_dialogs_controller.d.ts +10 -0
- package/dist/types/storage.d.ts +75 -0
- package/dist/util/entities.d.ts +1 -1
- package/dist/util/icon_list.d.ts +5 -1
- package/dist/util/icons.d.ts +1 -0
- package/dist/util/index.d.ts +1 -0
- package/dist/util/plurals.d.ts +0 -2
- package/dist/util/property_utils.d.ts +1 -1
- package/dist/util/resolutions.d.ts +15 -1
- package/dist/util/storage.d.ts +1 -1
- package/dist/util/useStorageUploadController.d.ts +2 -2
- package/package.json +15 -17
- package/src/app/Drawer.tsx +1 -1
- package/src/app/Scaffold.tsx +22 -27
- package/src/components/ArrayContainer.tsx +15 -10
- package/src/components/CircularProgressCenter.tsx +1 -1
- package/src/components/EntityCollectionTable/EntityCollectionRowActions.tsx +4 -9
- package/src/components/EntityCollectionTable/EntityCollectionTable.tsx +2 -2
- package/src/components/EntityCollectionTable/PropertyTableCell.tsx +4 -2
- package/src/components/EntityCollectionTable/fields/TableReferenceField.tsx +21 -14
- package/src/components/EntityCollectionTable/fields/TableStorageUpload.tsx +12 -4
- package/src/components/EntityCollectionTable/internal/EntityTableCell.tsx +17 -19
- package/src/components/EntityCollectionTable/internal/EntityTableCellActions.tsx +19 -7
- package/src/components/EntityCollectionTable/internal/popup_field/PopupFormField.tsx +3 -5
- package/src/components/EntityCollectionView/EntityCollectionView.tsx +26 -25
- package/src/components/EntityCollectionView/useSelectionController.tsx +19 -7
- package/src/components/EntityPreview.tsx +30 -31
- package/src/components/ErrorView.tsx +1 -1
- package/src/components/HomePage/DefaultHomePage.tsx +24 -18
- package/src/components/HomePage/NavigationCardBinding.tsx +1 -1
- package/src/components/HomePage/SmallNavigationCard.tsx +1 -2
- package/src/components/PropertyIdCopyTooltip.tsx +48 -0
- package/src/components/ReferenceTable/ReferenceSelectionTable.tsx +7 -6
- package/src/components/SearchIconsView.tsx +5 -2
- package/src/components/SelectableTable/SelectableTable.tsx +2 -4
- package/src/components/SelectableTable/filters/DateTimeFilterField.tsx +1 -1
- package/src/components/SelectableTable/filters/StringNumberFilterField.tsx +54 -7
- package/src/components/VirtualTable/VirtualTable.tsx +33 -9
- package/src/components/VirtualTable/VirtualTableCell.tsx +1 -9
- package/src/components/VirtualTable/VirtualTableProps.tsx +12 -8
- package/src/components/VirtualTable/fields/VirtualTableDateField.tsx +3 -3
- package/src/components/VirtualTable/fields/VirtualTableInput.tsx +2 -1
- package/src/components/VirtualTable/fields/VirtualTableNumberInput.tsx +2 -1
- package/src/components/VirtualTable/fields/VirtualTableSelect.tsx +14 -28
- package/src/components/common/useColumnsIds.tsx +13 -0
- package/src/components/common/useDataSourceEntityCollectionTableController.tsx +12 -12
- package/src/components/common/useTableSearchHelper.ts +1 -0
- package/src/components/index.tsx +1 -0
- package/src/core/DefaultAppBar.tsx +3 -3
- package/src/core/DefaultDrawer.tsx +18 -12
- package/src/core/DrawerNavigationItem.tsx +31 -27
- package/src/core/EntityEditView.tsx +136 -109
- package/src/core/FireCMS.tsx +2 -2
- package/src/core/NavigationRoutes.tsx +1 -1
- package/src/core/SideDialogs.tsx +1 -0
- package/src/core/field_configs.tsx +5 -5
- package/src/form/PropertyFieldBinding.tsx +24 -14
- package/src/form/components/CustomIdField.tsx +4 -2
- package/src/form/components/FieldHelperText.tsx +1 -1
- package/src/form/components/FormikArrayContainer.tsx +4 -1
- package/src/form/components/LabelWithIcon.tsx +27 -19
- package/src/form/components/LabelWithIconAndTooltip.tsx +28 -0
- package/src/form/components/StorageItemPreview.tsx +1 -0
- package/src/form/components/StorageUploadProgress.tsx +0 -1
- package/src/form/components/index.tsx +1 -0
- package/src/form/field_bindings/ArrayCustomShapedFieldBinding.tsx +22 -14
- package/src/form/field_bindings/ArrayOfReferencesFieldBinding.tsx +24 -22
- package/src/form/field_bindings/BlockFieldBinding.tsx +13 -11
- package/src/form/field_bindings/DateTimeFieldBinding.tsx +17 -14
- package/src/form/field_bindings/KeyValueFieldBinding.tsx +8 -7
- package/src/form/field_bindings/MapFieldBinding.tsx +16 -24
- package/src/form/field_bindings/MarkdownEditorFieldBinding.tsx +150 -0
- package/src/form/field_bindings/{MultiSelectBinding.tsx → MultiSelectFieldBinding.tsx} +25 -20
- package/src/form/field_bindings/ReadOnlyFieldBinding.tsx +9 -7
- package/src/form/field_bindings/ReferenceFieldBinding.tsx +27 -18
- package/src/form/field_bindings/RepeatFieldBinding.tsx +27 -17
- package/src/form/field_bindings/SelectFieldBinding.tsx +17 -10
- package/src/form/field_bindings/StorageUploadFieldBinding.tsx +36 -17
- package/src/form/field_bindings/SwitchFieldBinding.tsx +9 -5
- package/src/form/field_bindings/TextFieldBinding.tsx +26 -23
- package/src/form/index.tsx +4 -9
- package/src/form/useClearRestoreValue.tsx +2 -2
- package/src/hooks/data/delete.ts +2 -1
- package/src/hooks/data/save.ts +5 -2
- package/src/hooks/data/useDataSource.tsx +11 -3
- package/src/hooks/useBuildNavigationController.tsx +10 -3
- package/src/hooks/useProjectLog.tsx +18 -7
- package/src/internal/useBuildDataSource.ts +59 -76
- package/src/internal/useBuildSideDialogsController.tsx +1 -0
- package/src/internal/useBuildSideEntityController.tsx +17 -4
- package/src/internal/useUnsavedChangesDialog.tsx +3 -1
- package/src/preview/PropertyPreview.tsx +2 -2
- package/src/preview/PropertyPreviewProps.tsx +1 -1
- package/src/preview/components/BooleanPreview.tsx +1 -1
- package/src/preview/components/EnumValuesChip.tsx +1 -1
- package/src/preview/components/ImagePreview.tsx +4 -2
- package/src/preview/components/ReferencePreview.tsx +2 -13
- package/src/preview/components/UrlComponentPreview.tsx +1 -1
- package/src/preview/property_previews/ArrayOfMapsPreview.tsx +1 -1
- package/src/preview/property_previews/ArrayOfReferencesPreview.tsx +1 -1
- package/src/preview/property_previews/ArrayOfStorageComponentsPreview.tsx +1 -1
- package/src/preview/property_previews/ArrayOneOfPreview.tsx +2 -2
- package/src/preview/property_previews/ArrayPropertyPreview.tsx +1 -1
- package/src/preview/property_previews/MapPropertyPreview.tsx +1 -1
- package/src/preview/property_previews/SkeletonPropertyComponent.tsx +2 -2
- package/src/preview/property_previews/StringPropertyPreview.tsx +2 -2
- package/src/preview/util.ts +2 -2
- package/src/types/auth.tsx +1 -1
- package/src/types/collections.ts +15 -4
- package/src/types/datasource.ts +42 -27
- package/src/types/entities.ts +2 -0
- package/src/types/entity_overrides.tsx +2 -2
- package/src/types/fields.tsx +23 -17
- package/src/types/navigation.ts +1 -0
- package/src/types/properties.ts +18 -17
- package/src/types/side_dialogs_controller.tsx +13 -0
- package/src/types/storage.ts +82 -0
- package/src/util/entities.ts +1 -1
- package/src/util/icon_list.ts +14 -9
- package/src/util/icons.tsx +8 -2
- package/src/util/index.ts +1 -0
- package/src/util/plurals.ts +0 -2
- package/src/util/property_utils.tsx +1 -1
- package/src/util/references.ts +3 -0
- package/src/util/resolutions.ts +43 -11
- package/src/util/storage.ts +6 -2
- package/src/util/useStorageUploadController.tsx +15 -7
- package/dist/components/PropertyIdCopyTooltipContent.d.ts +0 -3
- package/dist/form/PropertiesForm.d.ts +0 -8
- package/dist/form/field_bindings/MarkdownFieldBinding.d.ts +0 -9
- package/src/components/PropertyIdCopyTooltipContent.tsx +0 -27
- package/src/form/PropertiesForm.tsx +0 -81
- package/src/form/field_bindings/MarkdownFieldBinding.tsx +0 -695
|
@@ -45,7 +45,7 @@ import { EntityCollectionViewActions } from "./EntityCollectionViewActions";
|
|
|
45
45
|
import {
|
|
46
46
|
AddIcon,
|
|
47
47
|
Button,
|
|
48
|
-
cls,
|
|
48
|
+
cls, focusedDisabled,
|
|
49
49
|
IconButton,
|
|
50
50
|
KeyboardTabIcon,
|
|
51
51
|
Markdown,
|
|
@@ -117,7 +117,7 @@ export type EntityCollectionViewProps<M extends Record<string, any>> = {
|
|
|
117
117
|
*
|
|
118
118
|
* @param fullPath
|
|
119
119
|
* @param collection
|
|
120
|
-
|
|
120
|
+
|
|
121
121
|
* @group Components
|
|
122
122
|
*/
|
|
123
123
|
export const EntityCollectionView = React.memo(
|
|
@@ -153,7 +153,7 @@ export const EntityCollectionView = React.memo(
|
|
|
153
153
|
}, [collection]);
|
|
154
154
|
|
|
155
155
|
const canCreateEntities = canCreateEntity(collection, authController, fullPath, null);
|
|
156
|
-
const [
|
|
156
|
+
const [highlightedEntity, setHighlightedEntity] = useState<Entity<M> | undefined>(undefined);
|
|
157
157
|
const [deleteEntityClicked, setDeleteEntityClicked] = React.useState<Entity<M> | Entity<M>[] | undefined>(undefined);
|
|
158
158
|
|
|
159
159
|
const [lastDeleteTimestamp, setLastDeleteTimestamp] = React.useState<number>(0);
|
|
@@ -162,12 +162,12 @@ export const EntityCollectionView = React.memo(
|
|
|
162
162
|
const [docsCount, setDocsCount] = useState<number>(0);
|
|
163
163
|
|
|
164
164
|
const unselectNavigatedEntity = useCallback(() => {
|
|
165
|
-
const currentSelection =
|
|
165
|
+
const currentSelection = highlightedEntity;
|
|
166
166
|
setTimeout(() => {
|
|
167
|
-
if (currentSelection ===
|
|
168
|
-
|
|
167
|
+
if (currentSelection === highlightedEntity)
|
|
168
|
+
setHighlightedEntity(undefined);
|
|
169
169
|
}, 2400);
|
|
170
|
-
}, [
|
|
170
|
+
}, [highlightedEntity]);
|
|
171
171
|
|
|
172
172
|
const checkInlineEditing = useCallback((entity?: Entity<any>): boolean => {
|
|
173
173
|
const collection = collectionRef.current;
|
|
@@ -186,13 +186,12 @@ export const EntityCollectionView = React.memo(
|
|
|
186
186
|
const usedSelectionController = collection.selectionController ?? selectionController;
|
|
187
187
|
const {
|
|
188
188
|
selectedEntities,
|
|
189
|
-
isEntitySelected,
|
|
190
189
|
setSelectedEntities
|
|
191
190
|
} = usedSelectionController;
|
|
192
191
|
|
|
193
|
-
useEffect(() => {
|
|
194
|
-
|
|
195
|
-
}, [selectedEntities]);
|
|
192
|
+
// useEffect(() => {
|
|
193
|
+
// setDeleteEntityClicked(undefined);
|
|
194
|
+
// }, [selectedEntities]);
|
|
196
195
|
|
|
197
196
|
const tableController = useDataSourceEntityCollectionTableController<M>({
|
|
198
197
|
fullPath,
|
|
@@ -208,9 +207,8 @@ export const EntityCollectionView = React.memo(
|
|
|
208
207
|
}, [tableController.setPopupCell]);
|
|
209
208
|
|
|
210
209
|
const onEntityClick = useCallback((clickedEntity: Entity<M>) => {
|
|
211
|
-
console.log("Entity clicked", clickedEntity)
|
|
212
210
|
const collection = collectionRef.current;
|
|
213
|
-
|
|
211
|
+
setHighlightedEntity(clickedEntity);
|
|
214
212
|
analyticsController.onAnalyticsEvent?.("edit_entity_clicked", {
|
|
215
213
|
path: clickedEntity.path,
|
|
216
214
|
entityId: clickedEntity.id
|
|
@@ -307,7 +305,7 @@ export const EntityCollectionView = React.memo(
|
|
|
307
305
|
value,
|
|
308
306
|
property,
|
|
309
307
|
entityId
|
|
310
|
-
}) => dataSource.checkUniqueField(fullPath, name, value, entityId),
|
|
308
|
+
}) => dataSource.checkUniqueField(fullPath, name, value, entityId, collection),
|
|
311
309
|
[fullPath]);
|
|
312
310
|
|
|
313
311
|
const onValueChange: OnCellValueChange<any, any> = ({
|
|
@@ -420,7 +418,7 @@ export const EntityCollectionView = React.memo(
|
|
|
420
418
|
<ReferencePreview
|
|
421
419
|
key={reference.path + "/" + reference.id}
|
|
422
420
|
reference={reference}
|
|
423
|
-
size={"
|
|
421
|
+
size={"smallest"}/>
|
|
424
422
|
);
|
|
425
423
|
})}
|
|
426
424
|
</div>
|
|
@@ -468,7 +466,7 @@ export const EntityCollectionView = React.memo(
|
|
|
468
466
|
return (largeLayout ? (80 + actionsWidth) : (70 + actionsWidth)) + (collapsedActions.length > 0 ? (largeLayout ? 40 : 30) : 0);
|
|
469
467
|
};
|
|
470
468
|
|
|
471
|
-
const tableRowActionsBuilder = ({
|
|
469
|
+
const tableRowActionsBuilder = useCallback(({
|
|
472
470
|
entity,
|
|
473
471
|
size,
|
|
474
472
|
width,
|
|
@@ -480,7 +478,7 @@ export const EntityCollectionView = React.memo(
|
|
|
480
478
|
frozen?: boolean
|
|
481
479
|
}) => {
|
|
482
480
|
|
|
483
|
-
const isSelected =
|
|
481
|
+
const isSelected = usedSelectionController.selectedEntities.map(e => e.id).includes(entity.id);
|
|
484
482
|
|
|
485
483
|
const actions = getActionsForEntity({
|
|
486
484
|
entity,
|
|
@@ -495,7 +493,7 @@ export const EntityCollectionView = React.memo(
|
|
|
495
493
|
isSelected={isSelected}
|
|
496
494
|
selectionEnabled={selectionEnabled}
|
|
497
495
|
size={size}
|
|
498
|
-
highlightEntity={
|
|
496
|
+
highlightEntity={setHighlightedEntity}
|
|
499
497
|
unhighlightEntity={unselectNavigatedEntity}
|
|
500
498
|
collection={collection}
|
|
501
499
|
fullPath={fullPath}
|
|
@@ -506,7 +504,7 @@ export const EntityCollectionView = React.memo(
|
|
|
506
504
|
/>
|
|
507
505
|
);
|
|
508
506
|
|
|
509
|
-
};
|
|
507
|
+
}, [updateLastDeleteTimestamp, usedSelectionController]);
|
|
510
508
|
|
|
511
509
|
const title = <Popover
|
|
512
510
|
open={popOverOpen}
|
|
@@ -610,7 +608,7 @@ export const EntityCollectionView = React.memo(
|
|
|
610
608
|
uniqueFieldValidator={uniqueFieldValidator}
|
|
611
609
|
title={title}
|
|
612
610
|
selectionController={usedSelectionController}
|
|
613
|
-
highlightedEntities={
|
|
611
|
+
highlightedEntities={highlightedEntity ? [highlightedEntity] : []}
|
|
614
612
|
defaultSize={collection.defaultSize}
|
|
615
613
|
properties={resolvedCollection.properties}
|
|
616
614
|
getPropertyFor={getPropertyFor}
|
|
@@ -662,19 +660,19 @@ export const EntityCollectionView = React.memo(
|
|
|
662
660
|
collection={collection}/>}
|
|
663
661
|
/>
|
|
664
662
|
|
|
665
|
-
<PopupFormField
|
|
663
|
+
{popupCell && <PopupFormField
|
|
666
664
|
key={`popup_form_${popupCell?.propertyKey}_${popupCell?.entity?.id}`}
|
|
667
665
|
open={Boolean(popupCell)}
|
|
668
666
|
onClose={onPopupClose}
|
|
669
667
|
cellRect={popupCell?.cellRect}
|
|
670
668
|
propertyKey={popupCell?.propertyKey}
|
|
671
669
|
collection={collection}
|
|
672
|
-
entity={popupCell
|
|
670
|
+
entity={popupCell.entity}
|
|
673
671
|
tableKey={tableKey.current}
|
|
674
672
|
customFieldValidator={uniqueFieldValidator}
|
|
675
673
|
path={resolvedFullPath}
|
|
676
674
|
onCellValueChange={onValueChange}
|
|
677
|
-
container={containerRef.current}/>
|
|
675
|
+
container={containerRef.current}/>}
|
|
678
676
|
|
|
679
677
|
{deleteEntityClicked &&
|
|
680
678
|
<DeleteEntityDialog
|
|
@@ -782,10 +780,13 @@ function EntityIdHeaderWidget({
|
|
|
782
780
|
const [searchString, setSearchString] = React.useState("");
|
|
783
781
|
const sideEntityController = useSideEntityController();
|
|
784
782
|
return (
|
|
785
|
-
<Tooltip title={!openPopup ? "Find by ID" : undefined}>
|
|
783
|
+
<Tooltip title={!openPopup ? "Find by ID" : undefined} asChild={false}>
|
|
786
784
|
<Popover
|
|
787
785
|
open={openPopup}
|
|
788
786
|
onOpenChange={setOpenPopup}
|
|
787
|
+
sideOffset={0}
|
|
788
|
+
align={"start"}
|
|
789
|
+
alignOffset={-117}
|
|
789
790
|
trigger={
|
|
790
791
|
<IconButton size={"small"}>
|
|
791
792
|
<SearchIcon size={"small"}/>
|
|
@@ -815,7 +816,7 @@ function EntityIdHeaderWidget({
|
|
|
815
816
|
setSearchString(e.target.value);
|
|
816
817
|
}}
|
|
817
818
|
value={searchString}
|
|
818
|
-
className={"flex-grow bg-transparent outline-none p-1"}/>
|
|
819
|
+
className={"flex-grow bg-transparent outline-none p-1 " + focusedDisabled}/>
|
|
819
820
|
<Button variant={"outlined"}
|
|
820
821
|
disabled={!(searchString.trim())}
|
|
821
822
|
type={"submit"}
|
|
@@ -7,19 +7,31 @@ export function useSelectionController<M extends Record<string, any> = any>(
|
|
|
7
7
|
|
|
8
8
|
const [selectedEntities, setSelectedEntities] = useState<Entity<M>[]>([]);
|
|
9
9
|
|
|
10
|
-
const toggleEntitySelection = useCallback((entity: Entity<M
|
|
10
|
+
const toggleEntitySelection = useCallback((entity: Entity<M>, newSelectedState?: boolean) => {
|
|
11
11
|
let newValue;
|
|
12
|
-
if (
|
|
13
|
-
|
|
14
|
-
|
|
12
|
+
if (newSelectedState === undefined) {
|
|
13
|
+
if (selectedEntities.map(e => e.id).includes(entity.id)) {
|
|
14
|
+
onSelectionChange?.(entity, false);
|
|
15
|
+
newValue = selectedEntities.filter((item: Entity<M>) => item.id !== entity.id);
|
|
16
|
+
} else {
|
|
17
|
+
onSelectionChange?.(entity, true);
|
|
18
|
+
newValue = [...selectedEntities, entity];
|
|
19
|
+
}
|
|
15
20
|
} else {
|
|
16
|
-
|
|
17
|
-
|
|
21
|
+
if (newSelectedState) {
|
|
22
|
+
onSelectionChange?.(entity, true);
|
|
23
|
+
newValue = [...selectedEntities, entity];
|
|
24
|
+
} else {
|
|
25
|
+
onSelectionChange?.(entity, false);
|
|
26
|
+
newValue = selectedEntities.filter((item: Entity<M>) => item.id !== entity.id);
|
|
27
|
+
}
|
|
18
28
|
}
|
|
19
29
|
setSelectedEntities(newValue);
|
|
20
30
|
}, [selectedEntities]);
|
|
21
31
|
|
|
22
|
-
const isEntitySelected = useCallback((entity: Entity<M>) =>
|
|
32
|
+
const isEntitySelected = useCallback((entity: Entity<M>) => {
|
|
33
|
+
return selectedEntities.map(e => e.id).includes(entity.id);
|
|
34
|
+
}, [selectedEntities]);
|
|
23
35
|
|
|
24
36
|
return {
|
|
25
37
|
selectedEntities,
|
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
getEntityPreviewKeys,
|
|
9
9
|
getEntityTitlePropertyKey,
|
|
10
10
|
getValueInPath,
|
|
11
|
+
IconForView,
|
|
11
12
|
resolveCollection
|
|
12
13
|
} from "../util";
|
|
13
14
|
import { cls, defaultBorderMixin, IconButton, KeyboardTabIcon, Skeleton, Tooltip, Typography } from "@firecms/ui";
|
|
@@ -76,18 +77,18 @@ export function EntityPreview({
|
|
|
76
77
|
return <EntityPreviewContainer onClick={disabled ? undefined : onClick}
|
|
77
78
|
hover={disabled ? undefined : hover}
|
|
78
79
|
size={size}>
|
|
79
|
-
{
|
|
80
|
-
<
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
<div className={"flex flex-col
|
|
89
|
-
|
|
90
|
-
{size !== "
|
|
80
|
+
<div className={cls("flex w-10 h-10 mr-2 shrink-0", size === "smallest" ? "my-0.5" : "m-2 self-start")}>
|
|
81
|
+
{imageProperty && <PropertyPreview property={imageProperty}
|
|
82
|
+
propertyKey={imagePropertyKey as string}
|
|
83
|
+
size={"smallest"}
|
|
84
|
+
value={getValueInPath(entity.values, imagePropertyKey as string)}/>}
|
|
85
|
+
{!imageProperty && <IconForView collectionOrView={collection} size={size} className={"m-auto"}/>}
|
|
86
|
+
</div>
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
<div className={"flex flex-col grow-1 w-full max-w-full m-1"}>
|
|
90
|
+
|
|
91
|
+
{size !== "smallest" && includeId && (
|
|
91
92
|
entity
|
|
92
93
|
? <div className={`${
|
|
93
94
|
size !== "medium"
|
|
@@ -131,10 +132,10 @@ export function EntityPreview({
|
|
|
131
132
|
propertyKey={key as string}
|
|
132
133
|
value={getValueInPath(entity.values, key)}
|
|
133
134
|
property={childProperty as ResolvedProperty}
|
|
134
|
-
size={"
|
|
135
|
+
size={"smallest"}/>
|
|
135
136
|
: <SkeletonPropertyComponent
|
|
136
137
|
property={childProperty as ResolvedProperty}
|
|
137
|
-
size={"
|
|
138
|
+
size={"smallest"}/>
|
|
138
139
|
}
|
|
139
140
|
</div>
|
|
140
141
|
);
|
|
@@ -143,11 +144,11 @@ export function EntityPreview({
|
|
|
143
144
|
</div>
|
|
144
145
|
|
|
145
146
|
{entity && includeEntityLink &&
|
|
146
|
-
<Tooltip title={`See details for ${entity.id}`}
|
|
147
|
-
className={size !== "tiny" ? "self-start" : ""}>
|
|
147
|
+
<Tooltip title={`See details for ${entity.id}`}>
|
|
148
148
|
<IconButton
|
|
149
149
|
color={"inherit"}
|
|
150
150
|
size={"small"}
|
|
151
|
+
className={size !== "smallest" ? "self-start" : ""}
|
|
151
152
|
onClick={(e) => {
|
|
152
153
|
e.stopPropagation();
|
|
153
154
|
analyticsController.onAnalyticsEvent?.("entity_click_from_reference", {
|
|
@@ -180,16 +181,17 @@ export type EntityPreviewContainerProps = {
|
|
|
180
181
|
onClick?: (e: React.SyntheticEvent) => void;
|
|
181
182
|
};
|
|
182
183
|
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
184
|
+
|
|
185
|
+
export const EntityPreviewContainer = React.forwardRef<HTMLDivElement, EntityPreviewContainerProps>(({
|
|
186
|
+
children,
|
|
187
|
+
hover,
|
|
188
|
+
onClick,
|
|
189
|
+
size,
|
|
190
|
+
style,
|
|
191
|
+
className,
|
|
192
|
+
fullwidth = true,
|
|
193
|
+
...props
|
|
194
|
+
}, ref) => {
|
|
193
195
|
return <div
|
|
194
196
|
ref={ref}
|
|
195
197
|
style={{
|
|
@@ -199,10 +201,11 @@ const EntityPreviewContainerInner = React.forwardRef<HTMLDivElement, EntityPrevi
|
|
|
199
201
|
}}
|
|
200
202
|
className={cls(
|
|
201
203
|
"bg-white dark:bg-gray-900",
|
|
204
|
+
"min-h-[42px]",
|
|
202
205
|
fullwidth ? "w-full" : "",
|
|
203
206
|
"items-center",
|
|
204
207
|
hover ? "hover:bg-slate-50 dark:hover:bg-gray-800 group-hover:bg-slate-50 dark:group-hover:bg-gray-800" : "",
|
|
205
|
-
size === "
|
|
208
|
+
size === "smallest" ? "p-1" : "px-2 py-1",
|
|
206
209
|
"flex border rounded-lg",
|
|
207
210
|
onClick ? "cursor-pointer" : "",
|
|
208
211
|
defaultBorderMixin,
|
|
@@ -217,7 +220,3 @@ const EntityPreviewContainerInner = React.forwardRef<HTMLDivElement, EntityPrevi
|
|
|
217
220
|
{children}
|
|
218
221
|
</div>;
|
|
219
222
|
});
|
|
220
|
-
|
|
221
|
-
EntityPreviewContainerInner.displayName = "EntityPreviewContainer";
|
|
222
|
-
|
|
223
|
-
export const EntityPreviewContainer = React.memo(EntityPreviewContainerInner) as React.FC<EntityPreviewContainerProps>;
|
|
@@ -1,29 +1,27 @@
|
|
|
1
|
-
import React, { useCallback, useEffect, useState } from "react";
|
|
1
|
+
import React, { useCallback, useEffect, useRef, useState } from "react";
|
|
2
2
|
|
|
3
3
|
import { useCustomizationController, useFireCMSContext, useNavigationController } from "../../hooks";
|
|
4
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
CMSAnalyticsEvent,
|
|
6
|
+
PluginGenericProps,
|
|
7
|
+
PluginHomePageAdditionalCardsProps,
|
|
8
|
+
TopNavigationEntry
|
|
9
|
+
} from "../../types";
|
|
5
10
|
|
|
6
11
|
import { toArray } from "../../util/arrays";
|
|
7
12
|
import { NavigationGroup } from "./NavigationGroup";
|
|
8
13
|
import { NavigationCardBinding } from "./NavigationCardBinding";
|
|
9
14
|
|
|
10
|
-
|
|
11
|
-
import * as JsSearch from "js-search";
|
|
15
|
+
import Fuse from "fuse.js"
|
|
12
16
|
|
|
13
17
|
import { Container, SearchBar } from "@firecms/ui";
|
|
14
18
|
import { FavouritesView } from "./FavouritesView";
|
|
15
19
|
import { useRestoreScroll } from "../../internal/useRestoreScroll";
|
|
16
20
|
|
|
17
|
-
const search = new JsSearch.Search("url");
|
|
18
|
-
search.addIndex("name");
|
|
19
|
-
search.addIndex("description");
|
|
20
|
-
search.addIndex("group");
|
|
21
|
-
search.addIndex("path");
|
|
22
|
-
|
|
23
21
|
/**
|
|
24
22
|
* Default entry view for the CMS. This component renders navigation cards
|
|
25
23
|
* for each collection defined in the navigation.
|
|
26
|
-
|
|
24
|
+
|
|
27
25
|
* @group Components
|
|
28
26
|
*/
|
|
29
27
|
export function DefaultHomePage({
|
|
@@ -49,6 +47,8 @@ export function DefaultHomePage({
|
|
|
49
47
|
const customizationController = useCustomizationController();
|
|
50
48
|
const navigationController = useNavigationController();
|
|
51
49
|
|
|
50
|
+
const fuse = useRef<Fuse<TopNavigationEntry> | null>(null);
|
|
51
|
+
|
|
52
52
|
if (!navigationController.topLevelNavigation)
|
|
53
53
|
throw Error("Navigation not ready in FireCMSHomePage");
|
|
54
54
|
|
|
@@ -64,13 +64,16 @@ export function DefaultHomePage({
|
|
|
64
64
|
} = navigationController.topLevelNavigation;
|
|
65
65
|
|
|
66
66
|
const [filteredUrls, setFilteredUrls] = useState<string[] | null>(null);
|
|
67
|
+
const performingSearch = Boolean(filteredUrls);
|
|
67
68
|
|
|
68
69
|
const filteredNavigationEntries = filteredUrls
|
|
69
|
-
?
|
|
70
|
+
? filteredUrls.map(url => navigationEntries.find(e => e.url === url)).filter(Boolean) as TopNavigationEntry[]
|
|
70
71
|
: navigationEntries;
|
|
71
72
|
|
|
72
73
|
useEffect(() => {
|
|
73
|
-
|
|
74
|
+
fuse.current = new Fuse(navigationEntries, {
|
|
75
|
+
keys: ["name", "description", "group", "path"]
|
|
76
|
+
});
|
|
74
77
|
}, [navigationEntries]);
|
|
75
78
|
|
|
76
79
|
const updateSearchResults = useCallback(
|
|
@@ -78,12 +81,15 @@ export function DefaultHomePage({
|
|
|
78
81
|
if (!value || value === "") {
|
|
79
82
|
setFilteredUrls(null);
|
|
80
83
|
} else {
|
|
81
|
-
const searchResult =
|
|
82
|
-
|
|
84
|
+
const searchResult = fuse.current?.search(value);
|
|
85
|
+
if (searchResult) {
|
|
86
|
+
setFilteredUrls(searchResult.map((e) => e.item.url));
|
|
87
|
+
}
|
|
83
88
|
}
|
|
84
89
|
}, []);
|
|
85
90
|
|
|
86
|
-
const
|
|
91
|
+
const filteredGroups = filteredUrls ? filteredNavigationEntries.map(entry => entry.group) : [];
|
|
92
|
+
const allGroups: Array<string | undefined> = filteredUrls ? filteredGroups.filter((group, index) => filteredGroups.indexOf(group) === index) : [...groups];
|
|
87
93
|
if (filteredNavigationEntries.filter(e => !e.group).length > 0 || filteredNavigationEntries.length === 0) {
|
|
88
94
|
allGroups.push(undefined);
|
|
89
95
|
}
|
|
@@ -139,7 +145,7 @@ export function DefaultHomePage({
|
|
|
139
145
|
{additionalActions}
|
|
140
146
|
</div>
|
|
141
147
|
|
|
142
|
-
<FavouritesView hidden={
|
|
148
|
+
<FavouritesView hidden={performingSearch}/>
|
|
143
149
|
|
|
144
150
|
{additionalChildrenStart}
|
|
145
151
|
|
|
@@ -163,7 +169,7 @@ export function DefaultHomePage({
|
|
|
163
169
|
|
|
164
170
|
const thisGroupCollections = filteredNavigationEntries
|
|
165
171
|
.filter((entry) => entry.group === group || (!entry.group && group === undefined));
|
|
166
|
-
if (thisGroupCollections.length === 0 && AdditionalCards.length === 0)
|
|
172
|
+
if (thisGroupCollections.length === 0 && (AdditionalCards.length === 0 || performingSearch))
|
|
167
173
|
return null;
|
|
168
174
|
return (
|
|
169
175
|
<NavigationGroup
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ArrowForwardIcon, cardClickableMixin, cardMixin, cls,
|
|
1
|
+
import { ArrowForwardIcon, cardClickableMixin, cardMixin, cls, Typography, } from "@firecms/ui";
|
|
2
2
|
|
|
3
3
|
import { Link } from "react-router-dom";
|
|
4
4
|
|
|
@@ -21,7 +21,6 @@ export function SmallNavigationCard({
|
|
|
21
21
|
tabIndex={0}
|
|
22
22
|
className={cls(cardMixin,
|
|
23
23
|
cardClickableMixin,
|
|
24
|
-
focusedMixin,
|
|
25
24
|
"cursor-pointer flex flex-row items-center px-4 py-2 text-inherit dark:text-inherit visited:text-inherit visited:dark:text-inherit hover:text-inherit hover:dark:text-inherit ")}
|
|
26
25
|
to={url}
|
|
27
26
|
>
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { ContentCopyIcon, IconButton, Tooltip, Typography } from "@firecms/ui";
|
|
2
|
+
import { useCallback, useState } from "react";
|
|
3
|
+
|
|
4
|
+
export function PropertyIdCopyTooltip({
|
|
5
|
+
propertyKey,
|
|
6
|
+
className,
|
|
7
|
+
children
|
|
8
|
+
}: {
|
|
9
|
+
propertyKey: string,
|
|
10
|
+
className?: string,
|
|
11
|
+
children: React.ReactNode
|
|
12
|
+
}) {
|
|
13
|
+
return <Tooltip title={<PropertyIdCopyTooltipContent propertyKey={propertyKey}/>}
|
|
14
|
+
delayDuration={800}
|
|
15
|
+
side={"top"}
|
|
16
|
+
asChild={false}
|
|
17
|
+
align={"start"}
|
|
18
|
+
sideOffset={8}
|
|
19
|
+
className={className}>
|
|
20
|
+
{children}
|
|
21
|
+
</Tooltip>
|
|
22
|
+
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function PropertyIdCopyTooltipContent({ propertyKey }: { propertyKey: string }) {
|
|
26
|
+
|
|
27
|
+
const [copied, setCopied] = useState(false);
|
|
28
|
+
|
|
29
|
+
return (
|
|
30
|
+
<div className={"flex flex-row gap-2 items-center justify-center text-white"}>
|
|
31
|
+
<div>
|
|
32
|
+
<Typography variant={"caption"} className={"min-w-20 text-slate-400"}
|
|
33
|
+
color={"disabled"}>{copied ? "Copied" : "Property ID"}</Typography>
|
|
34
|
+
<Typography variant={"caption"} className={"text-white"}><code>{propertyKey}</code></Typography>
|
|
35
|
+
</div>
|
|
36
|
+
<IconButton size={"small"}>
|
|
37
|
+
<ContentCopyIcon size={"smallest"}
|
|
38
|
+
className={"text-white"}
|
|
39
|
+
onClick={useCallback(() => {
|
|
40
|
+
navigator.clipboard.writeText(propertyKey);
|
|
41
|
+
setCopied(true);
|
|
42
|
+
setTimeout(() => setCopied(false), 2000);
|
|
43
|
+
}, [propertyKey])}
|
|
44
|
+
/>
|
|
45
|
+
</IconButton>
|
|
46
|
+
</div>
|
|
47
|
+
);
|
|
48
|
+
}
|
|
@@ -16,7 +16,7 @@ import {
|
|
|
16
16
|
} from "../../hooks";
|
|
17
17
|
import { ErrorView } from "../ErrorView";
|
|
18
18
|
import { AddIcon, Button, DialogActions, Typography } from "@firecms/ui";
|
|
19
|
-
import { canCreateEntity,
|
|
19
|
+
import { canCreateEntity, IconForView, resolveCollection } from "../../util";
|
|
20
20
|
import { useSelectionController } from "../EntityCollectionView/useSelectionController";
|
|
21
21
|
import { useColumnIds, useTableSearchHelper } from "../common";
|
|
22
22
|
import { useSideDialogContext } from "../../core";
|
|
@@ -109,6 +109,7 @@ export function ReferenceSelectionTable<M extends Record<string, any>>(
|
|
|
109
109
|
|
|
110
110
|
const fullPath = navigation.resolveAliasesFrom(pathInput);
|
|
111
111
|
|
|
112
|
+
console.trace("Opening reference dialog for path " + fullPath);
|
|
112
113
|
const dataSource = useDataSource(collection);
|
|
113
114
|
|
|
114
115
|
const [entitiesDisplayedFirst, setEntitiesDisplayedFirst] = useState<Entity<any>[]>([]);
|
|
@@ -247,7 +248,6 @@ export function ReferenceSelectionTable<M extends Record<string, any>>(
|
|
|
247
248
|
error={"Could not find collection with id " + collection}/>
|
|
248
249
|
}
|
|
249
250
|
|
|
250
|
-
// eslint-disable-next-line react-hooks/rules-of-hooks
|
|
251
251
|
const resolvedCollection = useMemo(() => resolveCollection({
|
|
252
252
|
collection: collection,
|
|
253
253
|
path: fullPath,
|
|
@@ -255,10 +255,8 @@ export function ReferenceSelectionTable<M extends Record<string, any>>(
|
|
|
255
255
|
fields: customizationController.propertyConfigs
|
|
256
256
|
}), [collection, customizationController.propertyConfigs, fullPath]);
|
|
257
257
|
|
|
258
|
-
// eslint-disable-next-line react-hooks/rules-of-hooks
|
|
259
258
|
const displayedColumnIds = useColumnIds(resolvedCollection, false);
|
|
260
259
|
|
|
261
|
-
// eslint-disable-next-line react-hooks/rules-of-hooks
|
|
262
260
|
const tableController = useDataSourceEntityCollectionTableController<M>({
|
|
263
261
|
fullPath,
|
|
264
262
|
collection,
|
|
@@ -272,7 +270,6 @@ export function ReferenceSelectionTable<M extends Record<string, any>>(
|
|
|
272
270
|
onTextSearchClick,
|
|
273
271
|
textSearchEnabled
|
|
274
272
|
} =
|
|
275
|
-
// eslint-disable-next-line react-hooks/rules-of-hooks
|
|
276
273
|
useTableSearchHelper({
|
|
277
274
|
collection,
|
|
278
275
|
fullPath,
|
|
@@ -293,7 +290,11 @@ export function ReferenceSelectionTable<M extends Record<string, any>>(
|
|
|
293
290
|
tableController={tableController}
|
|
294
291
|
enablePopupIcon={false}
|
|
295
292
|
tableRowActionsBuilder={tableRowActionsBuilder}
|
|
296
|
-
title={<Typography variant={"subtitle2"}>
|
|
293
|
+
title={<Typography variant={"subtitle2"} className={"flex flex-row gap-2"}>
|
|
294
|
+
<IconForView
|
|
295
|
+
size={"small"}
|
|
296
|
+
collectionOrView={collection}
|
|
297
|
+
className={"text-gray-300 dark:text-gray-600"}/>
|
|
297
298
|
{collection.singularName ? `Select ${collection.singularName}` : `Select from ${collection.name}`}
|
|
298
299
|
</Typography>}
|
|
299
300
|
defaultSize={collection.defaultSize}
|
|
@@ -31,7 +31,9 @@ export function SearchIconsView({
|
|
|
31
31
|
setKeys(null);
|
|
32
32
|
} else {
|
|
33
33
|
const searchResult = iconsSearch.search(value);
|
|
34
|
-
|
|
34
|
+
const limit = 50;
|
|
35
|
+
const limited = searchResult.slice(0, limit);
|
|
36
|
+
setKeys(limited.map((e) => e.item.key));
|
|
35
37
|
}
|
|
36
38
|
}, UPDATE_SEARCH_INDEX_WAIT_MS), []
|
|
37
39
|
);
|
|
@@ -57,7 +59,8 @@ export function SearchIconsView({
|
|
|
57
59
|
<div className={"flex max-w-full flex-wrap mt-4"}>
|
|
58
60
|
{icons.map((icon: string) => {
|
|
59
61
|
return (
|
|
60
|
-
<Tooltip title={icon} key={icon}
|
|
62
|
+
<Tooltip title={icon} key={icon}
|
|
63
|
+
asChild={true}>
|
|
61
64
|
<IconButton
|
|
62
65
|
shape={"square"}
|
|
63
66
|
toggled={selectedIcon === icon}
|
|
@@ -128,8 +128,6 @@ export const SelectableTable = React.memo<SelectableTableProps<any>>(
|
|
|
128
128
|
setFilterValues,
|
|
129
129
|
sortBy,
|
|
130
130
|
setSortBy,
|
|
131
|
-
setSearchString,
|
|
132
|
-
clearFilter,
|
|
133
131
|
itemCount,
|
|
134
132
|
setItemCount,
|
|
135
133
|
pageSize = 50,
|
|
@@ -223,7 +221,6 @@ export const SelectableTable = React.memo<SelectableTableProps<any>>(
|
|
|
223
221
|
onEndReached={loadNextPage}
|
|
224
222
|
onResetPagination={resetPagination}
|
|
225
223
|
error={dataLoadingError}
|
|
226
|
-
paginationEnabled={paginationEnabled}
|
|
227
224
|
onColumnResize={onColumnResize}
|
|
228
225
|
rowHeight={getRowHeight(size)}
|
|
229
226
|
loading={dataLoading}
|
|
@@ -248,7 +245,8 @@ export const SelectableTable = React.memo<SelectableTableProps<any>>(
|
|
|
248
245
|
);
|
|
249
246
|
|
|
250
247
|
},
|
|
251
|
-
|
|
248
|
+
() => false,
|
|
249
|
+
// equal
|
|
252
250
|
);
|
|
253
251
|
|
|
254
252
|
function createFilterField({
|
|
@@ -95,7 +95,7 @@ export function DateTimeFilterField({
|
|
|
95
95
|
size={"medium"}
|
|
96
96
|
locale={locale}
|
|
97
97
|
value={internalValue ?? undefined}
|
|
98
|
-
onChange={(dateValue: Date |
|
|
98
|
+
onChange={(dateValue: Date | null) => {
|
|
99
99
|
updateFilter(operation, dateValue === null ? undefined : dateValue);
|
|
100
100
|
}}
|
|
101
101
|
clearable={true}
|