@firecms/core 3.0.0-alpha.21 → 3.0.0-alpha.25

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 (41) hide show
  1. package/dist/components/TextareaAutosize.d.ts +2 -0
  2. package/dist/components/index.d.ts +2 -0
  3. package/dist/core/components/EntityCollectionTable/EntityCollectionTableProps.d.ts +8 -1
  4. package/dist/core/components/EntityCollectionTable/useEntityCollectionTableController.d.ts +1 -1
  5. package/dist/core/components/VirtualTable/VirtualTableHeader.d.ts +3 -0
  6. package/dist/core/components/VirtualTable/VirtualTableHeaderRow.d.ts +1 -1
  7. package/dist/core/components/VirtualTable/VirtualTableProps.d.ts +14 -0
  8. package/dist/core/components/VirtualTable/types.d.ts +2 -0
  9. package/dist/core/form_field_configs.d.ts +1 -1
  10. package/dist/core/util/make_properties_editable.d.ts +2 -1
  11. package/dist/core/util/property_utils.d.ts +2 -1
  12. package/dist/index.es.js +7886 -7752
  13. package/dist/index.es.js.map +1 -1
  14. package/dist/index.umd.js +105 -105
  15. package/dist/index.umd.js.map +1 -1
  16. package/dist/types/collections.d.ts +1 -0
  17. package/dist/types/plugins.d.ts +22 -0
  18. package/package.json +137 -136
  19. package/src/components/BooleanSwitch.tsx +1 -1
  20. package/src/components/BooleanSwitchWithLabel.tsx +2 -2
  21. package/src/components/CircularProgress.tsx +1 -1
  22. package/src/components/TextField.tsx +5 -6
  23. package/src/components/TextareaAutosize.tsx +8 -4
  24. package/src/components/index.tsx +2 -0
  25. package/src/core/components/EntityCollectionTable/EntityCollectionTable.tsx +18 -7
  26. package/src/core/components/EntityCollectionTable/EntityCollectionTableProps.tsx +13 -1
  27. package/src/core/components/EntityCollectionTable/internal/PropertyTableCell.tsx +4 -2
  28. package/src/core/components/EntityCollectionTable/useEntityCollectionTableController.tsx +1 -1
  29. package/src/core/components/EntityCollectionView/EntityCollectionView.tsx +110 -4
  30. package/src/core/components/VirtualTable/VirtualTable.tsx +19 -7
  31. package/src/core/components/VirtualTable/VirtualTableHeader.tsx +32 -32
  32. package/src/core/components/VirtualTable/VirtualTableHeaderRow.tsx +7 -2
  33. package/src/core/components/VirtualTable/VirtualTableProps.tsx +14 -0
  34. package/src/core/components/VirtualTable/VirtualTableRow.tsx +1 -1
  35. package/src/core/components/VirtualTable/types.tsx +2 -0
  36. package/src/core/util/make_properties_editable.ts +18 -1
  37. package/src/core/util/property_utils.tsx +6 -1
  38. package/src/form/components/LabelWithIcon.tsx +2 -3
  39. package/src/form/field_bindings/TextFieldBinding.tsx +1 -2
  40. package/src/types/collections.ts +2 -0
  41. package/src/types/plugins.tsx +24 -1
@@ -10,6 +10,7 @@ import {
10
10
  FilterValues,
11
11
  PartialEntityCollection,
12
12
  PropertyOrBuilder,
13
+ ResolvedProperty,
13
14
  SaveEntityProps,
14
15
  SelectionController
15
16
  } from "../../../types";
@@ -45,12 +46,12 @@ import {
45
46
  import { useUserConfigurationPersistence } from "../../../hooks/useUserConfigurationPersistence";
46
47
  import { EntityCollectionViewActions } from "./EntityCollectionViewActions";
47
48
  import { useEntityCollectionTableController } from "../EntityCollectionTable/useEntityCollectionTableController";
48
- import { Button, cn, Typography } from "../../../components";
49
+ import { Button, cn, IconButton, TextField, Tooltip, Typography } from "../../../components";
49
50
  import { Popover } from "../../../components/Popover";
50
51
  import { Skeleton } from "../../../components/Skeleton";
51
52
  import { setIn } from "formik";
52
53
  import { getSubcollectionColumnId } from "../EntityCollectionTable/internal/common";
53
- import { KeyboardTabIcon } from "../../../icons";
54
+ import { KeyboardTabIcon, SearchIcon } from "../../../icons";
54
55
  import { useColumnIds } from "./useColumnsIds";
55
56
  import { PopupFormField } from "../EntityCollectionTable/internal/popup_field/PopupFormField";
56
57
  import { GetPropertyForProps } from "../EntityCollectionTable/EntityCollectionTableProps";
@@ -228,6 +229,17 @@ export const EntityCollectionView = React.memo(
228
229
  setLastDeleteTimestamp(Date.now());
229
230
  }, [setSelectedEntities]);
230
231
 
232
+ let AddColumnComponent: React.ComponentType<{
233
+ fullPath: string,
234
+ parentPathSegments: string[],
235
+ collection: EntityCollection;
236
+ }> | undefined
237
+
238
+ // we are only using the first plugin that implements this
239
+ if (context?.plugins) {
240
+ AddColumnComponent = context.plugins.find(plugin => plugin.collectionView?.AddColumnComponent)?.collectionView?.AddColumnComponent;
241
+ }
242
+
231
243
  const onCollectionModifiedForUser = useCallback((path: string, partialCollection: PartialEntityCollection<M>) => {
232
244
  if (userConfigPersistence) {
233
245
  const currentStoredConfig = userConfigPersistence.getCollectionConfig(path);
@@ -305,7 +317,11 @@ export const EntityCollectionView = React.memo(
305
317
  fields: context.fields
306
318
  }), [collection, fullPath]);
307
319
 
308
- const getPropertyFor = useCallback(({ propertyKey, propertyValue, entity }: GetPropertyForProps<M>) => {
320
+ const getPropertyFor = useCallback(({
321
+ propertyKey,
322
+ propertyValue,
323
+ entity
324
+ }: GetPropertyForProps<M>) => {
309
325
  let propertyOrBuilder: PropertyOrBuilder<any, M> | undefined = getPropertyInPath<M>(collection.properties, propertyKey);
310
326
 
311
327
  // we might not find the property in the collection if combining property builders and map spread
@@ -485,6 +501,42 @@ export const EntityCollectionView = React.memo(
485
501
 
486
502
  </Popover>;
487
503
 
504
+ function buildAdditionalHeaderWidget({
505
+ property,
506
+ propertyKey,
507
+ onHover
508
+ }: {
509
+ property: ResolvedProperty,
510
+ propertyKey: string,
511
+ onHover: boolean
512
+ }) {
513
+ if (!context.plugins)
514
+ return null;
515
+ return <>
516
+ {context.plugins.filter(plugin => plugin.collectionView?.HeaderAction)
517
+ .map((plugin, i) => {
518
+ const HeaderAction = plugin.collectionView!.HeaderAction!;
519
+ return <HeaderAction
520
+ onHover={onHover}
521
+ key={`plugin_header_action_${i}`}
522
+ propertyKey={propertyKey}
523
+ property={property}
524
+ fullPath={fullPath}
525
+ parentPathSegments={parentPathSegments ?? []}/>;
526
+ })}
527
+ </>;
528
+ }
529
+
530
+ const addColumnComponentInternal = AddColumnComponent
531
+ ? function () {
532
+ if (typeof AddColumnComponent === "function")
533
+ return <AddColumnComponent fullPath={fullPath}
534
+ parentPathSegments={parentPathSegments ?? []}
535
+ collection={collection}/>;
536
+ return null;
537
+ }
538
+ : undefined;
539
+
488
540
  return (
489
541
  <div className={cn("overflow-hidden h-full w-full", className)}
490
542
  ref={containerRef}>
@@ -519,6 +571,11 @@ export const EntityCollectionView = React.memo(
519
571
  />}
520
572
  hoverRow={hoverRow}
521
573
  inlineEditing={checkInlineEditing()}
574
+ AdditionalHeaderWidget={buildAdditionalHeaderWidget}
575
+ AddColumnComponent={addColumnComponentInternal}
576
+ additionalIDHeaderWidget={<EntityIdHeaderWidget
577
+ path={fullPath}
578
+ collection={collection}/>}
522
579
  />
523
580
 
524
581
  <PopupFormField
@@ -633,4 +690,53 @@ function buildPropertyWidthOverwrite(key: string, width: number): PartialEntityC
633
690
  return { properties: { [key]: { columnWidth: width } } } as PartialEntityCollection;
634
691
  }
635
692
 
636
-
693
+ function EntityIdHeaderWidget({ collection, path }: {
694
+ collection: EntityCollection,
695
+ path: string
696
+ }) {
697
+ const [openPopup, setOpenPopup] = React.useState(false);
698
+ const [searchString, setSearchString] = React.useState("");
699
+ const sideEntityController = useSideEntityController();
700
+ return (
701
+ <Tooltip title={!openPopup ? "Find by ID" : undefined}>
702
+ <Popover
703
+ open={openPopup}
704
+ onOpenChange={setOpenPopup}
705
+ trigger={
706
+ <IconButton size={"small"}>
707
+ <SearchIcon size={"small"}/>
708
+ </IconButton>
709
+ }
710
+ >
711
+ <form noValidate={true}
712
+ onSubmit={(e) => {
713
+ e.preventDefault();
714
+ if (!searchString) return;
715
+ setOpenPopup(false);
716
+ return sideEntityController.open({
717
+ entityId: searchString,
718
+ path,
719
+ collection,
720
+ updateUrl: true
721
+ });
722
+ }}
723
+ className={"text-gray-900 dark:text-white w-96 max-w-full"}>
724
+
725
+ <div className="flex p-4 w-full gap-4">
726
+ <TextField
727
+ placeholder={"Find entity by ID"}
728
+ size={"small"}
729
+ onChange={(e) => setSearchString(e.target.value)}
730
+ value={searchString}
731
+ className={"flex-grow"}/>
732
+ <Button variant={"outlined"}
733
+ disabled={!searchString}
734
+ type={"submit"}
735
+ >Go</Button>
736
+ </div>
737
+ </form>
738
+ </Popover>
739
+
740
+ </Tooltip>
741
+ );
742
+ }
@@ -26,7 +26,10 @@ import { AssignmentIcon } from "../../../icons";
26
26
  const VirtualListContext = createContext<VirtualTableContextProps<any>>({} as any);
27
27
  VirtualListContext.displayName = "VirtualListContext";
28
28
 
29
- type InnerElementProps = { children: React.ReactNode, style: any };
29
+ type InnerElementProps = {
30
+ children: React.ReactNode,
31
+ style: any
32
+ };
30
33
 
31
34
  // eslint-disable-next-line react/display-name
32
35
  const innerElementType = forwardRef<HTMLDivElement, InnerElementProps>(({
@@ -107,7 +110,8 @@ export const VirtualTable = React.memo<VirtualTableProps<any>>(
107
110
  createFilterField,
108
111
  rowClassName,
109
112
  className,
110
- endAdornment
113
+ endAdornment,
114
+ AddColumnComponent
111
115
  }: VirtualTableProps<T>) {
112
116
 
113
117
  const sortByProperty: string | undefined = sortBy ? sortBy[0] : undefined;
@@ -186,11 +190,11 @@ export const VirtualTable = React.memo<VirtualTableProps<any>>(
186
190
 
187
191
  const maxScroll = Math.max((data?.length ?? 0) * getRowHeight(size) - bounds.height, 0);
188
192
  const onEndReachedInternal = useCallback((scrollOffset: number) => {
189
- if (onEndReached && (data?.length ?? 0) > 0 && scrollOffset > endReachCallbackThreshold.current + 500) {
193
+ if (onEndReached && (data?.length ?? 0) > 0 && scrollOffset > endReachCallbackThreshold.current + 600) {
190
194
  endReachCallbackThreshold.current = scrollOffset;
191
195
  onEndReached();
192
196
  }
193
- }, [data, onEndReached]);
197
+ }, [data?.length, onEndReached]);
194
198
 
195
199
  const onScroll = useCallback(({
196
200
  scrollOffset,
@@ -200,7 +204,7 @@ export const VirtualTable = React.memo<VirtualTableProps<any>>(
200
204
  scrollOffset: number,
201
205
  scrollUpdateWasRequested: boolean;
202
206
  }) => {
203
- if (!scrollUpdateWasRequested && (scrollOffset >= maxScroll - 500))
207
+ if (!scrollUpdateWasRequested && (scrollOffset >= maxScroll - 600))
204
208
  onEndReachedInternal(scrollOffset);
205
209
  }, [maxScroll, onEndReachedInternal]);
206
210
 
@@ -278,7 +282,8 @@ export const VirtualTable = React.memo<VirtualTableProps<any>>(
278
282
  hoverRow: hoverRow ?? false,
279
283
  createFilterField,
280
284
  rowClassName,
281
- endAdornment
285
+ endAdornment,
286
+ AddColumnComponent
282
287
  }}>
283
288
 
284
289
  <MemoizedList
@@ -288,6 +293,7 @@ export const VirtualTable = React.memo<VirtualTableProps<any>>(
288
293
  height={bounds.height}
289
294
  itemCount={(data?.length ?? 0) + (endAdornment ? 1 : 0)}
290
295
  onScroll={onScroll}
296
+ includeAddColumn={Boolean(AddColumnComponent)}
291
297
  itemSize={getRowHeight(size)}/>
292
298
 
293
299
  </VirtualListContext.Provider>
@@ -303,7 +309,8 @@ function MemoizedList({
303
309
  height,
304
310
  itemCount,
305
311
  onScroll,
306
- itemSize
312
+ itemSize,
313
+ includeAddColumn
307
314
  }: {
308
315
  outerRef: RefObject<HTMLDivElement>;
309
316
  width: number;
@@ -315,6 +322,7 @@ function MemoizedList({
315
322
  scrollUpdateWasRequested: boolean;
316
323
  }) => void;
317
324
  itemSize: number;
325
+ includeAddColumn?: boolean;
318
326
  }) {
319
327
 
320
328
  const Row = useCallback(({
@@ -360,6 +368,7 @@ function MemoizedList({
360
368
  top: `calc(${style.top}px + 48px)`
361
369
  }}
362
370
  size={size}>
371
+
363
372
  {columns.map((column: VirtualTableColumn, columnIndex: number) => {
364
373
  const cellData = rowData && rowData[column.key];
365
374
  return <VirtualTableCell
@@ -373,6 +382,9 @@ function MemoizedList({
373
382
  rowIndex={index}
374
383
  columnIndex={columnIndex}/>;
375
384
  })}
385
+
386
+ {includeAddColumn && <div className={"w-20"}/>}
387
+
376
388
  </VirtualTableRow>
377
389
  );
378
390
  }}
@@ -3,12 +3,9 @@ import equal from "react-fast-compare";
3
3
 
4
4
  import { VirtualTableColumn, VirtualTableSort, VirtualTableWhereFilterOp } from "./VirtualTableProps";
5
5
  import { ErrorBoundary } from "../ErrorBoundary";
6
- import { Button, IconButton } from "../../../components";
6
+ import { Badge, Button, cn, IconButton, Popover } from "../../../components";
7
7
  import { defaultBorderMixin } from "../../../styles";
8
- import { ArrowUpwardIcon, ExpandMoreIcon } from "../../../icons";
9
- import { Popover } from "../../../components/Popover";
10
- import { Badge } from "../../../components/Badge";
11
- import { cn } from "../../../components/util/cn";
8
+ import { ArrowUpwardIcon, FilterListIcon } from "../../../icons";
12
9
 
13
10
  interface FilterFormProps<T> {
14
11
  column: VirtualTableColumn<T>;
@@ -40,6 +37,7 @@ type VirtualTableHeaderProps<M extends Record<string, any>> = {
40
37
  onFilterUpdate: (column: VirtualTableColumn, filterForProperty?: [VirtualTableWhereFilterOp, any]) => void;
41
38
  onClickResizeColumn?: (columnIndex: number, column: VirtualTableColumn) => void;
42
39
  createFilterField?: (props: FilterFormFieldProps<any>) => React.ReactNode;
40
+ AdditionalHeaderWidget?: (props: { onHover: boolean }) => React.ReactNode;
43
41
  };
44
42
 
45
43
  export const VirtualTableHeader = React.memo<VirtualTableHeaderProps<any>>(
@@ -53,7 +51,8 @@ export const VirtualTableHeader = React.memo<VirtualTableHeaderProps<any>>(
53
51
  filter,
54
52
  column,
55
53
  onClickResizeColumn,
56
- createFilterField
54
+ createFilterField,
55
+ AdditionalHeaderWidget
57
56
  }: VirtualTableHeaderProps<M>) {
58
57
 
59
58
  const [onHover, setOnHover] = useState(false);
@@ -113,26 +112,31 @@ export const VirtualTableHeader = React.memo<VirtualTableHeaderProps<any>>(
113
112
 
114
113
  </div>
115
114
  </div>
116
-
117
- {column.sortable && (sort || hovered || openFilter) &&
118
- <Badge color="secondary"
119
- invisible={!sort}>
120
- <IconButton
121
- size={"small"}
122
- className={onHover || openFilter ? "bg-white dark:bg-gray-950" : undefined}
123
- onClick={() => {
124
- onColumnSort(column.key as Extract<keyof M, string>);
125
- }}
126
- >
127
- {!sort &&
128
- <ArrowUpwardIcon/>}
129
- {sort === "asc" &&
130
- <ArrowUpwardIcon/>}
131
- {sort === "desc" &&
132
- <ArrowUpwardIcon className={"rotate-180"}/>}
133
- </IconButton>
134
- </Badge>
135
- }
115
+ <>
116
+
117
+ {AdditionalHeaderWidget &&
118
+ <AdditionalHeaderWidget onHover={onHover || openFilter}/>}
119
+
120
+ {column.sortable && (sort || hovered || openFilter) &&
121
+ <Badge color="secondary"
122
+ invisible={!sort}>
123
+ <IconButton
124
+ size={"small"}
125
+ className={onHover || openFilter ? "bg-white dark:bg-gray-950" : undefined}
126
+ onClick={() => {
127
+ onColumnSort(column.key as Extract<keyof M, string>);
128
+ }}
129
+ >
130
+ {!sort &&
131
+ <ArrowUpwardIcon/>}
132
+ {sort === "asc" &&
133
+ <ArrowUpwardIcon/>}
134
+ {sort === "desc" &&
135
+ <ArrowUpwardIcon className={"rotate-180"}/>}
136
+ </IconButton>
137
+ </Badge>
138
+ }
139
+ </>
136
140
 
137
141
  {column.filter && createFilterField && <div>
138
142
  <Badge color="secondary"
@@ -146,7 +150,7 @@ export const VirtualTableHeader = React.memo<VirtualTableHeaderProps<any>>(
146
150
  className={onHover || openFilter ? "bg-white dark:bg-gray-950" : undefined}
147
151
  size={"small"}
148
152
  onClick={handleSettingsClick}>
149
- <ExpandMoreIcon/>
153
+ <FilterListIcon size={"small"}/>
150
154
  </IconButton>}
151
155
  >
152
156
  {column.filter &&
@@ -218,11 +222,7 @@ function FilterForm<M>({
218
222
 
219
223
  if (!filterField) return null;
220
224
  return (
221
- <div className={
222
- cn(
223
- "text-gray-900 dark:text-white",
224
- )
225
- }>
225
+ <div className={"text-gray-900 dark:text-white"}>
226
226
  <div
227
227
  className={cn(defaultBorderMixin, "py-4 px-6 text-xs font-semibold uppercase border-b")}>
228
228
  {column.title ?? id}
@@ -16,7 +16,8 @@ export const VirtualTableHeaderRow = ({
16
16
  filter,
17
17
  onColumnResize,
18
18
  onColumnResizeEnd,
19
- createFilterField
19
+ createFilterField,
20
+ AddColumnComponent
20
21
  }: VirtualTableContextProps<any>) => {
21
22
 
22
23
  const columnRefs = columns.map(() => createRef<HTMLDivElement>());
@@ -119,9 +120,13 @@ export const VirtualTableHeaderRow = ({
119
120
  onColumnSort={onColumnSort}
120
121
  onClickResizeColumn={onClickResizeColumn}
121
122
  column={column}
122
- createFilterField={createFilterField}/>
123
+ createFilterField={createFilterField}
124
+ AdditionalHeaderWidget={column.AdditionalHeaderWidget}/>
123
125
  </ErrorBoundary>;
124
126
  })}
127
+
128
+ {AddColumnComponent && <AddColumnComponent/>}
129
+
125
130
  </div>
126
131
  );
127
132
  };
@@ -136,6 +136,12 @@ export interface VirtualTableProps<T extends Record<string, any>> {
136
136
  */
137
137
  endAdornment?: React.ReactNode;
138
138
 
139
+ /**
140
+ * If adding this callback, a button to add a new column is displayed.
141
+ * @param column
142
+ */
143
+ AddColumnComponent?: React.ComponentType;
144
+
139
145
  }
140
146
 
141
147
  export type CellRendererParams<T extends any> = {
@@ -206,7 +212,15 @@ export interface VirtualTableColumn<CustomProps extends any = any> {
206
212
  */
207
213
  resizable?: boolean;
208
214
 
215
+ /**
216
+ * Custom props passed to the cell renderer
217
+ */
209
218
  custom?: CustomProps;
219
+
220
+ /**
221
+ * Additional children to be rendered in the header when hovering
222
+ */
223
+ AdditionalHeaderWidget?: (props: { onHover: boolean }) => React.ReactNode;
210
224
  }
211
225
 
212
226
  /**
@@ -15,7 +15,7 @@ export const VirtualTableRow = React.memo<VirtualTableRowProps<any>>(
15
15
  size,
16
16
  style,
17
17
  hoverRow,
18
- rowClassName
18
+ rowClassName,
19
19
  }: VirtualTableRowProps<T>) {
20
20
 
21
21
  const onClick = useCallback((event: React.SyntheticEvent) => onRowClick
@@ -1,3 +1,4 @@
1
+ import React from "react";
1
2
  import {
2
3
  CellRendererParams,
3
4
  OnRowClickParams,
@@ -39,4 +40,5 @@ export type VirtualTableContextProps<T extends any> = {
39
40
  createFilterField?: (props: FilterFormFieldProps<any>) => React.ReactNode;
40
41
  rowClassName?: (rowData: T) => string | undefined;
41
42
  endAdornment?: React.ReactNode;
43
+ AddColumnComponent?: React.ComponentType;
42
44
  };
@@ -1,4 +1,5 @@
1
- import { Properties } from "../../types";
1
+ import { Properties, PropertiesOrBuilders } from "../../types";
2
+ import { isPropertyBuilder } from "./entities";
2
3
 
3
4
  export function makePropertiesEditable(properties: Properties) {
4
5
  Object.keys(properties).forEach((key) => {
@@ -9,3 +10,19 @@ export function makePropertiesEditable(properties: Properties) {
9
10
  }
10
11
  });
11
12
  }
13
+
14
+ export function makePropertiesNonEditable(properties: PropertiesOrBuilders): PropertiesOrBuilders {
15
+ return Object.entries(properties).reduce((acc, [key, property]) => {
16
+ if (!isPropertyBuilder(property) && property.dataType === "map" && property.properties) {
17
+ const updated = { ...property, properties: makePropertiesNonEditable(property.properties as PropertiesOrBuilders) };
18
+ acc[key] = updated;
19
+ }
20
+ if (isPropertyBuilder(property)) {
21
+ acc[key] = property;
22
+ } else {
23
+ acc[key] = { ...property, editable: false };
24
+ }
25
+ return acc;
26
+ }, {} as PropertiesOrBuilders);
27
+
28
+ }
@@ -1,6 +1,6 @@
1
1
  import React from "react";
2
2
 
3
- import { FieldConfig, PropertiesOrBuilders, PropertyOrBuilder, ResolvedProperty } from "../../types";
3
+ import { EntityCollection, FieldConfig, PropertiesOrBuilders, PropertyOrBuilder, ResolvedProperty } from "../../types";
4
4
  import { getFieldConfig } from "../form_field_configs";
5
5
  import { isPropertyBuilder } from "./entities";
6
6
  import { resolveProperty } from "./resolutions";
@@ -123,3 +123,8 @@ export function getPropertiesWithPropertiesOrder<M extends Record<string, any>>(
123
123
  });
124
124
  return result;
125
125
  }
126
+
127
+ export function getDefaultPropertiesOrder(collection: EntityCollection<any>): string[] {
128
+ if (collection.propertiesOrder) return collection.propertiesOrder;
129
+ return [...Object.keys(collection.properties), ...(collection.additionalFields ?? []).map(field => field.id)];
130
+ }
@@ -1,5 +1,4 @@
1
1
  import React from "react";
2
- import { Typography } from "../../components/Typography";
3
2
 
4
3
  interface LabelWithIconProps {
5
4
  icon: React.ReactNode;
@@ -27,8 +26,8 @@ export function LabelWithIcon({
27
26
 
28
27
  {icon}
29
28
 
30
- <Typography component={"span"}
31
- className={`font-medium text-${small ? "base" : "sm"} origin-top-left transform ${small ? "translate-x-2 scale-75" : ""}`}>{(title ?? "") + (required ? " *" : "")}</Typography>
29
+ <span
30
+ className={`font-medium text-${small ? "base" : "sm"} origin-top-left transform ${small ? "translate-x-2 scale-75" : ""}`}>{(title ?? "") + (required ? " *" : "")}</span>
32
31
 
33
32
  </span>
34
33
  );
@@ -4,9 +4,8 @@ import { FieldProps, PreviewType } from "../../types";
4
4
  import { FieldHelperText, LabelWithIcon } from "../components";
5
5
  import { useClearRestoreValue } from "../../hooks";
6
6
  import { getIconForProperty } from "../../core";
7
- import { IconButton, TextField } from "../../components";
7
+ import { Collapse, IconButton, TextField } from "../../components";
8
8
  import { PropertyPreview } from "../../preview";
9
- import { Collapse } from "../../components/Collapse";
10
9
  import { ClearIcon } from "../../icons";
11
10
 
12
11
  interface TextFieldProps<T extends string | number> extends FieldProps<T> {
@@ -477,6 +477,8 @@ export type TableController<M extends Record<string, any> = any> = {
477
477
  sortBy?: [string, "asc" | "desc"]) => boolean;
478
478
  popupCell?: SelectedCellProps<M>;
479
479
  setPopupCell?: (popupCell?: SelectedCellProps<M>) => void;
480
+
481
+ onAddColumn?: (column: string) => void;
480
482
  }
481
483
 
482
484
  export type SelectedCellProps<M extends Record<string, any>> = {
@@ -3,7 +3,6 @@ import React, { PropsWithChildren } from "react";
3
3
  import { FireCMSContext } from "./firecms_context";
4
4
  import { CollectionActionsProps, EntityCollection } from "./collections";
5
5
  import { User } from "./user";
6
- import { FieldConfigId } from "./field_config";
7
6
  import { FieldProps, FormContext } from "./fields";
8
7
  import { CMSType, Property } from "./properties";
9
8
  import { EntityStatus } from "./entities";
@@ -105,6 +104,30 @@ export type FireCMSPlugin<PROPS = any, FORM_PROPS = any> = {
105
104
 
106
105
  }
107
106
 
107
+ collectionView?: {
108
+ /**
109
+ * Use this method to inject widgets to the entity collections header
110
+ * @param props
111
+ */
112
+ HeaderAction?: React.ComponentType<{
113
+ property: ResolvedProperty,
114
+ propertyKey: string,
115
+ fullPath: string,
116
+ parentPathSegments: string[],
117
+ onHover: boolean,
118
+ }>;
119
+
120
+ /**
121
+ * If you add this callback to your plugin, an add button will be added to the collection table.
122
+ * TODO: Only the first plugin that defines this callback will be used, at the moment.
123
+ */
124
+ AddColumnComponent?: React.ComponentType<{
125
+ fullPath: string,
126
+ parentPathSegments: string[],
127
+ collection: EntityCollection;
128
+ }>;
129
+ }
130
+
108
131
  }
109
132
 
110
133
  /**