@firecms/core 3.0.0-canary.125 → 3.0.0-canary.127

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/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@firecms/core",
3
3
  "type": "module",
4
- "version": "3.0.0-canary.125",
4
+ "version": "3.0.0-canary.127",
5
5
  "description": "Awesome Firebase/Firestore-based headless open-source CMS",
6
6
  "funding": {
7
7
  "url": "https://github.com/sponsors/firecmsco"
@@ -46,9 +46,9 @@
46
46
  "./package.json": "./package.json"
47
47
  },
48
48
  "dependencies": {
49
- "@firecms/editor": "^3.0.0-canary.125",
50
- "@firecms/formex": "^3.0.0-canary.125",
51
- "@firecms/ui": "^3.0.0-canary.125",
49
+ "@firecms/editor": "^3.0.0-canary.127",
50
+ "@firecms/formex": "^3.0.0-canary.127",
51
+ "@firecms/ui": "^3.0.0-canary.127",
52
52
  "@hello-pangea/dnd": "^16.6.0",
53
53
  "@radix-ui/react-portal": "^1.1.1",
54
54
  "clsx": "^2.1.1",
@@ -100,7 +100,7 @@
100
100
  "dist",
101
101
  "src"
102
102
  ],
103
- "gitHead": "501db4cb508b17b75c72c1244cdf88e25f8c1e47",
103
+ "gitHead": "0c46cee41d666c60817c9f4f0447f78fcfa07ddf",
104
104
  "publishConfig": {
105
105
  "access": "public"
106
106
  },
@@ -67,7 +67,6 @@ export const TableReferenceFieldInternal = React.memo(
67
67
  }, [updateValue]);
68
68
 
69
69
  const onMultipleEntitiesSelected = useCallback((entities: Entity<any>[]) => {
70
- console.log("onMultipleEntitiesSelected", entities);
71
70
  updateValue(entities.map((e) => getReferenceFrom(e)));
72
71
  }, [updateValue]);
73
72
 
@@ -179,7 +179,7 @@ function StorageUpload({
179
179
  onDropRejected: (fileRejections, event) => {
180
180
  for (const fileRejection of fileRejections) {
181
181
  for (const error of fileRejection.errors) {
182
- console.log("Error uploading file: ", error);
182
+ console.error("Error uploading file: ", error);
183
183
  if (error.code === "file-too-large") {
184
184
  snackbarContext.open({
185
185
  type: "error",
@@ -7,7 +7,9 @@ import {
7
7
  CollectionSize,
8
8
  Entity,
9
9
  EntityAction,
10
- EntityCollection, EntityTableController,
10
+ EntityCollection,
11
+ EntityReference,
12
+ EntityTableController,
11
13
  FilterValues,
12
14
  PartialEntityCollection,
13
15
  PropertyOrBuilder,
@@ -45,7 +47,8 @@ import { EntityCollectionViewActions } from "./EntityCollectionViewActions";
45
47
  import {
46
48
  AddIcon,
47
49
  Button,
48
- cls, focusedDisabled,
50
+ cls,
51
+ focusedDisabled,
49
52
  IconButton,
50
53
  KeyboardTabIcon,
51
54
  Markdown,
@@ -71,6 +74,7 @@ import { DeleteEntityDialog } from "../DeleteEntityDialog";
71
74
  import { useAnalyticsController } from "../../hooks/useAnalyticsController";
72
75
  import { useSelectionController } from "./useSelectionController";
73
76
  import { EntityCollectionViewStartActions } from "./EntityCollectionViewStartActions";
77
+ import { addRecentId, getRecentIds } from "./utils";
74
78
 
75
79
  const COLLECTION_GROUP_PARENT_ID = "collectionGroupParent";
76
80
 
@@ -207,7 +211,6 @@ export const EntityCollectionView = React.memo(
207
211
  }, [tableController.setPopupCell]);
208
212
 
209
213
  const onEntityClick = useCallback((clickedEntity: Entity<M>) => {
210
- console.log("Entity clicked", clickedEntity)
211
214
  const collection = collectionRef.current;
212
215
  setHighlightedEntity(clickedEntity);
213
216
  analyticsController.onAnalyticsEvent?.("edit_entity_clicked", {
@@ -468,11 +471,11 @@ export const EntityCollectionView = React.memo(
468
471
  };
469
472
 
470
473
  const tableRowActionsBuilder = useCallback(({
471
- entity,
472
- size,
473
- width,
474
- frozen
475
- }: {
474
+ entity,
475
+ size,
476
+ width,
477
+ frozen
478
+ }: {
476
479
  entity: Entity<any>,
477
480
  size: CollectionSize,
478
481
  width: number,
@@ -779,7 +782,10 @@ function EntityIdHeaderWidget({
779
782
  }) {
780
783
  const [openPopup, setOpenPopup] = React.useState(false);
781
784
  const [searchString, setSearchString] = React.useState("");
785
+ const [recentIds, setRecentIds] = React.useState<string[]>(getRecentIds(collection.id));
782
786
  const sideEntityController = useSideEntityController();
787
+
788
+
783
789
  return (
784
790
  <Tooltip title={!openPopup ? "Find by ID" : undefined} asChild={false}>
785
791
  <Popover
@@ -792,38 +798,58 @@ function EntityIdHeaderWidget({
792
798
  <IconButton size={"small"}>
793
799
  <SearchIcon size={"small"}/>
794
800
  </IconButton>
795
- }
796
- >
797
- <form noValidate={true}
798
- onSubmit={(e) => {
799
- e.preventDefault();
800
- if (!searchString) return;
801
- setOpenPopup(false);
802
- return sideEntityController.open({
803
- entityId: searchString.trim(),
804
- path,
805
- collection,
806
- updateUrl: true,
807
- });
808
- }}
809
- className={"text-gray-900 dark:text-white w-96 max-w-full"}>
810
-
811
- <div className="flex p-2 w-full gap-4">
812
- <input
813
- autoFocus={openPopup}
814
- placeholder={"Find entity by ID"}
815
- // size={"small"}
816
- onChange={(e) => {
817
- setSearchString(e.target.value);
818
- }}
819
- value={searchString}
820
- className={"flex-grow bg-transparent outline-none p-1 " + focusedDisabled}/>
821
- <Button variant={"outlined"}
822
- disabled={!(searchString.trim())}
823
- type={"submit"}
824
- >Go</Button>
825
- </div>
826
- </form>
801
+ }>
802
+ <div className={cls("my-2 rounded-lg bg-gray-50 dark:bg-gray-950 text-gray-900 dark:text-white")}>
803
+ <form noValidate={true}
804
+ onSubmit={(e) => {
805
+ e.preventDefault();
806
+ if (!searchString) return;
807
+ setOpenPopup(false);
808
+ setRecentIds(addRecentId(collection.id, searchString.trim()));
809
+ return sideEntityController.open({
810
+ entityId: searchString.trim(),
811
+ path,
812
+ collection,
813
+ updateUrl: true
814
+ });
815
+ }}
816
+ className={"w-96 max-w-full"}>
817
+
818
+ <div className="flex p-2 w-full gap-2">
819
+ <input
820
+ autoFocus={openPopup}
821
+ placeholder={"Find entity by ID"}
822
+ // size={"small"}
823
+ onChange={(e) => {
824
+ setSearchString(e.target.value);
825
+ }}
826
+ value={searchString}
827
+ className={"rounded-lg bg-white dark:bg-gray-800 flex-grow bg-transparent outline-none p-2 " + focusedDisabled}/>
828
+ <Button variant={"text"}
829
+ disabled={!(searchString.trim())}
830
+ type={"submit"}
831
+ ><KeyboardTabIcon/></Button>
832
+ </div>
833
+ </form>
834
+ {recentIds && recentIds.length > 0 && <div className="flex flex-col gap-2 p-2">
835
+ {recentIds.map(id => (
836
+ <ReferencePreview reference={new EntityReference(id, path)}
837
+ key={id}
838
+ hover={true}
839
+ onClick={() => {
840
+ setOpenPopup(false);
841
+ sideEntityController.open({
842
+ entityId: id,
843
+ path,
844
+ collection,
845
+ updateUrl: true
846
+ });
847
+ }}
848
+ includeEntityLink={false}
849
+ size={"small"}/>
850
+ ))}
851
+ </div>}
852
+ </div>
827
853
  </Popover>
828
854
 
829
855
  </Tooltip>
@@ -0,0 +1,19 @@
1
+ export function addRecentId(collectionId: string, id: string) {
2
+ const recentIds = getRecentIds(collectionId);
3
+ const newRecentIds = [id, ...recentIds.filter(i => i !== id)];
4
+ if (newRecentIds.length > 5) {
5
+ newRecentIds.pop();
6
+ }
7
+ saveSearchedIdsLocally(collectionId, newRecentIds);
8
+ return newRecentIds;
9
+ }
10
+
11
+ export function saveSearchedIdsLocally(collectionId: string, ids: string[]) {
12
+ localStorage.setItem("recent_id_searches::" + collectionId, JSON.stringify(ids));
13
+ }
14
+
15
+ export function getRecentIds(collectionId: string): string[] {
16
+ const stored = localStorage.getItem("recent_id_searches::" + collectionId);
17
+ if (!stored) return [];
18
+ return JSON.parse(stored);
19
+ }
@@ -22,7 +22,7 @@ export type EntityPreviewProps = {
22
22
  collection?: EntityCollection,
23
23
  hover?: boolean;
24
24
  previewProperties?: string[],
25
- disabled: undefined | boolean,
25
+ disabled?: boolean,
26
26
  entity: Entity<any>,
27
27
  includeId?: boolean,
28
28
  includeEntityLink?: boolean,
@@ -82,7 +82,6 @@ export function DefaultHomePage({
82
82
  setFilteredUrls(null);
83
83
  } else {
84
84
  const searchResult = fuse.current?.search(value);
85
- console.log("Search result", searchResult);
86
85
  if (searchResult) {
87
86
  setFilteredUrls(searchResult.map((e) => e.item.url));
88
87
  }
@@ -46,7 +46,6 @@ export function SearchIconsView({
46
46
  }, [query, updateSearchResults]);
47
47
 
48
48
  const icons = keys === null ? coolIconKeys : keys;
49
- console.log("Icons", icons);
50
49
 
51
50
  return (
52
51
  <>
@@ -93,7 +93,6 @@ export function StringNumberFilterField({
93
93
 
94
94
  const multiple = multipleSelectOperations.includes(operation);
95
95
 
96
- console.log("internalValue", { internalValue });
97
96
  return (
98
97
 
99
98
  <div className="flex w-[440px]">
@@ -1,6 +1,7 @@
1
1
  import { ArchiveIcon, DeleteIcon, FileCopyIcon, KeyboardTabIcon, OpenInNewIcon } from "@firecms/ui";
2
2
  import { EntityAction } from "../../types";
3
3
  import { DeleteEntityDialog } from "../DeleteEntityDialog";
4
+ import { addRecentId } from "../EntityCollectionView/utils";
4
5
 
5
6
  export const editEntityAction: EntityAction = {
6
7
  icon: <KeyboardTabIcon/>,
@@ -19,6 +20,9 @@ export const editEntityAction: EntityAction = {
19
20
  path: entity.path,
20
21
  entityId: entity.id
21
22
  });
23
+ if (collection) {
24
+ addRecentId(collection.id, entity.id);
25
+ }
22
26
  const path = collection?.collectionGroup ? entity.path : (fullPath ?? entity.path);
23
27
  context.sideEntityController.open({
24
28
  entityId: entity.id,
@@ -93,7 +93,7 @@ export function DefaultDrawer({
93
93
  .map((view, index) =>
94
94
  <DrawerNavigationItem
95
95
  key={`navigation_${index}`}
96
- icon={<IconForView collectionOrView={view.collection ?? view.view}/>}
96
+ icon={<IconForView collectionOrView={view.collection ?? view.view} size={"small"}/>}
97
97
  tooltipsOpen={tooltipsOpen}
98
98
  adminMenuOpen={adminMenuOpen}
99
99
  drawerOpen={drawerOpen}
@@ -299,7 +299,6 @@ export function EntityEditViewInner<M extends Record<string, any>>({
299
299
  onUpdate({ entity: updatedEntity });
300
300
 
301
301
  if (closeAfterSave) {
302
- console.log("Closing side dialog")
303
302
  sideDialogContext.setBlocked(false);
304
303
  sideDialogContext.close(true);
305
304
  onClose?.();
@@ -94,7 +94,6 @@ function SideDialogView({
94
94
  const handleDrawerCloseOk = () => {
95
95
  setBlocked(false);
96
96
  setDrawerCloseRequested(false);
97
- console.log("handleDrawerCloseOk");
98
97
  sideDialogsController.close();
99
98
  panel?.onClose?.();
100
99
  };
@@ -182,7 +182,7 @@ function FileDropComponent({
182
182
  onDropRejected: (fileRejections, event) => {
183
183
  for (const fileRejection of fileRejections) {
184
184
  for (const error of fileRejection.errors) {
185
- console.log("Error uploading file: ", error);
185
+ console.error("Error uploading file: ", error);
186
186
  if (error.code === "file-too-large") {
187
187
  snackbarContext.open({
188
188
  type: "error",
@@ -4,9 +4,11 @@ import { isPropertyBuilder } from "./entities";
4
4
  export function makePropertiesEditable(properties: Properties) {
5
5
  Object.keys(properties).forEach((key) => {
6
6
  const property = properties[key];
7
- property.editable = true;
8
- if (property.dataType === "map" && property.properties) {
9
- makePropertiesEditable(property.properties as Properties);
7
+ if (property) {
8
+ property.editable = true;
9
+ if (property.dataType === "map" && property.properties) {
10
+ makePropertiesEditable(property.properties as Properties);
11
+ }
10
12
  }
11
13
  });
12
14
  return properties;
@@ -15,13 +17,19 @@ export function makePropertiesEditable(properties: Properties) {
15
17
  export function makePropertiesNonEditable(properties: PropertiesOrBuilders): PropertiesOrBuilders {
16
18
  return Object.entries(properties).reduce((acc, [key, property]) => {
17
19
  if (!isPropertyBuilder(property) && property.dataType === "map" && property.properties) {
18
- const updated = { ...property, properties: makePropertiesNonEditable(property.properties as PropertiesOrBuilders) };
20
+ const updated = {
21
+ ...property,
22
+ properties: makePropertiesNonEditable(property.properties as PropertiesOrBuilders)
23
+ };
19
24
  acc[key] = updated;
20
25
  }
21
26
  if (isPropertyBuilder(property)) {
22
27
  acc[key] = property;
23
28
  } else {
24
- acc[key] = { ...property, editable: false };
29
+ acc[key] = {
30
+ ...property,
31
+ editable: false
32
+ };
25
33
  }
26
34
  return acc;
27
35
  }, {} as PropertiesOrBuilders);