@firecms/core 3.0.0-beta.13 → 3.0.0-beta.14

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 (71) hide show
  1. package/dist/components/ArrayContainer.d.ts +7 -12
  2. package/dist/components/EntityPreview.d.ts +4 -2
  3. package/dist/components/SelectableTable/SelectableTable.d.ts +1 -1
  4. package/dist/components/VirtualTable/VirtualTableProps.d.ts +0 -4
  5. package/dist/components/index.d.ts +1 -0
  6. package/dist/form/field_bindings/MapFieldBinding.d.ts +1 -1
  7. package/dist/form/field_bindings/StorageUploadFieldBinding.d.ts +3 -9
  8. package/dist/hooks/useBuildNavigationController.d.ts +2 -9
  9. package/dist/index.es.js +1183 -705
  10. package/dist/index.es.js.map +1 -1
  11. package/dist/index.umd.js +1191 -716
  12. package/dist/index.umd.js.map +1 -1
  13. package/dist/types/collections.d.ts +20 -2
  14. package/dist/types/firecms.d.ts +2 -1
  15. package/dist/types/navigation.d.ts +5 -0
  16. package/dist/types/plugins.d.ts +12 -0
  17. package/dist/types/side_entity_controller.d.ts +4 -0
  18. package/dist/util/callbacks.d.ts +2 -0
  19. package/dist/util/index.d.ts +1 -0
  20. package/package.json +8 -6
  21. package/src/components/ArrayContainer.tsx +409 -294
  22. package/src/components/ClearFilterSortButton.tsx +1 -1
  23. package/src/components/ConfirmationDialog.tsx +9 -9
  24. package/src/components/EntityCollectionTable/PropertyTableCell.tsx +1 -1
  25. package/src/components/EntityCollectionTable/internal/CollectionTableToolbar.tsx +1 -1
  26. package/src/components/EntityCollectionTable/internal/EntityTableCell.tsx +13 -2
  27. package/src/components/EntityCollectionTable/internal/popup_field/PopupFormField.tsx +53 -27
  28. package/src/components/EntityCollectionTable/internal/popup_field/useDraggable.tsx +9 -9
  29. package/src/components/EntityCollectionView/EntityCollectionView.tsx +4 -0
  30. package/src/components/EntityPreview.tsx +18 -14
  31. package/src/components/ErrorView.tsx +1 -1
  32. package/src/components/HomePage/DefaultHomePage.tsx +2 -1
  33. package/src/components/HomePage/NavigationCardBinding.tsx +3 -1
  34. package/src/components/SelectableTable/SelectableTable.tsx +140 -143
  35. package/src/components/VirtualTable/VirtualTable.tsx +8 -30
  36. package/src/components/VirtualTable/VirtualTableProps.tsx +0 -5
  37. package/src/components/VirtualTable/fields/VirtualTableInput.tsx +0 -1
  38. package/src/components/index.tsx +2 -0
  39. package/src/core/EntityEditView.tsx +26 -14
  40. package/src/core/EntitySidePanel.tsx +15 -20
  41. package/src/core/FireCMS.tsx +7 -1
  42. package/src/form/EntityForm.tsx +1 -1
  43. package/src/form/PropertyFieldBinding.tsx +0 -1
  44. package/src/form/components/CustomIdField.tsx +3 -1
  45. package/src/form/components/LabelWithIcon.tsx +1 -1
  46. package/src/form/field_bindings/ArrayOfReferencesFieldBinding.tsx +3 -2
  47. package/src/form/field_bindings/MapFieldBinding.tsx +5 -4
  48. package/src/form/field_bindings/MultiSelectFieldBinding.tsx +1 -1
  49. package/src/form/field_bindings/RepeatFieldBinding.tsx +1 -0
  50. package/src/form/field_bindings/SelectFieldBinding.tsx +2 -1
  51. package/src/form/field_bindings/StorageUploadFieldBinding.tsx +222 -154
  52. package/src/hooks/data/save.ts +0 -6
  53. package/src/hooks/useBuildNavigationController.tsx +30 -16
  54. package/src/internal/useBuildSideEntityController.tsx +1 -1
  55. package/src/preview/components/ImagePreview.tsx +2 -2
  56. package/src/preview/components/ReferencePreview.tsx +1 -1
  57. package/src/preview/property_previews/ArrayOfMapsPreview.tsx +1 -1
  58. package/src/preview/property_previews/MapPropertyPreview.tsx +1 -1
  59. package/src/preview/property_previews/SkeletonPropertyComponent.tsx +1 -1
  60. package/src/preview/property_previews/StringPropertyPreview.tsx +1 -1
  61. package/src/types/collections.ts +24 -2
  62. package/src/types/firecms.tsx +2 -2
  63. package/src/types/navigation.ts +6 -0
  64. package/src/types/plugins.tsx +16 -0
  65. package/src/types/side_entity_controller.tsx +5 -5
  66. package/src/util/callbacks.ts +119 -0
  67. package/src/util/index.ts +1 -0
  68. package/src/util/navigation_utils.ts +70 -55
  69. package/src/util/objects.ts +53 -20
  70. package/dist/components/EntityCollectionTable/internal/popup_field/ElementResizeListener.d.ts +0 -5
  71. package/src/components/EntityCollectionTable/internal/popup_field/ElementResizeListener.tsx +0 -59
@@ -1,4 +1,4 @@
1
- import React, { useCallback, useEffect, useRef } from "react";
1
+ import React, { useCallback, useEffect, useMemo, useRef } from "react";
2
2
  import {
3
3
  CollectionSize,
4
4
  Entity,
@@ -119,150 +119,147 @@ export type SelectableTableProps<M extends Record<string, any>> = {
119
119
  * @see VirtualTable
120
120
  * @group Components
121
121
  */
122
- export const SelectableTable = React.memo<SelectableTableProps<any>>(
123
- function SelectableTable<M extends Record<string, any>>
124
- ({
125
- onValueChange,
126
- cellRenderer,
127
- onEntityClick,
128
- onColumnResize,
129
- hoverRow = true,
130
- size = "m",
131
- inlineEditing = false,
132
- tableController:
133
- {
134
- data,
135
- dataLoading,
136
- noMoreToLoad,
137
- dataLoadingError,
138
- filterValues,
139
- setFilterValues,
140
- sortBy,
141
- setSortBy,
142
- itemCount,
143
- setItemCount,
144
- pageSize = 50,
145
- paginationEnabled,
146
- checkFilterCombination,
147
- setPopupCell
148
- },
149
- filterable = true,
150
- onScroll,
151
- initialScroll,
152
- emptyComponent,
153
- columns,
154
- forceFilter,
155
- highlightedRow,
156
- endAdornment,
157
- AddColumnComponent
158
- }: SelectableTableProps<M>) {
159
-
160
- const ref = useRef<HTMLDivElement>(null);
161
-
162
- const [selectedCell, setSelectedCell] = React.useState<SelectedCellProps<M> | undefined>(undefined);
163
-
164
- const loadNextPage = () => {
165
- if (!paginationEnabled || dataLoading || noMoreToLoad)
166
- return;
167
- if (itemCount !== undefined)
168
- setItemCount?.(itemCount + pageSize);
122
+ export const SelectableTable = function SelectableTable<M extends Record<string, any>>
123
+ ({
124
+ onValueChange,
125
+ cellRenderer,
126
+ onEntityClick,
127
+ onColumnResize,
128
+ hoverRow = true,
129
+ size = "m",
130
+ inlineEditing = false,
131
+ tableController:
132
+ {
133
+ data,
134
+ dataLoading,
135
+ noMoreToLoad,
136
+ dataLoadingError,
137
+ filterValues,
138
+ setFilterValues,
139
+ sortBy,
140
+ setSortBy,
141
+ itemCount,
142
+ setItemCount,
143
+ pageSize = 50,
144
+ paginationEnabled,
145
+ checkFilterCombination,
146
+ setPopupCell
147
+ },
148
+ filterable = true,
149
+ onScroll,
150
+ initialScroll,
151
+ emptyComponent,
152
+ columns,
153
+ forceFilter,
154
+ highlightedRow,
155
+ endAdornment,
156
+ AddColumnComponent
157
+ }: SelectableTableProps<M>) {
158
+
159
+ const ref = useRef<HTMLDivElement>(null);
160
+
161
+ const [selectedCell, setSelectedCell] = React.useState<SelectedCellProps<M> | undefined>(undefined);
162
+
163
+ const loadNextPage = () => {
164
+ if (!paginationEnabled || dataLoading || noMoreToLoad)
165
+ return;
166
+ if (itemCount !== undefined)
167
+ setItemCount?.(itemCount + pageSize);
168
+ };
169
+
170
+ const resetPagination = useCallback(() => {
171
+ setItemCount?.(pageSize);
172
+ }, [pageSize]);
173
+
174
+ const onRowClick = useCallback(({ rowData }: {
175
+ rowData: Entity<M>
176
+ }) => {
177
+ if (inlineEditing)
178
+ return;
179
+ return onEntityClick && onEntityClick(rowData);
180
+ }, [onEntityClick, inlineEditing]);
181
+
182
+ useOutsideAlerter(ref,
183
+ () => {
184
+ if (selectedCell) {
185
+ unselect();
186
+ }
187
+ },
188
+ Boolean(selectedCell));
189
+
190
+ const select = useCallback((cell?: SelectedCellProps<M>) => {
191
+ setSelectedCell(cell);
192
+ }, []);
193
+
194
+ const unselect = useCallback(() => {
195
+ setSelectedCell(undefined);
196
+ }, []);
197
+
198
+ // on ESC key press
199
+ useEffect(() => {
200
+ const escFunction = (event: any) => {
201
+ if (event.keyCode === 27) {
202
+ unselect();
203
+ }
169
204
  };
205
+ document.addEventListener("keydown", escFunction, false);
206
+ return () => {
207
+ document.removeEventListener("keydown", escFunction, false);
208
+ };
209
+ }, [unselect]);
210
+
211
+ const onFilterUpdate = useCallback((updatedFilterValues?: FilterValues<any>) => {
212
+ setFilterValues?.({ ...updatedFilterValues, ...forceFilter } as FilterValues<any>);
213
+ }, [forceFilter]);
214
+
215
+ const contextValue = useMemo(() => ({
216
+ setPopupCell: setPopupCell as ((cell?: SelectedCellProps<M>) => void),
217
+ select,
218
+ onValueChange,
219
+ size: size ?? "m",
220
+ selectedCell
221
+ }), [setPopupCell, select, onValueChange, size, selectedCell]);
222
+
223
+ return (
224
+ <SelectableTableContext.Provider
225
+ value={contextValue}>
226
+ <div className="h-full w-full flex flex-col bg-white dark:bg-surface-950"
227
+ ref={ref}>
228
+
229
+ <VirtualTable
230
+ data={data}
231
+ columns={columns}
232
+ cellRenderer={cellRenderer}
233
+ onRowClick={inlineEditing ? undefined : (onEntityClick ? onRowClick : undefined)}
234
+ onEndReached={loadNextPage}
235
+ onResetPagination={resetPagination}
236
+ error={dataLoadingError}
237
+ onColumnResize={onColumnResize}
238
+ rowHeight={getRowHeight(size)}
239
+ loading={dataLoading}
240
+ filter={filterValues}
241
+ onFilterUpdate={setFilterValues ? onFilterUpdate : undefined}
242
+ sortBy={sortBy}
243
+ onSortByUpdate={setSortBy as ((sortBy?: [string, "asc" | "desc"]) => void)}
244
+ hoverRow={hoverRow}
245
+ initialScroll={initialScroll}
246
+ onScroll={onScroll}
247
+ checkFilterCombination={checkFilterCombination}
248
+ createFilterField={filterable ? createFilterField : undefined}
249
+ rowClassName={useCallback((entity: Entity<M>) => {
250
+ return highlightedRow?.(entity) ? "bg-surface-100 bg-opacity-75 dark:bg-surface-800 dark:bg-opacity-75" : "";
251
+ }, [highlightedRow])}
252
+ className="flex-grow"
253
+ emptyComponent={emptyComponent}
254
+ endAdornment={endAdornment}
255
+ AddColumnComponent={AddColumnComponent}
256
+ />
257
+
258
+ </div>
259
+ </SelectableTableContext.Provider>
260
+ );
170
261
 
171
- const resetPagination = useCallback(() => {
172
- setItemCount?.(pageSize);
173
- }, [pageSize]);
174
-
175
- const onRowClick = useCallback(({ rowData }: {
176
- rowData: Entity<M>
177
- }) => {
178
- if (inlineEditing)
179
- return;
180
- return onEntityClick && onEntityClick(rowData);
181
- }, [onEntityClick, inlineEditing]);
182
-
183
- useOutsideAlerter(ref,
184
- () => {
185
- if (selectedCell) {
186
- unselect();
187
- }
188
- },
189
- Boolean(selectedCell));
190
-
191
- // on ESC key press
192
- useEffect(() => {
193
- const escFunction = (event: any) => {
194
- if (event.keyCode === 27) {
195
- unselect();
196
- }
197
- };
198
- document.addEventListener("keydown", escFunction, false);
199
- return () => {
200
- document.removeEventListener("keydown", escFunction, false);
201
- };
202
- });
203
-
204
- const select = useCallback((cell?: SelectedCellProps<M>) => {
205
- setSelectedCell(cell);
206
- }, []);
207
-
208
- const unselect = useCallback(() => {
209
- setSelectedCell(undefined);
210
- }, []);
211
-
212
- const onFilterUpdate = useCallback((updatedFilterValues?: FilterValues<any>) => {
213
- setFilterValues?.({ ...updatedFilterValues, ...forceFilter } as FilterValues<any>);
214
- }, [forceFilter]);
215
-
216
- return (
217
- <SelectableTableContext.Provider
218
- value={{
219
- setPopupCell: setPopupCell as ((cell?: SelectedCellProps<M>) => void),
220
- select,
221
- onValueChange,
222
- size: size ?? "m",
223
- selectedCell,
224
- }}
225
- >
226
- <div className="h-full w-full flex flex-col bg-white dark:bg-surface-950"
227
- ref={ref}>
228
-
229
- <VirtualTable
230
- data={data}
231
- columns={columns}
232
- cellRenderer={cellRenderer}
233
- onRowClick={inlineEditing ? undefined : (onEntityClick ? onRowClick : undefined)}
234
- onEndReached={loadNextPage}
235
- onResetPagination={resetPagination}
236
- error={dataLoadingError}
237
- onColumnResize={onColumnResize}
238
- rowHeight={getRowHeight(size)}
239
- loading={dataLoading}
240
- filter={filterValues}
241
- onFilterUpdate={setFilterValues ? onFilterUpdate : undefined}
242
- sortBy={sortBy}
243
- onSortByUpdate={setSortBy as ((sortBy?: [string, "asc" | "desc"]) => void)}
244
- hoverRow={hoverRow}
245
- initialScroll={initialScroll}
246
- onScroll={onScroll}
247
- checkFilterCombination={checkFilterCombination}
248
- createFilterField={filterable ? createFilterField : undefined}
249
- rowClassName={useCallback((entity: Entity<M>) => {
250
- return highlightedRow?.(entity) ? "bg-surface-100 bg-opacity-75 dark:bg-surface-800 dark:bg-opacity-75" : "";
251
- }, [highlightedRow])}
252
- className="flex-grow"
253
- emptyComponent={emptyComponent}
254
- endAdornment={endAdornment}
255
- AddColumnComponent={AddColumnComponent}
256
- />
257
-
258
- </div>
259
- </SelectableTableContext.Provider>
260
- );
261
-
262
- },
263
- () => false,
264
- // equal
265
- );
262
+ };
266
263
 
267
264
  function createFilterField({
268
265
  id,
@@ -115,7 +115,6 @@ export const VirtualTable = React.memo<VirtualTableProps<any>>(
115
115
  endAdornment,
116
116
  AddColumnComponent,
117
117
  initialScroll = 0,
118
- debug
119
118
  }: VirtualTableProps<T>) {
120
119
 
121
120
  const sortByProperty: string | undefined = sortBy ? sortBy[0] : undefined;
@@ -131,7 +130,8 @@ export const VirtualTable = React.memo<VirtualTableProps<any>>(
131
130
  // Set initial scroll position
132
131
  useEffect(() => {
133
132
  if (tableRef.current && initialScroll) {
134
- tableRef.current.scrollTo(0, initialScroll);
133
+ const { scrollLeft } = tableRef.current;
134
+ tableRef.current.scrollTo(scrollLeft, initialScroll);
135
135
  }
136
136
  }, [tableRef, initialScroll]);
137
137
 
@@ -170,14 +170,14 @@ export const VirtualTable = React.memo<VirtualTableProps<any>>(
170
170
  });
171
171
 
172
172
  const onColumnResizeInternal = useCallback((params: OnVirtualTableColumnResizeParams) => {
173
- if (debug)
174
- console.log("onColumnResizeInternal", params);
175
- setColumns(columns.map((column) => column.key === params.column.key ? params.column : column));
176
- }, [columns]);
173
+ setColumns(prevColumns =>
174
+ prevColumns.map((column) =>
175
+ column.key === params.column.key ? params.column : column
176
+ )
177
+ );
178
+ }, []);
177
179
 
178
180
  const onColumnResizeEndInternal = useCallback((params: OnVirtualTableColumnResizeParams) => {
179
- if (debug)
180
- console.log("onColumnResizeEndInternal", params);
181
181
  setColumns(columns.map((column) => column.key === params.column.key ? params.column : column));
182
182
  if (onColumnResize) {
183
183
  onColumnResize(params);
@@ -188,14 +188,10 @@ export const VirtualTable = React.memo<VirtualTableProps<any>>(
188
188
  const filterRef = useRef<VirtualTableFilterValues<any> | undefined>();
189
189
 
190
190
  useEffect(() => {
191
- if (debug)
192
- console.log("Filter updated", filterInput);
193
191
  filterRef.current = filterInput;
194
192
  }, [filterInput]);
195
193
 
196
194
  const scrollToTop = useCallback(() => {
197
- if (debug)
198
- console.log("scrollToTop");
199
195
  endReachCallbackThreshold.current = 0;
200
196
  if (tableRef.current) {
201
197
  tableRef.current.scrollTo(tableRef.current?.scrollLeft, 0);
@@ -204,9 +200,6 @@ export const VirtualTable = React.memo<VirtualTableProps<any>>(
204
200
 
205
201
  const onColumnSort = useCallback((key: string) => {
206
202
 
207
- if (debug)
208
- console.log("onColumnSort", key);
209
-
210
203
  const isDesc = sortByProperty === key && currentSort === "desc";
211
204
  const isAsc = sortByProperty === key && currentSort === "asc";
212
205
  const newSort = isAsc ? "desc" : (isDesc ? undefined : "asc");
@@ -234,12 +227,8 @@ export const VirtualTable = React.memo<VirtualTableProps<any>>(
234
227
  }, [checkFilterCombination, currentSort, onFilterUpdate, onResetPagination, onSortByUpdate, scrollToTop, sortByProperty]);
235
228
 
236
229
  const maxScroll = Math.max((data?.length ?? 0) * rowHeight - bounds.height, 0);
237
- if (debug)
238
- console.log("maxScroll", maxScroll);
239
230
 
240
231
  const onEndReachedInternal = useCallback((scrollOffset: number) => {
241
- if (debug)
242
- console.log("onEndReachedInternal", scrollOffset, endReachCallbackThreshold.current + endOffset);
243
232
  if (onEndReached && (data?.length ?? 0) > 0 && scrollOffset > endReachCallbackThreshold.current + endOffset) {
244
233
  endReachCallbackThreshold.current = scrollOffset;
245
234
  onEndReached();
@@ -255,12 +244,6 @@ export const VirtualTable = React.memo<VirtualTableProps<any>>(
255
244
  scrollOffset: number,
256
245
  scrollUpdateWasRequested: boolean;
257
246
  }) => {
258
- if (debug)
259
- console.log("onScroll", {
260
- scrollDirection,
261
- scrollOffset,
262
- scrollUpdateWasRequested
263
- });
264
247
  if (onScrollProp) {
265
248
  debouncedScroll({
266
249
  scrollDirection,
@@ -273,8 +256,6 @@ export const VirtualTable = React.memo<VirtualTableProps<any>>(
273
256
  }, [maxScroll, onEndReachedInternal]);
274
257
 
275
258
  const onFilterUpdateInternal = useCallback((column: VirtualTableColumn, filterForProperty?: [VirtualTableWhereFilterOp, any]) => {
276
- if (debug)
277
- console.log("onFilterUpdateInternal", column, filterForProperty);
278
259
 
279
260
  endReachCallbackThreshold.current = 0;
280
261
  const filter = filterRef.current;
@@ -337,9 +318,6 @@ export const VirtualTable = React.memo<VirtualTableProps<any>>(
337
318
  AddColumnComponent
338
319
  };
339
320
 
340
- if (debug)
341
- console.log("VirtualTable render", virtualListController);
342
-
343
321
  return (
344
322
  <div
345
323
  ref={measureRef}
@@ -162,11 +162,6 @@ export interface VirtualTableProps<T extends Record<string, any>> {
162
162
  */
163
163
  initialScroll?: number;
164
164
 
165
- /**
166
- * Debug mode
167
- */
168
- debug?: boolean;
169
-
170
165
  }
171
166
 
172
167
  export type CellRendererParams<T = any> = {
@@ -66,7 +66,6 @@ export function VirtualTableInput(props: {
66
66
  width: "100%",
67
67
  color: "unset",
68
68
  fontWeight: "unset",
69
- lineHeight: 1.7142857,
70
69
  fontSize: "unset",
71
70
  fontFamily: "unset",
72
71
  background: "unset",
@@ -36,3 +36,5 @@ export * from "./ArrayContainer";
36
36
  export * from "./ReferenceWidget";
37
37
  export * from "./SearchIconsView";
38
38
  export * from "./FieldCaption";
39
+
40
+ export * from "./EntityPreview";
@@ -182,11 +182,13 @@ export function EntityEditViewInner<M extends Record<string, any>>({
182
182
 
183
183
  const subcollections = (collection.subcollections ?? []).filter(c => !c.hideFromNavigation);
184
184
  const subcollectionsCount = subcollections?.length ?? 0;
185
- const customViews = collection.entityViews;
185
+ const customViews = collection.entityViews ?? [];
186
186
  const customViewsCount = customViews?.length ?? 0;
187
187
  const includeJsonView = collection.includeJsonView === undefined ? true : collection.includeJsonView;
188
188
  const hasAdditionalViews = customViewsCount > 0 || subcollectionsCount > 0 || includeJsonView;
189
189
 
190
+ const plugins = customizationController.plugins;
191
+
190
192
  const {
191
193
  resolvedEntityViews,
192
194
  selectedEntityView,
@@ -212,7 +214,6 @@ export function EntityEditViewInner<M extends Record<string, any>>({
212
214
  }
213
215
 
214
216
  if (!entityId) {
215
- console.error("INTERNAL: entityId is not defined");
216
217
  return null;
217
218
  }
218
219
 
@@ -254,6 +255,7 @@ export function EntityEditViewInner<M extends Record<string, any>>({
254
255
  <ErrorBoundary>
255
256
  {usedFormContext && <Builder
256
257
  collection={collection}
258
+ parentCollectionIds={parentCollectionIds}
257
259
  entity={usedEntity}
258
260
  modifiedValues={usedFormContext?.formex?.values ?? usedEntity?.values}
259
261
  formContext={usedFormContext}
@@ -335,6 +337,7 @@ export function EntityEditViewInner<M extends Record<string, any>>({
335
337
  entity={entity}
336
338
  path={path}
337
339
  collection={collection}/>
340
+ <div className="h-16"/>
338
341
  </div>
339
342
  </div> : null;
340
343
 
@@ -384,14 +387,24 @@ export function EntityEditViewInner<M extends Record<string, any>>({
384
387
  </Tab>
385
388
  );
386
389
 
387
- const customViewTabs = resolvedEntityViews.map((view) =>
388
- <Tab
389
- className="text-sm min-w-[120px]"
390
- value={view.key}
391
- key={`entity_detail_collection_tab_${view.name}`}>
392
- {view.name}
393
- </Tab>
394
- );
390
+ const customViewTabsStart = resolvedEntityViews.filter(view => view.position === "start")
391
+ .map((view) =>
392
+ <Tab
393
+ className={!view.tabComponent ? "text-sm min-w-[120px]" : undefined}
394
+ value={view.key}
395
+ key={`entity_detail_collection_tab_${view.name}`}>
396
+ {view.tabComponent ?? view.name}
397
+ </Tab>
398
+ );
399
+ const customViewTabsEnd = resolvedEntityViews.filter(view => !view.position || view.position === "end")
400
+ .map((view) =>
401
+ <Tab
402
+ className={!view.tabComponent ? "text-sm min-w-[120px]" : undefined}
403
+ value={view.key}
404
+ key={`entity_detail_collection_tab_${view.name}`}>
405
+ {view.tabComponent ?? view.name}
406
+ </Tab>
407
+ );
395
408
 
396
409
  const shouldShowTopBar = Boolean(barActions) || hasAdditionalViews;
397
410
 
@@ -417,11 +430,12 @@ export function EntityEditViewInner<M extends Record<string, any>>({
417
430
  {includeJsonView && <Tab
418
431
  disabled={!hasAdditionalViews}
419
432
  value={JSON_TAB_VALUE}
420
- innerClassName={"block"}
421
433
  className={"text-sm"}>
422
434
  <CodeIcon size={"small"}/>
423
435
  </Tab>}
424
436
 
437
+ {customViewTabsStart}
438
+
425
439
  <Tab
426
440
  disabled={!hasAdditionalViews}
427
441
  value={MAIN_TAB_VALUE}
@@ -430,7 +444,7 @@ export function EntityEditViewInner<M extends Record<string, any>>({
430
444
  </Tab>
431
445
 
432
446
 
433
- {customViewTabs}
447
+ {customViewTabsEnd}
434
448
 
435
449
  {subcollectionTabs}
436
450
  </Tabs>}
@@ -453,8 +467,6 @@ export function EntityEditViewInner<M extends Record<string, any>>({
453
467
 
454
468
  </div>;
455
469
 
456
- const plugins = customizationController.plugins;
457
-
458
470
  if (plugins) {
459
471
  plugins.forEach((plugin: FireCMSPlugin) => {
460
472
  if (plugin.form?.provider) {
@@ -19,11 +19,18 @@ import { useLocation, useNavigate } from "react-router-dom";
19
19
  */
20
20
  export function EntitySidePanel(props: EntitySidePanelProps) {
21
21
 
22
+ const {
23
+ allowFullScreen = true,
24
+ path,
25
+ entityId,
26
+ formProps,
27
+ } = props;
28
+
22
29
  const {
23
30
  blocked,
24
31
  setBlocked,
25
32
  setBlockedNavigationMessage,
26
- close
33
+ close,
27
34
  } = useSideDialogContext();
28
35
 
29
36
  const navigate = useNavigate();
@@ -64,22 +71,10 @@ export function EntitySidePanel(props: EntitySidePanelProps) {
64
71
  }
65
72
 
66
73
  const parentCollectionIds = useMemo(() => {
67
- return navigationController.getParentCollectionIds(props.path);
68
- }, [navigationController, props.path]);
69
-
70
- const collection = useMemo(() => {
71
- if (props.collection) {
72
- return props.collection;
73
- }
74
+ return navigationController.getParentCollectionIds(path);
75
+ }, [navigationController, path]);
74
76
 
75
- const registryCollection = navigationController.getCollection(props.path);
76
- if (registryCollection) {
77
- return registryCollection;
78
- }
79
-
80
- console.error("ERROR: No collection found in path `", props.path, "`. Entity id: ", props.entityId);
81
- throw Error("ERROR: No collection found in path `" + props.path + "`. Make sure you have defined a collection for this path in the root navigation.");
82
- }, [navigationController, props.collection]);
77
+ const collection = props.collection ?? navigationController.getCollection(path);
83
78
 
84
79
  useEffect(() => {
85
80
  function beforeunload(e: any) {
@@ -126,16 +121,16 @@ export function EntitySidePanel(props: EntitySidePanelProps) {
126
121
  onClick={onClose}>
127
122
  <CloseIcon size={"small"}/>
128
123
  </IconButton>
129
- <IconButton
124
+ {allowFullScreen && <IconButton
130
125
  className="self-center"
131
126
  onClick={() => {
132
- if (props.entityId)
127
+ if (entityId)
133
128
  navigate(location.pathname);
134
129
  else
135
130
  navigate(location.pathname + "#new");
136
131
  }}>
137
132
  <OpenInFullIcon size={"small"}/>
138
- </IconButton>
133
+ </IconButton>}
139
134
  </>}
140
135
  onTabChange={({
141
136
  path,
@@ -151,7 +146,7 @@ export function EntitySidePanel(props: EntitySidePanelProps) {
151
146
  collection,
152
147
  });
153
148
  }}
154
- formProps={props.formProps}
149
+ formProps={formProps}
155
150
  />
156
151
  </ErrorBoundary>
157
152
 
@@ -44,7 +44,7 @@ export function FireCMS<USER extends User>(props: FireCMSProps<USER>) {
44
44
  authController,
45
45
  storageSource,
46
46
  dataSourceDelegate,
47
- plugins,
47
+ plugins: pluginsProp,
48
48
  onAnalyticsEvent,
49
49
  propertyConfigs,
50
50
  entityViews,
@@ -53,6 +53,12 @@ export function FireCMS<USER extends User>(props: FireCMSProps<USER>) {
53
53
  apiKey
54
54
  } = props;
55
55
 
56
+ if (pluginsProp) {
57
+ console.warn("The `plugins` prop is deprecated in the FireCMS component. You should pass your plugins to `useBuildNavigationController` instead.");
58
+ }
59
+
60
+ const plugins = navigationController.plugins ?? pluginsProp;
61
+
56
62
  const sideDialogsController = useBuildSideDialogsController();
57
63
  const sideEntityController = useBuildSideEntityController(navigationController, sideDialogsController, authController);
58
64
 
@@ -621,7 +621,7 @@ export function EntityForm<M extends Record<string, any>>({
621
621
  <>
622
622
  {!Builder && <div className={"w-full py-2 flex flex-col items-start my-4 lg:my-6"}>
623
623
  <Typography
624
- className={"py-4 flex-grow line-clamp-1 " + (collection.hideIdFromForm ? "mb-2" : "mb-0")}
624
+ className={"my-4 flex-grow line-clamp-1 " + (collection.hideIdFromForm ? "mb-6" : "")}
625
625
  variant={"h4"}>
626
626
  {title ?? collection.singularName ?? collection.name}
627
627
  </Typography>
@@ -214,7 +214,6 @@ function FieldInternal<T extends CMSType, CustomProps, M extends Record<string,
214
214
 
215
215
  const customFieldProps: any = property.customProps;
216
216
  const value = formexFieldProps.field.value;
217
- // const initialValue = formexFieldProps.meta.initialValue;
218
217
  const error = getIn(formexFieldProps.form.errors, propertyKey);
219
218
  const touched = getIn(formexFieldProps.form.touched, propertyKey);
220
219
 
@@ -103,7 +103,9 @@ export function CustomIdField<M extends Record<string, any>>({
103
103
  size={"large"}
104
104
  error={error}
105
105
  fullWidth={true}
106
- onValueChange={(v) => onChange(v as string)}
106
+ onValueChange={(v) => {
107
+ onChange(v as string);
108
+ }}
107
109
  {...fieldProps}
108
110
  renderValue={(option) => {
109
111
  const enumConfig = enumValues.find(e => e.id === option);
@@ -24,7 +24,7 @@ export const LabelWithIcon = forwardRef<HTMLDivElement, LabelWithIconProps>(
24
24
  return (
25
25
  <div
26
26
  ref={ref}
27
- className={cls("inline-flex items-center my-0.5",
27
+ className={cls("align-middle inline-flex items-center my-0.5",
28
28
  small ? "gap-1" : "gap-2",
29
29
  className)}
30
30
  >