@firecms/core 3.0.0-canary.173 → 3.0.0-canary.175

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.
@@ -211,11 +211,11 @@ export type PropertyBuilder<T extends CMSType = any, M extends Record<string, an
211
211
  /**
212
212
  * @group Entity properties
213
213
  */
214
- export type PropertyOrBuilder<T extends CMSType = CMSType, M extends Record<string, any> = Record<string, any>> = Property<T> | PropertyBuilder<T, M>;
214
+ export type PropertyOrBuilder<T extends CMSType = CMSType, M extends Record<string, any> = any> = Property<T> | PropertyBuilder<T, M>;
215
215
  /**
216
216
  * @group Entity properties
217
217
  */
218
- export type PropertiesOrBuilders<M extends Record<string, any> = Record<string, any>> = {
218
+ export type PropertiesOrBuilders<M extends Record<string, any> = any> = {
219
219
  [k in keyof M]: PropertyOrBuilder<M[k], M>;
220
220
  };
221
221
  /**
@@ -12,7 +12,7 @@ export declare function buildCollection<M extends Record<string, any> = any, USE
12
12
  * @param property
13
13
  * @group Builder
14
14
  */
15
- export declare function buildProperty<T extends CMSType = CMSType, P extends PropertyOrBuilder<T> = PropertyOrBuilder<T>, M extends Record<string, any> = Record<string, any>>(property: P): P extends StringProperty ? StringProperty : P extends NumberProperty ? NumberProperty : P extends BooleanProperty ? BooleanProperty : P extends DateProperty ? DateProperty : P extends GeopointProperty ? GeopointProperty : P extends ReferenceProperty ? ReferenceProperty : P extends ArrayProperty ? ArrayProperty : P extends MapProperty ? MapProperty : P extends PropertyBuilder<T, M> ? PropertyBuilder<T, M> : never;
15
+ export declare function buildProperty<T extends CMSType = CMSType, P extends PropertyOrBuilder<T> = PropertyOrBuilder<T>, M extends Record<string, any> = any>(property: P): P extends StringProperty ? StringProperty : P extends NumberProperty ? NumberProperty : P extends BooleanProperty ? BooleanProperty : P extends DateProperty ? DateProperty : P extends GeopointProperty ? GeopointProperty : P extends ReferenceProperty ? ReferenceProperty : P extends ArrayProperty ? ArrayProperty : P extends MapProperty ? MapProperty : P extends PropertyBuilder<T, M> ? PropertyBuilder<T, M> : never;
16
16
  /**
17
17
  * Identity function we use to defeat the type system of Typescript and preserve
18
18
  * the properties keys.
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.173",
4
+ "version": "3.0.0-canary.175",
5
5
  "description": "Awesome Firebase/Firestore-based headless open-source CMS",
6
6
  "funding": {
7
7
  "url": "https://github.com/sponsors/firecmsco"
@@ -50,24 +50,24 @@
50
50
  "./package.json": "./package.json"
51
51
  },
52
52
  "dependencies": {
53
- "@firecms/editor": "^3.0.0-canary.173",
54
- "@firecms/formex": "^3.0.0-canary.173",
55
- "@firecms/ui": "^3.0.0-canary.173",
53
+ "@firecms/editor": "^3.0.0-canary.175",
54
+ "@firecms/formex": "^3.0.0-canary.175",
55
+ "@firecms/ui": "^3.0.0-canary.175",
56
56
  "@hello-pangea/dnd": "^17.0.0",
57
- "@radix-ui/react-portal": "^1.1.2",
57
+ "@radix-ui/react-portal": "^1.1.3",
58
58
  "clsx": "^2.1.1",
59
59
  "date-fns": "^3.6.0",
60
60
  "fuse.js": "^7.0.0",
61
61
  "history": "^5.3.0",
62
62
  "markdown-it": "^14.1.0",
63
- "notistack": "^3.0.1",
63
+ "notistack": "^3.0.2",
64
64
  "object-hash": "^3.0.0",
65
65
  "react-dropzone": "^14.3.5",
66
66
  "react-fast-compare": "^3.2.2",
67
67
  "react-image-file-resizer": "^0.4.8",
68
68
  "react-transition-group": "^4.4.5",
69
69
  "react-use-measure": "^2.1.1",
70
- "react-window": "^1.8.10",
70
+ "react-window": "^1.8.11",
71
71
  "yup": "^0.32.11"
72
72
  },
73
73
  "peerDependencies": {
@@ -78,13 +78,13 @@
78
78
  },
79
79
  "devDependencies": {
80
80
  "@jest/globals": "^29.7.0",
81
- "@testing-library/react": "^16.0.1",
82
- "@testing-library/user-event": "^14.5.2",
81
+ "@testing-library/react": "^16.2.0",
82
+ "@testing-library/user-event": "^14.6.1",
83
83
  "@types/jest": "^29.5.14",
84
- "@types/node": "^20.17.9",
84
+ "@types/node": "^20.17.14",
85
85
  "@types/object-hash": "^3.0.6",
86
- "@types/react": "^18.3.11",
87
- "@types/react-dom": "^18.3.1",
86
+ "@types/react": "^18.3.18",
87
+ "@types/react-dom": "^18.3.0",
88
88
  "@types/react-measure": "^2.0.12",
89
89
  "@vitejs/plugin-react": "^4.3.4",
90
90
  "babel-plugin-react-compiler": "beta",
@@ -92,19 +92,19 @@
92
92
  "eslint-plugin-react-compiler": "beta",
93
93
  "jest": "^29.7.0",
94
94
  "npm-run-all": "^4.1.5",
95
- "react-router": "^6.28.0",
96
- "react-router-dom": "^6.28.0",
95
+ "react-router": "^6.28.2",
96
+ "react-router-dom": "^6.28.2",
97
97
  "ts-jest": "^29.2.5",
98
98
  "ts-node": "^10.9.2",
99
99
  "tsd": "^0.31.2",
100
- "typescript": "^5.7.2",
101
- "vite": "^5.4.11"
100
+ "typescript": "^5.7.3",
101
+ "vite": "^5.4.14"
102
102
  },
103
103
  "files": [
104
104
  "dist",
105
105
  "src"
106
106
  ],
107
- "gitHead": "e406bf233e041e7361697bd583e622894a0bd631",
107
+ "gitHead": "0fd5d55e8bba65c5bd992606c3f8c61e0ad75ed7",
108
108
  "publishConfig": {
109
109
  "access": "public"
110
110
  },
@@ -76,12 +76,12 @@ import {
76
76
  } from "@firecms/ui";
77
77
  import { Formex, FormexController, getIn, setIn, useCreateFormex } from "@firecms/formex";
78
78
  import { useAnalyticsController } from "../hooks/useAnalyticsController";
79
- import { CustomIdField } from "../form/components/CustomIdField";
80
79
  import { CustomFieldValidator, getYupEntitySchema } from "../form/validation";
81
- import { ErrorFocus } from "../form/components/ErrorFocus";
82
80
  import { LabelWithIconAndTooltip, PropertyFieldBinding } from "../form";
83
81
  import { ValidationError } from "yup";
84
82
  import { getEntityFromCache, removeEntityFromCache, saveEntityToCache } from "../util/entity_cache";
83
+ import { CustomIdField } from "../form/components/CustomIdField";
84
+ import { ErrorFocus } from "../form/components/ErrorFocus";
85
85
 
86
86
  const MAIN_TAB_VALUE = "main_##Q$SC^#S6";
87
87
 
@@ -158,6 +158,46 @@ export function EntityEditView<M extends Record<string, any>, USER extends User>
158
158
  />;
159
159
  }
160
160
 
161
+ function FormLayout({
162
+ id,
163
+ formex,
164
+ children,
165
+ className
166
+ }: {
167
+ id?: string,
168
+ formex: FormexController<any>,
169
+ children: React.ReactNode,
170
+ className?: string
171
+ }) {
172
+
173
+ return <div
174
+ role="tabpanel"
175
+ id={id}
176
+ className={cls("relative flex flex-row max-w-4xl lg:max-w-3xl xl:max-w-4xl 2xl:max-w-5xl w-full h-fit", className)}>
177
+
178
+ <div className={cls("flex flex-col w-full pt-12 pb-16 px-4 sm:px-8 md:px-10")}>
179
+
180
+ {formex.dirty
181
+ ? <Tooltip title={"Unsaved changes"}
182
+ className={"self-end sticky top-4 z-10"}>
183
+ <Chip size={"small"} colorScheme={"orangeDarker"}>
184
+ <EditIcon size={"smallest"}/>
185
+ </Chip>
186
+ </Tooltip>
187
+ : <Tooltip title={"In sync with the database"}
188
+ className={"self-end sticky top-4 z-10"}>
189
+ <Chip size={"small"}>
190
+ <CheckIcon size={"smallest"}/>
191
+ </Chip>
192
+ </Tooltip>}
193
+
194
+ {children}
195
+
196
+ </div>
197
+
198
+ </div>
199
+ }
200
+
161
201
  export function EntityEditViewInner<M extends Record<string, any>>({
162
202
  path,
163
203
  entityId: entityIdProp,
@@ -535,7 +575,37 @@ export function EntityEditViewInner<M extends Record<string, any>>({
535
575
  const selectedEntityView = resolvedEntityViews.find(e => e.key === selectedTab);
536
576
  const shouldShowEntityActions = !autoSave && (selectedTab === MAIN_TAB_VALUE || selectedEntityView?.includeActions);
537
577
 
578
+ const secondaryForms: React.ReactNode[] | undefined = customViews && resolvedEntityViews
579
+ .filter(e => e.includeActions)
580
+ .map(
581
+ (customView, colIndex) => {
582
+ if (!customView)
583
+ return null;
584
+ const Builder = customView.Builder;
585
+ if (!Builder) {
586
+ console.error("customView.Builder is not defined");
587
+ return null;
588
+ }
589
+
590
+ return <FormLayout
591
+ key={`custom_view_${customView.key}`}
592
+ className={selectedTab !== customView.key ? "hidden" : ""}
593
+ formex={formex}>
594
+ <ErrorBoundary>
595
+ {formContext && <Builder
596
+ collection={collection}
597
+ entity={usedEntity}
598
+ modifiedValues={formex.values ?? usedEntity?.values}
599
+ formContext={formContext}
600
+ />}
601
+ </ErrorBoundary>
602
+ </FormLayout>
603
+
604
+ }
605
+ ).filter(Boolean);
606
+
538
607
  const customViewsView: React.ReactNode[] | undefined = customViews && resolvedEntityViews
608
+ .filter(e => !e.includeActions)
539
609
  .map(
540
610
  (customView, colIndex) => {
541
611
  if (!customView)
@@ -545,6 +615,7 @@ export function EntityEditViewInner<M extends Record<string, any>>({
545
615
  console.error("customView.Builder is not defined");
546
616
  return null;
547
617
  }
618
+
548
619
  return <div
549
620
  className={cls(defaultBorderMixin,
550
621
  "relative flex-grow w-full h-full overflow-auto",
@@ -572,12 +643,10 @@ export function EntityEditViewInner<M extends Record<string, any>>({
572
643
  (subcollection, colIndex) => {
573
644
  const subcollectionId = subcollection.id ?? subcollection.path;
574
645
  const fullPath = usedEntity ? `${path}/${usedEntity?.id}/${removeInitialAndTrailingSlashes(subcollectionId)}` : undefined;
646
+ if (selectedTab !== subcollectionId) return null;
575
647
  return (
576
648
  <div
577
- className={cls("relative flex-grow h-full overflow-auto w-full",
578
- {
579
- "hidden": selectedTab !== subcollectionId
580
- })}
649
+ className={"relative flex-grow h-full overflow-auto w-full"}
581
650
  key={`subcol_${subcollectionId}`}
582
651
  role="tabpanel">
583
652
 
@@ -882,22 +951,7 @@ export function EntityEditViewInner<M extends Record<string, any>>({
882
951
  : (!readOnly
883
952
  ? (
884
953
  <ErrorBoundary>
885
- <div className="flex flex-col w-full pt-12 pb-16 px-4 sm:px-8 md:px-10">
886
-
887
- {formex.dirty
888
- ? <Tooltip title={"Unsaved changes"}
889
- className={"self-end sticky top-4 z-10"}>
890
- <Chip size={"small"} colorScheme={"orangeDarker"}>
891
- <EditIcon size={"smallest"}/>
892
- </Chip>
893
- </Tooltip>
894
- : <Tooltip title={"In sync with the database"}
895
- className={"self-end sticky top-4 z-10"}>
896
- <Chip size={"small"}>
897
- <CheckIcon size={"smallest"}/>
898
- </Chip>
899
- </Tooltip>}
900
-
954
+ <>
901
955
  <div
902
956
  className={"w-full py-2 flex flex-col items-start mt-4 lg:mt-8 mb-8"}>
903
957
 
@@ -929,12 +983,11 @@ export function EntityEditViewInner<M extends Record<string, any>>({
929
983
  <ErrorFocus containerRef={formRef}/>
930
984
 
931
985
  </div>
932
-
933
- {actionsAtTheBottom && <div className="h-16"/>}
934
-
935
986
  </>}
936
987
 
937
- </div>
988
+ {actionsAtTheBottom && <div className="h-16"/>}
989
+
990
+ </>
938
991
  </ErrorBoundary>
939
992
  )
940
993
  : (
@@ -1028,29 +1081,26 @@ export function EntityEditViewInner<M extends Record<string, any>>({
1028
1081
  noValidate
1029
1082
  className={"flex-1 flex flex-row w-full overflow-y-auto justify-center"}>
1030
1083
 
1031
- <div
1032
- role="tabpanel"
1033
- hidden={!mainViewVisible}
1084
+ <FormLayout
1085
+ className={!mainViewVisible ? "hidden" : ""}
1034
1086
  id={`form_${path}`}
1035
- className={cls("relative flex flex-row max-w-4xl lg:max-w-3xl xl:max-w-4xl 2xl:max-w-5xl w-full h-fit", {
1036
- "hidden": !mainViewVisible
1037
- })}>
1038
-
1087
+ formex={formex}>
1039
1088
  {globalLoading
1040
- ? <div className="w-full pt-12 pb-16 px-4 sm:px-8 md:px-10"><CircularProgressCenter/></div>
1089
+ ? <div className="w-full pt-12 pb-16 px-4 sm:px-8 md:px-10">
1090
+ <CircularProgressCenter/>
1091
+ </div>
1041
1092
  : entityView}
1093
+ </FormLayout>
1042
1094
 
1043
- </div>
1044
-
1045
- {mainViewVisible && shouldShowEntityActions && !actionsAtTheBottom && dialogActions}
1095
+ {secondaryForms}
1046
1096
 
1047
- {customViewsView}
1097
+ {shouldShowEntityActions && dialogActions}
1048
1098
 
1049
- {subCollectionsViews}
1099
+ </form>
1050
1100
 
1051
- {shouldShowEntityActions && actionsAtTheBottom && dialogActions}
1101
+ {customViewsView}
1052
1102
 
1053
- </form>
1103
+ {subCollectionsViews}
1054
1104
 
1055
1105
  </div>;
1056
1106
 
@@ -74,7 +74,7 @@ export const PropertyFieldBinding = React.memo(PropertyFieldBindingInternal, (a:
74
74
  return false;
75
75
  }) as typeof PropertyFieldBindingInternal;
76
76
 
77
- function PropertyFieldBindingInternal<T extends CMSType = CMSType, M extends Record<string, any> = Record<string, any>>
77
+ function PropertyFieldBindingInternal<T extends CMSType = CMSType, M extends Record<string, any> = any>
78
78
  ({
79
79
  propertyKey,
80
80
  property,
@@ -175,7 +175,7 @@ function PropertyFieldBindingInternal<T extends CMSType = CMSType, M extends Rec
175
175
 
176
176
  }
177
177
 
178
- type ResolvedPropertyFieldBindingProps<T extends CMSType = CMSType, M extends Record<string, any> = Record<string, any>> =
178
+ type ResolvedPropertyFieldBindingProps<T extends CMSType = CMSType, M extends Record<string, any> = any> =
179
179
  Omit<PropertyFieldBindingProps<T, M>, "property">
180
180
  & {
181
181
  property: ResolvedProperty<T>
@@ -11,7 +11,7 @@ import {
11
11
  } from "../util/navigation_from_path";
12
12
  import { useBreadcrumbsController } from "../hooks/useBreadcrumbsController";
13
13
  import { toArray } from "../util/arrays";
14
- import { EntityCollectionView } from "../components";
14
+ import { EntityCollectionView, NotFoundPage } from "../components";
15
15
  import { UnsavedChangesDialog } from "../components/UnsavedChangesDialog";
16
16
 
17
17
  export function FireCMSRoute() {
@@ -191,11 +191,11 @@ function EntityFullScreenRoute({
191
191
  const lastCollectionEntry = navigationEntries.findLast((entry) => entry.type === "collection");
192
192
 
193
193
  if (isNew && !lastCollectionEntry) {
194
- throw new Error("No collection found in the navigation");
194
+ throw new Error("INTERNAL: No collection found in the navigation");
195
195
  }
196
196
 
197
197
  if (!isNew && !lastEntityEntry) {
198
- throw new Error("No entity found in the navigation");
198
+ return <NotFoundPage/>;
199
199
  }
200
200
 
201
201
  const collection = isNew ? lastCollectionEntry!.collection : lastEntityEntry!.parentCollection;
@@ -285,14 +285,14 @@ export type PropertyBuilder<T extends CMSType = any, M extends Record<string, an
285
285
  /**
286
286
  * @group Entity properties
287
287
  */
288
- export type PropertyOrBuilder<T extends CMSType = CMSType, M extends Record<string, any> = Record<string, any>> =
288
+ export type PropertyOrBuilder<T extends CMSType = CMSType, M extends Record<string, any> = any> =
289
289
  Property<T>
290
290
  | PropertyBuilder<T, M>;
291
291
 
292
292
  /**
293
293
  * @group Entity properties
294
294
  */
295
- export type PropertiesOrBuilders<M extends Record<string, any> = Record<string, any>> =
295
+ export type PropertiesOrBuilders<M extends Record<string, any> = any> =
296
296
  {
297
297
  [k in keyof M]: PropertyOrBuilder<M[k], M>;
298
298
  };
@@ -26,8 +26,10 @@ import {
26
26
  * @param collection
27
27
  * @group Builder
28
28
  */
29
- export function buildCollection<M extends Record<string, any> = any,
30
- USER extends User = User>(
29
+ export function buildCollection<
30
+ M extends Record<string, any> = any,
31
+ USER extends User = User>
32
+ (
31
33
  collection: EntityCollection<M, USER>
32
34
  ): EntityCollection<M, USER> {
33
35
  return collection;
@@ -39,7 +41,7 @@ export function buildCollection<M extends Record<string, any> = any,
39
41
  * @param property
40
42
  * @group Builder
41
43
  */
42
- export function buildProperty<T extends CMSType = CMSType, P extends PropertyOrBuilder<T> = PropertyOrBuilder<T>, M extends Record<string, any> = Record<string, any>>(
44
+ export function buildProperty<T extends CMSType = CMSType, P extends PropertyOrBuilder<T> = PropertyOrBuilder<T>, M extends Record<string, any> = any>(
43
45
  property: P
44
46
  ):
45
47
  P extends StringProperty ? StringProperty :