@firecms/core 3.0.0-alpha.84 → 3.0.0-beta.2-pre.1

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.
Files changed (44) hide show
  1. package/dist/components/ArrayContainer.d.ts +1 -3
  2. package/dist/components/EntityCollectionTable/EntityCollectionTableProps.d.ts +1 -2
  3. package/dist/components/EntityCollectionTable/{SimpleEntityCollectionTable.d.ts → SimpleTable.d.ts} +7 -26
  4. package/dist/components/EntityCollectionTable/index.d.ts +1 -1
  5. package/dist/components/EntityCollectionTable/useDataSourceEntityCollectionTableController.d.ts +1 -1
  6. package/dist/components/{FieldConfigBadge.d.ts → PropertyConfigBadge.d.ts} +1 -1
  7. package/dist/components/index.d.ts +1 -1
  8. package/dist/core/Scaffold.d.ts +5 -2
  9. package/dist/form/components/CustomIdField.d.ts +2 -1
  10. package/dist/index.es.js +4269 -4446
  11. package/dist/index.es.js.map +1 -1
  12. package/dist/index.umd.js +5 -5
  13. package/dist/index.umd.js.map +1 -1
  14. package/dist/types/datasource.d.ts +3 -3
  15. package/dist/types/entity_callbacks.d.ts +1 -1
  16. package/dist/util/strings.d.ts +1 -1
  17. package/package.json +16 -16
  18. package/src/components/ArrayContainer.tsx +31 -67
  19. package/src/components/EntityCollectionTable/EntityCollectionTable.tsx +78 -262
  20. package/src/components/EntityCollectionTable/EntityCollectionTableProps.tsx +1 -2
  21. package/src/components/EntityCollectionTable/{SimpleEntityCollectionTable.tsx → SimpleTable.tsx} +36 -73
  22. package/src/components/EntityCollectionTable/index.tsx +1 -1
  23. package/src/components/EntityCollectionTable/useDataSourceEntityCollectionTableController.tsx +1 -1
  24. package/src/components/EntityCollectionView/EntityCollectionView.tsx +9 -8
  25. package/src/components/EntityPreview.tsx +0 -1
  26. package/src/components/LabelWithIcon.tsx +1 -1
  27. package/src/components/{FieldConfigBadge.tsx → PropertyConfigBadge.tsx} +1 -1
  28. package/src/components/ReferenceSelectionInner.tsx +0 -1
  29. package/src/components/index.tsx +1 -1
  30. package/src/core/NavigationRoutes.tsx +0 -1
  31. package/src/core/Scaffold.tsx +11 -8
  32. package/src/form/EntityForm.tsx +12 -2
  33. package/src/form/components/CustomIdField.tsx +16 -4
  34. package/src/form/components/ErrorFocus.tsx +1 -1
  35. package/src/form/field_bindings/ArrayOfReferencesFieldBinding.tsx +1 -2
  36. package/src/form/field_bindings/MarkdownFieldBinding.tsx +2 -2
  37. package/src/form/field_bindings/ReadOnlyFieldBinding.tsx +1 -2
  38. package/src/form/field_bindings/ReferenceFieldBinding.tsx +1 -2
  39. package/src/hooks/useLargeLayout.tsx +10 -7
  40. package/src/internal/useBuildDataSource.ts +9 -9
  41. package/src/preview/PropertyPreviewProps.tsx +1 -1
  42. package/src/types/datasource.ts +3 -3
  43. package/src/types/entity_callbacks.ts +2 -1
  44. package/src/util/strings.ts +2 -1
@@ -2,7 +2,6 @@ import React, { useMemo } from "react";
2
2
  import { PropertyPreview } from "../preview";
3
3
  import { Entity, EntityCollection, ResolvedEntityCollection, ResolvedProperties } from "../types";
4
4
  import { resolveCollection } from "../util";
5
- import { useFireCMSContext } from "../hooks";
6
5
  import { cn, defaultBorderMixin, IconButton, OpenInNewIcon } from "@firecms/ui";
7
6
  import { CustomizationController } from "../types/customization_controller";
8
7
  import { useCustomizationController } from "../hooks/useCustomizationController";
@@ -26,7 +26,7 @@ export function LabelWithIcon({
26
26
  {icon}
27
27
 
28
28
  <span
29
- className={`font-medium text-${small ? "base" : "sm"} origin-top-left transform ${small ? "translate-x-2 scale-75" : ""}`}>{(title ?? "") + (required ? " *" : "")}</span>
29
+ className={`text-start font-medium text-${small ? "base" : "sm"} origin-top-left transform ${small ? "translate-x-2 scale-75" : ""}`}>{(title ?? "") + (required ? " *" : "")}</span>
30
30
 
31
31
  </span>
32
32
  );
@@ -2,7 +2,7 @@ import { getIconForWidget } from "../util";
2
2
  import { PropertyConfig } from "../types";
3
3
  import { getDefaultFieldConfig } from "../core";
4
4
 
5
- export function FieldConfigBadge({ propertyConfig }: { propertyConfig: PropertyConfig | undefined }): React.ReactNode {
5
+ export function PropertyConfigBadge({ propertyConfig }: { propertyConfig: PropertyConfig | undefined }): React.ReactNode {
6
6
  const classes = "h-8 w-8 p-1 rounded-full shadow text-white";
7
7
 
8
8
  const defaultPropertyConfig = typeof propertyConfig?.property === "object" ? getDefaultFieldConfig(propertyConfig.property) : undefined;
@@ -273,7 +273,6 @@ export function ReferenceSelectionInner<M extends Record<string, any>>(
273
273
  <div className="flex-grow">
274
274
  {entitiesDisplayedFirst &&
275
275
  <EntityCollectionTable
276
- debugKey={"reference_selection_table"}
277
276
  displayedColumnIds={displayedColumnIds}
278
277
  onEntityClick={onEntityClick}
279
278
  tableController={tableController}
@@ -12,7 +12,7 @@ export { CircularProgressCenter } from "./CircularProgressCenter";
12
12
  export * from "./HomePage";
13
13
  export * from "./EntityCollectionView/EntityCollectionView";
14
14
 
15
- export * from "./FieldConfigBadge";
15
+ export * from "./PropertyConfigBadge";
16
16
 
17
17
  export * from "./EntityCollectionTable";
18
18
  export * from "./NotFoundPage";
@@ -115,4 +115,3 @@ const buildCMSViewRoute = (path: string, cmsView: CMSView) => {
115
115
  element={cmsView.view}
116
116
  />;
117
117
  };
118
-
@@ -4,7 +4,7 @@ import { Link } from "react-router-dom";
4
4
 
5
5
  import { Drawer as DefaultDrawer, DrawerProps } from "./Drawer";
6
6
  import { useLargeLayout, useNavigationController } from "../hooks";
7
- import { CircularProgressCenter, ErrorBoundary, FireCMSAppBar, FireCMSAppBarProps, FireCMSLogo } from "../components";
7
+ import { ErrorBoundary, FireCMSAppBar as DefaultFireCMSAppBar, FireCMSAppBarProps, FireCMSLogo } from "../components";
8
8
  import { ChevronLeftIcon, cn, defaultBorderMixin, IconButton, MenuIcon, Sheet, Tooltip } from "@firecms/ui";
9
9
 
10
10
  export const DRAWER_WIDTH = 280;
@@ -24,6 +24,9 @@ export interface ScaffoldProps<ExtraDrawerProps = object, ExtraAppbarProps = obj
24
24
  */
25
25
  logo?: string;
26
26
 
27
+ /**
28
+ * Whether to include the drawer in the scaffold
29
+ */
27
30
  includeDrawer?: boolean;
28
31
 
29
32
  /**
@@ -46,12 +49,12 @@ export interface ScaffoldProps<ExtraDrawerProps = object, ExtraAppbarProps = obj
46
49
  * A component that gets rendered on the upper side of the main toolbar.
47
50
  * `toolbarExtraWidget` has no effect if this is set.
48
51
  */
49
- FireCMSAppBarComponent?: React.ComponentType<FireCMSAppBarProps<ExtraAppbarProps>>;
52
+ FireCMSAppBar?: React.ComponentType<FireCMSAppBarProps<ExtraAppbarProps>>;
50
53
 
51
54
  /**
52
55
  * Additional props passed to the custom AppBar
53
56
  */
54
- fireCMSAppBarComponentProps?: Partial<FireCMSAppBarProps> & ExtraAppbarProps;
57
+ fireCMSAppBarProps?: Partial<FireCMSAppBarProps> & ExtraAppbarProps;
55
58
 
56
59
  }
57
60
 
@@ -77,8 +80,8 @@ export const Scaffold = React.memo<PropsWithChildren<ScaffoldProps>>(
77
80
  autoOpenDrawer,
78
81
  Drawer = DefaultDrawer,
79
82
  drawerProps,
80
- FireCMSAppBarComponent = FireCMSAppBar,
81
- fireCMSAppBarComponentProps,
83
+ FireCMSAppBar = DefaultFireCMSAppBar,
84
+ fireCMSAppBarProps,
82
85
  } = props;
83
86
 
84
87
  const largeLayout = useLargeLayout();
@@ -108,10 +111,10 @@ export const Scaffold = React.memo<PropsWithChildren<ScaffoldProps>>(
108
111
  // }
109
112
  }}>
110
113
 
111
- <FireCMSAppBarComponent title={name}
114
+ <FireCMSAppBar title={name}
112
115
  includeDrawer={includeDrawer}
113
116
  drawerOpen={computedDrawerOpen}
114
- {...fireCMSAppBarComponentProps}/>
117
+ {...fireCMSAppBarProps}/>
115
118
 
116
119
  <StyledDrawer
117
120
  displayed={includeDrawer}
@@ -127,7 +130,7 @@ export const Scaffold = React.memo<PropsWithChildren<ScaffoldProps>>(
127
130
  hovered={onHover}
128
131
  drawerOpen={computedDrawerOpen}
129
132
  closeDrawer={handleDrawerClose}
130
- {...drawerProps}/> )}
133
+ {...drawerProps}/>)}
131
134
  </StyledDrawer>
132
135
 
133
136
  <main
@@ -206,6 +206,8 @@ function EntityFormInternal<M extends Record<string, any>>({
206
206
  const [entityIdError, setEntityIdError] = React.useState<boolean>(false);
207
207
  const [savingError, setSavingError] = React.useState<Error | undefined>();
208
208
 
209
+ const [customIdLoading, setCustomIdLoading] = React.useState<boolean>(false);
210
+
209
211
  const initialValuesRef = useRef<EntityValues<M>>(entity?.values ?? baseDataSourceValues as EntityValues<M>);
210
212
  const initialValues = initialValuesRef.current;
211
213
  const [internalValues, setInternalValues] = useState<EntityValues<M> | undefined>(initialValues);
@@ -234,10 +236,12 @@ function EntityFormInternal<M extends Record<string, any>>({
234
236
  });
235
237
 
236
238
  const onIdUpdate = inputCollection.callbacks?.onIdUpdate;
237
- useEffect(() => {
239
+
240
+ const doOnIdUpdate = useCallback(async () => {
238
241
  if (onIdUpdate && internalValues && (status === "new" || status === "copy")) {
242
+ setCustomIdLoading(true);
239
243
  try {
240
- const updatedId = onIdUpdate({
244
+ const updatedId = await onIdUpdate({
241
245
  collection: resolvedCollection,
242
246
  path,
243
247
  entityId,
@@ -249,9 +253,14 @@ function EntityFormInternal<M extends Record<string, any>>({
249
253
  onIdUpdateError && onIdUpdateError(e);
250
254
  console.error(e);
251
255
  }
256
+ setCustomIdLoading(false);
252
257
  }
253
258
  }, [entityId, internalValues, status]);
254
259
 
260
+ useEffect(() => {
261
+ doOnIdUpdate();
262
+ }, [doOnIdUpdate]);
263
+
255
264
  const underlyingChanges: Partial<EntityValues<M>> = useMemo(() => {
256
265
  if (initialValues && status === "existing") {
257
266
  return Object.entries(resolvedCollection.properties)
@@ -437,6 +446,7 @@ function EntityFormInternal<M extends Record<string, any>>({
437
446
  status={status}
438
447
  onChange={setEntityId}
439
448
  error={entityIdError}
449
+ loading={customIdLoading}
440
450
  entity={entity}/>}
441
451
 
442
452
  {entityId && <InnerForm
@@ -6,7 +6,16 @@ import { Entity, EntityStatus, EnumValueConfig, EnumValues } from "../../types";
6
6
 
7
7
  import { useClipboard, useSnackbarController } from "../../hooks";
8
8
  import { enumToObjectEntries } from "../../util";
9
- import { ContentCopyIcon, IconButton, OpenInNewIcon, Select, SelectItem, TextField, Tooltip } from "@firecms/ui";
9
+ import {
10
+ CircularProgress,
11
+ ContentCopyIcon,
12
+ IconButton,
13
+ OpenInNewIcon,
14
+ Select,
15
+ SelectItem,
16
+ TextField,
17
+ Tooltip
18
+ } from "@firecms/ui";
10
19
  import { EnumValuesChip } from "../../preview";
11
20
  import { useCustomizationController } from "../../hooks/useCustomizationController";
12
21
  import { CustomizationController } from "../../types/customization_controller";
@@ -17,14 +26,16 @@ export function CustomIdField<M extends Record<string, any>>({
17
26
  status,
18
27
  onChange,
19
28
  error,
20
- entity
29
+ entity,
30
+ loading
21
31
  }: {
22
32
  customId?: boolean | EnumValues | "optional"
23
33
  entityId?: string
24
34
  status: EntityStatus,
25
35
  onChange: (id?: string) => void,
26
36
  error: boolean,
27
- entity: Entity<M> | undefined
37
+ entity: Entity<M> | undefined,
38
+ loading: boolean
28
39
  }) {
29
40
 
30
41
  const disabled = status === "existing" || !customId;
@@ -48,7 +59,7 @@ export function CustomIdField<M extends Record<string, any>>({
48
59
 
49
60
  const fieldProps = {
50
61
  label: idSetAutomatically ? "ID is set automatically" : "ID",
51
- disabled,
62
+ disabled: disabled || loading,
52
63
  name: "id",
53
64
  value: (entity && status === "existing" ? entity.id : entityId) ?? "",
54
65
  endAdornment: entity
@@ -109,6 +120,7 @@ export function CustomIdField<M extends Record<string, any>>({
109
120
  {!enumValues &&
110
121
  <TextField {...fieldProps}
111
122
  error={error}
123
+ endAdornment={loading ? <CircularProgress size={"small"}/> : undefined}
112
124
  placeholder={customId === "optional" ? "Autogenerated ID, it can be manually changed" : (status === "new" || status === "copy" ? "ID of the new document" : "ID of the document")}
113
125
  onChange={(event) => {
114
126
  let value = event.target.value;
@@ -21,7 +21,7 @@ export const ErrorFocus = ({ containerRef }:
21
21
  if (scrollableParent) {
22
22
  const top = errorElement.getBoundingClientRect().top;
23
23
  scrollableParent.scrollTo({
24
- top: scrollableParent.scrollTop + top - 64,
24
+ top: scrollableParent.scrollTop + top,
25
25
  behavior: "smooth"
26
26
  });
27
27
  }
@@ -2,12 +2,11 @@ import React, { useCallback, useMemo } from "react";
2
2
  import { Entity, EntityCollection, EntityReference, FieldProps, ResolvedProperty } from "../../types";
3
3
  import { ReferencePreview } from "../../preview";
4
4
  import { FieldHelperText, FormikArrayContainer } from "../components";
5
- import { LabelWithIcon } from "../../components";
5
+ import { ErrorView, LabelWithIcon } from "../../components";
6
6
  import { getIconForProperty, getReferenceFrom } from "../../util";
7
7
 
8
8
  import { useClearRestoreValue, useNavigationController, useReferenceDialog } from "../../hooks";
9
9
  import { Button, ExpandablePanel } from "@firecms/ui";
10
- import { ErrorView } from "../../components";
11
10
 
12
11
  type ArrayOfReferencesFieldProps = FieldProps<EntityReference[]>;
13
12
 
@@ -13,9 +13,9 @@ import {
13
13
  fieldBackgroundDisabledMixin,
14
14
  fieldBackgroundHoverMixin,
15
15
  fieldBackgroundMixin,
16
- Typography
16
+ Typography,
17
+ useInjectStyles
17
18
  } from "@firecms/ui";
18
- import { useInjectStyles } from "@firecms/ui";
19
19
 
20
20
  const mdParser = new MarkdownIt();
21
21
  try {
@@ -4,10 +4,9 @@ import { Entity, FieldProps } from "../../types";
4
4
 
5
5
  import { PropertyPreview } from "../../preview";
6
6
  import { FieldHelperText } from "../components";
7
- import { LabelWithIcon } from "../../components";
7
+ import { ErrorBoundary, LabelWithIcon } from "../../components";
8
8
  import { getIconForProperty } from "../../util";
9
9
  import { cn, paperMixin } from "@firecms/ui";
10
- import { ErrorBoundary } from "../../components";
11
10
 
12
11
  /**
13
12
  *
@@ -4,11 +4,10 @@ import { Entity, EntityCollection, EntityReference, FieldProps } from "../../typ
4
4
  import { useClearRestoreValue, useNavigationController, useReferenceDialog } from "../../hooks";
5
5
  import { ReadOnlyFieldBinding } from "./ReadOnlyFieldBinding";
6
6
  import { FieldHelperText } from "../components";
7
- import { LabelWithIcon } from "../../components";
7
+ import { ErrorView, LabelWithIcon } from "../../components";
8
8
  import { ReferencePreview } from "../../preview";
9
9
  import { getIconForProperty, getReferenceFrom } from "../../util";
10
10
  import { Button } from "@firecms/ui";
11
- import { ErrorView } from "../../components";
12
11
 
13
12
  /**
14
13
  * Field that opens a reference selection dialog.
@@ -22,13 +22,14 @@ const notifyListeners = () => {
22
22
  };
23
23
 
24
24
  // Listen to resize events once, at a global level
25
- window.addEventListener("resize", () => {
26
- const newIsLargeLayout = checkLargeLayout("lg");
27
- if (newIsLargeLayout !== isLargeLayoutGlobal) {
28
- isLargeLayoutGlobal = newIsLargeLayout;
29
- notifyListeners();
30
- }
31
- });
25
+ if (typeof window !== "undefined")
26
+ window.addEventListener("resize", () => {
27
+ const newIsLargeLayout = checkLargeLayout("lg");
28
+ if (newIsLargeLayout !== isLargeLayoutGlobal) {
29
+ isLargeLayoutGlobal = newIsLargeLayout;
30
+ notifyListeners();
31
+ }
32
+ });
32
33
 
33
34
  export const useLargeLayout = () => {
34
35
  const [isLargeLayout, setIsLargeLayout] = useState(isLargeLayoutGlobal);
@@ -58,6 +59,8 @@ export const useLargeLayout = () => {
58
59
  };
59
60
 
60
61
  function checkLargeLayout(breakpoint: "xs" | "sm" | "md" | "lg" | "xl" | "2xl" | "3xl" = "lg"): boolean {
62
+ if (typeof window === "undefined")
63
+ return false;
61
64
  return window.matchMedia(`(min-width: ${breakpoints[breakpoint] + 1}px)`).matches;
62
65
  }
63
66
 
@@ -276,27 +276,27 @@ export function useBuildDataSource({
276
276
  return delegate.generateEntityId(path,);
277
277
  }, [delegate.generateEntityId]),
278
278
 
279
- countEntities: useCallback(async ({
280
- path,
281
- collection,
282
- filter,
283
- order,
284
- orderBy
285
- }: {
279
+ countEntities: delegate.countEntities ? async ({
280
+ path,
281
+ collection,
282
+ filter,
283
+ order,
284
+ orderBy
285
+ }: {
286
286
  path: string,
287
287
  collection: EntityCollection<any>,
288
288
  filter?: FilterValues<Extract<keyof any, string>>,
289
289
  orderBy?: string,
290
290
  order?: "desc" | "asc",
291
291
  }): Promise<number> => {
292
- return delegate.countEntities({
292
+ return delegate.countEntities!({
293
293
  path,
294
294
  filter,
295
295
  orderBy,
296
296
  order,
297
297
  isCollectionGroup: Boolean(collection.collectionGroup) ?? false
298
298
  });
299
- }, [delegate.countEntities]),
299
+ } : undefined,
300
300
 
301
301
  isFilterCombinationValid: useCallback(({
302
302
  path,
@@ -1,4 +1,4 @@
1
- import { CMSType, Entity, Property, ResolvedProperty } from "../types";
1
+ import { CMSType, Property, ResolvedProperty } from "../types";
2
2
 
3
3
  /**
4
4
  * @group Preview components
@@ -29,7 +29,7 @@ export interface FetchCollectionProps<M extends Record<string, any> = any> {
29
29
  collection?: EntityCollection<M> | ResolvedEntityCollection<M>;
30
30
  filter?: FilterValues<Extract<keyof M, string>>,
31
31
  limit?: number;
32
- startAfter?: any[];
32
+ startAfter?: any;
33
33
  orderBy?: string;
34
34
  searchString?: string;
35
35
  order?: "desc" | "asc";
@@ -209,7 +209,7 @@ export interface DataSource {
209
209
  /**
210
210
  * Count the number of entities in a collection
211
211
  */
212
- countEntities<M extends Record<string, any> = any>(props: FetchCollectionProps<M>): Promise<number>;
212
+ countEntities?<M extends Record<string, any> = any>(props: FetchCollectionProps<M>): Promise<number>;
213
213
 
214
214
  /**
215
215
  * Check if the given filter combination is valid
@@ -354,7 +354,7 @@ export interface DataSourceDelegate {
354
354
  /**
355
355
  * Count the number of entities in a collection
356
356
  */
357
- countEntities<M extends Record<string, any> = any>(props: FetchCollectionDelegateProps<M>): Promise<number>;
357
+ countEntities?<M extends Record<string, any> = any>(props: FetchCollectionDelegateProps<M>): Promise<number>;
358
358
 
359
359
  /**
360
360
  * Check if the given filter combination is valid
@@ -65,7 +65,8 @@ export type EntityCallbacks<M extends Record<string, any> = any, UserType extend
65
65
  *
66
66
  * @param idUpdateProps
67
67
  */
68
- onIdUpdate?(idUpdateProps: EntityIdUpdateProps<M>): string;
68
+ onIdUpdate?(idUpdateProps: EntityIdUpdateProps<M>): string | Promise<string>;
69
+
69
70
  }
70
71
 
71
72
  /**
@@ -50,7 +50,8 @@ export function slugify(text?: string, separator = "_", lowercase = true) {
50
50
  : text;
51
51
  }
52
52
 
53
- export function unslugify(slug: string): string {
53
+ export function unslugify(slug?: string): string {
54
+ if (!slug) return "";
54
55
  if (slug.includes("-") || slug.includes("_") || !slug.includes(" ")) {
55
56
  const result = slug.replace(/[-_]/g, " ");
56
57
  return result.replace(/\w\S*/g, function (txt) {