@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
@@ -10,7 +10,8 @@ import {
10
10
  defaultBorderMixin,
11
11
  IconButton,
12
12
  Tooltip,
13
- Typography
13
+ Typography,
14
+ ListView
14
15
  } from "@rebasepro/ui";
15
16
  import { PropertyPreview } from "../../preview";
16
17
  import {
@@ -401,7 +402,18 @@ export function EntityCollectionListView<M extends Record<string, unknown> = Rec
401
402
  "id"
402
403
  ]);
403
404
 
404
- const availableExtraKeys = allKeys.filter(k => !usedKeys.has(k) && resolvedCollection.properties[k]);
405
+ const availableExtraKeys = allKeys.filter(k => {
406
+ if (usedKeys.has(k)) return false;
407
+ const prop = resolvedCollection.properties[k] as Property | undefined;
408
+ if (!prop) return false;
409
+ // Exclude storage/image properties — they are already rendered in the image slot
410
+ if (prop.type === "string" && (prop.storage || prop.ui?.url === "image")) return false;
411
+ if (prop.type === "array" && prop.of && !Array.isArray(prop.of)) {
412
+ const inner = prop.of;
413
+ if (inner.type === "string" && (inner.storage || inner.ui?.url === "image")) return false;
414
+ }
415
+ return true;
416
+ });
405
417
 
406
418
  availableExtraKeys.forEach(key => {
407
419
  const prop = resolvedCollection.properties[key] as Property;
@@ -510,192 +522,62 @@ customEntityActions });
510
522
 
511
523
  const rowClasses = getRowClasses(size);
512
524
 
513
- // Initial loading state (no data yet)
514
- const isInitialLoading = dataLoading && data.length === 0;
515
- // Empty state
516
- const isEmpty = !dataLoading && data.length === 0 && !dataLoadingError;
517
-
518
- // ── Virtualization: scroll-parent windowing ──
519
- const estimatedRowHeight = getEstimatedRowHeight(size);
520
- const [effectiveScrollTop, setEffectiveScrollTop] = useState(0);
521
- const [viewportHeight, setViewportHeight] = useState(800);
522
-
523
- // Keep mutable refs for values used in the scroll handler to avoid
524
- // re-attaching the listener every time pagination state changes.
525
- const paginationStateRef = useRef({ paginationEnabled,
526
- noMoreToLoad,
527
- itemCount,
528
- pageSize });
529
- useEffect(() => {
530
- paginationStateRef.current = { paginationEnabled,
531
- noMoreToLoad,
532
- itemCount,
533
- pageSize };
534
- }, [paginationEnabled, noMoreToLoad, itemCount, pageSize]);
535
-
536
- useEffect(() => {
537
- const el = containerRef.current;
538
- if (!el) return;
539
- const scrollEl = getScrollParent(el);
540
- if (!scrollEl) return;
541
-
542
- let rafId: number | null = null;
543
-
544
- const update = () => {
545
- rafId = null;
546
- const scrollRect = scrollEl.getBoundingClientRect();
547
- const listRect = el.getBoundingClientRect();
548
-
549
- // How much of the list has scrolled past the viewport top
550
- const listTopRelative = listRect.top - scrollRect.top;
551
- setEffectiveScrollTop(Math.max(0, -listTopRelative));
552
- setViewportHeight(scrollRect.height);
553
-
554
- // Infinite scroll: trigger load-more when near the bottom
555
- const { paginationEnabled: pe, noMoreToLoad: nm, itemCount: ic, pageSize: ps } = paginationStateRef.current;
556
- if (
557
- pe &&
558
- !nm &&
559
- !isLoadingMore.current &&
560
- scrollEl.scrollHeight - scrollEl.scrollTop - scrollEl.clientHeight < LOAD_MORE_THRESHOLD
561
- ) {
562
- isLoadingMore.current = true;
563
- setItemCount?.((ic ?? ps) + ps);
564
- }
565
- };
525
+ const selectedIds = useMemo(() => new Set(selectionController?.selectedEntities.map(e => e.id)), [selectionController?.selectedEntities]);
526
+ const highlightedIds = useMemo(() => new Set(highlightedEntities?.map(e => e.id)), [highlightedEntities]);
566
527
 
567
- const onScroll = () => {
568
- if (rafId === null) rafId = requestAnimationFrame(update);
569
- };
570
-
571
- scrollEl.addEventListener("scroll", onScroll, { passive: true });
572
- const ro = new ResizeObserver(() => update());
573
- ro.observe(scrollEl);
574
- update(); // initial measurement
575
-
576
- return () => {
577
- scrollEl.removeEventListener("scroll", onScroll);
578
- ro.disconnect();
579
- if (rafId !== null) cancelAnimationFrame(rafId);
580
- };
581
- }, [setItemCount]); // stable deps only — mutable state via refs
582
-
583
- // Compute the visible window of rows
584
- const totalHeight = data.length * estimatedRowHeight;
585
- const startIndex = Math.max(0, Math.floor(effectiveScrollTop / estimatedRowHeight) - OVERSCAN_COUNT);
586
- const endIndex = Math.min(
587
- data.length,
588
- Math.ceil((effectiveScrollTop + viewportHeight) / estimatedRowHeight) + OVERSCAN_COUNT
589
- );
590
- const visibleData = data.slice(startIndex, endIndex);
591
- const offsetY = startIndex * estimatedRowHeight;
592
-
593
- // Footer height for loading/end indicators
594
- const footerHeight = dataLoading ? 48 : (!dataLoading && noMoreToLoad && data.length > 0) ? 32 : 0;
528
+ const handleRowSelectionChange = useCallback((entity: Entity<M>, selected: boolean) => {
529
+ handleSelectionChange(entity, selected);
530
+ }, [handleSelectionChange]);
595
531
 
596
532
  return (
597
- <div
598
- ref={containerRef}
599
- className={cls(
600
- "w-full",
601
- selectedEntityId === undefined && "rounded-lg overflow-hidden border " + defaultBorderMixin
602
- )}
603
- >
604
- {/* Error state */}
605
- {dataLoadingError && data.length === 0 ? (
606
- <div className="flex items-center justify-center p-8">
607
- <Typography className="text-red-500">
608
- Error loading data: {dataLoadingError.message}
609
- </Typography>
610
- </div>
611
- ) : isInitialLoading ? (
612
- <div className="flex items-center justify-center py-12 px-8">
613
- <CircularProgress size="small"/>
614
- </div>
615
- ) : isEmpty ? (
616
- <div className="w-full flex items-center justify-center py-12 px-8">
617
- {emptyComponent ?? (
618
- <Typography variant="label" color="secondary">
619
- No entries found
620
- </Typography>
621
- )}
622
- </div>
623
- ) : (
624
- /* Spacer with total height — no internal scroll.
625
- The nearest scrollable ancestor provides the scrollbar. */
626
- <div style={{ height: totalHeight + footerHeight,
627
- position: "relative" }}>
628
- {/* Windowed rows */}
629
- <div style={{ position: "absolute",
630
- top: offsetY,
631
- left: 0,
632
- right: 0 }}>
633
- {visibleData.map((entity, i) => {
634
- const actualIndex = startIndex + i;
635
- const isLast = actualIndex === data.length - 1;
636
- return (
637
- <div
638
- key={entity.id}
639
- style={{ height: estimatedRowHeight }}
640
- className={cls(
641
- !isLast && "border-b",
642
- !isLast && defaultBorderMixin
643
- )}
644
- >
645
- <ListRow
646
- entity={entity}
647
- collection={resolvedCollection}
648
- onClick={handleEntityClick}
649
- selected={isEntitySelected(entity)}
650
- highlighted={isEntityHighlighted(entity)}
651
- onSelectionChange={handleSelectionChange}
652
- selectionEnabled={selectionEnabled}
653
- columns={visibleColumns}
654
- slotKeys={slotKeys}
655
- rowClasses={rowClasses}
656
- showImage={showImage}
657
- size={size}
658
- isLast={isLast}
659
- isActive={selectedEntityId !== undefined && entity.id === selectedEntityId}
660
- listViewActions={getListViewActions(entity)}
661
- context={context}
662
- path={path}
663
- selectionController={selectionController}
664
- openEntityMode={openEntityMode}
665
- />
666
- </div>
667
- );
668
- })}
533
+ <ListView<Entity<M>>
534
+ data={data}
535
+ dataLoading={dataLoading}
536
+ noMoreToLoad={noMoreToLoad}
537
+ dataLoadingError={dataLoadingError}
538
+ itemCount={itemCount}
539
+ setItemCount={setItemCount}
540
+ pageSize={pageSize}
541
+ paginationEnabled={paginationEnabled}
542
+ onItemClick={handleEntityClick}
543
+ selectedIds={selectedIds}
544
+ highlightedIds={highlightedIds}
545
+ selectionEnabled={selectionEnabled}
546
+ emptyComponent={emptyComponent}
547
+ size={size}
548
+ selectedEntityId={selectedEntityId}
549
+ renderRow={useCallback(({ item: entity, style, className, selected, highlighted, isLast, onClick, onSelectionChange }) => {
550
+ return (
551
+ <div
552
+ key={entity.id}
553
+ style={style}
554
+ className={className}
555
+ >
556
+ <ListRow
557
+ entity={entity}
558
+ collection={resolvedCollection}
559
+ onClick={handleEntityClick}
560
+ selected={selected}
561
+ highlighted={highlighted}
562
+ onSelectionChange={handleRowSelectionChange}
563
+ selectionEnabled={selectionEnabled}
564
+ columns={visibleColumns}
565
+ slotKeys={slotKeys}
566
+ rowClasses={rowClasses}
567
+ showImage={showImage}
568
+ size={size}
569
+ isLast={isLast}
570
+ isActive={selectedEntityId !== undefined && entity.id === selectedEntityId}
571
+ listViewActions={getListViewActions(entity)}
572
+ context={context}
573
+ path={path}
574
+ selectionController={selectionController}
575
+ openEntityMode={openEntityMode}
576
+ />
669
577
  </div>
670
-
671
- {/* Loading / end indicators pinned at the bottom */}
672
- {dataLoading && (
673
- <div
674
- className="flex items-center justify-center py-3"
675
- style={{ position: "absolute",
676
- top: totalHeight,
677
- left: 0,
678
- right: 0 }}
679
- >
680
- <CircularProgress size="small"/>
681
- </div>
682
- )}
683
- {!dataLoading && noMoreToLoad && data.length > 0 && (
684
- <div
685
- className="flex items-center justify-center py-2 dark:bg-surface-900"
686
- style={{ position: "absolute",
687
- top: totalHeight,
688
- left: 0,
689
- right: 0 }}
690
- >
691
- <Typography variant="caption" color="secondary">
692
- All {data.length} entries loaded
693
- </Typography>
694
- </div>
695
- )}
696
- </div>
697
- )}
698
- </div>
578
+ );
579
+ }, [resolvedCollection, selectionEnabled, visibleColumns, slotKeys, rowClasses, showImage, size, selectedEntityId, getListViewActions, context, path, selectionController, openEntityMode, handleRowSelectionChange, handleEntityClick])}
580
+ />
699
581
  );
700
582
  }
701
583
 
@@ -7,10 +7,10 @@ import { useSearchParams } from "react-router-dom";
7
7
  import { deepEqual as equal } from "fast-equals"
8
8
 
9
9
 
10
- const EMPTY_ARRAY: any[] = [];
10
+ const EMPTY_ARRAY: never[] = [];
11
11
  const DEFAULT_ENTITY_OPEN_MODE = "split";
12
12
 
13
- import { CollectionSize, Entity, EntityReference, EntityTableController, FilterValues, PartialEntityCollection, ViewMode } from "@rebasepro/types";
13
+ import { CollectionSize, Entity, EntityReference, EntityTableController, FilterValues, getCollectionDataPath, PartialEntityCollection, ViewMode } from "@rebasepro/types";
14
14
  import {
15
15
  EntityCollectionRowActions,
16
16
  EntityCollectionTable
@@ -163,7 +163,7 @@ const EntityCollectionViewInner = React.memo(
163
163
  const collectionRegistry = useCollectionRegistryController();
164
164
  const urlController = useUrlController();
165
165
  const breadcrumbs = useBreadcrumbsController();
166
- const path = pathProp ?? collectionProp.slug;
166
+ const path = pathProp ?? getCollectionDataPath(collectionProp);
167
167
  const dataClient = useData();
168
168
  const sideEntityController = useSideEntityController();
169
169
  const authController = useAuthController();
@@ -215,7 +215,7 @@ const EntityCollectionViewInner = React.memo(
215
215
  }, 2400);
216
216
  }, [highlightedEntity]);
217
217
 
218
- const checkInlineEditing = useCallback((entity?: Entity<any>): boolean => {
218
+ const checkInlineEditing = useCallback((entity?: Entity<M>): boolean => {
219
219
  const collection = collectionRef.current;
220
220
  if (!canEdit(collection, path, entity ?? null)) {
221
221
  return false;
@@ -894,7 +894,7 @@ parentEntityIds,
894
894
  collection={collection}
895
895
  tableController={tableController}
896
896
  path={path}
897
- relativePath={collection.slug}
897
+ relativePath={getCollectionDataPath(collection)}
898
898
  selectionController={usedSelectionController}
899
899
  collectionEntitiesCount={docsCount ?? undefined}
900
900
  resolvedProperties={resolvedCollection.properties}
@@ -909,7 +909,7 @@ parentEntityIds,
909
909
  onNewClick={onNewClick}
910
910
  openNewDocument={openNewDocument}
911
911
  path={path}
912
- relativePath={collection.slug}
912
+ relativePath={getCollectionDataPath(collection)}
913
913
  selectionController={usedSelectionController}
914
914
  selectionEnabled={activeSelectionEnabled}
915
915
  collectionEntitiesCount={docsCount ?? undefined}
@@ -1301,25 +1301,8 @@ function EntitiesCount({
1301
1301
 
1302
1302
  let cancelled = false;
1303
1303
 
1304
- // Convert filterValues to PostgREST where clause
1305
- const whereMap: Record<string, string> = {};
1306
- if (filter) {
1307
- Object.entries(filter).forEach(([key, value]) => {
1308
- if (value && Array.isArray(value)) {
1309
- const [op, val] = value;
1310
- const postgrestOp = op === "==" ? "eq" : op === "!=" ? "neq" : op === ">" ? "gt" : op === ">=" ? "gte" : op === "<" ? "lt" : op === "<=" ? "lte" : op === "in" ? "in" : op === "not-in" ? "nin" : op === "array-contains" ? "cs" : op === "array-contains-any" ? "csa" : "eq";
1311
-
1312
- let stringVal: string;
1313
- if (Array.isArray(val)) {
1314
- stringVal = `(${val.join(",")})`;
1315
- } else {
1316
- stringVal = String(val);
1317
- }
1318
- whereMap[key] = `${postgrestOp}.${stringVal}`;
1319
- }
1320
- });
1321
- }
1322
- const whereParams = Object.keys(whereMap).length > 0 ? whereMap : undefined;
1304
+ // filterValues is already FilterValues — pass directly
1305
+ const whereParams = filter && Object.keys(filter).length > 0 ? filter : undefined;
1323
1306
  const orderByParams = sortByProperty ? `${String(sortByProperty)}:${currentSort}` : undefined;
1324
1307
 
1325
1308
  // Deduplicate inflight count requests (e.g. React StrictMode double-mount)
@@ -22,7 +22,7 @@ export function useCollectionInlineEditor<M extends Record<string, unknown>>({
22
22
  async ({ name, value, property, entityId }: Parameters<UniqueFieldValidator>[0]) => {
23
23
  const accessor = dataClient.collection(path);
24
24
  const res = await accessor.find({
25
- where: { [name]: "eq." + value }
25
+ where: { [name]: ["==", value] }
26
26
  });
27
27
 
28
28
  const conflictingEntities = res.data;
@@ -1,8 +1,8 @@
1
1
  import { useCallback, useMemo } from "react";
2
2
  import { setIn } from "@rebasepro/formex";
3
- import { EntityCollection, SaveEntityProps, RebaseData, RebaseContext, AnalyticsController } from "@rebasepro/types";
3
+ import { EntityCollection, SaveEntityProps, RebaseData, RebaseContext, AnalyticsController, Entity } from "@rebasepro/types";
4
4
  import { saveEntityWithCallbacks } from "@rebasepro/core";
5
- import { BoardItem } from "../board_types";
5
+ import { BoardItem } from "@rebasepro/ui";
6
6
  import { BoardDataController } from "../useBoardDataController";
7
7
  import { generateKeyBetween } from "fractional-indexing";
8
8
 
@@ -33,10 +33,10 @@ export function useKanbanDragAndDrop<M extends Record<string, unknown>>({
33
33
  // to compute a single new sort key for the moved item.
34
34
  // Only one DB write per drag — the moved item gets its new key.
35
35
  const handleItemsReorder = useCallback(async (
36
- items: BoardItem<M>[],
36
+ items: BoardItem<Entity<M>>[],
37
37
  moveInfo?: { itemId: string; sourceColumn: string; targetColumn: string; }
38
38
  ) => {
39
- const entity = items.find(item => item.id === moveInfo?.itemId)?.entity;
39
+ const entity = items.find(item => item.id === moveInfo?.itemId)?.data;
40
40
  if (!entity || !moveInfo) return;
41
41
 
42
42
  analyticsController.onAnalyticsEvent?.("kanban_card_moved", {
@@ -64,10 +64,10 @@ export function useKanbanDragAndDrop<M extends Record<string, unknown>>({
64
64
 
65
65
  // Get the order keys of the neighbours
66
66
  const prevKey = movedIndex > 0
67
- ? (targetColumnItems[movedIndex - 1].entity.values?.[orderProperty] as string | null) ?? null
67
+ ? (targetColumnItems[movedIndex - 1].data.values?.[orderProperty] as string | null) ?? null
68
68
  : null;
69
69
  const nextKey = movedIndex < targetColumnItems.length - 1
70
- ? (targetColumnItems[movedIndex + 1].entity.values?.[orderProperty] as string | null) ?? null
70
+ ? (targetColumnItems[movedIndex + 1].data.values?.[orderProperty] as string | null) ?? null
71
71
  : null;
72
72
 
73
73
  try {
@@ -238,25 +238,11 @@ export function useBoardDataController<M extends Record<string, unknown> = any,
238
238
  return;
239
239
  }
240
240
 
241
- // Build where map for this column
242
- const whereMap: Record<string, string> = {};
243
- if (currentFilterValues) {
244
- Object.entries(currentFilterValues).forEach(([key, value]) => {
245
- if (value && Array.isArray(value)) {
246
- const [op, val] = value;
247
- const postgrestOp = op === "==" ? "eq" : op === "!=" ? "neq" : op === ">" ? "gt" : op === ">=" ? "gte" : op === "<" ? "lt" : op === "<=" ? "lte" : op === "in" ? "in" : op === "not-in" ? "nin" : op === "array-contains" ? "cs" : op === "array-contains-any" ? "csa" : "eq";
248
-
249
- let stringVal: string;
250
- if (Array.isArray(val)) {
251
- stringVal = `(${val.join(",")})`;
252
- } else {
253
- stringVal = String(val);
254
- }
255
- whereMap[key] = `${postgrestOp}.${stringVal}`;
256
- }
257
- });
258
- }
259
- whereMap[currentColumnProperty] = `eq.${column}`;
241
+ // Build filter map for this column — pass FilterValues directly
242
+ const whereFilter: FilterValues<string> = {
243
+ ...(currentFilterValues ?? {}),
244
+ [currentColumnProperty]: ["==", column]
245
+ };
260
246
 
261
247
  const orderByParam = currentOrderProperty ? `${currentOrderProperty}:asc` : undefined;
262
248
 
@@ -420,14 +406,14 @@ values: { ...e.values,
420
406
  const accessor = currentDataClient.collection(currentResolvedPath);
421
407
  if (accessor.listen) {
422
408
  const unsubscribe = accessor.listen({
423
- where: whereMap,
409
+ where: whereFilter,
424
410
  limit: itemCount,
425
411
  orderBy: orderByParam
426
412
  }, res => onUpdate(res.data as Entity<M>[]), onError);
427
413
  unsubscribersRef.current[column] = unsubscribe;
428
414
  } else {
429
415
  accessor.find({
430
- where: whereMap,
416
+ where: whereFilter,
431
417
  limit: itemCount,
432
418
  orderBy: orderByParam
433
419
  })
@@ -479,27 +465,13 @@ values: { ...e.values,
479
465
  const accessor = currentDataClient.collection(currentResolvedPath);
480
466
  if (accessor.count) {
481
467
 
482
- const whereMap: Record<string, string> = {};
483
- if (currentFilterValues) {
484
- Object.entries(currentFilterValues).forEach(([key, value]) => {
485
- if (value && Array.isArray(value)) {
486
- const [op, val] = value;
487
- const postgrestOp = op === "==" ? "eq" : op === "!=" ? "neq" : op === ">" ? "gt" : op === ">=" ? "gte" : op === "<" ? "lt" : op === "<=" ? "lte" : op === "in" ? "in" : op === "not-in" ? "nin" : op === "array-contains" ? "cs" : op === "array-contains-any" ? "csa" : "eq";
488
-
489
- let stringVal: string;
490
- if (Array.isArray(val)) {
491
- stringVal = `(${val.join(",")})`;
492
- } else {
493
- stringVal = String(val);
494
- }
495
- whereMap[key] = `${postgrestOp}.${stringVal}`;
496
- }
497
- });
498
- }
499
- whereMap[currentColumnProperty] = `eq.${column}`;
468
+ const whereFilter: FilterValues<string> = {
469
+ ...(currentFilterValues ?? {}),
470
+ [currentColumnProperty]: ["==", column]
471
+ };
500
472
 
501
473
  accessor.count({
502
- where: whereMap
474
+ where: whereFilter
503
475
  }).then(count => {
504
476
  if (isCleaningUpRef.current) return;
505
477
  setColumnData(prev => ({
@@ -194,8 +194,8 @@ export function resolveCollectionSlotKeys(
194
194
  : [...previewKeys].sort((a, b) => {
195
195
  const propA = collection.properties[a] as Property | undefined;
196
196
  const propB = collection.properties[b] as Property | undefined;
197
- const scoreA = propA?.type === "string" ? (propA.multiline ? 2 : 1) : 0;
198
- const scoreB = propB?.type === "string" ? (propB.multiline ? 2 : 1) : 0;
197
+ const scoreA = propA?.type === "string" ? (propA.ui?.multiline ? 2 : 1) : 0;
198
+ const scoreB = propB?.type === "string" ? (propB.ui?.multiline ? 2 : 1) : 0;
199
199
  return scoreB - scoreA;
200
200
  });
201
201
  const subtitleKey = sortedPreviewKeys.length > 0 ? sortedPreviewKeys[0] : undefined;
@@ -468,7 +468,7 @@ function resolveRelationDisplayName(
468
468
 
469
469
  // Helper to check if a property is a visible, non-id string
470
470
  const isDisplayCandidate = (p: Property): boolean => {
471
- return p.type === "string" && !p.multiline && !p.markdown && !p.storage
471
+ return p.type === "string" && !p.ui?.multiline && !p.ui?.markdown && !p.storage
472
472
  && !("isId" in p && p.isId) && !isHiddenProp(p);
473
473
  };
474
474
 
@@ -2,7 +2,7 @@ import type { ComponentRef, EntityCollection, EntityCustomViewParams, FormViewCo
2
2
  import type { FormContext } from "../types/fields";
3
3
  import type { PluginFormActionProps } from "@rebasepro/types";
4
4
  import React, { lazy, Suspense, useCallback, useEffect, useMemo, useRef, useState } from "react";
5
- import { Entity, EntityStatus, Property } from "@rebasepro/types";
5
+ import { Entity, EntityStatus, getCollectionDataPath, Property } from "@rebasepro/types";
6
6
  import { PluginProviderStack, resolveComponentRef, useComponentOverride, CollectionComponentOverrideProvider } from "@rebasepro/core";
7
7
 
8
8
  import { EntityCollectionView, EntityView } from "../components";
@@ -347,7 +347,7 @@ entityId }
347
347
  // Subcollection views
348
348
  const subCollectionsViews = subcollections && subcollections.map((subcollection) => {
349
349
  const subcollectionId = subcollection.slug;
350
- const newFullPath = usedEntity ? `${path}/${usedEntity?.id}/${removeInitialAndTrailingSlashes(subcollection.slug)}` : undefined;
350
+ const newFullPath = usedEntity ? `${path}/${usedEntity?.id}/${removeInitialAndTrailingSlashes(getCollectionDataPath(subcollection))}` : undefined;
351
351
 
352
352
  if (activeTab !== subcollectionId) return null;
353
353
  return (
@@ -2,7 +2,7 @@ import type { ComponentRef, EntityCollection, EntityCustomViewParams, FormViewCo
2
2
  import type { FormContext } from "../types/fields";
3
3
  import type { PluginFormActionProps } from "@rebasepro/types";
4
4
  import React, { lazy, Suspense, useCallback, useEffect, useMemo, useRef, useState } from "react";
5
- import { Entity, EntityStatus } from "@rebasepro/types";
5
+ import { Entity, EntityStatus, getCollectionDataPath } from "@rebasepro/types";
6
6
  import { PluginProviderStack, resolveComponentRef, useComponentOverride, CollectionComponentOverrideProvider } from "@rebasepro/core";
7
7
 
8
8
  import { EntityCollectionView, EntityView } from "../components";
@@ -39,8 +39,9 @@ import {
39
39
  getIcon
40
40
  } from "@rebasepro/core";
41
41
  import { getEntityFromMemoryCache } from "@rebasepro/core";
42
- import { EntityForm } from "../form";
43
- import type { EntityFormProps, OnUpdateParams } from "../types/components/EntityFormProps";
42
+ import { EntityFormBinding } from "../form";
43
+ import type { EntityFormBindingProps } from "../form";
44
+ import type { OnUpdateParams } from "../types/components/EntityFormProps";
44
45
  import { EntityEditViewFormActions } from "./EntityEditViewFormActions";
45
46
  import { EntityJsonPreview } from "../components/EntityJsonPreview";
46
47
  // Lazy-load history view — only loaded when user clicks the HistoryIcon tab
@@ -85,7 +86,7 @@ export interface EntityEditViewProps<M extends Record<string, unknown> = Record<
85
86
  navigateBack?: () => void;
86
87
  layout?: "side_panel" | "full_screen" | "split" | "dialog";
87
88
  barActions?: (params: BarActionsParams) => React.ReactNode;
88
- formProps?: Partial<EntityFormProps<M>>,
89
+ formProps?: Partial<EntityFormBindingProps<M>>,
89
90
  /**
90
91
  * Pre-populate the form with these values when creating a new entity.
91
92
  * Only applied when the form is in "new" mode (no entityId).
@@ -197,7 +198,7 @@ export function EntityEditViewInner<M extends Record<string, unknown>>({
197
198
  }) {
198
199
 
199
200
  const ResolvedFormActions = useComponentOverride("Entity.FormActions", EntityEditViewFormActions);
200
- const ResolvedEntityForm = useComponentOverride("Entity.Form", EntityForm) as typeof EntityForm;
201
+ const ResolvedEntityForm = useComponentOverride("Entity.Form", EntityFormBinding) as typeof EntityFormBinding;
201
202
  const ResolvedCollectionView = useComponentOverride("Collection.View", EntityCollectionView);
202
203
 
203
204
  const context = useRebaseContext();
@@ -415,7 +416,7 @@ parentEntityIds,
415
416
 
416
417
  const subCollectionsViews = subcollections && subcollections.map((subcollection) => {
417
418
  const subcollectionId = subcollection.slug;
418
- const newFullPath = usedEntity ? `${path}/${usedEntity?.id}/${removeInitialAndTrailingSlashes(subcollection.slug)}` : undefined;
419
+ const newFullPath = usedEntity ? `${path}/${usedEntity?.id}/${removeInitialAndTrailingSlashes(getCollectionDataPath(subcollection))}` : undefined;
419
420
 
420
421
  if (activeTab !== subcollectionId) return null;
421
422
  return (