@rebasepro/admin 0.0.1-canary.ca2cb6e → 0.0.1-canary.cbdd980
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.
- package/dist/{CollectionEditorDialog-B2M9lCyL.js → CollectionEditorDialog-ywdxhs1L.js} +42 -31
- package/dist/CollectionEditorDialog-ywdxhs1L.js.map +1 -0
- package/dist/{CollectionsStudioView-WG6soyfs.js → CollectionsStudioView-BDzMFzqH.js} +12 -12
- package/dist/CollectionsStudioView-BDzMFzqH.js.map +1 -0
- package/dist/{ContentHomePage-CDF_a6Lp.js → ContentHomePage-0tHuEIm_.js} +26 -26
- package/dist/ContentHomePage-0tHuEIm_.js.map +1 -0
- package/dist/{ExportCollectionAction-Dc0VOWMN.js → ExportCollectionAction-BIrq92To.js} +2 -2
- package/dist/{ExportCollectionAction-Dc0VOWMN.js.map → ExportCollectionAction-BIrq92To.js.map} +1 -1
- package/dist/{ImportCollectionAction-DpCagAOy.js → ImportCollectionAction-h8yg_To8.js} +2 -2
- package/dist/{ImportCollectionAction-DpCagAOy.js.map → ImportCollectionAction-h8yg_To8.js.map} +1 -1
- package/dist/{PropertyEditView-DS67DxoT.js → PropertyEditView-BuZrNnBN.js} +82 -104
- package/dist/PropertyEditView-BuZrNnBN.js.map +1 -0
- package/dist/{RolesView-CIuYBimF.js → RolesView-CMPsaIXo.js} +7 -5
- package/dist/RolesView-CMPsaIXo.js.map +1 -0
- package/dist/{UsersView-B5zelXnH.js → UsersView-BkeblMVT.js} +14 -35
- package/dist/UsersView-BkeblMVT.js.map +1 -0
- package/dist/collection_editor/ConfigControllerProvider.d.ts +0 -4
- package/dist/collection_editor/types/collection_editor_controller.d.ts +6 -3
- package/dist/collection_editor/types/config_controller.d.ts +14 -7
- package/dist/collection_editor/ui/AddKanbanColumnAction.d.ts +3 -2
- package/dist/collection_editor/ui/CollectionViewHeaderAction.d.ts +3 -2
- package/dist/collection_editor/ui/EditorCollectionAction.d.ts +1 -1
- package/dist/collection_editor/ui/EditorCollectionActionStart.d.ts +1 -1
- package/dist/collection_editor/ui/EditorEntityAction.d.ts +1 -1
- package/dist/collection_editor/ui/KanbanSetupAction.d.ts +3 -2
- package/dist/collection_editor/ui/PropertyAddColumnComponent.d.ts +3 -2
- package/dist/collection_editor/ui/collection_editor/CollectionDetailsForm.d.ts +3 -4
- package/dist/collection_editor/ui/collection_editor/CollectionEditorDialog.d.ts +2 -3
- package/dist/collection_editor/ui/collection_editor/CollectionPropertiesEditorForm.d.ts +1 -2
- package/dist/collection_editor/ui/collection_editor/SubcollectionsEditTab.d.ts +3 -2
- package/dist/collection_editor_ui.js +3 -3
- package/dist/components/EntityCollectionTable/EntityCollectionTable.d.ts +1 -1
- package/dist/components/EntityCollectionTable/EntityCollectionTableProps.d.ts +1 -1
- package/dist/components/EntityCollectionTable/column_utils.d.ts +2 -2
- package/dist/components/EntityCollectionTable/fields/TableMultipleRelationField.d.ts +1 -1
- package/dist/components/EntityCollectionTable/fields/TableReferenceField.d.ts +1 -1
- package/dist/components/EntityCollectionTable/fields/TableRelationField.d.ts +1 -1
- package/dist/components/EntityCollectionTable/fields/TableRelationSelectorField.d.ts +2 -2
- package/dist/components/EntityCollectionTable/internal/CollectionTableToolbar.d.ts +5 -1
- package/dist/components/EntityCollectionView/EntityCollectionBoardView.d.ts +3 -2
- package/dist/components/EntityCollectionView/EntityCollectionView.d.ts +2 -1
- package/dist/components/EntityCollectionView/EntityCollectionViewActions.d.ts +4 -2
- package/dist/components/EntityCollectionView/EntityCollectionViewStartActions.d.ts +4 -2
- package/dist/components/EntityCollectionView/FiltersDialog.d.ts +2 -2
- package/dist/components/EntityCollectionView/SplitListView.d.ts +3 -2
- package/dist/components/EntityEditView.d.ts +9 -2
- package/dist/components/RebaseCMS.d.ts +1 -1
- package/dist/components/ReferenceTable/EntitySelectionTable.d.ts +2 -2
- package/dist/components/ReferenceWidget.d.ts +2 -2
- package/dist/components/RelationSelector.d.ts +1 -1
- package/dist/components/SelectableTable/SelectableTable.d.ts +2 -2
- package/dist/editor.js +2 -2
- package/dist/editor.js.map +1 -1
- package/dist/hooks/navigation/useNavigationRegistry.d.ts +2 -1
- package/dist/hooks/useEntityHistory.d.ts +1 -1
- package/dist/{index-Dey5WJpO.js → index-BuZaHcyc.js} +3 -3
- package/dist/index-BuZaHcyc.js.map +1 -0
- package/dist/{index-CHxgwt6E.js → index-CS6uJ7oW.js} +11 -4
- package/dist/index-CS6uJ7oW.js.map +1 -0
- package/dist/{index-CBhrgpR7.js → index-eRJbMvHi.js} +3 -3
- package/dist/index-eRJbMvHi.js.map +1 -0
- package/dist/index.js +79 -63
- package/dist/index.js.map +1 -1
- package/dist/{useEntityHistory-Dcj4zhGj.js → useEntityHistory-UVsSclfZ.js} +3 -1
- package/dist/useEntityHistory-UVsSclfZ.js.map +1 -0
- package/dist/util/navigation_utils.d.ts +10 -1
- package/dist/{util-BQ82ySL3.js → util-zfU1zOCX.js} +1658 -1262
- package/dist/util-zfU1zOCX.js.map +1 -0
- package/package.json +9 -17
- package/src/collection_editor/ConfigControllerProvider.tsx +19 -28
- package/src/collection_editor/types/collection_editor_controller.tsx +3 -3
- package/src/collection_editor/types/config_controller.tsx +7 -7
- package/src/collection_editor/ui/AddKanbanColumnAction.tsx +4 -4
- package/src/collection_editor/ui/CollectionViewHeaderAction.tsx +3 -3
- package/src/collection_editor/ui/EditorCollectionAction.tsx +3 -3
- package/src/collection_editor/ui/EditorCollectionActionStart.tsx +7 -7
- package/src/collection_editor/ui/EditorEntityAction.tsx +3 -3
- package/src/collection_editor/ui/HomePageEditorCollectionAction.tsx +4 -2
- package/src/collection_editor/ui/KanbanSetupAction.tsx +4 -3
- package/src/collection_editor/ui/MissingReferenceWidget.tsx +3 -2
- package/src/collection_editor/ui/NewCollectionButton.tsx +2 -1
- package/src/collection_editor/ui/NewCollectionCard.tsx +2 -1
- package/src/collection_editor/ui/PropertyAddColumnComponent.tsx +3 -3
- package/src/collection_editor/ui/collection_editor/CollectionDetailsForm.tsx +5 -50
- package/src/collection_editor/ui/collection_editor/CollectionEditorDialog.tsx +12 -20
- package/src/collection_editor/ui/collection_editor/CollectionPropertiesEditorForm.tsx +1 -3
- package/src/collection_editor/ui/collection_editor/CollectionRLSTab.tsx +17 -2
- package/src/collection_editor/ui/collection_editor/CollectionRelationsTab.tsx +3 -3
- package/src/collection_editor/ui/collection_editor/CollectionStudioView.tsx +2 -1
- package/src/collection_editor/ui/collection_editor/CollectionsStudioView.tsx +18 -12
- package/src/collection_editor/ui/collection_editor/DisplaySettingsForm.tsx +1 -2
- package/src/collection_editor/ui/collection_editor/GetCodeDialog.tsx +1 -2
- package/src/collection_editor/ui/collection_editor/PropertyFieldPreview.tsx +6 -6
- package/src/collection_editor/ui/collection_editor/SubcollectionsEditTab.tsx +4 -4
- package/src/collection_editor/ui/collection_editor/properties/MapPropertyField.tsx +2 -2
- package/src/collection_editor/ui/collection_editor/properties/ReferencePropertyField.tsx +15 -49
- package/src/collection_editor/ui/collection_editor/properties/advanced/AdvancedPropertyValidation.tsx +4 -5
- package/src/collection_editor/ui/collection_editor/templates/pages_template.ts +1 -1
- package/src/collection_editor/ui/collection_editor/templates/products_template.ts +2 -2
- package/src/components/DefaultAppBar.tsx +2 -2
- package/src/components/DefaultDrawer.tsx +25 -17
- package/src/components/DrawerNavigationGroup.tsx +4 -4
- package/src/components/DrawerNavigationItem.tsx +6 -6
- package/src/components/EntityCollectionTable/EntityCollectionRowActions.tsx +5 -3
- package/src/components/EntityCollectionTable/EntityCollectionTable.tsx +5 -5
- package/src/components/EntityCollectionTable/EntityCollectionTableProps.tsx +1 -1
- package/src/components/EntityCollectionTable/PropertyTableCell.tsx +4 -4
- package/src/components/EntityCollectionTable/column_utils.tsx +3 -3
- package/src/components/EntityCollectionTable/fields/TableMultipleRelationField.tsx +3 -3
- package/src/components/EntityCollectionTable/fields/TableReferenceField.tsx +3 -3
- package/src/components/EntityCollectionTable/fields/TableRelationField.tsx +4 -4
- package/src/components/EntityCollectionTable/fields/TableRelationSelectorField.tsx +3 -3
- package/src/components/EntityCollectionTable/internal/CollectionTableToolbar.tsx +8 -2
- package/src/components/EntityCollectionTable/internal/EntityTableCell.tsx +2 -2
- package/src/components/EntityCollectionTable/internal/common.tsx +5 -5
- package/src/components/EntityCollectionTable/internal/popup_field/PopupFormField.tsx +1 -1
- package/src/components/EntityCollectionTable/table_bindings.tsx +45 -35
- package/src/components/EntityCollectionView/EntityBoardCard.tsx +18 -19
- package/src/components/EntityCollectionView/EntityCard.tsx +2 -2
- package/src/components/EntityCollectionView/EntityCollectionBoardView.tsx +42 -14
- package/src/components/EntityCollectionView/EntityCollectionCardView.tsx +4 -3
- package/src/components/EntityCollectionView/EntityCollectionListView.tsx +159 -56
- package/src/components/EntityCollectionView/EntityCollectionView.tsx +169 -75
- package/src/components/EntityCollectionView/EntityCollectionViewActions.tsx +23 -13
- package/src/components/EntityCollectionView/EntityCollectionViewStartActions.tsx +21 -12
- package/src/components/EntityCollectionView/FiltersDialog.tsx +7 -7
- package/src/components/EntityCollectionView/SplitListView.tsx +24 -8
- package/src/components/EntityCollectionView/useEntityPreviewSlots.ts +33 -5
- package/src/components/EntityEditView.tsx +85 -85
- package/src/components/EntitySidePanel.tsx +18 -10
- package/src/components/HomePage/ContentHomePage.tsx +24 -15
- package/src/components/HomePage/NavigationCard.tsx +4 -4
- package/src/components/HomePage/NavigationGroup.tsx +2 -2
- package/src/components/RebaseAuthGate.tsx +2 -0
- package/src/components/RebaseCMS.tsx +4 -3
- package/src/components/RebaseNavigation.tsx +8 -5
- package/src/components/ReferenceTable/EntitySelectionTable.tsx +4 -4
- package/src/components/ReferenceWidget.tsx +3 -3
- package/src/components/RelationSelector.tsx +33 -5
- package/src/components/SelectableTable/SelectableTable.tsx +7 -7
- package/src/components/UserSelector.tsx +1 -1
- package/src/components/admin/RolesView.tsx +10 -3
- package/src/components/admin/UsersView.tsx +13 -25
- package/src/components/app/Scaffold.tsx +4 -4
- package/src/components/field_configs.tsx +29 -32
- package/src/components/history/EntityHistoryView.tsx +12 -1
- package/src/editor/editor.tsx +2 -2
- package/src/form/EntityForm.tsx +5 -4
- package/src/form/PropertyFieldBinding.tsx +14 -10
- package/src/form/components/FieldHelperText.tsx +1 -1
- package/src/form/field_bindings/ArrayCustomShapedFieldBinding.tsx +3 -3
- package/src/form/field_bindings/ArrayOfReferencesFieldBinding.tsx +5 -5
- package/src/form/field_bindings/BlockFieldBinding.tsx +4 -4
- package/src/form/field_bindings/DateTimeFieldBinding.tsx +1 -1
- package/src/form/field_bindings/KeyValueFieldBinding.tsx +1 -1
- package/src/form/field_bindings/MapFieldBinding.tsx +7 -7
- package/src/form/field_bindings/MarkdownEditorFieldBinding.tsx +1 -1
- package/src/form/field_bindings/MultipleRelationFieldBinding.tsx +3 -3
- package/src/form/field_bindings/ReferenceAsStringFieldBinding.tsx +7 -7
- package/src/form/field_bindings/ReferenceFieldBinding.tsx +2 -2
- package/src/form/field_bindings/RelationFieldBinding.tsx +4 -4
- package/src/form/field_bindings/RepeatFieldBinding.tsx +5 -5
- package/src/form/field_bindings/SelectFieldBinding.tsx +1 -1
- package/src/form/field_bindings/StorageUploadFieldBinding.tsx +1 -1
- package/src/form/field_bindings/SwitchFieldBinding.tsx +1 -1
- package/src/form/field_bindings/TextFieldBinding.tsx +7 -7
- package/src/form/field_bindings/UserSelectFieldBinding.tsx +1 -1
- package/src/form/useClearRestoreValue.tsx +1 -1
- package/src/form/validation.ts +1 -1
- package/src/hooks/navigation/contexts/CollectionRegistryContext.tsx +2 -1
- package/src/hooks/navigation/useBuildCollectionRegistryController.tsx +15 -3
- package/src/hooks/navigation/useNavigationRegistry.ts +14 -3
- package/src/hooks/navigation/useResolvedViews.tsx +1 -3
- package/src/hooks/navigation/useTopLevelNavigation.ts +1 -1
- package/src/hooks/navigation/utils.ts +1 -1
- package/src/hooks/useEntityHistory.ts +7 -2
- package/src/preview/PropertyPreview.tsx +27 -23
- package/src/preview/components/StorageThumbnail.tsx +4 -1
- package/src/preview/property_previews/ArrayOfMapsPreview.tsx +1 -1
- package/src/preview/property_previews/ArrayOfReferencesPreview.tsx +1 -1
- package/src/preview/property_previews/ArrayOfRelationsPreview.tsx +1 -1
- package/src/preview/property_previews/SkeletonPropertyComponent.tsx +3 -3
- package/src/preview/property_previews/StringPropertyPreview.tsx +3 -3
- package/src/routes/RebaseRoute.tsx +57 -11
- package/src/util/navigation_utils.ts +21 -2
- package/src/util/previews.ts +15 -6
- package/src/util/property_utils.tsx +3 -3
- package/dist/CollectionEditorDialog-B2M9lCyL.js.map +0 -1
- package/dist/CollectionsStudioView-WG6soyfs.js.map +0 -1
- package/dist/ContentHomePage-CDF_a6Lp.js.map +0 -1
- package/dist/PropertyEditView-DS67DxoT.js.map +0 -1
- package/dist/RolesView-CIuYBimF.js.map +0 -1
- package/dist/UsersView-B5zelXnH.js.map +0 -1
- package/dist/index-CBhrgpR7.js.map +0 -1
- package/dist/index-CHxgwt6E.js.map +0 -1
- package/dist/index-Dey5WJpO.js.map +0 -1
- package/dist/useEntityHistory-Dcj4zhGj.js.map +0 -1
- package/dist/util-BQ82ySL3.js.map +0 -1
|
@@ -41,7 +41,7 @@ export interface TableFieldConfig {
|
|
|
41
41
|
}
|
|
42
42
|
|
|
43
43
|
export function isStorageProperty(property: Property) {
|
|
44
|
-
if (property.type === "string" && property.markdown)
|
|
44
|
+
if (property.type === "string" && property.ui?.markdown)
|
|
45
45
|
return false;
|
|
46
46
|
if (property.type === "string" && (property as StringProperty).storage)
|
|
47
47
|
return true;
|
|
@@ -81,7 +81,7 @@ path: referenceProperty.path as string }) : undefined;
|
|
|
81
81
|
includeId={referenceProperty.includeId}
|
|
82
82
|
includeEntityLink={referenceProperty.includeEntityLink}
|
|
83
83
|
title={property.name}
|
|
84
|
-
|
|
84
|
+
fixedFilter={referenceProperty.fixedFilter}
|
|
85
85
|
/>
|
|
86
86
|
);
|
|
87
87
|
},
|
|
@@ -181,8 +181,8 @@ path: referenceProperty.path as string }) : undefined;
|
|
|
181
181
|
),
|
|
182
182
|
fullHeight: true
|
|
183
183
|
};
|
|
184
|
-
} else if (stringProperty.markdown || !stringProperty.storage || !stringProperty.reference) {
|
|
185
|
-
const multiline = Boolean(stringProperty.multiline) || Boolean(stringProperty.markdown);
|
|
184
|
+
} else if (stringProperty.ui?.markdown || !stringProperty.storage || !stringProperty.reference) {
|
|
185
|
+
const multiline = Boolean(stringProperty.ui?.multiline) || Boolean(stringProperty.ui?.markdown);
|
|
186
186
|
return {
|
|
187
187
|
Component: ({ error, validationError, disabled, selected, internalValue, updateValue }: any) => (
|
|
188
188
|
<VirtualTableInput
|
|
@@ -238,11 +238,11 @@ path: referenceProperty.path as string }) : undefined;
|
|
|
238
238
|
size={size}
|
|
239
239
|
path={property.path}
|
|
240
240
|
multiselect={false}
|
|
241
|
-
previewProperties={property.previewProperties}
|
|
241
|
+
previewProperties={property.ui?.previewProperties}
|
|
242
242
|
includeId={property.includeId}
|
|
243
243
|
includeEntityLink={property.includeEntityLink}
|
|
244
244
|
title={property.name ?? propertyKey}
|
|
245
|
-
|
|
245
|
+
fixedFilter={property.fixedFilter}
|
|
246
246
|
/>
|
|
247
247
|
),
|
|
248
248
|
allowScroll: false
|
|
@@ -250,39 +250,14 @@ path: referenceProperty.path as string }) : undefined;
|
|
|
250
250
|
}
|
|
251
251
|
} else if (property.type === "relation") {
|
|
252
252
|
if (property.relation) {
|
|
253
|
-
if (property.widget === "dialog") {
|
|
253
|
+
if (property.ui?.widget === "dialog") {
|
|
254
254
|
return {
|
|
255
|
-
Component:
|
|
256
|
-
<TableRelationField
|
|
257
|
-
name={propertyKey}
|
|
258
|
-
internalValue={internalValue as EntityRelation}
|
|
259
|
-
updateValue={updateValue}
|
|
260
|
-
disabled={disabled}
|
|
261
|
-
size={size}
|
|
262
|
-
multiselect={false}
|
|
263
|
-
relation={property.relation}
|
|
264
|
-
previewProperties={property.previewProperties}
|
|
265
|
-
includeId={property.includeId}
|
|
266
|
-
includeEntityLink={property.includeEntityLink}
|
|
267
|
-
title={property.name ?? propertyKey}
|
|
268
|
-
forceFilter={property.forceFilter}
|
|
269
|
-
/>
|
|
270
|
-
),
|
|
255
|
+
Component: RelationDialogBindingComponent,
|
|
271
256
|
allowScroll: false
|
|
272
257
|
};
|
|
273
258
|
} else {
|
|
274
259
|
return {
|
|
275
|
-
Component:
|
|
276
|
-
<TableRelationSelectorField
|
|
277
|
-
name={propertyKey}
|
|
278
|
-
internalValue={internalValue as EntityRelation}
|
|
279
|
-
updateValue={updateValue}
|
|
280
|
-
disabled={disabled}
|
|
281
|
-
size={"small"}
|
|
282
|
-
relation={property.relation!}
|
|
283
|
-
forceFilter={property.forceFilter}
|
|
284
|
-
/>
|
|
285
|
-
),
|
|
260
|
+
Component: RelationSelectorBindingComponent,
|
|
286
261
|
allowScroll: false
|
|
287
262
|
};
|
|
288
263
|
}
|
|
@@ -330,7 +305,7 @@ path: referenceProperty.path as string }) : undefined;
|
|
|
330
305
|
path={ofProp.path}
|
|
331
306
|
previewProperties={ofProp.previewProperties}
|
|
332
307
|
title={arrayProperty.name}
|
|
333
|
-
|
|
308
|
+
fixedFilter={ofProp.fixedFilter}
|
|
334
309
|
includeId={ofProp.includeId}
|
|
335
310
|
includeEntityLink={ofProp.includeEntityLink}
|
|
336
311
|
/>
|
|
@@ -344,3 +319,38 @@ path: referenceProperty.path as string }) : undefined;
|
|
|
344
319
|
|
|
345
320
|
return undefined;
|
|
346
321
|
}
|
|
322
|
+
|
|
323
|
+
/** Stable component for relation fields rendered with the dialog widget */
|
|
324
|
+
function RelationDialogBindingComponent({ propertyKey, internalValue, updateValue, disabled, size, property }: any) {
|
|
325
|
+
return (
|
|
326
|
+
<TableRelationField
|
|
327
|
+
name={propertyKey}
|
|
328
|
+
internalValue={internalValue as EntityRelation}
|
|
329
|
+
updateValue={updateValue}
|
|
330
|
+
disabled={disabled}
|
|
331
|
+
size={size}
|
|
332
|
+
multiselect={false}
|
|
333
|
+
relation={property.relation}
|
|
334
|
+
previewProperties={property.ui?.previewProperties}
|
|
335
|
+
includeId={property.includeId}
|
|
336
|
+
includeEntityLink={property.includeEntityLink}
|
|
337
|
+
title={property.name ?? propertyKey}
|
|
338
|
+
fixedFilter={property.fixedFilter}
|
|
339
|
+
/>
|
|
340
|
+
);
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
/** Stable component for relation fields rendered with the inline selector */
|
|
344
|
+
function RelationSelectorBindingComponent({ propertyKey, internalValue, updateValue, disabled, property }: any) {
|
|
345
|
+
return (
|
|
346
|
+
<TableRelationSelectorField
|
|
347
|
+
name={propertyKey}
|
|
348
|
+
internalValue={internalValue as EntityRelation}
|
|
349
|
+
updateValue={updateValue}
|
|
350
|
+
disabled={disabled}
|
|
351
|
+
size={"small"}
|
|
352
|
+
relation={property.relation!}
|
|
353
|
+
fixedFilter={property.fixedFilter}
|
|
354
|
+
/>
|
|
355
|
+
);
|
|
356
|
+
}
|
|
@@ -147,29 +147,28 @@ function EntityBoardCardInner<M extends Record<string, unknown> = Record<string,
|
|
|
147
147
|
</div>
|
|
148
148
|
)}
|
|
149
149
|
|
|
150
|
-
{/* Selection checkbox overlay —
|
|
151
|
-
{
|
|
150
|
+
{/* Selection checkbox overlay — always in the DOM, visibility controlled by CSS */}
|
|
151
|
+
<div className={cls(
|
|
152
|
+
"absolute inset-0 flex items-center justify-center rounded-md transition-[opacity,background-color] duration-200",
|
|
153
|
+
!selectionEnabled && "invisible pointer-events-none",
|
|
154
|
+
selectionEnabled && (selected
|
|
155
|
+
? "opacity-100 bg-primary/10 dark:bg-primary/20"
|
|
156
|
+
: "opacity-0 group-hover/card:opacity-100")
|
|
157
|
+
)}>
|
|
152
158
|
<div className={cls(
|
|
153
|
-
"
|
|
159
|
+
"transition-transform duration-200",
|
|
154
160
|
selected
|
|
155
|
-
? "
|
|
156
|
-
: "
|
|
161
|
+
? "scale-100"
|
|
162
|
+
: "scale-75 group-hover/card:scale-100"
|
|
157
163
|
)}>
|
|
158
|
-
<
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
<Checkbox
|
|
165
|
-
checked={selected ?? false}
|
|
166
|
-
onCheckedChange={handleSelectionChange}
|
|
167
|
-
size="small"
|
|
168
|
-
padding={false}
|
|
169
|
-
/>
|
|
170
|
-
</div>
|
|
164
|
+
<Checkbox
|
|
165
|
+
checked={selected ?? false}
|
|
166
|
+
onCheckedChange={handleSelectionChange}
|
|
167
|
+
size="small"
|
|
168
|
+
padding={false}
|
|
169
|
+
/>
|
|
171
170
|
</div>
|
|
172
|
-
|
|
171
|
+
</div>
|
|
173
172
|
</div>
|
|
174
173
|
|
|
175
174
|
{/* Content */}
|
|
@@ -94,8 +94,8 @@ export function EntityCard<M extends Record<string, unknown> = Record<string, un
|
|
|
94
94
|
"cursor-pointer overflow-hidden group relative",
|
|
95
95
|
"transition-all duration-200",
|
|
96
96
|
"hover:shadow-lg hover:-translate-y-0.5",
|
|
97
|
-
selected && "ring-2 ring-primary",
|
|
98
|
-
highlighted && !selected && "ring-2 ring-primary ring-opacity-50"
|
|
97
|
+
selected && "ring-2 ring-primary bg-surface-accent-50 dark:bg-surface-accent-900",
|
|
98
|
+
highlighted && !selected && "ring-2 ring-primary ring-opacity-50 bg-surface-accent-50/50 dark:bg-surface-accent-900"
|
|
99
99
|
)}
|
|
100
100
|
onClick={handleClick}
|
|
101
101
|
>
|
|
@@ -30,7 +30,7 @@ export type EntityCollectionBoardViewProps<M extends Record<string, unknown> = R
|
|
|
30
30
|
collection: EntityCollection<M>;
|
|
31
31
|
tableController: EntityTableController<M>;
|
|
32
32
|
fullPath: string;
|
|
33
|
-
|
|
33
|
+
parentCollectionSlugs?: string[], parentEntityIds?: string[];
|
|
34
34
|
columnProperty: string;
|
|
35
35
|
onEntityClick?: (entity: Entity<M>) => void;
|
|
36
36
|
selectionController?: SelectionController<M>;
|
|
@@ -48,7 +48,8 @@ export function EntityCollectionBoardView<M extends Record<string, unknown> = Re
|
|
|
48
48
|
collection,
|
|
49
49
|
tableController,
|
|
50
50
|
fullPath,
|
|
51
|
-
|
|
51
|
+
parentCollectionSlugs = [],
|
|
52
|
+
parentEntityIds = [],
|
|
52
53
|
columnProperty,
|
|
53
54
|
onEntityClick,
|
|
54
55
|
selectionController,
|
|
@@ -225,13 +226,13 @@ export function EntityCollectionBoardView<M extends Record<string, unknown> = Re
|
|
|
225
226
|
.forEach(plugin => {
|
|
226
227
|
plugin.hooks!.onKanbanColumnsReorder!({
|
|
227
228
|
fullPath,
|
|
228
|
-
|
|
229
|
+
parentCollectionSlugs, parentEntityIds,
|
|
229
230
|
collection,
|
|
230
231
|
kanbanColumnProperty: columnProperty,
|
|
231
232
|
newColumnsOrder: newColumns
|
|
232
233
|
});
|
|
233
234
|
});
|
|
234
|
-
}, [plugins, fullPath,
|
|
235
|
+
}, [plugins, fullPath, parentCollectionSlugs, parentEntityIds, collection, columnProperty, analyticsController]);
|
|
235
236
|
|
|
236
237
|
// Collection-level count queries to detect missing order property
|
|
237
238
|
// Just TWO counts: total and ordered (for the entire collection, not per column)
|
|
@@ -432,24 +433,51 @@ export function EntityCollectionBoardView<M extends Record<string, unknown> = Re
|
|
|
432
433
|
return selectionController?.isEntitySelected(entity) ?? false;
|
|
433
434
|
}, [selectionController]);
|
|
434
435
|
|
|
435
|
-
|
|
436
|
-
|
|
436
|
+
// Store latest callbacks in refs so the stable ItemComponent always
|
|
437
|
+
// reads fresh values without changing its own identity.
|
|
438
|
+
const handleEntityClickRef = useRef(handleEntityClick);
|
|
439
|
+
handleEntityClickRef.current = handleEntityClick;
|
|
440
|
+
const isEntitySelectedRef = useRef(isEntitySelected);
|
|
441
|
+
isEntitySelectedRef.current = isEntitySelected;
|
|
442
|
+
const handleSelectionChangeRef = useRef(handleSelectionChange);
|
|
443
|
+
handleSelectionChangeRef.current = handleSelectionChange;
|
|
444
|
+
const selectionEnabledRef = useRef(selectionEnabled);
|
|
445
|
+
selectionEnabledRef.current = selectionEnabled;
|
|
446
|
+
|
|
447
|
+
// Stable callback wrappers — identity never changes, delegates to refs.
|
|
448
|
+
const stableOnClick = useCallback((entity: Entity<M>) => {
|
|
449
|
+
handleEntityClickRef.current(entity);
|
|
450
|
+
}, []);
|
|
451
|
+
const stableOnSelectionChange = useCallback((entity: Entity<M>, sel: boolean) => {
|
|
452
|
+
handleSelectionChangeRef.current(entity, sel);
|
|
453
|
+
}, []);
|
|
454
|
+
|
|
455
|
+
// Build a single, truly stable component reference.
|
|
456
|
+
// Uses refs for ALL dynamic values so the component type never changes.
|
|
457
|
+
// When ItemComponent identity changes, React.memo'd SortableItem remounts
|
|
458
|
+
// the card → DOM is destroyed/recreated → CSS :hover state is lost → flicker.
|
|
459
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
460
|
+
const ItemComponent = useMemo(() => {
|
|
461
|
+
const Comp = (props: BoardItemViewProps<M>) => (
|
|
437
462
|
<EntityBoardCard
|
|
438
463
|
{...props}
|
|
439
|
-
collection={
|
|
440
|
-
onClick={
|
|
441
|
-
selected={
|
|
442
|
-
onSelectionChange={
|
|
443
|
-
selectionEnabled={
|
|
464
|
+
collection={collectionRef.current as EntityCollection<M>}
|
|
465
|
+
onClick={stableOnClick}
|
|
466
|
+
selected={isEntitySelectedRef.current(props.item.entity)}
|
|
467
|
+
onSelectionChange={stableOnSelectionChange}
|
|
468
|
+
selectionEnabled={selectionEnabledRef.current}
|
|
444
469
|
/>
|
|
445
470
|
);
|
|
446
|
-
|
|
471
|
+
Comp.displayName = "KanbanItemComponent";
|
|
472
|
+
return Comp;
|
|
473
|
+
}, []);
|
|
447
474
|
|
|
448
475
|
// Get KanbanSetupComponent from plugin slots
|
|
449
476
|
const kanbanSetupSlots = useSlot("kanban.setup", {
|
|
450
477
|
collection,
|
|
451
478
|
fullPath,
|
|
452
|
-
|
|
479
|
+
parentCollectionSlugs,
|
|
480
|
+
parentEntityIds
|
|
453
481
|
});
|
|
454
482
|
const KanbanSetupComponent = kanbanSetupSlots.length > 0 ? () => <>{kanbanSetupSlots[0]}</> : null;
|
|
455
483
|
|
|
@@ -457,7 +485,7 @@ export function EntityCollectionBoardView<M extends Record<string, unknown> = Re
|
|
|
457
485
|
const addKanbanColumnSlots = useSlot("kanban.add-column", {
|
|
458
486
|
collection,
|
|
459
487
|
fullPath,
|
|
460
|
-
|
|
488
|
+
parentCollectionSlugs, parentEntityIds,
|
|
461
489
|
columnProperty
|
|
462
490
|
});
|
|
463
491
|
const AddKanbanColumnComponent = addKanbanColumnSlots.length > 0 ? () => <>{addKanbanColumnSlots[0]}</> : null;
|
|
@@ -5,7 +5,6 @@ import { EntityCard } from "./EntityCard";
|
|
|
5
5
|
import {
|
|
6
6
|
cls,
|
|
7
7
|
CircularProgress,
|
|
8
|
-
CircularProgressCenter,
|
|
9
8
|
Typography
|
|
10
9
|
} from "@rebasepro/ui";
|
|
11
10
|
import { useAuthController, useCustomizationController } from "@rebasepro/core";
|
|
@@ -213,9 +212,11 @@ export function EntityCollectionCardView<M extends Record<string, unknown> = Rec
|
|
|
213
212
|
</Typography>
|
|
214
213
|
</div>
|
|
215
214
|
) : isInitialLoading ? (
|
|
216
|
-
<
|
|
215
|
+
<div className="flex items-center justify-center py-12 px-8">
|
|
216
|
+
<CircularProgress size="small"/>
|
|
217
|
+
</div>
|
|
217
218
|
) : isEmpty ? (
|
|
218
|
-
<div className="
|
|
219
|
+
<div className="flex items-center justify-center py-12 px-8">
|
|
219
220
|
{emptyComponent ?? (
|
|
220
221
|
<Typography variant="label" color="secondary">
|
|
221
222
|
No entries found
|
|
@@ -7,7 +7,6 @@ import {
|
|
|
7
7
|
Checkbox,
|
|
8
8
|
Chip,
|
|
9
9
|
CircularProgress,
|
|
10
|
-
CircularProgressCenter,
|
|
11
10
|
cls,
|
|
12
11
|
defaultBorderMixin,
|
|
13
12
|
Typography
|
|
@@ -88,6 +87,44 @@ function getRowClasses(size: CollectionSize): string {
|
|
|
88
87
|
}
|
|
89
88
|
}
|
|
90
89
|
|
|
90
|
+
/**
|
|
91
|
+
* Estimated row height in pixels for virtualization, based on size.
|
|
92
|
+
*/
|
|
93
|
+
function getEstimatedRowHeight(size: CollectionSize): number {
|
|
94
|
+
switch (size) {
|
|
95
|
+
case "xs": return 44;
|
|
96
|
+
case "s": return 52;
|
|
97
|
+
case "m": return 64;
|
|
98
|
+
case "l": return 76;
|
|
99
|
+
case "xl": return 88;
|
|
100
|
+
default: return 64;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/** Number of extra rows rendered above/below the viewport. */
|
|
105
|
+
const OVERSCAN_COUNT = 8;
|
|
106
|
+
|
|
107
|
+
/** Threshold in pixels from the bottom of the scroll area to trigger loading more. */
|
|
108
|
+
const LOAD_MORE_THRESHOLD = 400;
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Walk up the DOM from `element` to find the nearest scrollable ancestor.
|
|
112
|
+
*/
|
|
113
|
+
function getScrollParent(element: HTMLElement | null): HTMLElement | null {
|
|
114
|
+
let parent = element?.parentElement ?? null;
|
|
115
|
+
while (parent) {
|
|
116
|
+
const style = getComputedStyle(parent);
|
|
117
|
+
if (
|
|
118
|
+
style.overflowY === "auto" || style.overflowY === "scroll" ||
|
|
119
|
+
style.overflow === "auto" || style.overflow === "scroll"
|
|
120
|
+
) {
|
|
121
|
+
return parent;
|
|
122
|
+
}
|
|
123
|
+
parent = parent.parentElement;
|
|
124
|
+
}
|
|
125
|
+
return document.documentElement;
|
|
126
|
+
}
|
|
127
|
+
|
|
91
128
|
/**
|
|
92
129
|
* Returns true if a property type should NOT be rendered via
|
|
93
130
|
* PropertyPreview in list row columns (because it would blow up height).
|
|
@@ -446,22 +483,77 @@ export function EntityCollectionListView<M extends Record<string, unknown> = Rec
|
|
|
446
483
|
// Empty state
|
|
447
484
|
const isEmpty = !dataLoading && data.length === 0 && !dataLoadingError;
|
|
448
485
|
|
|
486
|
+
// ── Virtualization: scroll-parent windowing ──
|
|
487
|
+
const estimatedRowHeight = getEstimatedRowHeight(size);
|
|
488
|
+
const [effectiveScrollTop, setEffectiveScrollTop] = useState(0);
|
|
489
|
+
const [viewportHeight, setViewportHeight] = useState(800);
|
|
490
|
+
|
|
491
|
+
// Keep mutable refs for values used in the scroll handler to avoid
|
|
492
|
+
// re-attaching the listener every time pagination state changes.
|
|
493
|
+
const paginationStateRef = useRef({ paginationEnabled, noMoreToLoad, itemCount, pageSize });
|
|
494
|
+
useEffect(() => {
|
|
495
|
+
paginationStateRef.current = { paginationEnabled, noMoreToLoad, itemCount, pageSize };
|
|
496
|
+
}, [paginationEnabled, noMoreToLoad, itemCount, pageSize]);
|
|
449
497
|
|
|
450
|
-
// Sentinel ref for IntersectionObserver-based infinite scroll
|
|
451
|
-
const sentinelRef = useRef<HTMLDivElement | null>(null);
|
|
452
498
|
useEffect(() => {
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
499
|
+
const el = containerRef.current;
|
|
500
|
+
if (!el) return;
|
|
501
|
+
const scrollEl = getScrollParent(el);
|
|
502
|
+
if (!scrollEl) return;
|
|
503
|
+
|
|
504
|
+
let rafId: number | null = null;
|
|
505
|
+
|
|
506
|
+
const update = () => {
|
|
507
|
+
rafId = null;
|
|
508
|
+
const scrollRect = scrollEl.getBoundingClientRect();
|
|
509
|
+
const listRect = el.getBoundingClientRect();
|
|
510
|
+
|
|
511
|
+
// How much of the list has scrolled past the viewport top
|
|
512
|
+
const listTopRelative = listRect.top - scrollRect.top;
|
|
513
|
+
setEffectiveScrollTop(Math.max(0, -listTopRelative));
|
|
514
|
+
setViewportHeight(scrollRect.height);
|
|
515
|
+
|
|
516
|
+
// Infinite scroll: trigger load-more when near the bottom
|
|
517
|
+
const { paginationEnabled: pe, noMoreToLoad: nm, itemCount: ic, pageSize: ps } = paginationStateRef.current;
|
|
518
|
+
if (
|
|
519
|
+
pe &&
|
|
520
|
+
!nm &&
|
|
521
|
+
!isLoadingMore.current &&
|
|
522
|
+
scrollEl.scrollHeight - scrollEl.scrollTop - scrollEl.clientHeight < LOAD_MORE_THRESHOLD
|
|
523
|
+
) {
|
|
458
524
|
isLoadingMore.current = true;
|
|
459
|
-
setItemCount?.((
|
|
525
|
+
setItemCount?.((ic ?? ps) + ps);
|
|
460
526
|
}
|
|
461
|
-
}
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
527
|
+
};
|
|
528
|
+
|
|
529
|
+
const onScroll = () => {
|
|
530
|
+
if (rafId === null) rafId = requestAnimationFrame(update);
|
|
531
|
+
};
|
|
532
|
+
|
|
533
|
+
scrollEl.addEventListener("scroll", onScroll, { passive: true });
|
|
534
|
+
const ro = new ResizeObserver(() => update());
|
|
535
|
+
ro.observe(scrollEl);
|
|
536
|
+
update(); // initial measurement
|
|
537
|
+
|
|
538
|
+
return () => {
|
|
539
|
+
scrollEl.removeEventListener("scroll", onScroll);
|
|
540
|
+
ro.disconnect();
|
|
541
|
+
if (rafId !== null) cancelAnimationFrame(rafId);
|
|
542
|
+
};
|
|
543
|
+
}, [setItemCount]); // stable deps only — mutable state via refs
|
|
544
|
+
|
|
545
|
+
// Compute the visible window of rows
|
|
546
|
+
const totalHeight = data.length * estimatedRowHeight;
|
|
547
|
+
const startIndex = Math.max(0, Math.floor(effectiveScrollTop / estimatedRowHeight) - OVERSCAN_COUNT);
|
|
548
|
+
const endIndex = Math.min(
|
|
549
|
+
data.length,
|
|
550
|
+
Math.ceil((effectiveScrollTop + viewportHeight) / estimatedRowHeight) + OVERSCAN_COUNT
|
|
551
|
+
);
|
|
552
|
+
const visibleData = data.slice(startIndex, endIndex);
|
|
553
|
+
const offsetY = startIndex * estimatedRowHeight;
|
|
554
|
+
|
|
555
|
+
// Footer height for loading/end indicators
|
|
556
|
+
const footerHeight = dataLoading ? 48 : (!dataLoading && noMoreToLoad && data.length > 0) ? 32 : 0;
|
|
465
557
|
|
|
466
558
|
return (
|
|
467
559
|
<div
|
|
@@ -479,9 +571,11 @@ export function EntityCollectionListView<M extends Record<string, unknown> = Rec
|
|
|
479
571
|
</Typography>
|
|
480
572
|
</div>
|
|
481
573
|
) : isInitialLoading ? (
|
|
482
|
-
<
|
|
574
|
+
<div className="flex items-center justify-center py-12 px-8">
|
|
575
|
+
<CircularProgress size="small"/>
|
|
576
|
+
</div>
|
|
483
577
|
) : isEmpty ? (
|
|
484
|
-
<div className="flex items-center justify-center
|
|
578
|
+
<div className="flex items-center justify-center py-12 px-8">
|
|
485
579
|
{emptyComponent ?? (
|
|
486
580
|
<Typography variant="label" color="secondary">
|
|
487
581
|
No entries found
|
|
@@ -489,55 +583,64 @@ export function EntityCollectionListView<M extends Record<string, unknown> = Rec
|
|
|
489
583
|
)}
|
|
490
584
|
</div>
|
|
491
585
|
) : (
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
586
|
+
/* Spacer with total height — no internal scroll.
|
|
587
|
+
The nearest scrollable ancestor provides the scrollbar. */
|
|
588
|
+
<div style={{ height: totalHeight + footerHeight, position: "relative" }}>
|
|
589
|
+
{/* Windowed rows */}
|
|
590
|
+
<div style={{ position: "absolute", top: offsetY, left: 0, right: 0 }}>
|
|
591
|
+
{visibleData.map((entity, i) => {
|
|
592
|
+
const actualIndex = startIndex + i;
|
|
593
|
+
const isLast = actualIndex === data.length - 1;
|
|
594
|
+
return (
|
|
595
|
+
<div
|
|
596
|
+
key={entity.id}
|
|
597
|
+
style={{ height: estimatedRowHeight }}
|
|
598
|
+
className={cls(
|
|
599
|
+
!isLast && "border-b",
|
|
600
|
+
!isLast && defaultBorderMixin
|
|
601
|
+
)}
|
|
602
|
+
>
|
|
603
|
+
<ListRow
|
|
604
|
+
entity={entity}
|
|
605
|
+
collection={resolvedCollection}
|
|
606
|
+
onClick={handleEntityClick}
|
|
607
|
+
selected={isEntitySelected(entity)}
|
|
608
|
+
highlighted={isEntityHighlighted(entity)}
|
|
609
|
+
onSelectionChange={handleSelectionChange}
|
|
610
|
+
selectionEnabled={selectionEnabled}
|
|
611
|
+
columns={visibleColumns}
|
|
612
|
+
slotKeys={slotKeys}
|
|
613
|
+
rowClasses={rowClasses}
|
|
614
|
+
showImage={showImage}
|
|
615
|
+
size={size}
|
|
616
|
+
isLast={isLast}
|
|
617
|
+
isActive={selectedEntityId !== undefined && entity.id === selectedEntityId}
|
|
618
|
+
/>
|
|
619
|
+
</div>
|
|
620
|
+
);
|
|
621
|
+
})}
|
|
622
|
+
</div>
|
|
527
623
|
|
|
624
|
+
{/* Loading / end indicators pinned at the bottom */}
|
|
528
625
|
{dataLoading && (
|
|
529
|
-
<div
|
|
626
|
+
<div
|
|
627
|
+
className="flex items-center justify-center py-3"
|
|
628
|
+
style={{ position: "absolute", top: totalHeight, left: 0, right: 0 }}
|
|
629
|
+
>
|
|
530
630
|
<CircularProgress size="small"/>
|
|
531
631
|
</div>
|
|
532
632
|
)}
|
|
533
633
|
{!dataLoading && noMoreToLoad && data.length > 0 && (
|
|
534
|
-
<div
|
|
634
|
+
<div
|
|
635
|
+
className="flex items-center justify-center py-2 dark:bg-surface-900"
|
|
636
|
+
style={{ position: "absolute", top: totalHeight, left: 0, right: 0 }}
|
|
637
|
+
>
|
|
535
638
|
<Typography variant="caption" color="secondary">
|
|
536
639
|
All {data.length} entries loaded
|
|
537
640
|
</Typography>
|
|
538
641
|
</div>
|
|
539
642
|
)}
|
|
540
|
-
|
|
643
|
+
</div>
|
|
541
644
|
)}
|
|
542
645
|
</div>
|
|
543
646
|
);
|
|
@@ -617,11 +720,11 @@ const ListRow = React.memo(function ListRow<M extends Record<string, unknown>>({
|
|
|
617
720
|
"flex items-center gap-4 cursor-pointer group transition-colors duration-200 relative h-full",
|
|
618
721
|
rowClasses,
|
|
619
722
|
isActive
|
|
620
|
-
? "bg-surface-accent-100 dark:bg-surface-accent-950 hover:bg-surface-accent-200 dark:hover:bg-surface-accent-
|
|
723
|
+
? "bg-surface-accent-100 dark:bg-surface-accent-950 hover:bg-surface-accent-200 dark:hover:bg-surface-accent-950"
|
|
621
724
|
: selected
|
|
622
|
-
? "bg-
|
|
725
|
+
? "bg-surface-accent-50 dark:bg-surface-accent-900 hover:bg-surface-accent-100 dark:hover:bg-surface-accent-950"
|
|
623
726
|
: highlighted
|
|
624
|
-
? "bg-surface-accent-50 dark:bg-surface-accent-
|
|
727
|
+
? "bg-surface-accent-50 dark:bg-surface-accent-900 hover:bg-surface-50 dark:hover:bg-surface-800/40"
|
|
625
728
|
: "bg-white dark:bg-surface-900 hover:bg-surface-50 dark:hover:bg-surface-800/40"
|
|
626
729
|
)}
|
|
627
730
|
onClick={handleClick}
|