@firecms/core 3.0.1 → 3.1.0-canary.7d91b7c

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 (185) hide show
  1. package/README.md +1 -1
  2. package/dist/components/AIIcon.d.ts +16 -0
  3. package/dist/components/EntityCollectionTable/EntityCollectionRowActions.d.ts +7 -1
  4. package/dist/components/EntityCollectionTable/EntityCollectionTable.d.ts +1 -1
  5. package/dist/components/EntityCollectionTable/EntityCollectionTableProps.d.ts +14 -0
  6. package/dist/components/EntityCollectionTable/PropertyTableCell.d.ts +6 -0
  7. package/dist/components/EntityCollectionTable/internal/CollectionTableToolbar.d.ts +5 -4
  8. package/dist/components/EntityCollectionTable/internal/EntityTableCell.d.ts +6 -0
  9. package/dist/components/EntityCollectionTable/internal/popup_field/useDraggable.d.ts +2 -2
  10. package/dist/components/EntityCollectionView/Board.d.ts +2 -0
  11. package/dist/components/EntityCollectionView/BoardColumn.d.ts +42 -0
  12. package/dist/components/EntityCollectionView/BoardColumnTitle.d.ts +9 -0
  13. package/dist/components/EntityCollectionView/BoardSortableList.d.ts +14 -0
  14. package/dist/components/EntityCollectionView/EntityBoardCard.d.ts +26 -0
  15. package/dist/components/EntityCollectionView/EntityCard.d.ts +19 -0
  16. package/dist/components/EntityCollectionView/EntityCollectionBoardView.d.ts +20 -0
  17. package/dist/components/EntityCollectionView/EntityCollectionCardView.d.ts +31 -0
  18. package/dist/components/EntityCollectionView/EntityCollectionViewActions.d.ts +2 -2
  19. package/dist/components/EntityCollectionView/EntityCollectionViewStartActions.d.ts +7 -3
  20. package/dist/components/EntityCollectionView/FiltersDialog.d.ts +14 -0
  21. package/dist/components/EntityCollectionView/ViewModeToggle.d.ts +44 -0
  22. package/dist/components/EntityCollectionView/board_types.d.ts +105 -0
  23. package/dist/components/EntityCollectionView/useBoardDataController.d.ts +60 -0
  24. package/dist/components/ErrorBoundary.d.ts +1 -1
  25. package/dist/components/SelectableTable/SelectableTable.d.ts +5 -1
  26. package/dist/components/SelectableTable/filters/DateTimeFilterField.d.ts +2 -1
  27. package/dist/components/VirtualTable/VirtualTableCell.d.ts +6 -0
  28. package/dist/components/VirtualTable/VirtualTableHeader.d.ts +3 -1
  29. package/dist/components/VirtualTable/VirtualTableHeaderRow.d.ts +1 -1
  30. package/dist/components/VirtualTable/VirtualTableProps.d.ts +11 -0
  31. package/dist/components/VirtualTable/fields/VirtualTableDateField.d.ts +1 -0
  32. package/dist/components/VirtualTable/types.d.ts +2 -0
  33. package/dist/components/index.d.ts +3 -0
  34. package/dist/contexts/index.d.ts +10 -0
  35. package/dist/core/DrawerNavigationGroup.d.ts +45 -0
  36. package/dist/core/index.d.ts +1 -0
  37. package/dist/form/components/ErrorFocus.d.ts +1 -1
  38. package/dist/form/validation.d.ts +3 -2
  39. package/dist/hooks/useBreadcrumbsController.d.ts +16 -0
  40. package/dist/hooks/useCollapsedGroups.d.ts +4 -1
  41. package/dist/index.es.js +5266 -1578
  42. package/dist/index.es.js.map +1 -1
  43. package/dist/index.umd.js +5260 -1573
  44. package/dist/index.umd.js.map +1 -1
  45. package/dist/internal/useRestoreScroll.d.ts +1 -1
  46. package/dist/preview/PropertyPreviewProps.d.ts +5 -0
  47. package/dist/preview/components/DatePreview.d.ts +13 -3
  48. package/dist/preview/components/ImagePreview.d.ts +5 -1
  49. package/dist/preview/components/StorageThumbnail.d.ts +2 -1
  50. package/dist/preview/components/UrlComponentPreview.d.ts +2 -1
  51. package/dist/preview/property_previews/ArrayOfStorageComponentsPreview.d.ts +1 -1
  52. package/dist/preview/property_previews/ArrayOfStringsPreview.d.ts +1 -1
  53. package/dist/preview/property_previews/SkeletonPropertyComponent.d.ts +1 -1
  54. package/dist/types/analytics.d.ts +1 -1
  55. package/dist/types/collections.d.ts +50 -2
  56. package/dist/types/datasource.d.ts +0 -1
  57. package/dist/types/plugins.d.ts +62 -1
  58. package/dist/types/properties.d.ts +259 -4
  59. package/dist/util/__tests__/conditions.test.d.ts +1 -0
  60. package/dist/util/__tests__/objects.test.d.ts +1 -0
  61. package/dist/util/conditions.d.ts +26 -0
  62. package/dist/util/entities.d.ts +2 -3
  63. package/dist/util/index.d.ts +2 -1
  64. package/dist/util/property_utils.d.ts +2 -1
  65. package/dist/util/resolutions.d.ts +3 -3
  66. package/package.json +14 -11
  67. package/src/app/Scaffold.tsx +14 -15
  68. package/src/components/AIIcon.tsx +39 -0
  69. package/src/components/ArrayContainer.tsx +1 -4
  70. package/src/components/ClearFilterSortButton.tsx +19 -16
  71. package/src/components/ConfirmationDialog.tsx +0 -2
  72. package/src/components/DeleteEntityDialog.tsx +2 -4
  73. package/src/components/EntityCollectionTable/EntityCollectionRowActions.tsx +74 -41
  74. package/src/components/EntityCollectionTable/EntityCollectionTable.tsx +130 -79
  75. package/src/components/EntityCollectionTable/EntityCollectionTableProps.tsx +121 -104
  76. package/src/components/EntityCollectionTable/PropertyTableCell.tsx +132 -103
  77. package/src/components/EntityCollectionTable/internal/CollectionTableToolbar.tsx +20 -42
  78. package/src/components/EntityCollectionTable/internal/EntityTableCell.tsx +90 -49
  79. package/src/components/EntityCollectionTable/internal/EntityTableCellActions.tsx +1 -1
  80. package/src/components/EntityCollectionTable/internal/popup_field/useDraggable.tsx +11 -11
  81. package/src/components/EntityCollectionView/Board.tsx +324 -0
  82. package/src/components/EntityCollectionView/BoardColumn.tsx +158 -0
  83. package/src/components/EntityCollectionView/BoardColumnTitle.tsx +45 -0
  84. package/src/components/EntityCollectionView/BoardSortableList.tsx +172 -0
  85. package/src/components/EntityCollectionView/EntityBoardCard.tsx +212 -0
  86. package/src/components/EntityCollectionView/EntityCard.tsx +235 -0
  87. package/src/components/EntityCollectionView/EntityCollectionBoardView.tsx +733 -0
  88. package/src/components/EntityCollectionView/EntityCollectionCardView.tsx +244 -0
  89. package/src/components/EntityCollectionView/EntityCollectionView.tsx +519 -203
  90. package/src/components/EntityCollectionView/EntityCollectionViewActions.tsx +31 -19
  91. package/src/components/EntityCollectionView/EntityCollectionViewStartActions.tsx +84 -15
  92. package/src/components/EntityCollectionView/FiltersDialog.tsx +249 -0
  93. package/src/components/EntityCollectionView/ViewModeToggle.tsx +199 -0
  94. package/src/components/EntityCollectionView/board_types.ts +113 -0
  95. package/src/components/EntityCollectionView/useBoardDataController.tsx +490 -0
  96. package/src/components/ErrorTooltip.tsx +2 -1
  97. package/src/components/HomePage/DefaultHomePage.tsx +47 -10
  98. package/src/components/HomePage/HomePageDnD.tsx +56 -41
  99. package/src/components/HomePage/NavigationCard.tsx +20 -18
  100. package/src/components/HomePage/NavigationGroup.tsx +17 -16
  101. package/src/components/HomePage/RenameGroupDialog.tsx +0 -2
  102. package/src/components/HomePage/SmallNavigationCard.tsx +10 -9
  103. package/src/components/ReferenceTable/ReferenceSelectionTable.tsx +3 -10
  104. package/src/components/ReferenceWidget.tsx +2 -4
  105. package/src/components/SelectableTable/SelectableTable.tsx +75 -67
  106. package/src/components/SelectableTable/filters/BooleanFilterField.tsx +7 -6
  107. package/src/components/SelectableTable/filters/DateTimeFilterField.tsx +39 -40
  108. package/src/components/SelectableTable/filters/ReferenceFilterField.tsx +38 -38
  109. package/src/components/SelectableTable/filters/StringNumberFilterField.tsx +49 -58
  110. package/src/components/UnsavedChangesDialog.tsx +0 -2
  111. package/src/components/UserDisplay.tsx +4 -4
  112. package/src/components/VirtualTable/VirtualTable.tsx +272 -118
  113. package/src/components/VirtualTable/VirtualTableCell.tsx +18 -2
  114. package/src/components/VirtualTable/VirtualTableHeader.tsx +59 -50
  115. package/src/components/VirtualTable/VirtualTableHeaderRow.tsx +158 -42
  116. package/src/components/VirtualTable/VirtualTableProps.tsx +14 -1
  117. package/src/components/VirtualTable/VirtualTableRow.tsx +1 -1
  118. package/src/components/VirtualTable/fields/VirtualTableDateField.tsx +3 -0
  119. package/src/components/VirtualTable/fields/VirtualTableSelect.tsx +19 -6
  120. package/src/components/VirtualTable/types.tsx +2 -0
  121. package/src/components/common/useColumnsIds.tsx +95 -3
  122. package/src/components/index.tsx +4 -0
  123. package/src/contexts/BreacrumbsContext.tsx +15 -8
  124. package/src/contexts/index.ts +10 -0
  125. package/src/core/DefaultAppBar.tsx +40 -27
  126. package/src/core/DefaultDrawer.tsx +42 -56
  127. package/src/core/DrawerNavigationGroup.tsx +118 -0
  128. package/src/core/DrawerNavigationItem.tsx +4 -3
  129. package/src/core/EntityEditView.tsx +41 -43
  130. package/src/core/EntitySidePanel.tsx +28 -26
  131. package/src/core/SideDialogs.tsx +4 -2
  132. package/src/core/field_configs.tsx +14 -9
  133. package/src/core/index.tsx +1 -0
  134. package/src/form/EntityForm.tsx +69 -60
  135. package/src/form/PropertyFieldBinding.tsx +61 -46
  136. package/src/form/components/ErrorFocus.tsx +3 -3
  137. package/src/form/components/StorageItemPreview.tsx +2 -1
  138. package/src/form/field_bindings/ArrayOfReferencesFieldBinding.tsx +0 -1
  139. package/src/form/field_bindings/DateTimeFieldBinding.tsx +17 -16
  140. package/src/form/field_bindings/KeyValueFieldBinding.tsx +0 -1
  141. package/src/form/field_bindings/MapFieldBinding.tsx +69 -67
  142. package/src/form/field_bindings/MarkdownEditorFieldBinding.tsx +22 -18
  143. package/src/form/field_bindings/StorageUploadFieldBinding.tsx +83 -83
  144. package/src/form/field_bindings/TextFieldBinding.tsx +71 -35
  145. package/src/form/validation.ts +245 -160
  146. package/src/hooks/useBreadcrumbsController.tsx +18 -0
  147. package/src/hooks/useBuildNavigationController.tsx +46 -23
  148. package/src/hooks/useCollapsedGroups.ts +12 -4
  149. package/src/hooks/useValidateAuthenticator.tsx +1 -1
  150. package/src/internal/useBuildDataSource.ts +68 -34
  151. package/src/internal/useBuildSideDialogsController.tsx +11 -8
  152. package/src/internal/useBuildSideEntityController.tsx +2 -4
  153. package/src/internal/useRestoreScroll.tsx +26 -14
  154. package/src/preview/PropertyPreview.tsx +41 -32
  155. package/src/preview/PropertyPreviewProps.tsx +6 -0
  156. package/src/preview/components/DatePreview.tsx +72 -4
  157. package/src/preview/components/EmptyValue.tsx +1 -1
  158. package/src/preview/components/ImagePreview.tsx +37 -21
  159. package/src/preview/components/StorageThumbnail.tsx +16 -12
  160. package/src/preview/components/UrlComponentPreview.tsx +28 -25
  161. package/src/preview/property_previews/ArrayOfStorageComponentsPreview.tsx +9 -7
  162. package/src/preview/property_previews/ArrayOfStringsPreview.tsx +11 -9
  163. package/src/preview/property_previews/ArrayPropertyPreview.tsx +26 -24
  164. package/src/preview/property_previews/SkeletonPropertyComponent.tsx +61 -56
  165. package/src/routes/CustomCMSRoute.tsx +1 -0
  166. package/src/routes/FireCMSRoute.tsx +26 -13
  167. package/src/types/analytics.ts +10 -0
  168. package/src/types/collections.ts +57 -3
  169. package/src/types/datasource.ts +54 -56
  170. package/src/types/plugins.tsx +69 -1
  171. package/src/types/properties.ts +347 -27
  172. package/src/util/__tests__/conditions.test.ts +506 -0
  173. package/src/util/__tests__/objects.test.ts +196 -0
  174. package/src/util/callbacks.ts +6 -3
  175. package/src/util/collections.ts +51 -6
  176. package/src/util/conditions.ts +339 -0
  177. package/src/util/entities.ts +29 -30
  178. package/src/util/entity_cache.ts +2 -1
  179. package/src/util/index.ts +2 -1
  180. package/src/util/join_collections.ts +10 -8
  181. package/src/util/objects.ts +31 -13
  182. package/src/util/{references.ts → previews.ts} +16 -2
  183. package/src/util/property_utils.tsx +37 -11
  184. package/src/util/resolutions.ts +62 -58
  185. /package/dist/util/{references.d.ts → previews.d.ts} +0 -0
@@ -34,7 +34,7 @@ export type FilterFormFieldProps<CustomProps> = {
34
34
  };
35
35
 
36
36
  type VirtualTableHeaderProps<M extends Record<string, any>> = {
37
- resizeHandleRef: RefObject<HTMLDivElement>;
37
+ resizeHandleRef: RefObject<HTMLDivElement | null>;
38
38
  columnIndex: number;
39
39
  isResizingIndex: number;
40
40
  column: VirtualTableColumn<any>;
@@ -45,22 +45,26 @@ type VirtualTableHeaderProps<M extends Record<string, any>> = {
45
45
  onClickResizeColumn?: (columnIndex: number, column: VirtualTableColumn) => void;
46
46
  createFilterField?: (props: FilterFormFieldProps<any>) => React.ReactNode;
47
47
  AdditionalHeaderWidget?: (props: { onHover: boolean }) => React.ReactNode;
48
+ isDragging?: boolean;
49
+ isDraggable?: boolean;
48
50
  };
49
51
 
50
52
  export const VirtualTableHeader = React.memo<VirtualTableHeaderProps<any>>(
51
53
  function VirtualTableHeader<M extends Record<string, any>>({
52
- resizeHandleRef,
53
- columnIndex,
54
- isResizingIndex,
55
- sort,
56
- onColumnSort,
57
- onFilterUpdate,
58
- filter,
59
- column,
60
- onClickResizeColumn,
61
- createFilterField,
62
- AdditionalHeaderWidget
63
- }: VirtualTableHeaderProps<M>) {
54
+ resizeHandleRef,
55
+ columnIndex,
56
+ isResizingIndex,
57
+ sort,
58
+ onColumnSort,
59
+ onFilterUpdate,
60
+ filter,
61
+ column,
62
+ onClickResizeColumn,
63
+ createFilterField,
64
+ AdditionalHeaderWidget,
65
+ isDragging,
66
+ isDraggable
67
+ }: VirtualTableHeaderProps<M>) {
64
68
 
65
69
  const [onHover, setOnHover] = useState(false);
66
70
 
@@ -85,14 +89,16 @@ export const VirtualTableHeader = React.memo<VirtualTableHeaderProps<any>>(
85
89
  return (
86
90
  <ErrorBoundary>
87
91
  <div
88
- className={cls("flex py-0 px-3 h-full text-xs uppercase font-semibold relative select-none items-center bg-surface-50 dark:bg-surface-900",
92
+ className={cls("flex py-0 px-3 h-full text-xs uppercase font-semibold relative select-none items-center",
93
+ isDragging
94
+ ? "bg-primary-bg dark:bg-primary-bg-dark"
95
+ : "bg-surface-50 dark:bg-surface-900",
89
96
  "text-text-secondary hover:text-text-primary dark:text-text-secondary-dark dark:hover:text-text-primary-dark",
90
- "hover:bg-surface-100 dark:hover:bg-surface-800 hover:bg-opacity-50 dark:hover:bg-opacity-50",
91
- column.frozen ? "sticky left-0 z-10" : "relative z-0"
97
+ !isDragging && "hover:bg-surface-100 dark:hover:bg-surface-800 hover:bg-opacity-50 hover:bg-surface-100/50 dark:hover:bg-opacity-50 dark:hover:bg-surface-800/50",
98
+ column.frozen ? "sticky left-0 z-10" : "relative z-0",
99
+ isDraggable && "cursor-grab"
92
100
  )}
93
101
  style={{
94
- // transition: "color 250ms cubic-bezier(0.4, 0, 0.2, 1) 0ms",
95
- // fontSize: "0.750rem",
96
102
  left: column.frozen ? 0 : undefined,
97
103
  minWidth: column.width,
98
104
  maxWidth: column.width
@@ -123,11 +129,11 @@ export const VirtualTableHeader = React.memo<VirtualTableHeaderProps<any>>(
123
129
  <>
124
130
 
125
131
  {AdditionalHeaderWidget &&
126
- <AdditionalHeaderWidget onHover={onHover || openFilter}/>}
132
+ <AdditionalHeaderWidget onHover={onHover || openFilter} />}
127
133
 
128
134
  {column.sortable && (sort || hovered || openFilter) &&
129
135
  <Badge color="secondary"
130
- invisible={!sort}>
136
+ invisible={!sort}>
131
137
  <IconButton
132
138
  size={"small"}
133
139
  className={onHover || openFilter ? "bg-white dark:bg-surface-950" : undefined}
@@ -136,11 +142,11 @@ export const VirtualTableHeader = React.memo<VirtualTableHeaderProps<any>>(
136
142
  }}
137
143
  >
138
144
  {!sort &&
139
- <ArrowUpwardIcon/>}
145
+ <ArrowUpwardIcon />}
140
146
  {sort === "asc" &&
141
- <ArrowUpwardIcon/>}
147
+ <ArrowUpwardIcon />}
142
148
  {sort === "desc" &&
143
- <ArrowUpwardIcon className={"rotate-180"}/>}
149
+ <ArrowUpwardIcon className={"rotate-180"} />}
144
150
  </IconButton>
145
151
  </Badge>
146
152
  }
@@ -148,7 +154,7 @@ export const VirtualTableHeader = React.memo<VirtualTableHeaderProps<any>>(
148
154
 
149
155
  {column.filter && createFilterField && <div>
150
156
  <Badge color="secondary"
151
- invisible={!filter}>
157
+ invisible={!filter}>
152
158
 
153
159
  <Popover
154
160
  open={openFilter}
@@ -160,16 +166,16 @@ export const VirtualTableHeader = React.memo<VirtualTableHeaderProps<any>>(
160
166
  className={onHover || openFilter ? "bg-white dark:bg-surface-950" : undefined}
161
167
  size={"small"}
162
168
  onClick={handleSettingsClick}>
163
- <FilterListIcon size={"small"}/>
169
+ <FilterListIcon size={"small"} />
164
170
  </IconButton>}
165
171
  >
166
172
  <FilterForm column={column}
167
- filter={filter}
168
- onHover={onHover}
169
- onFilterUpdate={update}
170
- createFilterField={createFilterField}
171
- hidden={hidden}
172
- setHidden={setHidden}/>
173
+ filter={filter}
174
+ onHover={onHover}
175
+ onFilterUpdate={update}
176
+ createFilterField={createFilterField}
177
+ hidden={hidden}
178
+ setHidden={setHidden} />
173
179
 
174
180
  </Popover>
175
181
 
@@ -179,11 +185,17 @@ export const VirtualTableHeader = React.memo<VirtualTableHeaderProps<any>>(
179
185
 
180
186
  {column.resizable && <div
181
187
  ref={resizeHandleRef}
188
+ data-no-dnd="true"
182
189
  className={cls(
183
190
  "absolute h-full w-[6px] top-0 right-0 cursor-col-resize",
184
191
  hovered && "bg-surface-300 dark:bg-surface-700"
185
192
  )}
186
- onMouseDown={onClickResizeColumn ? () => onClickResizeColumn(columnIndex, column) : undefined}
193
+ onPointerDown={(e) => {
194
+ e.stopPropagation();
195
+ if (onClickResizeColumn) {
196
+ onClickResizeColumn(columnIndex, column);
197
+ }
198
+ }}
187
199
  />}
188
200
  </div>
189
201
 
@@ -192,14 +204,14 @@ export const VirtualTableHeader = React.memo<VirtualTableHeaderProps<any>>(
192
204
  }, equal) as React.FunctionComponent<VirtualTableHeaderProps<any>>;
193
205
 
194
206
  function FilterForm<M>({
195
- column,
196
- onFilterUpdate,
197
- filter,
198
- onHover,
199
- createFilterField,
200
- hidden,
201
- setHidden
202
- }: FilterFormProps<M>) {
207
+ column,
208
+ onFilterUpdate,
209
+ filter,
210
+ onHover,
211
+ createFilterField,
212
+ hidden,
213
+ setHidden
214
+ }: FilterFormProps<M>) {
203
215
 
204
216
  const id = column.key;
205
217
 
@@ -233,17 +245,17 @@ function FilterForm<M>({
233
245
  if (!filterField) return null;
234
246
  return (
235
247
  <form noValidate={true}
236
- onSubmit={(e) => {
237
- e.stopPropagation();
238
- e.preventDefault();
239
- submit();
240
- }}
241
- className={"text-surface-900 dark:text-white"}>
248
+ onSubmit={(e) => {
249
+ e.stopPropagation();
250
+ e.preventDefault();
251
+ submit();
252
+ }}
253
+ className={"text-surface-900 dark:text-white"}>
242
254
  <div
243
255
  className={cls(defaultBorderMixin, "py-4 px-6 typography-label border-b")}>
244
256
  {column.title ?? id}
245
257
  </div>
246
- {filterField && <div className="m-4">
258
+ {filterField && <div className="m-4 w-[400px]">
247
259
  {filterField}
248
260
  </div>}
249
261
  <div className="flex justify-end m-4">
@@ -251,13 +263,10 @@ function FilterForm<M>({
251
263
  className="mr-4"
252
264
  disabled={!filterIsSet}
253
265
  variant={"text"}
254
- color="primary"
255
266
  type="reset"
256
267
  aria-label="filter clear"
257
268
  onClick={reset}>Clear</Button>
258
269
  <Button
259
- variant="outlined"
260
- color="primary"
261
270
  type="submit">Filter</Button>
262
271
  </div>
263
272
  </form>
@@ -1,25 +1,132 @@
1
- import React, { createRef, useCallback, useEffect, useState } from "react";
1
+ import React, { createRef, useCallback, useEffect, useMemo, useState } from "react";
2
2
 
3
3
  import { VirtualTableColumn, VirtualTableWhereFilterOp } from "./VirtualTableProps";
4
4
  import { ErrorBoundary } from "../ErrorBoundary";
5
5
  import { VirtualTableHeader } from "./VirtualTableHeader";
6
6
  import { VirtualTableContextProps } from "./types";
7
7
  import { cls, defaultBorderMixin } from "@firecms/ui";
8
+ import { useSortable } from "@dnd-kit/sortable";
9
+ import { CSS } from "@dnd-kit/utilities";
10
+
11
+ // Sortable column header wrapper
12
+ const SortableColumnHeader = ({
13
+ column,
14
+ columnIndex,
15
+ columnRefs,
16
+ isResizing,
17
+ onFilterUpdate,
18
+ filter,
19
+ sortByProperty,
20
+ currentSort,
21
+ onColumnSort,
22
+ onClickResizeColumn,
23
+ createFilterField,
24
+ isDragging,
25
+ isDraggable
26
+ }: {
27
+ column: VirtualTableColumn;
28
+ columnIndex: number;
29
+ columnRefs: React.RefObject<HTMLDivElement | null>[];
30
+ isResizing: number;
31
+ onFilterUpdate: any;
32
+ filter: [VirtualTableWhereFilterOp, any] | undefined;
33
+ sortByProperty: string | undefined;
34
+ currentSort: "asc" | "desc" | undefined;
35
+ onColumnSort: any;
36
+ onClickResizeColumn: (index: number) => void;
37
+ createFilterField: any;
38
+ isDragging: boolean;
39
+ isDraggable: boolean;
40
+ }) => {
41
+ const [isPressing, setIsPressing] = useState(false);
42
+
43
+ const {
44
+ attributes,
45
+ listeners,
46
+ setNodeRef,
47
+ transform,
48
+ transition,
49
+ } = useSortable({
50
+ id: column.key,
51
+ disabled: !isDraggable || column.frozen
52
+ });
53
+
54
+ const style = {
55
+ // Only use translate, ignore any scale transforms
56
+ transform: transform ? `translateX(${transform.x}px)` : undefined,
57
+ // Don't transition the dragged item - only other items should animate
58
+ transition: isDragging ? undefined : transition,
59
+ minWidth: column.width,
60
+ maxWidth: column.width,
61
+ width: column.width,
62
+ };
63
+
64
+ // Combine our press handlers with dnd-kit listeners
65
+ const combinedListeners = isDraggable ? {
66
+ ...listeners,
67
+ onPointerDown: (e: React.PointerEvent) => {
68
+ setIsPressing(true);
69
+ listeners?.onPointerDown?.(e);
70
+ },
71
+ onPointerUp: () => setIsPressing(false),
72
+ onPointerCancel: () => setIsPressing(false),
73
+ } : {};
74
+
75
+ // Reset pressing state when drag ends
76
+ React.useEffect(() => {
77
+ if (!isDragging) {
78
+ setIsPressing(false);
79
+ }
80
+ }, [isDragging]);
81
+
82
+ return (
83
+ <div
84
+ ref={setNodeRef}
85
+ style={style}
86
+ className={cls(
87
+ "flex-shrink-0 h-full",
88
+ column.frozen && "sticky left-0 z-10"
89
+ )}
90
+ {...attributes}
91
+ {...combinedListeners}
92
+ >
93
+ <VirtualTableHeader
94
+ resizeHandleRef={columnRefs[columnIndex]}
95
+ columnIndex={columnIndex}
96
+ isResizingIndex={isResizing}
97
+ onFilterUpdate={onFilterUpdate}
98
+ filter={filter}
99
+ sort={sortByProperty === column.key ? currentSort : undefined}
100
+ onColumnSort={onColumnSort}
101
+ onClickResizeColumn={onClickResizeColumn}
102
+ column={column}
103
+ createFilterField={createFilterField}
104
+ AdditionalHeaderWidget={column.AdditionalHeaderWidget}
105
+ isDragging={isDragging || isPressing}
106
+ isDraggable={isDraggable} />
107
+ </div>
108
+ );
109
+ };
8
110
 
9
111
  export const VirtualTableHeaderRow = ({
10
- columns,
11
- currentSort,
12
- onColumnSort,
13
- onFilterUpdate,
14
- sortByProperty,
15
- filter,
16
- onColumnResize,
17
- onColumnResizeEnd,
18
- createFilterField,
19
- AddColumnComponent
20
- }: VirtualTableContextProps<any>) => {
21
-
22
- const columnRefs = columns.map(() => createRef<HTMLDivElement>());
112
+ columns,
113
+ currentSort,
114
+ onColumnSort,
115
+ onFilterUpdate,
116
+ sortByProperty,
117
+ filter,
118
+ onColumnResize,
119
+ onColumnResizeEnd,
120
+ createFilterField,
121
+ AddColumnComponent,
122
+ onColumnsOrderChange,
123
+ data,
124
+ cellRenderer: CellRenderer,
125
+ rowHeight = 54,
126
+ draggingColumnId
127
+ }: VirtualTableContextProps<any>) => {
128
+
129
+ const columnRefs = useMemo(() => columns.map(() => createRef<HTMLDivElement>()), [columns.length]);
23
130
  const [isResizing, setIsResizing] = useState(-1);
24
131
 
25
132
  const adjustWidthColumn = useCallback((index: number, width: number, end: boolean) => {
@@ -99,33 +206,42 @@ export const VirtualTableHeaderRow = ({
99
206
  }, [setCursorDocument]);
100
207
 
101
208
  return (
102
- <div
103
- className={cls(defaultBorderMixin, "z-20 sticky min-w-full flex w-fit flex-row top-0 left-0 h-12 border-b bg-surface-50 dark:bg-surface-900")}>
104
- {columns.map((c, columnIndex) => {
105
- const column = columns[columnIndex];
106
-
107
- const filterForThisProperty: [VirtualTableWhereFilterOp, any] | undefined =
108
- column && filter && filter[column.key]
109
- ? filter[column.key]
110
- : undefined;
111
- return <ErrorBoundary key={"header_" + column.key}>
112
- <VirtualTableHeader
113
- resizeHandleRef={columnRefs[columnIndex]}
114
- columnIndex={columnIndex}
115
- isResizingIndex={isResizing}
116
- onFilterUpdate={onFilterUpdate}
117
- filter={filterForThisProperty}
118
- sort={sortByProperty === column.key ? currentSort : undefined}
119
- onColumnSort={onColumnSort}
120
- onClickResizeColumn={onClickResizeColumn}
121
- column={column}
122
- createFilterField={createFilterField}
123
- AdditionalHeaderWidget={column.AdditionalHeaderWidget}/>
124
- </ErrorBoundary>;
125
- })}
126
-
127
- {AddColumnComponent && <AddColumnComponent/>}
128
-
129
- </div>
209
+ <>
210
+ <div
211
+ className={cls(defaultBorderMixin, "z-20 sticky min-w-full flex w-fit flex-row top-0 left-0 h-12 border-b bg-surface-50 dark:bg-surface-900")}>
212
+ {columns.map((column, columnIndex) => {
213
+ const filterForThisProperty: [VirtualTableWhereFilterOp, any] | undefined =
214
+ column && filter && filter[column.key]
215
+ ? filter[column.key]
216
+ : undefined;
217
+
218
+ const isDraggable = !column.frozen && !!onColumnsOrderChange;
219
+ const isDragging = draggingColumnId === column.key;
220
+
221
+ return (
222
+ <ErrorBoundary key={"header_" + column.key}>
223
+ <SortableColumnHeader
224
+ column={column}
225
+ columnIndex={columnIndex}
226
+ columnRefs={columnRefs}
227
+ isResizing={isResizing}
228
+ onFilterUpdate={onFilterUpdate}
229
+ filter={filterForThisProperty}
230
+ sortByProperty={sortByProperty}
231
+ currentSort={currentSort}
232
+ onColumnSort={onColumnSort}
233
+ onClickResizeColumn={onClickResizeColumn}
234
+ createFilterField={createFilterField}
235
+ isDragging={isDragging}
236
+ isDraggable={isDraggable}
237
+ />
238
+ </ErrorBoundary>
239
+ );
240
+ })}
241
+
242
+ {AddColumnComponent && <AddColumnComponent />}
243
+
244
+ </div>
245
+ </>
130
246
  );
131
247
  };
@@ -38,7 +38,7 @@ export interface VirtualTableProps<T extends Record<string, any>> {
38
38
  * @param sortBy
39
39
  */
40
40
  checkFilterCombination?: (filterValues: VirtualTableFilterValues<Extract<keyof T, string>>,
41
- sortBy?: [string, "asc" | "desc"]) => boolean;
41
+ sortBy?: [string, "asc" | "desc"]) => boolean;
42
42
 
43
43
  /**
44
44
  * A callback function when scrolling the table to near the end
@@ -162,6 +162,12 @@ export interface VirtualTableProps<T extends Record<string, any>> {
162
162
  */
163
163
  initialScroll?: number;
164
164
 
165
+ /**
166
+ * Callback when columns are reordered via drag-and-drop.
167
+ * @param columns The new column order
168
+ */
169
+ onColumnsOrderChange?: (columns: VirtualTableColumn[]) => void;
170
+
165
171
  }
166
172
 
167
173
  export type CellRendererParams<T = any> = {
@@ -172,6 +178,13 @@ export type CellRendererParams<T = any> = {
172
178
  rowIndex: number;
173
179
  width: number;
174
180
  isScrolling?: boolean;
181
+ // Sortable props for dnd-kit integration
182
+ sortableNodeRef?: (node: HTMLElement | null) => void;
183
+ sortableStyle?: React.CSSProperties;
184
+ sortableAttributes?: Record<string, any>;
185
+ isDragging?: boolean;
186
+ isDraggable?: boolean;
187
+ frozen?: boolean;
175
188
  };
176
189
 
177
190
  /**
@@ -29,7 +29,7 @@ export const VirtualTableRow = React.memo<VirtualTableRowProps<any>>(
29
29
  return (
30
30
  <div
31
31
  className={cls(
32
- "flex min-w-full text-sm border-b border-surface-200 dark:border-surface-800 border-opacity-40 dark:border-opacity-40",
32
+ "flex min-w-full text-sm border-b border-surface-200 dark:border-surface-800 border-opacity-40 border-surface-200/40 dark:border-opacity-40 dark:border-surface-800/40",
33
33
  rowClassName ? rowClassName(rowData) : "",
34
34
  {
35
35
  "hover:bg-opacity-95": hoverRow,
@@ -6,6 +6,7 @@ export function VirtualTableDateField(props: {
6
6
  name: string;
7
7
  error: Error | undefined;
8
8
  mode?: "date" | "date_time";
9
+ timezone?: string;
9
10
  internalValue: Date | undefined | null;
10
11
  updateValue: (newValue: (Date | null)) => void;
11
12
  focused: boolean;
@@ -18,6 +19,7 @@ export function VirtualTableDateField(props: {
18
19
  disabled,
19
20
  error,
20
21
  mode,
22
+ timezone,
21
23
  internalValue,
22
24
  updateValue
23
25
  } = props;
@@ -31,6 +33,7 @@ export function VirtualTableDateField(props: {
31
33
  inputClassName={cls("w-full h-full", focusedDisabled)}
32
34
  className={cls("w-full h-full", focusedDisabled)}
33
35
  mode={mode}
36
+ timezone={timezone}
34
37
  locale={locale}
35
38
  />
36
39
  );
@@ -62,14 +62,24 @@ export function VirtualTableSelect(props: {
62
62
  key={`${enumKey}`}
63
63
  enumKey={String(enumKey)}
64
64
  enumValues={enumValues}
65
- size={small ? "small" : "medium"}/>;
65
+ size={small ? "small" : "medium"} />;
66
66
  };
67
67
 
68
+ // When the dropdown closes (including on escape), restore focus to the trigger
69
+ const handleOpenChange = useCallback((open: boolean) => {
70
+ if (!open && ref.current) {
71
+ // Use setTimeout to ensure focus is restored after Radix finishes its cleanup
72
+ setTimeout(() => {
73
+ ref.current?.focus({ preventScroll: true });
74
+ }, 0);
75
+ }
76
+ }, []);
77
+
68
78
  return (
69
79
  multiple
70
80
  ? <MultiSelect
71
81
  inputRef={ref}
72
- className="w-full h-full p-0 bg-transparent"
82
+ className="w-full h-full p-0 bg-transparent outline-none"
73
83
  position={"item-aligned"}
74
84
  disabled={disabled}
75
85
  includeClear={false}
@@ -77,7 +87,8 @@ export function VirtualTableSelect(props: {
77
87
  value={validValue
78
88
  ? ((internalValue as any[]).map(v => v.toString()))
79
89
  : ([])}
80
- onValueChange={onChange}>
90
+ onValueChange={onChange}
91
+ onOpenChange={handleOpenChange}>
81
92
  {enumValues?.map((enumConfig) => (
82
93
  <MultiSelectItem
83
94
  key={enumConfig.id}
@@ -85,7 +96,7 @@ export function VirtualTableSelect(props: {
85
96
  <EnumValuesChip
86
97
  enumKey={enumConfig.id}
87
98
  enumValues={enumValues}
88
- size={small ? "small" : "medium"}/>
99
+ size={small ? "small" : "medium"} />
89
100
  </MultiSelectItem>
90
101
  ))}
91
102
  </MultiSelect>
@@ -93,7 +104,8 @@ export function VirtualTableSelect(props: {
93
104
  inputRef={ref}
94
105
  size={"large"}
95
106
  fullWidth={true}
96
- className="w-full h-full p-0 bg-transparent"
107
+ className="w-full h-full p-0 bg-transparent outline-none [&_button]:ring-0 [&_button]:ring-offset-0"
108
+ inputClassName="ring-0 ring-offset-0 focus:ring-0 focus-visible:ring-0 outline-none focus:outline-none focus-visible:outline-none focus-visible:ring-offset-0"
97
109
  position={"item-aligned"}
98
110
  disabled={disabled}
99
111
  padding={false}
@@ -101,6 +113,7 @@ export function VirtualTableSelect(props: {
101
113
  ? internalValue?.toString()
102
114
  : ""}
103
115
  onValueChange={onChange}
116
+ onOpenChange={handleOpenChange}
104
117
  renderValue={renderValue}>
105
118
  {enumValues?.map((enumConfig) => (
106
119
  <SelectItem
@@ -109,7 +122,7 @@ export function VirtualTableSelect(props: {
109
122
  <EnumValuesChip
110
123
  enumKey={enumConfig.id}
111
124
  enumValues={enumValues}
112
- size={small ? "small" : "medium"}/>
125
+ size={small ? "small" : "medium"} />
113
126
  </SelectItem>
114
127
  ))}
115
128
  </Select>
@@ -40,4 +40,6 @@ export type VirtualTableContextProps<T extends any> = {
40
40
  rowClassName?: (rowData: T) => string | undefined;
41
41
  endAdornment?: React.ReactNode;
42
42
  AddColumnComponent?: React.ComponentType;
43
+ onColumnsOrderChange?: (columns: VirtualTableColumn[]) => void;
44
+ draggingColumnId?: string | null;
43
45
  };