@firecms/core 3.0.0-canary.9 → 3.0.0-canary.90

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 (246) hide show
  1. package/README.md +1 -1
  2. package/dist/app/AppBar.d.ts +12 -0
  3. package/dist/app/Drawer.d.ts +17 -0
  4. package/dist/app/Scaffold.d.ts +30 -0
  5. package/dist/app/index.d.ts +4 -0
  6. package/dist/app/useApp.d.ts +16 -0
  7. package/dist/components/CircularProgressCenter.d.ts +1 -1
  8. package/dist/components/ClearFilterSortButton.d.ts +5 -0
  9. package/dist/components/EntityCollectionTable/EntityCollectionRowActions.d.ts +11 -12
  10. package/dist/components/EntityCollectionTable/EntityCollectionTable.d.ts +1 -1
  11. package/dist/components/EntityCollectionTable/EntityCollectionTableProps.d.ts +5 -3
  12. package/dist/components/EntityCollectionTable/PropertyTableCell.d.ts +1 -0
  13. package/dist/components/EntityCollectionTable/column_utils.d.ts +1 -2
  14. package/dist/components/EntityCollectionTable/fields/TableReferenceField.d.ts +2 -0
  15. package/dist/components/EntityCollectionTable/internal/CollectionTableToolbar.d.ts +1 -4
  16. package/dist/components/EntityCollectionTable/internal/EntityTableCell.d.ts +2 -2
  17. package/dist/components/EntityCollectionTable/internal/popup_field/PopupFormField.d.ts +1 -1
  18. package/dist/components/EntityCollectionView/EntityCollectionView.d.ts +12 -2
  19. package/dist/components/EntityCollectionView/EntityCollectionViewStartActions.d.ts +11 -0
  20. package/dist/components/EntityPreview.d.ts +5 -4
  21. package/dist/components/ErrorView.d.ts +1 -1
  22. package/dist/components/HomePage/DefaultHomePage.d.ts +1 -1
  23. package/dist/components/HomePage/NavigationCardBinding.d.ts +1 -1
  24. package/dist/components/ReferenceWidget.d.ts +3 -1
  25. package/dist/components/SelectableTable/SelectableTable.d.ts +1 -1
  26. package/dist/components/SelectableTable/filters/ReferenceFilterField.d.ts +2 -1
  27. package/dist/components/VirtualTable/VirtualTableProps.d.ts +15 -12
  28. package/dist/components/VirtualTable/types.d.ts +3 -3
  29. package/dist/components/{EntityCollectionTable/internal → common}/default_entity_actions.d.ts +1 -1
  30. package/dist/components/common/index.d.ts +1 -0
  31. package/dist/components/common/table_height.d.ts +5 -0
  32. package/dist/components/common/types.d.ts +4 -6
  33. package/dist/components/common/useDataSourceEntityCollectionTableController.d.ts +3 -0
  34. package/dist/components/index.d.ts +2 -1
  35. package/dist/contexts/AuthControllerContext.d.ts +1 -1
  36. package/dist/{components/FireCMSAppBar.d.ts → core/DefaultAppBar.d.ts} +6 -9
  37. package/dist/core/DefaultDrawer.d.ts +19 -0
  38. package/dist/core/DrawerNavigationItem.d.ts +9 -0
  39. package/dist/core/EntityEditView.d.ts +17 -3
  40. package/dist/core/FireCMS.d.ts +1 -1
  41. package/dist/core/NavigationRoutes.d.ts +3 -3
  42. package/dist/core/index.d.ts +3 -4
  43. package/dist/form/PropertiesForm.d.ts +8 -0
  44. package/dist/form/components/ErrorFocus.d.ts +1 -1
  45. package/dist/form/components/FieldHelperText.d.ts +3 -3
  46. package/dist/form/components/StorageItemPreview.d.ts +4 -4
  47. package/dist/form/field_bindings/MapFieldBinding.d.ts +1 -1
  48. package/dist/form/field_bindings/StorageUploadFieldBinding.d.ts +2 -4
  49. package/dist/form/index.d.ts +0 -2
  50. package/dist/hooks/data/delete.d.ts +2 -2
  51. package/dist/hooks/data/save.d.ts +2 -3
  52. package/dist/hooks/data/useDataSource.d.ts +1 -1
  53. package/dist/hooks/data/useEntityFetch.d.ts +3 -3
  54. package/dist/hooks/index.d.ts +2 -0
  55. package/dist/hooks/useBuildNavigationController.d.ts +1 -2
  56. package/dist/hooks/useProjectLog.d.ts +2 -2
  57. package/dist/hooks/useValidateAuthenticator.d.ts +21 -0
  58. package/dist/index.d.ts +1 -0
  59. package/dist/index.es.js +15552 -11933
  60. package/dist/index.es.js.map +1 -1
  61. package/dist/index.umd.js +19643 -7
  62. package/dist/index.umd.js.map +1 -1
  63. package/dist/internal/useBuildDataSource.d.ts +1 -16
  64. package/dist/internal/useRestoreScroll.d.ts +1 -1
  65. package/dist/preview/PropertyPreviewProps.d.ts +6 -4
  66. package/dist/preview/components/ReferencePreview.d.ts +2 -1
  67. package/dist/preview/components/StorageThumbnail.d.ts +2 -1
  68. package/dist/preview/components/UrlComponentPreview.d.ts +2 -1
  69. package/dist/types/auth.d.ts +26 -2
  70. package/dist/types/collections.d.ts +31 -7
  71. package/dist/types/datasource.d.ts +34 -20
  72. package/dist/types/entities.d.ts +5 -1
  73. package/dist/types/entity_actions.d.ts +14 -0
  74. package/dist/types/entity_callbacks.d.ts +2 -2
  75. package/dist/types/fields.d.ts +31 -30
  76. package/dist/types/index.d.ts +0 -1
  77. package/dist/types/navigation.d.ts +5 -5
  78. package/dist/types/plugins.d.ts +16 -6
  79. package/dist/types/properties.d.ts +17 -4
  80. package/dist/types/storage.d.ts +11 -3
  81. package/dist/util/collections.d.ts +1 -1
  82. package/dist/util/entities.d.ts +1 -1
  83. package/dist/util/icon_synonyms.d.ts +1 -97
  84. package/dist/util/icons.d.ts +2 -2
  85. package/dist/util/navigation_utils.d.ts +2 -2
  86. package/dist/util/objects.d.ts +1 -1
  87. package/dist/util/plurals.d.ts +0 -2
  88. package/dist/util/resolutions.d.ts +13 -13
  89. package/dist/util/storage.d.ts +23 -2
  90. package/dist/util/useStorageUploadController.d.ts +1 -1
  91. package/dist/util/useTraceUpdate.d.ts +1 -0
  92. package/package.json +130 -119
  93. package/src/app/AppBar.tsx +18 -0
  94. package/src/app/Drawer.tsx +25 -0
  95. package/src/app/Scaffold.tsx +249 -0
  96. package/src/app/index.ts +4 -0
  97. package/src/app/useApp.tsx +32 -0
  98. package/src/components/CircularProgressCenter.tsx +1 -1
  99. package/src/components/ClearFilterSortButton.tsx +41 -0
  100. package/src/components/EntityCollectionTable/EntityCollectionRowActions.tsx +9 -18
  101. package/src/components/EntityCollectionTable/EntityCollectionTable.tsx +21 -20
  102. package/src/components/EntityCollectionTable/EntityCollectionTableProps.tsx +10 -6
  103. package/src/components/EntityCollectionTable/PropertyTableCell.tsx +38 -34
  104. package/src/components/EntityCollectionTable/column_utils.tsx +3 -3
  105. package/src/components/EntityCollectionTable/fields/TableReferenceField.tsx +11 -2
  106. package/src/components/EntityCollectionTable/fields/TableStorageUpload.tsx +14 -6
  107. package/src/components/EntityCollectionTable/internal/CollectionTableToolbar.tsx +29 -34
  108. package/src/components/EntityCollectionTable/internal/EntityTableCell.tsx +16 -12
  109. package/src/components/EntityCollectionTable/internal/popup_field/PopupFormField.tsx +4 -5
  110. package/src/components/EntityCollectionView/EntityCollectionView.tsx +69 -45
  111. package/src/components/EntityCollectionView/EntityCollectionViewStartActions.tsx +68 -0
  112. package/src/components/EntityCollectionView/useSelectionController.tsx +19 -7
  113. package/src/components/EntityPreview.tsx +15 -9
  114. package/src/components/EntityView.tsx +5 -5
  115. package/src/components/ErrorView.tsx +1 -1
  116. package/src/components/HomePage/DefaultHomePage.tsx +3 -3
  117. package/src/components/HomePage/NavigationCard.tsx +3 -3
  118. package/src/components/HomePage/NavigationCardBinding.tsx +1 -1
  119. package/src/components/HomePage/SmallNavigationCard.tsx +5 -5
  120. package/src/components/PropertyIdCopyTooltipContent.tsx +2 -3
  121. package/src/components/ReferenceTable/ReferenceSelectionTable.tsx +1 -0
  122. package/src/components/ReferenceWidget.tsx +22 -12
  123. package/src/components/SearchIconsView.tsx +5 -5
  124. package/src/components/SelectableTable/SelectableTable.tsx +7 -7
  125. package/src/components/SelectableTable/filters/BooleanFilterField.tsx +2 -3
  126. package/src/components/SelectableTable/filters/DateTimeFilterField.tsx +22 -7
  127. package/src/components/SelectableTable/filters/ReferenceFilterField.tsx +28 -6
  128. package/src/components/SelectableTable/filters/StringNumberFilterField.tsx +35 -15
  129. package/src/components/VirtualTable/VirtualTable.tsx +70 -37
  130. package/src/components/VirtualTable/VirtualTableCell.tsx +1 -1
  131. package/src/components/VirtualTable/VirtualTableHeader.tsx +4 -4
  132. package/src/components/VirtualTable/VirtualTableHeaderRow.tsx +2 -2
  133. package/src/components/VirtualTable/VirtualTableProps.tsx +18 -14
  134. package/src/components/VirtualTable/VirtualTableRow.tsx +4 -5
  135. package/src/components/VirtualTable/fields/VirtualTableDateField.tsx +1 -1
  136. package/src/components/VirtualTable/types.tsx +2 -3
  137. package/src/components/{EntityCollectionTable/internal → common}/default_entity_actions.tsx +2 -2
  138. package/src/components/common/index.ts +1 -0
  139. package/src/components/{VirtualTable/common.tsx → common/table_height.tsx} +5 -2
  140. package/src/components/common/types.tsx +4 -6
  141. package/src/components/common/useColumnsIds.tsx +10 -2
  142. package/src/components/common/useDataSourceEntityCollectionTableController.tsx +11 -0
  143. package/src/components/common/useTableSearchHelper.ts +52 -12
  144. package/src/components/index.tsx +2 -1
  145. package/src/contexts/AuthControllerContext.tsx +1 -1
  146. package/src/contexts/DialogsProvider.tsx +2 -2
  147. package/src/{components/FireCMSAppBar.tsx → core/DefaultAppBar.tsx} +52 -37
  148. package/src/core/DefaultDrawer.tsx +177 -0
  149. package/src/core/DrawerNavigationItem.tsx +62 -0
  150. package/src/core/EntityEditView.tsx +676 -133
  151. package/src/core/EntitySidePanel.tsx +1 -2
  152. package/src/core/FireCMS.tsx +39 -44
  153. package/src/core/NavigationRoutes.tsx +7 -8
  154. package/src/core/field_configs.tsx +2 -3
  155. package/src/core/index.tsx +3 -4
  156. package/src/form/PropertiesForm.tsx +81 -0
  157. package/src/form/PropertyFieldBinding.tsx +29 -7
  158. package/src/form/components/FieldHelperText.tsx +3 -3
  159. package/src/form/components/StorageItemPreview.tsx +20 -11
  160. package/src/form/components/StorageUploadProgress.tsx +3 -3
  161. package/src/form/field_bindings/ArrayCustomShapedFieldBinding.tsx +8 -5
  162. package/src/form/field_bindings/ArrayOfReferencesFieldBinding.tsx +8 -5
  163. package/src/form/field_bindings/BlockFieldBinding.tsx +2 -2
  164. package/src/form/field_bindings/KeyValueFieldBinding.tsx +44 -39
  165. package/src/form/field_bindings/MapFieldBinding.tsx +11 -3
  166. package/src/form/field_bindings/MarkdownFieldBinding.tsx +2 -2
  167. package/src/form/field_bindings/ReadOnlyFieldBinding.tsx +2 -9
  168. package/src/form/field_bindings/ReferenceFieldBinding.tsx +15 -13
  169. package/src/form/field_bindings/RepeatFieldBinding.tsx +10 -7
  170. package/src/form/field_bindings/SelectFieldBinding.tsx +3 -3
  171. package/src/form/field_bindings/StorageUploadFieldBinding.tsx +22 -43
  172. package/src/form/field_bindings/SwitchFieldBinding.tsx +1 -1
  173. package/src/form/index.tsx +4 -4
  174. package/src/form/validation.ts +1 -17
  175. package/src/hooks/data/delete.ts +3 -3
  176. package/src/hooks/data/save.ts +4 -2
  177. package/src/hooks/data/useDataSource.tsx +2 -2
  178. package/src/hooks/data/useEntityFetch.tsx +3 -3
  179. package/src/hooks/index.tsx +3 -0
  180. package/src/hooks/useBuildLocalConfigurationPersistence.tsx +8 -10
  181. package/src/hooks/useBuildModeController.tsx +11 -5
  182. package/src/hooks/useBuildNavigationController.tsx +137 -61
  183. package/src/hooks/useProjectLog.tsx +21 -8
  184. package/src/hooks/useResolvedNavigationFrom.tsx +1 -1
  185. package/src/hooks/useValidateAuthenticator.tsx +115 -0
  186. package/src/index.ts +1 -0
  187. package/src/internal/useBuildDataSource.ts +56 -49
  188. package/src/internal/useBuildSideEntityController.tsx +88 -21
  189. package/src/preview/PropertyPreview.tsx +9 -16
  190. package/src/preview/PropertyPreviewProps.tsx +4 -8
  191. package/src/preview/components/BooleanPreview.tsx +4 -2
  192. package/src/preview/components/EnumValuesChip.tsx +1 -1
  193. package/src/preview/components/ImagePreview.tsx +21 -33
  194. package/src/preview/components/ReferencePreview.tsx +23 -23
  195. package/src/preview/components/StorageThumbnail.tsx +5 -1
  196. package/src/preview/components/UrlComponentPreview.tsx +44 -11
  197. package/src/preview/property_previews/ArrayOfMapsPreview.tsx +0 -1
  198. package/src/preview/property_previews/ArrayOfReferencesPreview.tsx +2 -1
  199. package/src/preview/property_previews/ArrayOfStorageComponentsPreview.tsx +0 -1
  200. package/src/preview/property_previews/ArrayOfStringsPreview.tsx +0 -1
  201. package/src/preview/property_previews/ArrayOneOfPreview.tsx +2 -3
  202. package/src/preview/property_previews/ArrayPropertyPreview.tsx +2 -3
  203. package/src/preview/property_previews/MapPropertyPreview.tsx +5 -5
  204. package/src/preview/property_previews/StringPropertyPreview.tsx +2 -2
  205. package/src/types/auth.tsx +35 -2
  206. package/src/types/collections.ts +37 -8
  207. package/src/types/customization_controller.tsx +0 -1
  208. package/src/types/datasource.ts +41 -24
  209. package/src/types/entities.ts +9 -1
  210. package/src/types/entity_actions.tsx +16 -3
  211. package/src/types/entity_callbacks.ts +2 -2
  212. package/src/types/fields.tsx +33 -33
  213. package/src/types/index.ts +0 -1
  214. package/src/types/navigation.ts +6 -7
  215. package/src/types/plugins.tsx +18 -8
  216. package/src/types/properties.ts +22 -6
  217. package/src/types/storage.ts +12 -3
  218. package/src/util/collections.ts +1 -1
  219. package/src/util/entities.ts +5 -4
  220. package/src/util/enums.ts +1 -1
  221. package/src/util/icon_list.ts +2 -2
  222. package/src/util/icon_synonyms.ts +3 -99
  223. package/src/util/navigation_utils.ts +6 -6
  224. package/src/util/objects.ts +25 -28
  225. package/src/util/permissions.ts +1 -0
  226. package/src/util/plurals.ts +0 -2
  227. package/src/util/resolutions.ts +32 -31
  228. package/src/util/storage.ts +75 -21
  229. package/src/util/strings.ts +2 -2
  230. package/src/util/useStorageUploadController.tsx +21 -3
  231. package/src/util/useTraceUpdate.tsx +2 -1
  232. package/dist/components/VirtualTable/common.d.ts +0 -2
  233. package/dist/core/Drawer.d.ts +0 -23
  234. package/dist/core/Scaffold.d.ts +0 -55
  235. package/dist/core/SideEntityView.d.ts +0 -7
  236. package/dist/form/EntityForm.d.ts +0 -77
  237. package/dist/internal/useBuildCustomizationController.d.ts +0 -2
  238. package/dist/internal/useLocaleConfig.d.ts +0 -1
  239. package/dist/types/appcheck.d.ts +0 -26
  240. package/src/core/Drawer.tsx +0 -191
  241. package/src/core/Scaffold.tsx +0 -281
  242. package/src/core/SideEntityView.tsx +0 -38
  243. package/src/form/EntityForm.tsx +0 -728
  244. package/src/internal/useBuildCustomizationController.tsx +0 -5
  245. package/src/internal/useLocaleConfig.tsx +0 -18
  246. package/src/types/appcheck.ts +0 -29
@@ -4,7 +4,7 @@ import { Entity, EntityCollection, EntityReference } from "../../../types";
4
4
  import { ReferencePreview } from "../../../preview";
5
5
  import { getReferenceFrom } from "../../../util";
6
6
  import { useNavigationController, useReferenceDialog } from "../../../hooks";
7
- import { Button, Select, SelectItem } from "@firecms/ui";
7
+ import { Button, Checkbox, Label, Select, SelectItem } from "@firecms/ui";
8
8
 
9
9
  interface ReferenceFilterFieldProps {
10
10
  name: string,
@@ -13,6 +13,7 @@ interface ReferenceFilterFieldProps {
13
13
  isArray?: boolean;
14
14
  path?: string;
15
15
  title?: string;
16
+ includeId?: boolean;
16
17
  previewProperties?: string[];
17
18
  hidden: boolean;
18
19
  setHidden: (hidden: boolean) => void;
@@ -40,6 +41,7 @@ export function ReferenceFilterField({
40
41
  isArray,
41
42
  path,
42
43
  title,
44
+ includeId = true,
43
45
  previewProperties,
44
46
  setHidden
45
47
  }: ReferenceFilterFieldProps) {
@@ -54,7 +56,7 @@ export function ReferenceFilterField({
54
56
 
55
57
  const [fieldOperation, fieldValue] = value || [possibleOperations[0], undefined];
56
58
  const [operation, setOperation] = useState<VirtualTableWhereFilterOp>(fieldOperation);
57
- const [internalValue, setInternalValue] = useState<EntityReference | EntityReference[] | undefined>(fieldValue);
59
+ const [internalValue, setInternalValue] = useState<EntityReference | EntityReference[] | undefined | null>(fieldValue);
58
60
 
59
61
  const selectedEntityIds = internalValue
60
62
  ? (Array.isArray(internalValue) ? internalValue.map((ref) => {
@@ -65,7 +67,7 @@ export function ReferenceFilterField({
65
67
  }).filter(Boolean) as string[] : [internalValue.id])
66
68
  : [];
67
69
 
68
- function updateFilter(op: VirtualTableWhereFilterOp, val?: EntityReference | EntityReference[]) {
70
+ function updateFilter(op: VirtualTableWhereFilterOp, val?: EntityReference | EntityReference[] | null) {
69
71
 
70
72
  const prevOpIsArray = multipleSelectOperations.includes(operation);
71
73
  const newOpIsArray = multipleSelectOperations.includes(op);
@@ -134,7 +136,8 @@ export function ReferenceFilterField({
134
136
  onClick={doOpenDialog}
135
137
  reference={reference}
136
138
  hover={true}
137
- allowEntityNavigation={false}
139
+ includeId={includeId}
140
+ includeEntityLink={false}
138
141
  />
139
142
  );
140
143
  };
@@ -142,7 +145,7 @@ export function ReferenceFilterField({
142
145
  return (
143
146
 
144
147
  <div className="flex w-[440px] flex-row">
145
- <div className="w-[120px]">
148
+ <div className="w-[140px]">
146
149
  <Select value={operation}
147
150
  onValueChange={(value) => {
148
151
  updateFilter(value as VirtualTableWhereFilterOp, internalValue);
@@ -156,21 +159,40 @@ export function ReferenceFilterField({
156
159
  </Select>
157
160
  </div>
158
161
 
159
- <div className="flex-grow ml-2 h-full">
162
+ <div className="flex-grow ml-2 h-full gap-2 flex flex-col">
160
163
 
161
164
  {internalValue && Array.isArray(internalValue) && <div>
162
165
  {internalValue.map((ref, index) => buildEntry(ref))}
163
166
  </div>}
167
+
164
168
  {internalValue && !Array.isArray(internalValue) && <div>
165
169
  {buildEntry(internalValue)}
166
170
  </div>}
171
+
167
172
  {(!internalValue || (Array.isArray(internalValue) && internalValue.length === 0)) &&
168
173
  <Button onClick={doOpenDialog}
169
174
  variant={"outlined"}
175
+ size={"large"}
170
176
  className="h-full w-full">
171
177
  {multiple ? "Select references" : "Select reference"}
172
178
  </Button>
173
179
  }
180
+
181
+ {!isArray && <Label
182
+ className="border cursor-pointer rounded-md p-2 flex items-center gap-2 [&:has(:checked)]:bg-gray-100 dark:[&:has(:checked)]:bg-gray-800"
183
+ htmlFor="null-filter"
184
+ >
185
+ <Checkbox id="null-filter"
186
+ checked={internalValue === null}
187
+ size={"small"}
188
+ onCheckedChange={(checked) => {
189
+ if (internalValue !== null)
190
+ updateFilter(operation, null);
191
+ else updateFilter(operation, undefined);
192
+ }}/>
193
+ Filter for null values
194
+ </Label>}
195
+
174
196
  </div>
175
197
 
176
198
  </div>
@@ -1,7 +1,7 @@
1
1
  import React, { useState } from "react";
2
2
  import { EnumValuesChip } from "../../../preview";
3
3
  import { VirtualTableWhereFilterOp } from "../../VirtualTable";
4
- import { ClearIcon, IconButton, Select, SelectItem, TextField } from "@firecms/ui";
4
+ import { Checkbox, ClearIcon, IconButton, Label, Select, SelectItem, TextField } from "@firecms/ui";
5
5
  import { EnumValueConfig } from "../../../types";
6
6
 
7
7
  interface StringNumberFilterFieldProps {
@@ -50,15 +50,15 @@ export function StringNumberFilterField({
50
50
 
51
51
  const [fieldOperation, fieldValue] = value || [possibleOperations[0], undefined];
52
52
  const [operation, setOperation] = useState<VirtualTableWhereFilterOp>(fieldOperation);
53
- const [internalValue, setInternalValue] = useState<string | number | string[] | number[] | undefined>(fieldValue);
53
+ const [internalValue, setInternalValue] = useState<string | number | string[] | number[] | null | undefined>(fieldValue);
54
54
 
55
- function updateFilter(op: VirtualTableWhereFilterOp, val: string | number | string[] | number[] | undefined) {
55
+ function updateFilter(op: VirtualTableWhereFilterOp, val: string | number | string[] | number[] | null | undefined) {
56
56
  let newValue = val;
57
57
  const prevOpIsArray = multipleSelectOperations.includes(operation);
58
58
  const newOpIsArray = multipleSelectOperations.includes(op);
59
59
  if (prevOpIsArray !== newOpIsArray) {
60
60
  // @ts-ignore
61
- newValue = newOpIsArray ? (typeof val === "string" || typeof val === "number" ? [val] : []) : "";
61
+ newValue = newOpIsArray ? (typeof val === "string" || typeof val === "number" ? [val] : []) : undefined;
62
62
  }
63
63
 
64
64
  if (typeof newValue === "number" && isNaN(newValue))
@@ -84,7 +84,7 @@ export function StringNumberFilterField({
84
84
  const multiple = multipleSelectOperations.includes(operation);
85
85
  return (
86
86
 
87
- <div className="flex w-[440px] items-center">
87
+ <div className="flex w-[440px]">
88
88
  <div className={"w-[80px]"}>
89
89
  <Select value={operation}
90
90
  position={"item-aligned"}
@@ -100,11 +100,11 @@ export function StringNumberFilterField({
100
100
  </Select>
101
101
  </div>
102
102
 
103
- <div className="flex-grow ml-2">
103
+ <div className="flex-grow ml-2 flex flex-col gap-2">
104
104
 
105
105
  {!enumValues && <TextField
106
106
  type={dataType === "number" ? "number" : undefined}
107
- value={internalValue !== undefined ? String(internalValue) : ""}
107
+ value={internalValue !== undefined && internalValue != null ? String(internalValue) : ""}
108
108
  onChange={(evt) => {
109
109
  const val = dataType === "number"
110
110
  ? parseFloat(evt.target.value)
@@ -118,26 +118,31 @@ export function StringNumberFilterField({
118
118
  />}
119
119
 
120
120
  {enumValues &&
121
-
122
121
  <Select
123
122
  position={"item-aligned"}
124
123
  value={internalValue !== undefined
125
124
  ? (Array.isArray(internalValue) ? internalValue.map(e => String(e)) : String(internalValue))
126
125
  : isArray ? [] : ""}
127
126
  onValueChange={(value) => {
128
- updateFilter(operation, dataType === "number" ? parseInt(value as string) : value as string)
127
+ if (value !== "")
128
+ updateFilter(operation, dataType === "number" ? parseInt(value as string) : value as string)
129
129
  }}
130
130
  multiple={multiple}
131
131
  endAdornment={internalValue && <IconButton
132
- className="absolute right-3 top-2"
132
+ className="absolute right-2 top-3"
133
133
  onClick={(e) => updateFilter(operation, undefined)}>
134
134
  <ClearIcon/>
135
135
  </IconButton>}
136
- renderValue={(enumKey) => <EnumValuesChip
137
- key={`select_value_${name}_${enumKey}`}
138
- enumKey={enumKey}
139
- enumValues={enumValues}
140
- size={"small"}/>}>
136
+ renderValue={(enumKey) => {
137
+ if (enumKey === null)
138
+ return "Filter for null values";
139
+
140
+ return <EnumValuesChip
141
+ key={`select_value_${name}_${enumKey}`}
142
+ enumKey={enumKey}
143
+ enumValues={enumValues}
144
+ size={"small"}/>;
145
+ }}>
141
146
  {enumValues.map((enumConfig) => (
142
147
  <SelectItem key={`select_value_${name}_${enumConfig.id}`}
143
148
  value={String(enumConfig.id)}>
@@ -150,6 +155,21 @@ export function StringNumberFilterField({
150
155
  </Select>
151
156
  }
152
157
 
158
+ {!isArray && <Label
159
+ className="border cursor-pointer rounded-md p-2 flex items-center gap-2 [&:has(:checked)]:bg-gray-100 dark:[&:has(:checked)]:bg-gray-800"
160
+ htmlFor="null-filter"
161
+ >
162
+ <Checkbox id="null-filter"
163
+ checked={internalValue === null}
164
+ size={"small"}
165
+ onCheckedChange={(checked) => {
166
+ if (internalValue !== null)
167
+ updateFilter(operation, null);
168
+ else updateFilter(operation, undefined);
169
+ }}/>
170
+ Filter for null values
171
+ </Label>}
172
+
153
173
  </div>
154
174
 
155
175
  </div>
@@ -15,12 +15,11 @@ import {
15
15
  VirtualTableWhereFilterOp
16
16
  } from "./VirtualTableProps";
17
17
 
18
- import { getRowHeight } from "./common";
19
18
  import { VirtualTableContextProps } from "./types";
20
19
  import { VirtualTableHeaderRow } from "./VirtualTableHeaderRow";
21
20
  import { VirtualTableRow } from "./VirtualTableRow";
22
21
  import { VirtualTableCell } from "./VirtualTableCell";
23
- import { AssignmentIcon, cn, Markdown, Typography } from "@firecms/ui";
22
+ import { AssignmentIcon, CenteredView, cls, Typography } from "@firecms/ui";
24
23
 
25
24
  const VirtualListContext = createContext<VirtualTableContextProps<any>>({} as any);
26
25
  VirtualListContext.displayName = "VirtualListContext";
@@ -92,7 +91,8 @@ export const VirtualTable = React.memo<VirtualTableProps<any>>(
92
91
  data,
93
92
  onResetPagination,
94
93
  onEndReached,
95
- size = "m",
94
+ endOffset = 600,
95
+ rowHeight = 54,
96
96
  columns: columnsProp,
97
97
  onRowClick,
98
98
  onColumnResize,
@@ -108,9 +108,11 @@ export const VirtualTable = React.memo<VirtualTableProps<any>>(
108
108
  hoverRow,
109
109
  createFilterField,
110
110
  rowClassName,
111
+ style,
111
112
  className,
112
113
  endAdornment,
113
- AddColumnComponent
114
+ AddColumnComponent,
115
+ debug
114
116
  }: VirtualTableProps<T>) {
115
117
 
116
118
  const sortByProperty: string | undefined = sortBy ? sortBy[0] : undefined;
@@ -128,10 +130,14 @@ export const VirtualTable = React.memo<VirtualTableProps<any>>(
128
130
  const [measureRef, bounds] = useMeasure();
129
131
 
130
132
  const onColumnResizeInternal = useCallback((params: OnVirtualTableColumnResizeParams) => {
133
+ if (debug)
134
+ console.log("onColumnResizeInternal", params);
131
135
  setColumns(columns.map((column) => column.key === params.column.key ? params.column : column));
132
136
  }, [columns]);
133
137
 
134
138
  const onColumnResizeEndInternal = useCallback((params: OnVirtualTableColumnResizeParams) => {
139
+ if (debug)
140
+ console.log("onColumnResizeEndInternal", params);
135
141
  setColumns(columns.map((column) => column.key === params.column.key ? params.column : column));
136
142
  if (onColumnResize) {
137
143
  onColumnResize(params);
@@ -142,10 +148,14 @@ export const VirtualTable = React.memo<VirtualTableProps<any>>(
142
148
  const filterRef = useRef<VirtualTableFilterValues<any> | undefined>();
143
149
 
144
150
  useEffect(() => {
151
+ if (debug)
152
+ console.log("Filter updated", filterInput);
145
153
  filterRef.current = filterInput;
146
154
  }, [filterInput]);
147
155
 
148
156
  const scrollToTop = useCallback(() => {
157
+ if (debug)
158
+ console.log("scrollToTop");
149
159
  endReachCallbackThreshold.current = 0;
150
160
  if (tableRef.current) {
151
161
  // scrollRef.current = [scrollRef.current[0], 0];
@@ -155,6 +165,9 @@ export const VirtualTable = React.memo<VirtualTableProps<any>>(
155
165
 
156
166
  const onColumnSort = useCallback((key: string) => {
157
167
 
168
+ if (debug)
169
+ console.log("onColumnSort", key);
170
+
158
171
  const isDesc = sortByProperty === key && currentSort === "desc";
159
172
  const isAsc = sortByProperty === key && currentSort === "asc";
160
173
  const newSort = isAsc ? "desc" : (isDesc ? undefined : "asc");
@@ -181,21 +194,21 @@ export const VirtualTable = React.memo<VirtualTableProps<any>>(
181
194
  scrollToTop();
182
195
  }, [checkFilterCombination, currentSort, onFilterUpdate, onResetPagination, onSortByUpdate, scrollToTop, sortByProperty]);
183
196
 
184
- const resetSort = useCallback(() => {
185
- endReachCallbackThreshold.current = 0;
186
- if (onSortByUpdate)
187
- onSortByUpdate(undefined);
188
- }, [onSortByUpdate]);
197
+ const maxScroll = Math.max((data?.length ?? 0) * rowHeight - bounds.height, 0);
198
+ if (debug)
199
+ console.log("maxScroll", maxScroll);
189
200
 
190
- const maxScroll = Math.max((data?.length ?? 0) * getRowHeight(size) - bounds.height, 0);
191
201
  const onEndReachedInternal = useCallback((scrollOffset: number) => {
192
- if (onEndReached && (data?.length ?? 0) > 0 && scrollOffset > endReachCallbackThreshold.current + 600) {
202
+ if (debug)
203
+ console.log("onEndReachedInternal", scrollOffset, endReachCallbackThreshold.current + endOffset);
204
+ if (onEndReached && (data?.length ?? 0) > 0 && scrollOffset > endReachCallbackThreshold.current + endOffset) {
193
205
  endReachCallbackThreshold.current = scrollOffset;
194
206
  onEndReached();
195
207
  }
196
208
  }, [data?.length, onEndReached]);
197
209
 
198
210
  const onScroll = useCallback(({
211
+ scrollDirection,
199
212
  scrollOffset,
200
213
  scrollUpdateWasRequested
201
214
  }: {
@@ -203,11 +216,19 @@ export const VirtualTable = React.memo<VirtualTableProps<any>>(
203
216
  scrollOffset: number,
204
217
  scrollUpdateWasRequested: boolean;
205
218
  }) => {
206
- if (!scrollUpdateWasRequested && (scrollOffset >= maxScroll - 600))
219
+ if(debug)
220
+ console.log("onScroll", {
221
+ scrollDirection,
222
+ scrollOffset,
223
+ scrollUpdateWasRequested
224
+ });
225
+ if (!scrollUpdateWasRequested && (scrollOffset >= maxScroll - endOffset))
207
226
  onEndReachedInternal(scrollOffset);
208
227
  }, [maxScroll, onEndReachedInternal]);
209
228
 
210
229
  const onFilterUpdateInternal = useCallback((column: VirtualTableColumn, filterForProperty?: [VirtualTableWhereFilterOp, any]) => {
230
+ if(debug)
231
+ console.log("onFilterUpdateInternal", column, filterForProperty);
211
232
 
212
233
  endReachCallbackThreshold.current = 0;
213
234
  const filter = filterRef.current;
@@ -225,24 +246,7 @@ export const VirtualTable = React.memo<VirtualTableProps<any>>(
225
246
  }
226
247
 
227
248
  if (onFilterUpdate) onFilterUpdate(newFilterValue);
228
-
229
- if (column.key !== sortByProperty) {
230
- resetSort();
231
- }
232
- }, [checkFilterCombination, currentSort, onFilterUpdate, resetSort, sortByProperty]);
233
-
234
- const buildErrorView = useCallback(() => (
235
- <div
236
- className="h-full flex flex-col items-center justify-center sticky left-0">
237
-
238
- <Typography variant={"h6"}>
239
- {"Error fetching data from the data source"}
240
- </Typography>
241
-
242
- {error?.message && <Markdown className={"px-4 break-all"} source={error.message}/>}
243
-
244
- </div>
245
- ), [error?.message]);
249
+ }, [checkFilterCombination, currentSort, onFilterUpdate, sortByProperty]);
246
250
 
247
251
  const buildEmptyView = useCallback(() => {
248
252
  if (loading)
@@ -255,11 +259,22 @@ export const VirtualTable = React.memo<VirtualTableProps<any>>(
255
259
  }, [emptyComponent, loading]);
256
260
 
257
261
  const empty = !loading && (data?.length ?? 0) === 0;
258
- const customView = error ? buildErrorView() : (empty ? buildEmptyView() : undefined);
262
+ const customView = error
263
+ ? <CenteredView maxWidth={"2xl"}
264
+ className="flex flex-col gap-2">
265
+
266
+ <Typography variant={"h6"}>
267
+ {"Error fetching data from the data source"}
268
+ </Typography>
269
+
270
+ {error?.message && <SafeLinkRenderer text={error.message}/>}
271
+
272
+ </CenteredView>
273
+ : (empty ? buildEmptyView() : undefined);
259
274
 
260
275
  const virtualListController = {
261
276
  data,
262
- size,
277
+ rowHeight: rowHeight,
263
278
  cellRenderer,
264
279
  columns,
265
280
  currentSort,
@@ -278,23 +293,27 @@ export const VirtualTable = React.memo<VirtualTableProps<any>>(
278
293
  AddColumnComponent
279
294
  };
280
295
 
296
+ if(debug)
297
+ console.log("VirtualTable render", virtualListController);
298
+
281
299
  // useTraceUpdate(virtualListController);
282
300
  return (
283
301
  <div
284
302
  ref={measureRef}
285
- className={cn("h-full w-full", className)}>
303
+ style={style}
304
+ className={cls("h-full w-full", className)}>
286
305
  <VirtualListContext.Provider
287
306
  value={virtualListController}>
288
307
 
289
308
  <MemoizedList
290
309
  outerRef={tableRef}
291
- key={size}
310
+ key={rowHeight}
292
311
  width={bounds.width}
293
312
  height={bounds.height}
294
313
  itemCount={(data?.length ?? 0) + (endAdornment ? 1 : 0)}
295
314
  onScroll={onScroll}
296
315
  includeAddColumn={Boolean(AddColumnComponent)}
297
- itemSize={getRowHeight(size)}/>
316
+ itemSize={rowHeight}/>
298
317
 
299
318
  </VirtualListContext.Provider>
300
319
  </div>
@@ -334,7 +353,7 @@ function MemoizedList({
334
353
  onRowClick,
335
354
  data,
336
355
  columns,
337
- size = "m",
356
+ rowHeight = 54,
338
357
  cellRenderer,
339
358
  hoverRow,
340
359
  rowClassName,
@@ -367,7 +386,7 @@ function MemoizedList({
367
386
  ...style,
368
387
  top: `calc(${style.top}px + 48px)`
369
388
  }}
370
- size={size}>
389
+ rowHeight={rowHeight}>
371
390
 
372
391
  {columns.map((column: VirtualTableColumn, columnIndex: number) => {
373
392
  const cellData = rowData && rowData[column.key];
@@ -403,3 +422,17 @@ function MemoizedList({
403
422
  {Row}
404
423
  </List>;
405
424
  }
425
+
426
+ const SafeLinkRenderer: React.FC<{
427
+ text: string;
428
+ }> = ({ text }) => {
429
+ const urlRegex = /https?:\/\/[^\s]+/g;
430
+ const htmlContent = text.replace(urlRegex, (url) => {
431
+ // For each URL found, replace it with an HTML <a> tag
432
+ return `<a href="${url}" class="underline" target="_blank">Link</a><br/>`;
433
+ });
434
+
435
+ return (
436
+ <div className={"break-all"} dangerouslySetInnerHTML={{ __html: htmlContent }}/>
437
+ );
438
+ };
@@ -16,7 +16,7 @@ type VirtualTableCellProps<T extends any> = {
16
16
  };
17
17
 
18
18
  export const VirtualTableCell = React.memo<VirtualTableCellProps<any>>(
19
- function VirtualTableCell<T extends any>(props: VirtualTableCellProps<T>) {
19
+ function VirtualTableCell<T>(props: VirtualTableCellProps<T>) {
20
20
  // @ts-ignore
21
21
  return props.rowData && props.cellRenderer(
22
22
  {
@@ -7,7 +7,7 @@ import {
7
7
  ArrowUpwardIcon,
8
8
  Badge,
9
9
  Button,
10
- cn,
10
+ cls,
11
11
  defaultBorderMixin,
12
12
  FilterListIcon,
13
13
  IconButton,
@@ -85,7 +85,7 @@ export const VirtualTableHeader = React.memo<VirtualTableHeaderProps<any>>(
85
85
  return (
86
86
  <ErrorBoundary>
87
87
  <div
88
- className={cn("flex py-0 px-3 h-full text-xs uppercase font-semibold relative select-none items-center bg-gray-50 dark:bg-gray-900",
88
+ className={cls("flex py-0 px-3 h-full text-xs uppercase font-semibold relative select-none items-center bg-gray-50 dark:bg-gray-900",
89
89
  "text-gray-600 hover:text-gray-800 dark:text-gray-400 dark:hover:text-gray-200 ",
90
90
  "hover:bg-gray-100 dark:hover:bg-gray-800 hover:bg-opacity-50 dark:hover:bg-opacity-50",
91
91
  column.frozen ? "sticky left-0 z-10" : "relative z-0"
@@ -179,7 +179,7 @@ export const VirtualTableHeader = React.memo<VirtualTableHeaderProps<any>>(
179
179
 
180
180
  {column.resizable && <div
181
181
  ref={resizeHandleRef}
182
- className={cn(
182
+ className={cls(
183
183
  "absolute h-full w-[6px] top-0 right-0 cursor-col-resize",
184
184
  hovered && "bg-gray-300 dark:bg-gray-700"
185
185
  )}
@@ -240,7 +240,7 @@ function FilterForm<M>({
240
240
  }}
241
241
  className={"text-gray-900 dark:text-white"}>
242
242
  <div
243
- className={cn(defaultBorderMixin, "py-4 px-6 text-xs font-semibold uppercase border-b")}>
243
+ className={cls(defaultBorderMixin, "py-4 px-6 text-xs font-semibold uppercase border-b")}>
244
244
  {column.title ?? id}
245
245
  </div>
246
246
  {filterField && <div className="m-4">
@@ -4,7 +4,7 @@ import { VirtualTableColumn, VirtualTableWhereFilterOp } from "./VirtualTablePro
4
4
  import { ErrorBoundary } from "../ErrorBoundary";
5
5
  import { VirtualTableHeader } from "./VirtualTableHeader";
6
6
  import { VirtualTableContextProps } from "./types";
7
- import { cn, defaultBorderMixin } from "@firecms/ui";
7
+ import { cls, defaultBorderMixin } from "@firecms/ui";
8
8
 
9
9
  export const VirtualTableHeaderRow = ({
10
10
  columns,
@@ -100,7 +100,7 @@ export const VirtualTableHeaderRow = ({
100
100
 
101
101
  return (
102
102
  <div
103
- className={cn(defaultBorderMixin, "z-20 sticky min-w-full flex w-fit flex-row top-0 left-0 h-12 border-b bg-gray-50 dark:bg-gray-900")}>
103
+ className={cls(defaultBorderMixin, "z-20 sticky min-w-full flex w-fit flex-row top-0 left-0 h-12 border-b bg-gray-50 dark:bg-gray-900")}>
104
104
  {columns.map((c, columnIndex) => {
105
105
  const column = columns[columnIndex];
106
106
 
@@ -31,11 +31,6 @@ export interface VirtualTableProps<T extends Record<string, any>> {
31
31
  */
32
32
  cellRenderer: React.ComponentType<CellRendererParams<T>>;
33
33
 
34
- /**
35
- * If enabled, content is loaded in batch
36
- */
37
- paginationEnabled?: boolean;
38
-
39
34
  /**
40
35
  * Set this callback if you want to support some combinations
41
36
  * of filter combinations only.
@@ -50,6 +45,11 @@ export interface VirtualTableProps<T extends Record<string, any>> {
50
45
  */
51
46
  onEndReached?: () => void;
52
47
 
48
+ /**
49
+ * Offset in pixels where the onEndReached callback is triggered
50
+ */
51
+ endOffset?: number;
52
+
53
53
  /**
54
54
  * When the pagination should be reset. E.g. the filters or sorting
55
55
  * has been reset.
@@ -69,7 +69,7 @@ export interface VirtualTableProps<T extends Record<string, any>> {
69
69
  /**
70
70
  * Size of the table
71
71
  */
72
- size?: VirtualTableSize,
72
+ rowHeight?: number,
73
73
 
74
74
  /**
75
75
  * In case this table should have some filters set by default
@@ -131,6 +131,11 @@ export interface VirtualTableProps<T extends Record<string, any>> {
131
131
  */
132
132
  className?: string;
133
133
 
134
+ /**
135
+ * Style applied to the table
136
+ */
137
+ style?: React.CSSProperties;
138
+
134
139
  /**
135
140
  * Component rendered at the end of the table, after scroll
136
141
  */
@@ -142,9 +147,14 @@ export interface VirtualTableProps<T extends Record<string, any>> {
142
147
  */
143
148
  AddColumnComponent?: React.ComponentType;
144
149
 
150
+ /**
151
+ * Debug mode
152
+ */
153
+ debug?: boolean;
154
+
145
155
  }
146
156
 
147
- export type CellRendererParams<T extends any = any> = {
157
+ export type CellRendererParams<T = any> = {
148
158
  column: VirtualTableColumn;
149
159
  columns: VirtualTableColumn[];
150
160
  columnIndex: number;
@@ -158,7 +168,7 @@ export type CellRendererParams<T extends any = any> = {
158
168
  * @see Table
159
169
  * @group Components
160
170
  */
161
- export interface VirtualTableColumn<CustomProps extends any = any> {
171
+ export interface VirtualTableColumn<CustomProps = any> {
162
172
 
163
173
  /**
164
174
  * Data key for the cell value, could be "a.b.c"
@@ -232,12 +242,6 @@ export type OnVirtualTableColumnResizeParams = {
232
242
  column: VirtualTableColumn
233
243
  };
234
244
 
235
- /**
236
- * @see Table
237
- * @group Components
238
- */
239
- export type VirtualTableSize = "xs" | "s" | "m" | "l" | "xl";
240
-
241
245
  /**
242
246
  * @see Table
243
247
  * @group Components
@@ -2,9 +2,8 @@ import React, { useCallback } from "react";
2
2
 
3
3
  import equal from "react-fast-compare"
4
4
 
5
- import { getRowHeight } from "./common";
6
5
  import { VirtualTableRowProps } from "./types";
7
- import { cn } from "@firecms/ui";
6
+ import { cls } from "@firecms/ui";
8
7
 
9
8
  export const VirtualTableRow = React.memo<VirtualTableRowProps<any>>(
10
9
  function VirtualTableRow<T>({
@@ -12,7 +11,7 @@ export const VirtualTableRow = React.memo<VirtualTableRowProps<any>>(
12
11
  rowIndex,
13
12
  children,
14
13
  onRowClick,
15
- size,
14
+ rowHeight,
16
15
  style,
17
16
  hoverRow,
18
17
  rowClassName
@@ -29,7 +28,7 @@ export const VirtualTableRow = React.memo<VirtualTableRowProps<any>>(
29
28
 
30
29
  return (
31
30
  <div
32
- className={cn(
31
+ className={cls(
33
32
  "flex min-w-full text-sm border-b border-gray-200 dark:border-gray-800 border-opacity-40 dark:border-opacity-40",
34
33
  rowClassName ? rowClassName(rowData) : "",
35
34
  {
@@ -40,7 +39,7 @@ export const VirtualTableRow = React.memo<VirtualTableRowProps<any>>(
40
39
  onClick={onClick}
41
40
  style={{
42
41
  ...(style),
43
- height: getRowHeight(size),
42
+ height: rowHeight,
44
43
  width: "fit-content"
45
44
  }}
46
45
  >
@@ -25,7 +25,7 @@ export function VirtualTableDateField(props: {
25
25
  return (
26
26
  <DateTimeField
27
27
  value={internalValue ?? undefined}
28
- onChange={(dateValue) => updateValue(dateValue)}
28
+ onChange={(dateValue) => updateValue(dateValue ?? null)}
29
29
  size={"medium"}
30
30
  invisible={true}
31
31
  className={"w-full h-full"}