@rebasepro/admin 0.6.1 → 0.8.0

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 (187) hide show
  1. package/dist/{CollectionEditorDialog-HNXGxXdO.js → CollectionEditorDialog-BO_aQ-Z4.js} +236 -155
  2. package/dist/CollectionEditorDialog-BO_aQ-Z4.js.map +1 -0
  3. package/dist/{PropertyEditView-C2wp8kQ9.js → PropertyEditView-DKzOpMjR.js} +190 -50
  4. package/dist/PropertyEditView-DKzOpMjR.js.map +1 -0
  5. package/dist/{CollectionsStudioView-B1mN33GB.js → RouterCollectionsStudioView-BQTIExjA.js} +61 -39
  6. package/dist/RouterCollectionsStudioView-BQTIExjA.js.map +1 -0
  7. package/dist/collection_editor/_cms_internals.d.ts +1 -1
  8. package/dist/collection_editor/api/generateCollectionApi.d.ts +3 -2
  9. package/dist/collection_editor/extensibility_types.d.ts +135 -0
  10. package/dist/collection_editor/index.d.ts +6 -0
  11. package/dist/collection_editor/serializable_types.d.ts +370 -0
  12. package/dist/collection_editor/serializable_utils.d.ts +50 -0
  13. package/dist/collection_editor/ui/collection_editor/CollectionEditorDialog.d.ts +3 -3
  14. package/dist/collection_editor/ui/collection_editor/CollectionPropertiesEditorForm.d.ts +12 -1
  15. package/dist/collection_editor/ui/collection_editor/CollectionStudioView.d.ts +13 -1
  16. package/dist/collection_editor/ui/collection_editor/CollectionsStudioView.d.ts +24 -2
  17. package/dist/collection_editor/ui/collection_editor/DisplaySettingsForm.d.ts +2 -1
  18. package/dist/collection_editor/ui/collection_editor/GeneralSettingsForm.d.ts +4 -1
  19. package/dist/collection_editor/ui/collection_editor/PropertyEditView.d.ts +12 -1
  20. package/dist/collection_editor/ui/collection_editor/RouterCollectionStudioView.d.ts +15 -0
  21. package/dist/collection_editor/ui/collection_editor/RouterCollectionsStudioView.d.ts +14 -0
  22. package/dist/collection_editor/useJsonCollectionsConfigController.d.ts +37 -0
  23. package/dist/collection_editor/useSafeSnackbarController.d.ts +2 -0
  24. package/dist/collection_editor_ui.d.ts +4 -0
  25. package/dist/collection_editor_ui.js +33 -4
  26. package/dist/collection_editor_ui.js.map +1 -0
  27. package/dist/components/EntityCollectionTable/EntityCollectionTable.d.ts +1 -1
  28. package/dist/components/EntityCollectionTable/index.d.ts +1 -1
  29. package/dist/components/EntityCollectionView/EntityBoardCard.d.ts +3 -3
  30. package/dist/components/EntityCollectionView/hooks/useKanbanDragAndDrop.d.ts +3 -3
  31. package/dist/components/EntityEditView.d.ts +3 -2
  32. package/dist/components/RebaseCMS.d.ts +1 -1
  33. package/dist/components/SelectableTable/SelectionStore.d.ts +12 -21
  34. package/dist/components/field_configs.d.ts +1 -1
  35. package/dist/editor/editor.d.ts +0 -6
  36. package/dist/editor.js +1 -3
  37. package/dist/editor.js.map +1 -1
  38. package/dist/form/EntityForm.d.ts +10 -9
  39. package/dist/form/EntityFormBinding.d.ts +27 -0
  40. package/dist/form/components/StorageUploadProgress.d.ts +6 -1
  41. package/dist/form/form_utils.d.ts +8 -0
  42. package/dist/form/index.d.ts +5 -2
  43. package/dist/{history-BO6VT85a.js → history-B3cqBzEV.js} +2 -2
  44. package/dist/{history-BO6VT85a.js.map → history-B3cqBzEV.js.map} +1 -1
  45. package/dist/hooks/navigation/contexts/CollectionRegistryContext.d.ts +1 -1
  46. package/dist/hooks/navigation/useAsyncResolver.d.ts +26 -0
  47. package/dist/hooks/navigation/useBuildCollectionRegistryController.d.ts +2 -0
  48. package/dist/hooks/navigation/useTopLevelNavigation.d.ts +1 -1
  49. package/dist/hooks/navigation/utils.d.ts +1 -1
  50. package/dist/index.d.ts +3 -1
  51. package/dist/index.js +2 -2
  52. package/dist/preview/components/StorageThumbnail.d.ts +3 -1
  53. package/dist/routes/CustomViewRoute.d.ts +2 -1
  54. package/dist/{src-BYniefVu.js → src-cxV1ODRG.js} +1731 -1710
  55. package/dist/src-cxV1ODRG.js.map +1 -0
  56. package/dist/types/components/EntityFormActionsProps.d.ts +1 -1
  57. package/dist/types/components/EntityFormProps.d.ts +59 -0
  58. package/dist/types/components/PropertyPreviewProps.d.ts +2 -1
  59. package/dist/types/fields.d.ts +10 -7
  60. package/dist/util/dataControllerAdapter.d.ts +30 -0
  61. package/dist/util/navigation_utils.d.ts +1 -1
  62. package/dist/util/propertyConfigMapper.d.ts +17 -0
  63. package/package.json +8 -8
  64. package/src/collection_editor/_cms_internals.ts +1 -1
  65. package/src/collection_editor/api/generateCollectionApi.ts +3 -2
  66. package/src/collection_editor/extensibility_types.ts +168 -0
  67. package/src/collection_editor/index.ts +46 -0
  68. package/src/collection_editor/pgColumnToProperty.ts +2 -2
  69. package/src/collection_editor/serializable_types.ts +474 -0
  70. package/src/collection_editor/serializable_utils.ts +576 -0
  71. package/src/collection_editor/ui/collection_editor/AICollectionGeneratorPopover.tsx +6 -6
  72. package/src/collection_editor/ui/collection_editor/CollectionEditorDialog.tsx +170 -89
  73. package/src/collection_editor/ui/collection_editor/CollectionPropertiesEditorForm.tsx +95 -78
  74. package/src/collection_editor/ui/collection_editor/CollectionStudioView.tsx +23 -20
  75. package/src/collection_editor/ui/collection_editor/CollectionsStudioView.tsx +79 -27
  76. package/src/collection_editor/ui/collection_editor/DisplaySettingsForm.tsx +9 -7
  77. package/src/collection_editor/ui/collection_editor/GeneralSettingsForm.tsx +22 -5
  78. package/src/collection_editor/ui/collection_editor/GetCodeDialog.tsx +3 -3
  79. package/src/collection_editor/ui/collection_editor/PropertyEditView.tsx +154 -18
  80. package/src/collection_editor/ui/collection_editor/PropertyFieldPreview.tsx +6 -6
  81. package/src/collection_editor/ui/collection_editor/PropertyTree.tsx +1 -0
  82. package/src/collection_editor/ui/collection_editor/RouterCollectionStudioView.tsx +44 -0
  83. package/src/collection_editor/ui/collection_editor/RouterCollectionsStudioView.tsx +41 -0
  84. package/src/collection_editor/ui/collection_editor/properties/EnumPropertyField.tsx +4 -3
  85. package/src/collection_editor/ui/collection_editor/properties/StoragePropertyField.tsx +35 -1
  86. package/src/collection_editor/ui/collection_editor/templates/blog_template.ts +1 -1
  87. package/src/collection_editor/ui/collection_editor/utils/supported_fields.tsx +0 -1
  88. package/src/collection_editor/ui/collection_editor/utils/update_property_for_widget.ts +7 -29
  89. package/src/collection_editor/useJsonCollectionsConfigController.tsx +286 -0
  90. package/src/collection_editor/useSafeSnackbarController.ts +9 -0
  91. package/src/collection_editor_ui.ts +6 -0
  92. package/src/components/DefaultDrawer.tsx +14 -4
  93. package/src/components/DrawerNavigationGroup.tsx +2 -2
  94. package/src/components/EntityCollectionTable/EntityCollectionTable.tsx +11 -9
  95. package/src/components/EntityCollectionTable/fields/TableMultipleRelationField.tsx +2 -1
  96. package/src/components/EntityCollectionTable/fields/TableRelationField.tsx +2 -1
  97. package/src/components/EntityCollectionTable/fields/TableStorageUpload.tsx +23 -20
  98. package/src/components/EntityCollectionTable/index.tsx +1 -1
  99. package/src/components/EntityCollectionTable/table_bindings.tsx +24 -44
  100. package/src/components/EntityCollectionView/EntityBoardCard.tsx +6 -6
  101. package/src/components/EntityCollectionView/EntityCollectionBoardView.tsx +13 -12
  102. package/src/components/EntityCollectionView/EntityCollectionCardView.tsx +40 -190
  103. package/src/components/EntityCollectionView/EntityCollectionListView.tsx +66 -184
  104. package/src/components/EntityCollectionView/EntityCollectionView.tsx +8 -25
  105. package/src/components/EntityCollectionView/hooks/useCollectionInlineEditor.ts +1 -1
  106. package/src/components/EntityCollectionView/hooks/useKanbanDragAndDrop.ts +6 -6
  107. package/src/components/EntityCollectionView/useBoardDataController.tsx +12 -40
  108. package/src/components/EntityCollectionView/useEntityPreviewSlots.ts +3 -3
  109. package/src/components/EntityDetailView.tsx +2 -2
  110. package/src/components/EntityEditView.tsx +7 -6
  111. package/src/components/EntityEditViewFormActions.tsx +35 -29
  112. package/src/components/HomePage/ContentHomePage.tsx +14 -5
  113. package/src/components/RebaseCMS.tsx +3 -3
  114. package/src/components/RebaseNavigation.tsx +74 -9
  115. package/src/components/RelationSelector.tsx +2 -2
  116. package/src/components/SelectableTable/SelectableTable.tsx +13 -5
  117. package/src/components/SelectableTable/SelectionStore.ts +18 -48
  118. package/src/components/SideDialogs.tsx +0 -7
  119. package/src/components/common/default_entity_actions.tsx +22 -11
  120. package/src/components/field_configs.tsx +0 -14
  121. package/src/contexts/BreacrumbsContext.tsx +15 -6
  122. package/src/editor/editor.tsx +4 -7
  123. package/src/editor/selectors/ai-selector.tsx +0 -1
  124. package/src/form/EntityForm.tsx +191 -428
  125. package/src/form/EntityFormActions.tsx +35 -49
  126. package/src/form/EntityFormBinding.tsx +323 -0
  127. package/src/form/components/FormLayout.tsx +1 -1
  128. package/src/form/components/StorageUploadProgress.tsx +9 -2
  129. package/src/form/field_bindings/MarkdownEditorFieldBinding.tsx +12 -1
  130. package/src/form/field_bindings/MultipleRelationFieldBinding.tsx +4 -4
  131. package/src/form/field_bindings/ReadOnlyFieldBinding.tsx +7 -2
  132. package/src/form/field_bindings/RelationFieldBinding.tsx +5 -7
  133. package/src/form/field_bindings/StorageUploadFieldBinding.tsx +19 -15
  134. package/src/form/form_utils.ts +163 -0
  135. package/src/form/index.tsx +14 -6
  136. package/src/hooks/navigation/contexts/CollectionRegistryContext.tsx +2 -2
  137. package/src/hooks/navigation/contexts/NavigationStateContext.tsx +2 -2
  138. package/src/hooks/navigation/contexts/UrlContext.tsx +1 -2
  139. package/src/hooks/navigation/useAsyncResolver.ts +100 -0
  140. package/src/hooks/navigation/useBuildCollectionRegistryController.tsx +15 -8
  141. package/src/hooks/navigation/useBuildNavigationStateController.tsx +12 -8
  142. package/src/hooks/navigation/useNavigationResolution.ts +33 -0
  143. package/src/hooks/navigation/useResolvedCollections.ts +29 -71
  144. package/src/hooks/navigation/useResolvedViews.tsx +40 -70
  145. package/src/hooks/navigation/useTopLevelNavigation.ts +20 -16
  146. package/src/hooks/navigation/utils.ts +7 -4
  147. package/src/index.ts +6 -1
  148. package/src/preview/PropertyPreview.tsx +1 -16
  149. package/src/preview/components/StorageThumbnail.tsx +24 -7
  150. package/src/routes/CustomViewRoute.tsx +14 -6
  151. package/src/types/components/EntityFormActionsProps.tsx +1 -1
  152. package/src/types/components/EntityFormProps.tsx +68 -0
  153. package/src/types/components/PropertyPreviewProps.tsx +1 -1
  154. package/src/types/fields.tsx +9 -8
  155. package/src/util/dataControllerAdapter.ts +93 -0
  156. package/src/util/navigation_utils.ts +1 -1
  157. package/src/util/previews.ts +20 -0
  158. package/src/util/propertyConfigMapper.ts +134 -0
  159. package/src/util/property_utils.tsx +1 -2
  160. package/dist/CollectionEditorDialog-HNXGxXdO.js.map +0 -1
  161. package/dist/CollectionsStudioView-B1mN33GB.js.map +0 -1
  162. package/dist/PropertyEditView-C2wp8kQ9.js.map +0 -1
  163. package/dist/components/EntityCollectionTable/fields/VirtualTableDateField.d.ts +0 -12
  164. package/dist/components/EntityCollectionTable/fields/VirtualTableInput.d.ts +0 -10
  165. package/dist/components/EntityCollectionTable/fields/VirtualTableNumberInput.d.ts +0 -9
  166. package/dist/components/EntityCollectionTable/fields/VirtualTableSwitch.d.ts +0 -8
  167. package/dist/components/EntityCollectionView/Board.d.ts +0 -3
  168. package/dist/components/EntityCollectionView/BoardColumn.d.ts +0 -42
  169. package/dist/components/EntityCollectionView/BoardColumnTitle.d.ts +0 -9
  170. package/dist/components/EntityCollectionView/BoardSortableList.d.ts +0 -14
  171. package/dist/components/EntityCollectionView/board_types.d.ts +0 -105
  172. package/dist/form/field_bindings/ReferenceAsStringFieldBinding.d.ts +0 -11
  173. package/dist/hooks/navigation/useNavigationRegistry.d.ts +0 -12
  174. package/dist/hooks/navigation/useNavigationURLs.d.ts +0 -11
  175. package/dist/src-BYniefVu.js.map +0 -1
  176. package/src/components/EntityCollectionTable/fields/VirtualTableDateField.tsx +0 -39
  177. package/src/components/EntityCollectionTable/fields/VirtualTableInput.tsx +0 -90
  178. package/src/components/EntityCollectionTable/fields/VirtualTableNumberInput.tsx +0 -83
  179. package/src/components/EntityCollectionTable/fields/VirtualTableSwitch.tsx +0 -32
  180. package/src/components/EntityCollectionView/Board.tsx +0 -475
  181. package/src/components/EntityCollectionView/BoardColumn.tsx +0 -165
  182. package/src/components/EntityCollectionView/BoardColumnTitle.tsx +0 -46
  183. package/src/components/EntityCollectionView/BoardSortableList.tsx +0 -177
  184. package/src/components/EntityCollectionView/board_types.ts +0 -113
  185. package/src/form/field_bindings/ReferenceAsStringFieldBinding.tsx +0 -141
  186. package/src/hooks/navigation/useNavigationRegistry.ts +0 -164
  187. package/src/hooks/navigation/useNavigationURLs.ts +0 -56
@@ -1,12 +1,14 @@
1
1
  import React from "react";
2
2
  import { ArrayProperty, DateProperty, Entity, EntityReference, EntityRelation, NumberProperty, Property, ReferenceProperty, RelationProperty, StringProperty } from "@rebasepro/types";
3
- import { TableSize } from "@rebasepro/core";
3
+ import { TableSize, useCustomizationController } from "@rebasepro/core";
4
+ import {
5
+ VirtualTableInput,
6
+ VirtualTableNumberInput,
7
+ VirtualTableSwitch,
8
+ VirtualTableDateField
9
+ } from "@rebasepro/ui";
4
10
 
5
- import { VirtualTableInput } from "./fields/VirtualTableInput";
6
11
  import { VirtualTableSelect } from "./fields/VirtualTableSelect";
7
- import { VirtualTableNumberInput } from "./fields/VirtualTableNumberInput";
8
- import { VirtualTableSwitch } from "./fields/VirtualTableSwitch";
9
- import { VirtualTableDateField } from "./fields/VirtualTableDateField";
10
12
  import { VirtualTableUserSelect } from "./fields/VirtualTableUserSelect";
11
13
  import { TableStorageUpload } from "./fields/TableStorageUpload";
12
14
  import { TableReferenceField } from "./fields/TableReferenceField";
@@ -63,33 +65,7 @@ export function getTableBindingForProperty(
63
65
  ): TableFieldConfig | undefined {
64
66
  const isAStorageProperty = isStorageProperty(property);
65
67
 
66
- if (property.type === "string" && (property as StringProperty).reference?.path) {
67
- return {
68
- Component: ({ propertyKey, property, internalValue, updateValue, disabled, size, path }: TableFieldBindingProps) => {
69
- const referenceProperty = (property as StringProperty).reference;
70
- if (!referenceProperty) return null;
71
- const referenceValue = internalValue ? new EntityReference({ id: internalValue as string,
72
- path: referenceProperty.path as string }) : undefined;
73
- return (
74
- <TableReferenceField
75
- name={propertyKey}
76
- internalValue={referenceValue}
77
- updateValue={(v) => updateValue(v ? (v as EntityReference).id : null)}
78
- disabled={disabled}
79
- size={size}
80
- path={referenceProperty.path as string}
81
- multiselect={false}
82
- previewProperties={referenceProperty.ui?.previewProperties}
83
- includeId={referenceProperty.includeId}
84
- includeEntityLink={referenceProperty.includeEntityLink}
85
- title={property.name}
86
- fixedFilter={referenceProperty.fixedFilter}
87
- />
88
- );
89
- },
90
- allowScroll: false
91
- };
92
- } else if (isAStorageProperty) {
68
+ if (isAStorageProperty) {
93
69
  return {
94
70
  Component: ({ validationError, error, disabled, selected, openPopup, property, entity, path, internalValue, size, updateValue, propertyKey }: TableFieldBindingProps) => (
95
71
  <TableStorageUpload
@@ -183,7 +159,7 @@ export function getTableBindingForProperty(
183
159
  ),
184
160
  fullHeight: true
185
161
  };
186
- } else if (stringProperty.ui?.markdown || !stringProperty.storage || !stringProperty.reference) {
162
+ } else if (stringProperty.ui?.markdown || !stringProperty.storage) {
187
163
  const multiline = Boolean(stringProperty.ui?.multiline) || Boolean(stringProperty.ui?.markdown);
188
164
  return {
189
165
  Component: ({ error, validationError, disabled, selected, internalValue, updateValue }: TableFieldBindingProps) => (
@@ -213,17 +189,21 @@ export function getTableBindingForProperty(
213
189
  };
214
190
  } else if (property.type === "date") {
215
191
  return {
216
- Component: ({ propertyKey, error, validationError, disabled, selected, property, internalValue, updateValue }: TableFieldBindingProps) => (
217
- <VirtualTableDateField
218
- name={propertyKey}
219
- error={validationError ?? error}
220
- disabled={disabled}
221
- mode={(property as DateProperty).mode}
222
- focused={selected}
223
- internalValue={internalValue as Date}
224
- updateValue={updateValue}
225
- />
226
- ),
192
+ Component: ({ propertyKey, error, validationError, disabled, selected, property, internalValue, updateValue }: TableFieldBindingProps) => {
193
+ const { locale } = useCustomizationController();
194
+ return (
195
+ <VirtualTableDateField
196
+ name={propertyKey}
197
+ error={validationError ?? error}
198
+ disabled={disabled}
199
+ mode={(property as DateProperty).mode}
200
+ focused={selected}
201
+ internalValue={internalValue as Date}
202
+ updateValue={updateValue}
203
+ locale={locale}
204
+ />
205
+ );
206
+ },
227
207
  fullHeight: true,
228
208
  hideOverflow: false,
229
209
  allowScroll: false
@@ -5,10 +5,10 @@ import { Checkbox, Chip, cls, defaultBorderMixin, Markdown } from "@rebasepro/ui
5
5
  import { PropertyPreview } from "../../preview";
6
6
  import { useAuthController, useCustomizationController } from "@rebasepro/core";
7
7
  import { IconForView } from "@rebasepro/core";
8
- import { BoardItemViewProps } from "./board_types";
8
+ import { BoardItemViewProps } from "@rebasepro/ui";
9
9
  import { useCollectionSlotKeys, resolveEntitySlots } from "./useEntityPreviewSlots";
10
10
 
11
- export type EntityBoardCardProps<M extends Record<string, unknown> = Record<string, unknown>> = BoardItemViewProps<M> & {
11
+ export type EntityBoardCardProps<M extends Record<string, unknown> = Record<string, unknown>> = BoardItemViewProps<Entity<M>> & {
12
12
  collection: EntityCollection<M>;
13
13
  onClick?: (entity: Entity<M>) => void;
14
14
  selected?: boolean;
@@ -35,7 +35,7 @@ function EntityBoardCardInner<M extends Record<string, unknown> = Record<string,
35
35
  onSelectionChange,
36
36
  selectionEnabled = false
37
37
  }: EntityBoardCardProps<M>) {
38
- const entity = item.entity;
38
+ const entity = item.data;
39
39
  const authController = useAuthController();
40
40
  const customizationController = useCustomizationController();
41
41
 
@@ -247,14 +247,14 @@ export function createEntityBoardCardComponent<M extends Record<string, unknown>
247
247
  onSelectionChange?: (entity: Entity<M>, selected: boolean) => void;
248
248
  selectionEnabled?: boolean;
249
249
  }
250
- ): React.ComponentType<BoardItemViewProps<M>> {
251
- return function EntityBoardCardWrapper(props: BoardItemViewProps<M>) {
250
+ ): React.ComponentType<BoardItemViewProps<Entity<M>>> {
251
+ return function EntityBoardCardWrapper(props: BoardItemViewProps<Entity<M>>) {
252
252
  return (
253
253
  <EntityBoardCard
254
254
  {...props}
255
255
  collection={collection}
256
256
  onClick={options.onClick}
257
- selected={options.isEntitySelected?.(props.item.entity)}
257
+ selected={options.isEntitySelected?.(props.item.data)}
258
258
  onSelectionChange={options.onSelectionChange}
259
259
  selectionEnabled={options.selectionEnabled}
260
260
  />
@@ -1,9 +1,6 @@
1
-
2
1
  import type { EntityCollection } from "@rebasepro/types";
3
2
  import React, { useCallback, useEffect, useMemo, useRef, useState } from "react";
4
3
  import { Entity, EntityTableController, EnumValueConfig, SaveEntityProps, SelectionController } from "@rebasepro/types";
5
- import { Board } from "./Board";
6
- import { BoardItem, BoardItemViewProps, ColumnLoadingState } from "./board_types";
7
4
  import { EntityBoardCard } from "./EntityBoardCard";
8
5
  import {
9
6
  Button,
@@ -19,7 +16,11 @@ import {
19
16
  iconSize,
20
17
  RefreshCwIcon,
21
18
  Tooltip,
22
- Typography
19
+ Typography,
20
+ KanbanView,
21
+ BoardItem,
22
+ BoardItemViewProps,
23
+ ColumnLoadingState
23
24
  } from "@rebasepro/ui";
24
25
  import { resolveEnumValues } from "@rebasepro/common";
25
26
  import { getPropertyInPath } from "../../util/property_utils";
@@ -280,7 +281,7 @@ parentEntityIds,
280
281
  }).catch(e => console.warn("Failed to get total count:", e));
281
282
 
282
283
  accessor.count({
283
- where: { [orderProperty]: "neq.null" }
284
+ where: { [orderProperty]: ["!=", null] }
284
285
  }).then(count => {
285
286
  orderedCount = count;
286
287
  completed++;
@@ -318,10 +319,10 @@ parentEntityIds,
318
319
  }, [columns, boardDataController.columnData]);
319
320
 
320
321
  // Convert entities to board items per column (data already sorted by orderProperty from controller)
321
- const boardItems: BoardItem<M>[] = useMemo(() => {
322
+ const boardItems: BoardItem<Entity<M>>[] = useMemo(() => {
322
323
  return allEntities.map((entity: Entity<M>) => ({
323
324
  id: String(entity.id),
324
- entity
325
+ data: entity
325
326
  }));
326
327
  }, [allEntities]);
327
328
 
@@ -341,11 +342,11 @@ parentEntityIds,
341
342
  }, [columns, boardDataController.columnData]);
342
343
 
343
344
  // Use the lookup map to assign columns - ensures items stay in the column they were fetched for
344
- const assignColumn = useCallback((item: BoardItem<M>): string => {
345
+ const assignColumn = useCallback((item: BoardItem<Entity<M>>): string => {
345
346
  const column = entityColumnMap[item.id];
346
347
  if (column) return column;
347
348
  // Fallback: read from entity values (for newly created items or edge cases)
348
- const value = item.entity.values?.[columnProperty];
349
+ const value = item.data.values?.[columnProperty];
349
350
  if (value && columns.includes(String(value))) return String(value);
350
351
  return columns[0] || "";
351
352
  }, [entityColumnMap, columnProperty, columns]);
@@ -462,12 +463,12 @@ parentEntityIds,
462
463
  // When ItemComponent identity changes, React.memo'd SortableItem remounts
463
464
  // the card → DOM is destroyed/recreated → CSS :hover state is lost → flicker.
464
465
  const ItemComponent = useMemo(() => {
465
- const Comp = (props: BoardItemViewProps<M>) => (
466
+ const Comp = (props: BoardItemViewProps<Entity<M>>) => (
466
467
  <EntityBoardCard
467
468
  {...props}
468
469
  collection={collectionRef.current as EntityCollection<M>}
469
470
  onClick={stableOnClick}
470
- selected={isEntitySelectedRef.current(props.item.entity)}
471
+ selected={isEntitySelectedRef.current(props.item.data)}
471
472
  onSelectionChange={stableOnSelectionChange}
472
473
  selectionEnabled={selectionEnabledRef.current}
473
474
  />
@@ -592,7 +593,7 @@ parentEntityIds,
592
593
 
593
594
  {/* Main board */}
594
595
  <div className="flex-1 overflow-auto no-scrollbar">
595
- <Board
596
+ <KanbanView
596
597
  data={boardItems}
597
598
  columns={columns}
598
599
  columnLabels={columnLabels}
@@ -1,11 +1,12 @@
1
1
  import type { EntityCollection } from "@rebasepro/types";
2
- import React, { useCallback, useEffect, useRef } from "react";
2
+ import React, { useCallback, useEffect, useMemo, useRef } from "react";
3
3
  import { CollectionSize, Entity, EntityTableController, SelectionController } from "@rebasepro/types";
4
4
  import { EntityCard } from "./EntityCard";
5
5
  import {
6
6
  cls,
7
7
  CircularProgress,
8
- Typography
8
+ Typography,
9
+ CardView
9
10
  } from "@rebasepro/ui";
10
11
  import { useComponentOverride } from "@rebasepro/core";
11
12
 
@@ -92,8 +93,6 @@ export function EntityCollectionCardView<M extends Record<string, unknown> = Rec
92
93
  }: EntityCollectionCardViewProps<M>) {
93
94
 
94
95
  const ResolvedEntityCard = useComponentOverride("Collection.Card", EntityCard) as typeof EntityCard;
95
- const containerRef = useRef<HTMLDivElement>(null);
96
- const hasRestoredScroll = useRef(false);
97
96
 
98
97
  const {
99
98
  data,
@@ -106,116 +105,6 @@ export function EntityCollectionCardView<M extends Record<string, unknown> = Rec
106
105
  paginationEnabled
107
106
  } = tableController;
108
107
 
109
- // Track if we're currently loading to prevent multiple simultaneous load requests
110
- const isLoadingMore = useRef(false);
111
-
112
- // Keep mutable refs for values used in the scroll listener callback
113
- // to avoid re-attaching the listener every time pagination state changes.
114
- const paginationStateRef = useRef({ paginationEnabled,
115
- noMoreToLoad,
116
- dataLoading,
117
- itemCount,
118
- pageSize });
119
- useEffect(() => {
120
- paginationStateRef.current = { paginationEnabled,
121
- noMoreToLoad,
122
- dataLoading,
123
- itemCount,
124
- pageSize };
125
- }, [paginationEnabled, noMoreToLoad, dataLoading, itemCount, pageSize]);
126
-
127
- // Reset loading flag when new data arrives (separate effect, like list view)
128
- useEffect(() => {
129
- if (!dataLoading) isLoadingMore.current = false;
130
- }, [dataLoading]);
131
-
132
- // Infinite scroll and resize observer
133
- useEffect(() => {
134
- const el = containerRef.current;
135
- if (!el) return;
136
- const scrollEl = getScrollParent(el);
137
- if (!scrollEl) return;
138
-
139
- let rafId: number | null = null;
140
-
141
- const update = () => {
142
- rafId = null;
143
-
144
- // Infinite scroll: trigger load-more when near the bottom
145
- const { paginationEnabled: pe, noMoreToLoad: nm, itemCount: ic, pageSize: ps } = paginationStateRef.current;
146
- if (
147
- pe &&
148
- !nm &&
149
- !isLoadingMore.current &&
150
- scrollEl.scrollHeight - scrollEl.scrollTop - scrollEl.clientHeight < 400
151
- ) {
152
- isLoadingMore.current = true;
153
- setItemCount?.((ic ?? ps) + ps);
154
- }
155
- };
156
-
157
- const onScrollEvent = () => {
158
- if (rafId === null) rafId = requestAnimationFrame(update);
159
- };
160
-
161
- scrollEl.addEventListener("scroll", onScrollEvent, { passive: true });
162
- const ro = new ResizeObserver(() => update());
163
- ro.observe(scrollEl);
164
- update(); // initial measurement
165
-
166
- return () => {
167
- scrollEl.removeEventListener("scroll", onScrollEvent);
168
- ro.disconnect();
169
- if (rafId !== null) cancelAnimationFrame(rafId);
170
- };
171
- }, [setItemCount]);
172
-
173
- // Scroll restoration — deferred to after layout paint
174
- useEffect(() => {
175
- if (!containerRef.current || !initialScroll || hasRestoredScroll.current || data.length === 0) return;
176
-
177
- const scrollEl = getScrollParent(containerRef.current);
178
- if (!scrollEl) return;
179
-
180
- let attempts = 0;
181
- const maxAttempts = 5;
182
-
183
- const tryRestore = () => {
184
- if (scrollEl.scrollHeight >= initialScroll || attempts >= maxAttempts) {
185
- scrollEl.scrollTop = initialScroll;
186
- hasRestoredScroll.current = true;
187
- } else {
188
- attempts++;
189
- requestAnimationFrame(tryRestore);
190
- }
191
- };
192
-
193
- requestAnimationFrame(tryRestore);
194
- }, [initialScroll, data.length]);
195
-
196
- // Scroll tracking: call onScroll when user scrolls
197
- const lastScrollOffset = useRef(0);
198
- useEffect(() => {
199
- const el = containerRef.current;
200
- if (!el || !onScroll) return;
201
- const scrollEl = getScrollParent(el);
202
- if (!scrollEl) return;
203
-
204
- const handleScroll = () => {
205
- const currentOffset = scrollEl.scrollTop;
206
- const direction = currentOffset > lastScrollOffset.current ? "forward" : "backward";
207
- lastScrollOffset.current = currentOffset;
208
- onScroll({
209
- scrollDirection: direction,
210
- scrollOffset: currentOffset,
211
- scrollUpdateWasRequested: false
212
- });
213
- };
214
-
215
- scrollEl.addEventListener("scroll", handleScroll, { passive: true });
216
- return () => scrollEl.removeEventListener("scroll", handleScroll);
217
- }, [onScroll]);
218
-
219
108
  const handleEntityClick = useCallback((entity: Entity<M>) => {
220
109
  onEntityClick?.(entity);
221
110
  }, [onEntityClick]);
@@ -224,84 +113,45 @@ pageSize };
224
113
  selectionController?.toggleEntitySelection(entity, selected);
225
114
  }, [selectionController]);
226
115
 
227
- const isEntitySelected = useCallback((entity: Entity<M>) => {
228
- return selectionController?.isEntitySelected(entity) ?? false;
229
- }, [selectionController]);
230
-
231
- const isEntityHighlighted = useCallback((entity: Entity<M>) => {
232
- return highlightedEntities?.some(e => e.id === entity.id && e.path === entity.path) ?? false;
233
- }, [highlightedEntities]);
116
+ const selectedIds = useMemo(() => new Set(selectionController?.selectedEntities.map(e => e.id)), [selectionController?.selectedEntities]);
117
+ const highlightedIds = useMemo(() => new Set(highlightedEntities?.map(e => e.id)), [highlightedEntities]);
234
118
 
235
- const gridColumnsClass = getGridColumnsClass(size);
236
-
237
- // Initial loading state (no data yet)
238
- const isInitialLoading = dataLoading && data.length === 0 && !dataLoadingError;
239
- // Empty state
240
- const isEmpty = !dataLoading && data.length === 0 && !dataLoadingError;
119
+ const handleRowSelectionChange = useCallback((entity: Entity<M>, selected: boolean) => {
120
+ handleSelectionChange(entity, selected);
121
+ }, [handleSelectionChange]);
241
122
 
242
123
  return (
243
- <div
244
- ref={containerRef}
245
- className="w-full p-4"
246
- >
247
- {/* Error state */}
248
- {dataLoadingError && data.length === 0 ? (
249
- <div className="h-full flex items-center justify-center p-8">
250
- <Typography className="text-red-500">
251
- Error loading data: {dataLoadingError.message}
252
- </Typography>
253
- </div>
254
- ) : isInitialLoading ? (
255
- <div className="flex items-center justify-center py-12 px-8">
256
- <CircularProgress size="small"/>
257
- </div>
258
- ) : isEmpty ? (
259
- <div className="w-full flex items-center justify-center py-12 px-8">
260
- {emptyComponent ?? (
261
- <Typography variant="label" color="secondary">
262
- No entries found
263
- </Typography>
264
- )}
265
- </div>
266
- ) : (
267
- <>
268
- {/* Card Grid with max-width container */}
269
- <div className="max-w-7xl mx-auto">
270
- <div className={cls(
271
- "grid gap-4",
272
- gridColumnsClass
273
- )}>
274
- {data.map((entity) => (
275
- <ResolvedEntityCard
276
- key={`${entity.path}_${entity.id}`}
277
- entity={entity}
278
- collection={collection}
279
- onClick={handleEntityClick}
280
- selected={isEntitySelected(entity)}
281
- highlighted={isEntityHighlighted(entity)}
282
- onSelectionChange={handleSelectionChange}
283
- selectionEnabled={selectionEnabled}
284
- size={size}
285
- />
286
- ))}
287
- </div>
288
-
289
- {/* Load more trigger / Loading indicator */}
290
- <div
291
- className="flex items-center justify-center py-8"
292
- >
293
- {dataLoading && (
294
- <CircularProgress size="small"/>
295
- )}
296
- {!dataLoading && noMoreToLoad && data.length > 0 && (
297
- <Typography variant="caption" color="secondary">
298
- All {data.length} entries loaded
299
- </Typography>
300
- )}
301
- </div>
302
- </div>
303
- </>
304
- )}
305
- </div>
124
+ <CardView<Entity<M>>
125
+ data={data}
126
+ dataLoading={dataLoading}
127
+ noMoreToLoad={noMoreToLoad}
128
+ dataLoadingError={dataLoadingError}
129
+ itemCount={itemCount}
130
+ setItemCount={setItemCount}
131
+ pageSize={pageSize}
132
+ paginationEnabled={paginationEnabled}
133
+ onItemClick={handleEntityClick}
134
+ selectedIds={selectedIds}
135
+ highlightedIds={highlightedIds}
136
+ selectionEnabled={selectionEnabled}
137
+ onSelectionChange={handleRowSelectionChange}
138
+ onScroll={onScroll}
139
+ initialScroll={initialScroll}
140
+ size={size}
141
+ emptyComponent={emptyComponent}
142
+ renderCard={useCallback((entity, { selected, highlighted, onClick }) => (
143
+ <ResolvedEntityCard
144
+ key={`${entity.path}_${entity.id}`}
145
+ entity={entity}
146
+ collection={collection}
147
+ onClick={onClick as any}
148
+ selected={selected}
149
+ highlighted={highlighted}
150
+ onSelectionChange={handleRowSelectionChange}
151
+ selectionEnabled={selectionEnabled}
152
+ size={size}
153
+ />
154
+ ), [collection, selectionEnabled, size, handleRowSelectionChange, ResolvedEntityCard])}
155
+ />
306
156
  );
307
157
  }