@firecms/core 3.0.0-canary.178 → 3.0.0-canary.179

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.
@@ -103,6 +103,11 @@ export interface BaseProperty<T extends CMSType, CustomProps = any> {
103
103
  * save the new config. The saved config will then become the source of truth.
104
104
  */
105
105
  editable?: boolean;
106
+ /**
107
+ * A number between 0 and 100 that indicates the width of the field in the form view.
108
+ * It defaults to 100, but you can set it to 50 to have two fields in the same row.
109
+ */
110
+ widthPercentage?: number;
106
111
  }
107
112
  /**
108
113
  * @group Entity properties
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.178",
4
+ "version": "3.0.0-canary.179",
5
5
  "description": "Awesome Firebase/Firestore-based headless open-source CMS",
6
6
  "funding": {
7
7
  "url": "https://github.com/sponsors/firecmsco"
@@ -50,9 +50,9 @@
50
50
  "./package.json": "./package.json"
51
51
  },
52
52
  "dependencies": {
53
- "@firecms/editor": "^3.0.0-canary.178",
54
- "@firecms/formex": "^3.0.0-canary.178",
55
- "@firecms/ui": "^3.0.0-canary.178",
53
+ "@firecms/editor": "^3.0.0-canary.179",
54
+ "@firecms/formex": "^3.0.0-canary.179",
55
+ "@firecms/ui": "^3.0.0-canary.179",
56
56
  "@hello-pangea/dnd": "^17.0.0",
57
57
  "@radix-ui/react-portal": "^1.1.3",
58
58
  "clsx": "^2.1.1",
@@ -104,7 +104,7 @@
104
104
  "dist",
105
105
  "src"
106
106
  ],
107
- "gitHead": "87cb7b5b305fbcca1223aa8e3a62d6eee0f3570d",
107
+ "gitHead": "39125fe4c1a15167d203baee64f2bbd5376eb766",
108
108
  "publishConfig": {
109
109
  "access": "public"
110
110
  },
@@ -1,6 +1,6 @@
1
1
  import React from "react";
2
2
 
3
- import { canCreateEntity, canDeleteEntity, fullPathToCollectionSegments } from "../../util";
3
+ import { canCreateEntity, canDeleteEntity } from "../../util";
4
4
  import { useAuthController, useCustomizationController, useFireCMSContext, useLargeLayout } from "../../hooks";
5
5
  import { CollectionActionsProps, EntityCollection, EntityTableController, SelectionController } from "../../types";
6
6
  import { AddIcon, Button, DeleteIcon, IconButton, Tooltip } from "@firecms/ui";
@@ -124,7 +124,9 @@ export function EntityCollectionViewActions<M extends Record<string, any>>({
124
124
 
125
125
  return (
126
126
  <>
127
- {actions}
127
+ <ErrorBoundary>
128
+ {actions}
129
+ </ErrorBoundary>
128
130
  {multipleDeleteButton}
129
131
  {addButton}
130
132
  </>
@@ -4,7 +4,7 @@ import { useCallback, useState } from "react";
4
4
  export function PropertyIdCopyTooltip({
5
5
  propertyKey,
6
6
  className,
7
- children
7
+ children,
8
8
  }: {
9
9
  propertyKey: string,
10
10
  className?: string,
@@ -13,11 +13,10 @@ export function PropertyIdCopyTooltip({
13
13
  return <Tooltip title={<PropertyIdCopyTooltipContent propertyKey={propertyKey}/>}
14
14
  delayDuration={800}
15
15
  side={"top"}
16
- asChild={false}
17
16
  align={"start"}
18
17
  sideOffset={8}
19
18
  className={className}>
20
- {children}
19
+ {children}
21
20
  </Tooltip>
22
21
 
23
22
  }
@@ -127,6 +127,7 @@ export function EntityEditView<M extends Record<string, any>, USER extends User>
127
127
  entityId,
128
128
  ...props
129
129
  }: EntityEditViewProps<M>) {
130
+
130
131
  const {
131
132
  entity,
132
133
  dataLoading,
@@ -173,7 +174,7 @@ function FormLayout({
173
174
  return <div
174
175
  role="tabpanel"
175
176
  id={id}
176
- className={cls("relative flex flex-row max-w-4xl lg:max-w-3xl xl:max-w-5xl 2xl:max-w-7xl w-full h-fit", className)}>
177
+ className={cls("relative flex flex-row max-w-4xl lg:max-w-3xl xl:max-w-4xl 2xl:max-w-6xl w-full h-fit", className)}>
177
178
 
178
179
  <div className={cls("flex flex-col w-full pt-12 pb-16 px-4 sm:px-8 md:px-10")}>
179
180
 
@@ -225,8 +226,6 @@ export function EntityEditViewInner<M extends Record<string, any>>({
225
226
  console.warn(`The collection ${collection.path} has customId and formAutoSave enabled. This is not supported and formAutoSave will be ignored`);
226
227
  }
227
228
 
228
- const actionsAtTheBottom = !largeLayout || layout === "side_panel";
229
-
230
229
  const [saving, setSaving] = useState(false);
231
230
  /**
232
231
  * These are the values that are being saved. They are debounced.
@@ -316,7 +315,6 @@ export function EntityEditViewInner<M extends Record<string, any>>({
316
315
  const hasAdditionalViews = customViewsCount > 0 || subcollectionsCount > 0;
317
316
 
318
317
  const [usedEntity, setUsedEntity] = useState<Entity<M> | undefined>(entity);
319
- const [readOnly, setReadOnly] = useState<boolean | undefined>(undefined);
320
318
 
321
319
  const baseDataSourceValuesRef = useRef<Partial<EntityValues<M>> | null>(cachedDirtyValues ?? getDataSourceEntityValues(initialResolvedCollection, status, usedEntity));
322
320
 
@@ -325,16 +323,17 @@ export function EntityEditViewInner<M extends Record<string, any>>({
325
323
  setUsedEntity(entity);
326
324
  }, [entity]);
327
325
 
328
- useEffect(() => {
329
- if (status === "new") {
330
- setReadOnly(false);
326
+ const canEdit = useMemo(() => {
327
+ if (status === "new" || status === "copy") {
328
+ return true;
331
329
  } else {
332
- const editEnabled = usedEntity ? canEditEntity(collection, authController, path, usedEntity ?? null) : false;
333
- if (usedEntity)
334
- setReadOnly(!editEnabled);
330
+ return usedEntity ? canEditEntity(collection, authController, path, usedEntity ?? null) : false;
335
331
  }
336
332
  }, [authController, usedEntity, status]);
337
333
 
334
+ const readOnly = !canEdit;
335
+ console.log("readOnly", readOnly);
336
+
338
337
  const onPreSaveHookError = useCallback((e: Error) => {
339
338
  setSaving(false);
340
339
  snackbarController.open({
@@ -573,7 +572,8 @@ export function EntityEditViewInner<M extends Record<string, any>>({
573
572
  : [];
574
573
 
575
574
  const selectedEntityView = resolvedEntityViews.find(e => e.key === selectedTab);
576
- const shouldShowEntityActions = selectedTab === MAIN_TAB_VALUE || selectedEntityView?.includeActions;
575
+ const shouldShowEntityActions = !readOnly && (selectedTab === MAIN_TAB_VALUE || selectedEntityView?.includeActions);
576
+ const actionsAtTheBottom = !largeLayout || layout === "side_panel" || shouldShowEntityActions === "bottom";
577
577
 
578
578
  const secondaryForms: React.ReactNode[] | undefined = customViews && resolvedEntityViews
579
579
  .filter(e => e.includeActions)
@@ -618,7 +618,7 @@ export function EntityEditViewInner<M extends Record<string, any>>({
618
618
 
619
619
  return <div
620
620
  className={cls(defaultBorderMixin,
621
- "relative flex-grow w-full h-full overflow-auto",
621
+ "relative flex-1 w-full h-full overflow-auto",
622
622
  {
623
623
  "hidden": selectedTab !== customView.key
624
624
  })}
@@ -646,7 +646,7 @@ export function EntityEditViewInner<M extends Record<string, any>>({
646
646
  if (selectedTab !== subcollectionId) return null;
647
647
  return (
648
648
  <div
649
- className={"relative flex-grow h-full overflow-auto w-full"}
649
+ className={"relative flex-1 h-full overflow-auto w-full"}
650
650
  key={`subcol_${subcollectionId}`}
651
651
  role="tabpanel">
652
652
 
@@ -827,9 +827,12 @@ export function EntityEditViewInner<M extends Record<string, any>>({
827
827
  }
828
828
  }, [formex.isSubmitting, autoSave, underlyingChanges, entity, formex.values, formex.touched, formex.setFieldValue]);
829
829
 
830
+ const formFieldKeys = getFormFieldKeys(resolvedCollection);
831
+ const resolvedProperties = formFieldKeys.map(key => resolvedCollection.properties[key]);
832
+
830
833
  const formFields = (
831
- <>
832
- {(getFormFieldKeys(resolvedCollection))
834
+ <div className={"flex flex-wrap gap-x-4 w-full space-y-8"}>
835
+ {formFieldKeys
833
836
  .map((key) => {
834
837
 
835
838
  const property = resolvedCollection.properties[key];
@@ -843,6 +846,7 @@ export function EntityEditViewInner<M extends Record<string, any>>({
843
846
  const disabled = (!autoSave && formex.isSubmitting) || isReadOnly(property) || Boolean(property.disabled);
844
847
  const hidden = isHidden(property);
845
848
  if (hidden) return null;
849
+ const widthPercentage = property.widthPercentage ?? 100;
846
850
  const cmsFormFieldProps: PropertyFieldBindingProps<any, M> = {
847
851
  propertyKey: key,
848
852
  disabled,
@@ -857,6 +861,8 @@ export function EntityEditViewInner<M extends Record<string, any>>({
857
861
 
858
862
  return (
859
863
  <div id={`form_field_${key}`}
864
+ className={"relative"}
865
+ style={{ width: widthPercentage === 100 ? "100%" : `calc(${widthPercentage}% - 8px)` }}
860
866
  key={`field_${resolvedCollection.name}_${key}`}>
861
867
  <ErrorBoundary>
862
868
  <PropertyFieldBinding {...cmsFormFieldProps}/>
@@ -874,21 +880,22 @@ export function EntityEditViewInner<M extends Record<string, any>>({
874
880
 
875
881
  const child = Builder
876
882
  ? <Builder entity={entity} context={context}/>
877
- : <>
883
+ : <div className={"w-full"}>
878
884
  {additionalField.value?.({
879
885
  entity,
880
886
  context
881
887
  })?.toString()}
882
- </>;
888
+ </div>;
889
+
883
890
  return (
884
- <div key={`additional_${key}`}>
891
+ <div key={`additional_${key}`} className={"w-full"}>
885
892
  <LabelWithIconAndTooltip
886
893
  propertyKey={key}
887
894
  icon={<NotesIcon size={"small"}/>}
888
895
  title={additionalField.name}
889
896
  className={"text-text-secondary dark:text-text-secondary-dark ml-3.5"}/>
890
897
  <div
891
- className={cls(paperMixin, "min-h-14 p-4 md:p-6 overflow-x-scroll no-scrollbar")}>
898
+ className={cls(paperMixin, "w-full min-h-14 p-4 md:p-6 overflow-x-scroll no-scrollbar")}>
892
899
 
893
900
  <ErrorBoundary>
894
901
  {child}
@@ -904,7 +911,7 @@ export function EntityEditViewInner<M extends Record<string, any>>({
904
911
  })
905
912
  .filter(Boolean)}
906
913
 
907
- </>);
914
+ </div>);
908
915
 
909
916
  const disabled = formex.isSubmitting || (!modified && status === "existing");
910
917
  const formRef = React.useRef<HTMLDivElement>(null);
@@ -961,7 +968,7 @@ export function EntityEditViewInner<M extends Record<string, any>>({
961
968
  </Typography>
962
969
  <Alert color={"base"} className={"w-full"} size={"small"}>
963
970
  <code
964
- className={"text-xs select-all text-text-secondary dark:text-text-secondary-dark"}>{path}/{entityId}</code>
971
+ className={"text-xs select-all text-text-secondary dark:text-text-secondary-dark"}>{entity?.path ?? path}/{entityId}</code>
965
972
  </Alert>
966
973
  </div>
967
974
 
@@ -991,18 +998,18 @@ export function EntityEditViewInner<M extends Record<string, any>>({
991
998
  </ErrorBoundary>
992
999
  )
993
1000
  : (
994
- <>
1001
+ <div className={"flex flex-col"}>
995
1002
  <Typography
996
1003
  className={"mt-16 mb-8 mx-8"}
997
1004
  variant={"h4"}>{collection.singularName ?? collection.name}
998
1005
  </Typography>
999
1006
  <EntityView
1000
- className={"px-12"}
1007
+ className={"px-8"}
1001
1008
  entity={usedEntity as Entity<M>}
1002
1009
  path={path}
1003
1010
  collection={collection}/>
1004
1011
 
1005
- </>
1012
+ </div>
1006
1013
  ));
1007
1014
 
1008
1015
  const subcollectionTabs = subcollections && subcollections.map(
@@ -1032,6 +1039,7 @@ export function EntityEditViewInner<M extends Record<string, any>>({
1032
1039
  }, [entityId, onIdChange]);
1033
1040
 
1034
1041
  const shouldShowTopBar = Boolean(barActions) || hasAdditionalViews;
1042
+ const shouldIncludeForm = selectedTab === MAIN_TAB_VALUE || selectedEntityView?.includeActions;
1035
1043
 
1036
1044
  let result = <div className="relative flex flex-col h-full w-full bg-white dark:bg-surface-900">
1037
1045
 
@@ -1079,7 +1087,8 @@ export function EntityEditViewInner<M extends Record<string, any>>({
1079
1087
  return onDiscard();
1080
1088
  }}
1081
1089
  noValidate
1082
- className={"flex-1 flex flex-row w-full overflow-y-auto justify-center"}>
1090
+ className={cls("flex-1 flex flex-row w-full overflow-y-auto justify-center",
1091
+ shouldIncludeForm ? "" : "hidden")}>
1083
1092
 
1084
1093
  <FormLayout
1085
1094
  className={!mainViewVisible ? "hidden" : ""}
@@ -1205,6 +1214,7 @@ function buildBottomActions<M extends object>({
1205
1214
  layout
1206
1215
  }: ActionsViewProps<M>) {
1207
1216
 
1217
+ const canClose = layout === "side_panel";
1208
1218
  return <DialogActions position={"absolute"}>
1209
1219
 
1210
1220
  {savingError &&
@@ -1246,7 +1256,7 @@ function buildBottomActions<M extends object>({
1246
1256
  </Button>
1247
1257
 
1248
1258
  <Button
1249
- variant="text"
1259
+ variant={canClose ? "text" : "filled"}
1250
1260
  color="primary"
1251
1261
  type="submit"
1252
1262
  disabled={disabled || isSubmitting}
@@ -1258,7 +1268,7 @@ function buildBottomActions<M extends object>({
1258
1268
  {status === "new" && "Create"}
1259
1269
  </Button>
1260
1270
 
1261
- <LoadingButton
1271
+ {canClose && <LoadingButton
1262
1272
  variant="filled"
1263
1273
  color="primary"
1264
1274
  type="submit"
@@ -1270,7 +1280,7 @@ function buildBottomActions<M extends object>({
1270
1280
  {status === "existing" && "Save and close"}
1271
1281
  {status === "copy" && "Create copy and close"}
1272
1282
  {status === "new" && "Create and close"}
1273
- </LoadingButton>
1283
+ </LoadingButton>}
1274
1284
 
1275
1285
  </DialogActions>;
1276
1286
  }
@@ -1290,7 +1300,7 @@ function buildSideActions<M extends object>({
1290
1300
  }: ActionsViewProps<M>) {
1291
1301
 
1292
1302
  return <div
1293
- className={cls("overflow-auto h-full flex flex-col gap-2 w-80 xl:w-96 px-4 py-16 sticky top-0 border-l", defaultBorderMixin)}>
1303
+ className={cls("overflow-auto h-full flex flex-col gap-2 w-80 2xl:w-96 px-4 py-16 sticky top-0 border-l", defaultBorderMixin)}>
1294
1304
 
1295
1305
  <LoadingButton
1296
1306
  fullWidth={true}
@@ -5,13 +5,13 @@ import { Field, FieldProps as FormexFieldProps, getIn } from "@firecms/formex";
5
5
 
6
6
  import {
7
7
  CMSType,
8
- EntityCollection,
9
8
  FieldProps,
10
9
  FireCMSPlugin,
11
10
  PluginFieldBuilderParams,
12
11
  Property,
13
12
  PropertyFieldBindingProps,
14
13
  PropertyOrBuilder,
14
+ ResolvedEntityCollection,
15
15
  ResolvedProperty
16
16
  } from "../types";
17
17
  import { ReadOnlyFieldBinding } from "./field_bindings/ReadOnlyFieldBinding";
@@ -147,7 +147,9 @@ function PropertyFieldBindingInternal<T extends CMSType = CMSType, M extends Rec
147
147
  console.warn(`No field component found for property ${propertyKey}`);
148
148
  console.warn("Property:", property);
149
149
  return (
150
- <div>{`Currently the field ${resolvedProperty.dataType} is not supported`}</div>
150
+ <div className={"w-full"}>
151
+ {`Currently the field ${resolvedProperty.dataType} is not supported`}
152
+ </div>
151
153
  );
152
154
  }
153
155
 
@@ -199,11 +201,11 @@ function FieldInternal<T extends CMSType, CustomProps, M extends Record<string,
199
201
  },
200
202
  formexFieldProps
201
203
  }:
202
- {
203
- Component: ComponentType<FieldProps<T, any, M>>,
204
- componentProps: ResolvedPropertyFieldBindingProps<T, M>,
205
- formexFieldProps: FormexFieldProps<T, any>
206
- }) {
204
+ {
205
+ Component: ComponentType<FieldProps<T, any, M>>,
206
+ componentProps: ResolvedPropertyFieldBindingProps<T, M>,
207
+ formexFieldProps: FormexFieldProps<T, any>
208
+ }) {
207
209
 
208
210
  const { plugins } = useCustomizationController();
209
211
 
@@ -296,7 +298,7 @@ const shouldPropertyReRender = (property: PropertyOrBuilder | ResolvedProperty,
296
298
 
297
299
  interface UseWrappedComponentParams<T extends CMSType = CMSType, M extends Record<string, any> = any> {
298
300
  path?: string,
299
- collection?: EntityCollection<M>,
301
+ collection?: ResolvedEntityCollection<M>,
300
302
  propertyKey: string,
301
303
  property: ResolvedProperty<T>,
302
304
  Component: ComponentType<FieldProps<T, any, M>>,
@@ -327,7 +329,7 @@ function useWrappedComponent<T extends CMSType = CMSType, M extends Record<strin
327
329
  Field: Component,
328
330
  plugin,
329
331
  path,
330
- collection
332
+ collection,
331
333
  };
332
334
  const enabled = plugin.form?.fieldBuilderEnabled?.(params);
333
335
  if (enabled === undefined || enabled)
@@ -1,4 +1,5 @@
1
1
  import React, { forwardRef } from "react";
2
+ import { cls } from "@firecms/ui";
2
3
 
3
4
  interface LabelWithIconProps {
4
5
  icon: React.ReactNode;
@@ -23,9 +24,11 @@ export const LabelWithIcon = forwardRef<HTMLDivElement, LabelWithIconProps>(
23
24
  return (
24
25
  <div
25
26
  ref={ref}
26
- className={`inline-flex items-center my-0.5 ${small ? "gap-1" : "gap-2"} ${className ?? ""}`}
27
+ className={cls("inline-flex items-center my-0.5",
28
+ small ? "gap-1" : "gap-2",
29
+ className)}
27
30
  >
28
- {icon}
31
+ {icon}
29
32
  <span
30
33
  className={`text-start font-medium text-${small ? "base" : "sm"} origin-top-left transform ${
31
34
  small ? "translate-x-2 scale-75" : ""
@@ -33,7 +36,7 @@ export const LabelWithIcon = forwardRef<HTMLDivElement, LabelWithIconProps>(
33
36
  >
34
37
  {(title ?? "") + (required ? " *" : "")}
35
38
  </span>
36
- </div>
39
+ </div>
37
40
  );
38
41
  }
39
42
  );
@@ -54,7 +54,7 @@ export function ArrayCustomShapedFieldBinding<T extends Array<any>>({
54
54
  icon={getIconForProperty(property, "small")}
55
55
  required={property.validation?.required}
56
56
  title={property.name}
57
- className={"flex-grow text-text-secondary dark:text-text-secondary-dark"}/>
57
+ className={"h-8 flex-grow text-text-secondary dark:text-text-secondary-dark"}/>
58
58
  {Array.isArray(value) && <Typography variant={"caption"} className={"px-4"}>({value.length})</Typography>}
59
59
  </>);
60
60
 
@@ -106,7 +106,7 @@ export function ArrayOfReferencesFieldBinding({
106
106
  icon={getIconForProperty(property, "small")}
107
107
  required={property.validation?.required}
108
108
  title={property.name}
109
- className={"flex flex-grow text-text-secondary dark:text-text-secondary-dark"}/>
109
+ className={"h-8 flex flex-grow text-text-secondary dark:text-text-secondary-dark"}/>
110
110
  {Array.isArray(value) && <Typography variant={"caption"} className={"px-4"}>({value.length})</Typography>}
111
111
  </>);
112
112
 
@@ -95,15 +95,6 @@ export function MapFieldBinding({
95
95
  </>
96
96
  ;
97
97
 
98
- const title = (
99
- <LabelWithIconAndTooltip
100
- propertyKey={propertyKey}
101
- icon={getIconForProperty(property, "small")}
102
- required={property.validation?.required}
103
- title={property.name}
104
- className={"text-text-secondary dark:text-text-secondary-dark"}/>
105
- );
106
-
107
98
  return (
108
99
  <ErrorBoundary>
109
100
 
@@ -113,8 +104,16 @@ export function MapFieldBinding({
113
104
  expanded
114
105
  });
115
106
  }}
107
+ className={property.widthPercentage !== undefined ? "mt-8" : undefined}
116
108
  innerClassName={"px-2 md:px-4 pb-2 md:pb-4 pt-1 md:pt-2 bg-white dark:bg-surface-900"}
117
- title={title}>{mapFormView}</ExpandablePanel>}
109
+ title={<LabelWithIconAndTooltip
110
+ propertyKey={propertyKey}
111
+ icon={getIconForProperty(property, "small")}
112
+ required={property.validation?.required}
113
+ title={property.name}
114
+ className={"text-text-secondary dark:text-text-secondary-dark"}/>}>
115
+ {mapFormView}
116
+ </ExpandablePanel>}
118
117
 
119
118
  {minimalistView && mapFormView}
120
119
 
@@ -135,7 +135,7 @@ export function MarkdownEditorFieldBinding({
135
135
  icon={getIconForProperty(property, "small")}
136
136
  required={property.validation?.required}
137
137
  title={property.name}
138
- className={"text-text-secondary dark:text-text-secondary-dark ml-3.5"}/>
138
+ className={"h-8 text-text-secondary dark:text-text-secondary-dark ml-3.5"}/>
139
139
  <div className={cls("rounded-md", fieldBackgroundMixin, fieldBackgroundHoverMixin)}>
140
140
  {editor}
141
141
  </div>
@@ -92,7 +92,7 @@ export function MultiSelectFieldBinding({
92
92
  icon={getIconForProperty(property, "small")}
93
93
  required={property.validation?.required}
94
94
  title={property.name}
95
- className={"text-text-secondary dark:text-text-secondary-dark ml-3.5"}/>}
95
+ className={"h-8 text-text-secondary dark:text-text-secondary-dark ml-3.5"}/>}
96
96
  onValueChange={(updatedValue: string[]) => {
97
97
  let newValue: EnumType[] | null;
98
98
  if (of && (of as ResolvedProperty)?.dataType === "number") {
@@ -39,11 +39,11 @@ export function ReadOnlyFieldBinding({
39
39
  icon={getIconForProperty(property, "small")}
40
40
  required={property.validation?.required}
41
41
  title={property.name}
42
- className={"text-text-secondary dark:text-text-secondary-dark ml-3.5"}/>
42
+ className={"h-8 text-text-secondary dark:text-text-secondary-dark ml-3.5"}/>
43
43
  }
44
44
 
45
45
  <div
46
- className={cls(paperMixin, "min-h-14 p-4 md:p-6 overflow-x-scroll no-scrollbar")}>
46
+ className={cls(paperMixin, "w-full min-h-14 p-4 md:p-6 overflow-x-scroll no-scrollbar")}>
47
47
 
48
48
  <ErrorBoundary>
49
49
  <PropertyPreview propertyKey={propertyKey}
@@ -90,7 +90,7 @@ function ReferenceFieldBindingInternal({
90
90
  icon={getIconForProperty(property, "small")}
91
91
  required={property.validation?.required}
92
92
  title={property.name}
93
- className={"text-text-secondary dark:text-text-secondary-dark ml-3.5"}/>}
93
+ className={"h-8 text-text-secondary dark:text-text-secondary-dark ml-3.5"}/>}
94
94
 
95
95
  {!collection && <ErrorView
96
96
  error={"The specified collection does not exist. Check console"}/>}
@@ -90,6 +90,7 @@ export function RepeatFieldBinding<T extends Array<any>>({
90
90
  includeAddButton={!property.disabled}
91
91
  newDefaultEntry={getDefaultValueFor(property.of)}
92
92
  onValueChange={(value) => setFieldValue(propertyKey, value)}
93
+ className={property.widthPercentage !== undefined ? "mt-8" : undefined}
93
94
  />;
94
95
 
95
96
  const title = (<>
@@ -98,7 +99,7 @@ export function RepeatFieldBinding<T extends Array<any>>({
98
99
  icon={getIconForProperty(property, "small")}
99
100
  required={property.validation?.required}
100
101
  title={property.name}
101
- className={"flex flex-grow text-text-secondary dark:text-text-secondary-dark"}/>
102
+ className={"h-8 flex flex-grow text-text-secondary dark:text-text-secondary-dark"}/>
102
103
  {Array.isArray(value) && <Typography variant={"caption"} className={"px-4"}>({value.length})</Typography>}
103
104
  </>);
104
105
 
@@ -62,7 +62,7 @@ export function SelectFieldBinding<T extends EnumType>({
62
62
  icon={getIconForProperty(property, "small")}
63
63
  required={property.validation?.required}
64
64
  title={property.name}
65
- className={"text-text-secondary dark:text-text-secondary-dark ml-3.5"}
65
+ className={"h-8 text-text-secondary dark:text-text-secondary-dark ml-3.5 my-0"}
66
66
  />
67
67
  </PropertyIdCopyTooltip>}
68
68
  endAdornment={
@@ -103,7 +103,7 @@ export function StorageUploadFieldBinding({
103
103
  icon={getIconForProperty(property, "small")}
104
104
  required={property.validation?.required}
105
105
  title={property.name}
106
- className={"text-text-secondary dark:text-text-secondary-dark ml-3.5"}/>}
106
+ className={"h-8text-text-secondary dark:text-text-secondary-dark ml-3.5"}/>}
107
107
 
108
108
  <StorageUpload
109
109
  value={internalValue}
@@ -43,6 +43,7 @@ export const SwitchFieldBinding = function SwitchFieldBinding({
43
43
  value={value}
44
44
  onValueChange={(v) => setValue(v)}
45
45
  error={showError}
46
+ className={property.widthPercentage !== undefined ? "mt-8" : undefined}
46
47
  label={<LabelWithIcon
47
48
  icon={getIconForProperty(property, "small")}
48
49
  required={property.validation?.required}
@@ -75,6 +75,7 @@ export function TextFieldBinding<T extends string | number>({
75
75
  value={value}
76
76
  onChange={onChange}
77
77
  autoFocus={autoFocus}
78
+ className={property.widthPercentage !== undefined ? "mt-8" : undefined}
78
79
  label={<LabelWithIcon
79
80
  icon={getIconForProperty(property, "small")}
80
81
  required={property.validation?.required}
@@ -156,6 +156,12 @@ export interface BaseProperty<T extends CMSType, CustomProps = any> {
156
156
  * save the new config. The saved config will then become the source of truth.
157
157
  */
158
158
  editable?: boolean;
159
+
160
+ /**
161
+ * A number between 0 and 100 that indicates the width of the field in the form view.
162
+ * It defaults to 100, but you can set it to 50 to have two fields in the same row.
163
+ */
164
+ widthPercentage?: number;
159
165
  }
160
166
 
161
167
  /**
@@ -115,6 +115,9 @@ export function mergeCollection(target: EntityCollection,
115
115
  if (modifiedCollection)
116
116
  resultCollection = modifiedCollection;
117
117
  }
118
+
119
+ // @ts-ignore
120
+ resultCollection["merged"] = true;
118
121
  return resultCollection
119
122
  }
120
123
 
@@ -22,11 +22,11 @@ export function getParentReferencesFromPath(props: {
22
22
 
23
23
  const collection: EntityCollection<any> | undefined = collections && collections.find((entry) => entry.id === subpathCombination || entry.path === subpathCombination);
24
24
 
25
+ // If we find a collection, we add the reference and continue
25
26
  if (collection) {
26
- const path = collection.path;
27
27
  const collectionPath = currentFullPath && currentFullPath.length > 0
28
- ? (currentFullPath + "/" + path)
29
- : path;
28
+ ? (currentFullPath + "/" + collection.path)
29
+ : collection.path;
30
30
 
31
31
  const restOfThePath = removeInitialAndTrailingSlashes(removeInitialAndTrailingSlashes(path).replace(subpathCombination, ""));
32
32
  const nextSegments = restOfThePath.length > 0 ? restOfThePath.split("/") : [];