@rebasepro/admin 0.13.0 → 0.13.1-canary.g06dbe5b
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-CjXkWZ64.js → CollectionEditorDialog-C3DbFNqp.js} +3 -3
- package/dist/{CollectionEditorDialog-CjXkWZ64.js.map → CollectionEditorDialog-C3DbFNqp.js.map} +1 -1
- package/dist/{PropertyEditView-W8I3YV89.js → PropertyEditView-DFH8rXRN.js} +5 -5
- package/dist/PropertyEditView-DFH8rXRN.js.map +1 -0
- package/dist/{RouterCollectionsStudioView-BsLTNoqX.js → RouterCollectionsStudioView-DiUDOu4b.js} +9 -13
- package/dist/RouterCollectionsStudioView-DiUDOu4b.js.map +1 -0
- package/dist/collection_editor/serializable_types.d.ts +1 -0
- package/dist/collection_editor_ui.js +4 -4
- package/dist/components/AdditionalFieldValue.d.ts +25 -0
- package/dist/components/ClearFilterSortButton.d.ts +7 -1
- package/dist/components/CollectionViewBinding/CollectionViewBinding.d.ts +16 -0
- package/dist/components/CollectionViewBinding/EntityCardBinding.d.ts +3 -1
- package/dist/components/CollectionViewBinding/SearchExplanation.d.ts +48 -0
- package/dist/components/CollectionViewBinding/SearchHighlight.d.ts +101 -0
- package/dist/components/CollectionViewBinding/SlotValue.d.ts +63 -0
- package/dist/components/CollectionViewBinding/SortButton.d.ts +7 -1
- package/dist/components/CollectionViewBinding/SplitListCloseButton.d.ts +17 -4
- package/dist/components/CollectionViewBinding/SplitListShowButton.d.ts +12 -0
- package/dist/components/CollectionViewBinding/usePreviewSlots.d.ts +55 -5
- package/dist/components/DetailViewBinding.d.ts +15 -0
- package/dist/components/DrawerNavigationGroup.d.ts +9 -6
- package/dist/components/DrawerNavigationItem.d.ts +16 -6
- package/dist/components/EditViewBinding.d.ts +30 -1
- package/dist/components/EntityDisplayHeader.d.ts +53 -0
- package/dist/components/EntityIdentityBar.d.ts +43 -1
- package/dist/components/EntityViewBinding.d.ts +60 -6
- package/dist/components/RebaseLayout.d.ts +1 -1
- package/dist/components/RebaseShell.d.ts +1 -1
- package/dist/components/RelationSelector.d.ts +8 -1
- package/dist/components/UserSelector.d.ts +8 -1
- package/dist/components/app/Scaffold.d.ts +9 -3
- package/dist/components/index.d.ts +2 -0
- package/dist/data_export/export/export.d.ts +7 -0
- package/dist/data_export/export/fetch_export_data.d.ts +48 -0
- package/dist/data_export/export/index.d.ts +1 -0
- package/dist/data_import/utils/csv.d.ts +30 -0
- package/dist/data_import/utils/index.d.ts +2 -0
- package/dist/data_import/utils/save_entities.d.ts +38 -0
- package/dist/data_import/utils/transforms.d.ts +9 -0
- package/dist/{export-J79rMoNj.js → export-Bd5i6Uvm.js} +175 -39
- package/dist/export-Bd5i6Uvm.js.map +1 -0
- package/dist/form/components/FieldBlock.d.ts +58 -4
- package/dist/form/components/FormRail.d.ts +13 -0
- package/dist/form/components/FormSections.d.ts +15 -1
- package/dist/form/components/useRailVisible.d.ts +5 -4
- package/dist/form/form_utils.d.ts +38 -1
- package/dist/{history-D7L_JlLO.js → history-D9R6LUrH.js} +4 -4
- package/dist/{history-D7L_JlLO.js.map → history-D9R6LUrH.js.map} +1 -1
- package/dist/hooks/useEntityDisplay.d.ts +102 -0
- package/dist/hooks/useEntityDisplayTitle.d.ts +18 -9
- package/dist/{import-BvYl--U4.js → import-pt_j-0G3.js} +6 -5
- package/dist/import-pt_j-0G3.js.map +1 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.js +158 -60
- package/dist/index.js.map +1 -1
- package/dist/preview/compact_text.d.ts +23 -0
- package/dist/preview/components/BooleanPreview.d.ts +3 -1
- package/dist/preview/property_previews/ArrayOfStringsPreview.d.ts +1 -1
- package/dist/preview/property_previews/ArrayOneOfPreview.d.ts +1 -1
- package/dist/preview/property_previews/ArrayPropertyPreview.d.ts +1 -1
- package/dist/preview/property_previews/MapPropertyPreview.d.ts +1 -1
- package/dist/preview/property_previews/StringPropertyPreview.d.ts +1 -1
- package/dist/routes/RebaseRoute.d.ts +1 -1
- package/dist/types/components/PropertyPreviewProps.d.ts +19 -0
- package/dist/util/number_format.d.ts +14 -0
- package/dist/util/previews.d.ts +9 -2
- package/dist/util/view_constants.d.ts +24 -0
- package/dist/util/view_mode.d.ts +18 -0
- package/dist/{util-GiwPgxnL.js → util-nX_-JRuF.js} +3984 -1774
- package/dist/util-nX_-JRuF.js.map +1 -0
- package/package.json +10 -9
- package/src/collection_editor/serializable_types.ts +1 -0
- package/src/collection_editor/ui/collection_editor/CollectionsStudioView.tsx +9 -11
- package/src/collection_editor/ui/collection_editor/properties/MapPropertyField.tsx +1 -1
- package/src/collection_editor/ui/collection_editor/properties/advanced/AdvancedPropertyValidation.tsx +5 -2
- package/src/collection_editor/useLocalCollectionsConfigController.tsx +10 -3
- package/src/components/AdditionalFieldValue.tsx +68 -0
- package/src/components/ClearFilterSortButton.tsx +16 -7
- package/src/components/CollectionTableBinding/CollectionTableBinding.tsx +7 -8
- package/src/components/CollectionTableBinding/PropertyTableCell.tsx +2 -2
- package/src/components/CollectionTableBinding/column_utils.tsx +15 -6
- package/src/components/CollectionTableBinding/internal/popup_field/PopupFormField.tsx +2 -2
- package/src/components/CollectionTableBinding/table_bindings.tsx +7 -1
- package/src/components/CollectionViewBinding/BoardCardBinding.tsx +26 -27
- package/src/components/CollectionViewBinding/CollectionBoardViewBinding.tsx +13 -7
- package/src/components/CollectionViewBinding/CollectionCardViewBinding.tsx +2 -1
- package/src/components/CollectionViewBinding/CollectionListViewBinding.tsx +822 -354
- package/src/components/CollectionViewBinding/CollectionViewActions.tsx +1 -1
- package/src/components/CollectionViewBinding/CollectionViewBinding.tsx +51 -12
- package/src/components/CollectionViewBinding/CollectionViewStartActions.tsx +9 -0
- package/src/components/CollectionViewBinding/EntityCardBinding.tsx +51 -39
- package/src/components/CollectionViewBinding/SearchExplanation.tsx +106 -0
- package/src/components/CollectionViewBinding/SearchHighlight.tsx +311 -0
- package/src/components/CollectionViewBinding/SlotValue.tsx +156 -0
- package/src/components/CollectionViewBinding/SortButton.tsx +16 -4
- package/src/components/CollectionViewBinding/SplitListCloseButton.tsx +23 -7
- package/src/components/CollectionViewBinding/SplitListShowButton.tsx +28 -0
- package/src/components/CollectionViewBinding/SplitListView.tsx +47 -7
- package/src/components/CollectionViewBinding/ViewModeToggle.tsx +10 -5
- package/src/components/CollectionViewBinding/useBoardDataController.tsx +23 -6
- package/src/components/CollectionViewBinding/usePreviewSlots.ts +183 -64
- package/src/components/CollectionViewBinding/utils.ts +7 -4
- package/src/components/DefaultDrawer.tsx +19 -23
- package/src/components/DetailViewBinding.tsx +109 -62
- package/src/components/DrawerNavigationGroup.tsx +16 -39
- package/src/components/DrawerNavigationItem.tsx +44 -16
- package/src/components/EditViewBinding.tsx +233 -50
- package/src/components/EntityDisplayHeader.tsx +170 -0
- package/src/components/EntityIdentityBar.tsx +163 -23
- package/src/components/EntityInspector.tsx +24 -6
- package/src/components/EntityPreviewBinding.tsx +58 -18
- package/src/components/EntityViewBinding.tsx +344 -41
- package/src/components/InlineEntityPreview.tsx +10 -10
- package/src/components/RebaseLayout.tsx +2 -2
- package/src/components/RebaseShell.tsx +2 -2
- package/src/components/ReferenceTable/SelectionTableBinding.tsx +4 -2
- package/src/components/RelationSelector.tsx +35 -10
- package/src/components/SideDialogs.tsx +15 -3
- package/src/components/SidePanelBinding.tsx +122 -66
- package/src/components/UserSelector.tsx +67 -13
- package/src/components/app/Scaffold.tsx +112 -10
- package/src/components/history/LastEditedByIndicator.tsx +7 -12
- package/src/components/index.ts +3 -0
- package/src/data_export/export/ExportCollectionAction.tsx +101 -48
- package/src/data_export/export/export.ts +63 -16
- package/src/data_export/export/fetch_export_data.ts +90 -0
- package/src/data_export/export/index.ts +1 -0
- package/src/data_import/components/ImportSaveInProgress.tsx +35 -36
- package/src/data_import/import/ImportCollectionAction.tsx +13 -2
- package/src/data_import/utils/csv.ts +153 -0
- package/src/data_import/utils/data.ts +16 -2
- package/src/data_import/utils/file_to_json.ts +51 -5
- package/src/data_import/utils/index.ts +2 -0
- package/src/data_import/utils/save_entities.ts +138 -0
- package/src/data_import/utils/transforms.ts +23 -0
- package/src/form/EntityForm.tsx +74 -53
- package/src/form/EntityFormBinding.tsx +24 -11
- package/src/form/PropertyFieldBinding.tsx +2 -2
- package/src/form/components/FieldBlock.tsx +80 -10
- package/src/form/components/FormRail.tsx +19 -2
- package/src/form/components/FormSections.tsx +32 -5
- package/src/form/components/StorageUploadProgress.tsx +9 -3
- package/src/form/components/useRailVisible.tsx +5 -4
- package/src/form/field_bindings/ArrayCustomShapedFieldBinding.tsx +2 -2
- package/src/form/field_bindings/BinaryFieldBinding.tsx +10 -5
- package/src/form/field_bindings/BlockFieldBinding.tsx +3 -3
- package/src/form/field_bindings/GeopointFieldBinding.tsx +23 -16
- package/src/form/field_bindings/MapFieldBinding.tsx +2 -2
- package/src/form/field_bindings/MarkdownEditorFieldBinding.tsx +13 -7
- package/src/form/field_bindings/MultiSelectFieldBinding.tsx +7 -1
- package/src/form/field_bindings/ReadOnlyFieldBinding.tsx +21 -2
- package/src/form/field_bindings/ReferenceFieldBinding.tsx +1 -1
- package/src/form/field_bindings/RelationFieldBinding.tsx +5 -2
- package/src/form/field_bindings/RepeatFieldBinding.tsx +3 -3
- package/src/form/field_bindings/StorageUploadFieldBinding.tsx +2 -2
- package/src/form/field_bindings/UserSelectFieldBinding.tsx +4 -1
- package/src/form/field_bindings/VectorFieldBinding.tsx +6 -4
- package/src/form/form_utils.ts +78 -0
- package/src/hooks/navigation/useBuildCollectionRegistryController.tsx +29 -5
- package/src/hooks/useEntityDisplay.tsx +262 -0
- package/src/hooks/useEntityDisplayTitle.tsx +27 -38
- package/src/index.ts +9 -1
- package/src/preview/PropertyPreview.tsx +25 -5
- package/src/preview/compact_text.ts +68 -0
- package/src/preview/components/BooleanPreview.tsx +5 -2
- package/src/preview/components/ReferencePreview.tsx +5 -8
- package/src/preview/components/RelationPreview.tsx +8 -7
- package/src/preview/components/StorageThumbnail.tsx +5 -2
- package/src/preview/property_previews/ArrayOfRelationsPreview.tsx +6 -3
- package/src/preview/property_previews/ArrayOfStringsPreview.tsx +26 -1
- package/src/preview/property_previews/ArrayOneOfPreview.tsx +10 -1
- package/src/preview/property_previews/ArrayPropertyPreview.tsx +10 -1
- package/src/preview/property_previews/MapPropertyPreview.tsx +52 -1
- package/src/preview/property_previews/NumberPropertyPreview.tsx +9 -1
- package/src/preview/property_previews/StringPropertyPreview.tsx +8 -5
- package/src/routes/RebaseRoute.tsx +62 -4
- package/src/types/components/PropertyPreviewProps.tsx +21 -0
- package/src/util/navigation_utils.ts +4 -2
- package/src/util/number_format.ts +46 -0
- package/src/util/previews.ts +8 -55
- package/src/util/property_utils.tsx +6 -1
- package/src/util/view_constants.ts +27 -0
- package/src/util/view_mode.ts +36 -0
- package/dist/PropertyEditView-W8I3YV89.js.map +0 -1
- package/dist/RouterCollectionsStudioView-BsLTNoqX.js.map +0 -1
- package/dist/export-J79rMoNj.js.map +0 -1
- package/dist/import-BvYl--U4.js.map +0 -1
- package/dist/util-GiwPgxnL.js.map +0 -1
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rebasepro/admin",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.13.
|
|
4
|
+
"version": "0.13.1-canary.g06dbe5b",
|
|
5
5
|
"description": "Rebase CMS — content management views, forms, and routing",
|
|
6
6
|
"funding": {
|
|
7
7
|
"url": "https://github.com/sponsors/rebaseco"
|
|
@@ -83,14 +83,14 @@
|
|
|
83
83
|
"react-dropzone": "^19.1.1",
|
|
84
84
|
"react-use-measure": "^2.1.7",
|
|
85
85
|
"zod": "^4.4.3",
|
|
86
|
-
"@rebasepro/admin-types": "0.13.
|
|
87
|
-
"@rebasepro/app": "0.13.
|
|
88
|
-
"@rebasepro/common": "0.13.
|
|
89
|
-
"@rebasepro/inference": "0.13.
|
|
90
|
-
"@rebasepro/
|
|
91
|
-
"@rebasepro/
|
|
92
|
-
"@rebasepro/
|
|
93
|
-
"@rebasepro/
|
|
86
|
+
"@rebasepro/admin-types": "0.13.1-canary.g06dbe5b",
|
|
87
|
+
"@rebasepro/app": "0.13.1-canary.g06dbe5b",
|
|
88
|
+
"@rebasepro/common": "0.13.1-canary.g06dbe5b",
|
|
89
|
+
"@rebasepro/inference": "0.13.1-canary.g06dbe5b",
|
|
90
|
+
"@rebasepro/ui": "0.13.1-canary.g06dbe5b",
|
|
91
|
+
"@rebasepro/utils": "0.13.1-canary.g06dbe5b",
|
|
92
|
+
"@rebasepro/forms": "0.13.1-canary.g06dbe5b",
|
|
93
|
+
"@rebasepro/types": "0.13.1-canary.g06dbe5b"
|
|
94
94
|
},
|
|
95
95
|
"peerDependencies": {
|
|
96
96
|
"react": ">=19.2.7",
|
|
@@ -107,6 +107,7 @@
|
|
|
107
107
|
"@types/react-dom": "^19.2.3",
|
|
108
108
|
"@types/react-measure": "^2.0.12",
|
|
109
109
|
"@vitejs/plugin-react": "^6.0.4",
|
|
110
|
+
"fast-check": "^4.9.0",
|
|
110
111
|
"jest": "^30.4.2",
|
|
111
112
|
"react-router": "^8.3.0",
|
|
112
113
|
"ts-jest": "^29.4.12",
|
|
@@ -155,6 +155,7 @@ export interface SerializableAdminRelationOptions extends SerializableAdminBaseO
|
|
|
155
155
|
fixedFilter?: FilterValues<string>;
|
|
156
156
|
includeId?: boolean;
|
|
157
157
|
includeEntityLink?: boolean;
|
|
158
|
+
renderInForm?: boolean;
|
|
158
159
|
}
|
|
159
160
|
|
|
160
161
|
/** JSON-serializable version of `AdminArrayOptions`. */
|
|
@@ -17,6 +17,7 @@ import { CollectionsConfigController } from "../../types/config_controller";
|
|
|
17
17
|
import { CollectionStudioView } from "./CollectionStudioView";
|
|
18
18
|
import type { CollectionEditorExtensionProps } from "../../extensibility_types";
|
|
19
19
|
import type { AdminCollection } from "@rebasepro/admin-types";
|
|
20
|
+
import { readStoredString, writeStoredString } from "@rebasepro/utils";
|
|
20
21
|
|
|
21
22
|
export interface CollectionsStudioViewProps extends CollectionEditorExtensionProps {
|
|
22
23
|
configController: CollectionsConfigController;
|
|
@@ -77,21 +78,18 @@ export function CollectionsStudioView({
|
|
|
77
78
|
const collections = collectionsProp ?? configController.collections ?? [];
|
|
78
79
|
|
|
79
80
|
// ── Sidebar sizing ──────────────────────────────────────────────────
|
|
81
|
+
// Checked, not just parsed: `parseFloat` answers NaN for anything it cannot
|
|
82
|
+
// read, and a NaN pane size lays the sidebar out to nothing — with the bad
|
|
83
|
+
// value still in storage on reload. `SplitListView.getSavedPanelSize` is the
|
|
84
|
+
// same read, done this way.
|
|
80
85
|
const [sidebarSize, setSidebarSize] = useState(() => {
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
} catch (e) {
|
|
85
|
-
return 25;
|
|
86
|
-
}
|
|
86
|
+
const saved = readStoredString("rebase_collections_editor_sidebar_size");
|
|
87
|
+
const parsed = saved === null ? NaN : parseFloat(saved);
|
|
88
|
+
return Number.isFinite(parsed) && parsed > 0 && parsed < 100 ? parsed : 25;
|
|
87
89
|
});
|
|
88
90
|
|
|
89
91
|
useEffect(() => {
|
|
90
|
-
|
|
91
|
-
localStorage.setItem("rebase_collections_editor_sidebar_size", sidebarSize.toString());
|
|
92
|
-
} catch (e) {
|
|
93
|
-
// ignore local storage error
|
|
94
|
-
}
|
|
92
|
+
writeStoredString("rebase_collections_editor_sidebar_size", sidebarSize.toString());
|
|
95
93
|
}, [sidebarSize]);
|
|
96
94
|
|
|
97
95
|
return (
|
|
@@ -111,7 +111,7 @@ export function MapPropertyField({ disabled, getData, allowDataInference, proper
|
|
|
111
111
|
position={"start"}
|
|
112
112
|
size={"medium"}
|
|
113
113
|
label={t("spread_children_as_columns")}
|
|
114
|
-
onValueChange={(v) => setFieldValue("
|
|
114
|
+
onValueChange={(v) => setFieldValue("admin.spreadChildren", v)}
|
|
115
115
|
value={values.admin?.spreadChildren ?? false}
|
|
116
116
|
/>
|
|
117
117
|
<FieldCaption>
|
|
@@ -7,8 +7,11 @@ export function AdvancedPropertyValidation({ disabled }: {
|
|
|
7
7
|
disabled: boolean
|
|
8
8
|
}) {
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
|
|
10
|
+
// `ui` was renamed to `admin` in 0.11. Nothing reads `ui.*` — the toggles did
|
|
11
|
+
// nothing — and the boot validator treats the old name as a removed key, so a
|
|
12
|
+
// save through here left a project that would not start.
|
|
13
|
+
const hideFromCollection = "admin.hideFromCollection";
|
|
14
|
+
const readOnly = "admin.readOnly";
|
|
12
15
|
|
|
13
16
|
return (
|
|
14
17
|
|
|
@@ -202,13 +202,18 @@ collectionData });
|
|
|
202
202
|
await request("/collection/delete", { collectionId: id });
|
|
203
203
|
},
|
|
204
204
|
|
|
205
|
+
// The follow-up write carries exactly one key, and `partial` is what says
|
|
206
|
+
// so. Read as a whole-collection save it deletes everything it does not
|
|
207
|
+
// mention — `securityRules` first, which then falls back to the
|
|
208
|
+
// directory default and widens who can read the collection.
|
|
205
209
|
saveProperty: async ({ path, propertyKey, property, newPropertiesOrder }: SavePropertyParams) => {
|
|
206
210
|
await request("/property/save", { collectionId: path,
|
|
207
211
|
propertyKey,
|
|
208
212
|
propertyConfig: property });
|
|
209
213
|
if (newPropertiesOrder) {
|
|
210
214
|
await request("/collection/save", { collectionId: path,
|
|
211
|
-
collectionData: { propertiesOrder: newPropertiesOrder }
|
|
215
|
+
collectionData: { propertiesOrder: newPropertiesOrder },
|
|
216
|
+
partial: true });
|
|
212
217
|
}
|
|
213
218
|
},
|
|
214
219
|
deleteProperty: async ({ path, propertyKey, newPropertiesOrder }: DeletePropertyParams) => {
|
|
@@ -216,14 +221,16 @@ collectionData: { propertiesOrder: newPropertiesOrder } });
|
|
|
216
221
|
propertyKey });
|
|
217
222
|
if (newPropertiesOrder) {
|
|
218
223
|
await request("/collection/save", { collectionId: path,
|
|
219
|
-
collectionData: { propertiesOrder: newPropertiesOrder }
|
|
224
|
+
collectionData: { propertiesOrder: newPropertiesOrder },
|
|
225
|
+
partial: true });
|
|
220
226
|
}
|
|
221
227
|
},
|
|
222
228
|
|
|
223
229
|
updatePropertiesOrder: async ({ collection, fullPath, newPropertiesOrder }: UpdatePropertiesOrderParams) => {
|
|
224
230
|
const collectionId = (collection as AdminCollection & { id?: string }).id || fullPath.split("/").pop();
|
|
225
231
|
await request("/collection/save", { collectionId,
|
|
226
|
-
collectionData: { propertiesOrder: newPropertiesOrder }
|
|
232
|
+
collectionData: { propertiesOrder: newPropertiesOrder },
|
|
233
|
+
partial: true });
|
|
227
234
|
},
|
|
228
235
|
updateKanbanColumnsOrder: async () => {
|
|
229
236
|
// Kanban order mapping logic can be added later if needed natively.
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import type { AdditionalFieldDelegate, RebaseContext } from "@rebasepro/admin-types";
|
|
2
|
+
import type { Entity } from "@rebasepro/types";
|
|
3
|
+
import React, { useEffect, useState } from "react";
|
|
4
|
+
|
|
5
|
+
export interface AdditionalFieldValueProps<M extends Record<string, unknown>> {
|
|
6
|
+
field: AdditionalFieldDelegate<M>;
|
|
7
|
+
entity: Entity<M>;
|
|
8
|
+
context: RebaseContext;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Renders an `additionalFields` entry that supplies a `value` rather than a
|
|
13
|
+
* `Builder`.
|
|
14
|
+
*
|
|
15
|
+
* `value` is typed `string | number | Promise<string | number>`, and the promise
|
|
16
|
+
* arm has been part of that type from the start — the CSV export awaits it
|
|
17
|
+
* correctly. Every *renderer* called `.toString()` on the result instead, so an
|
|
18
|
+
* async value drew the literal text `[object Promise]` in the cell: a shape the
|
|
19
|
+
* type promised, that worked in one of its two consumers, and failed silently in
|
|
20
|
+
* the other. That is what made the obvious port of a FireCMS column — one that
|
|
21
|
+
* fetches a title from a subcollection — look supported and not be.
|
|
22
|
+
*
|
|
23
|
+
* Three call sites had the same three lines copied between them (the table cell,
|
|
24
|
+
* the form and the read-only record), which is why the bug reached all three.
|
|
25
|
+
* Now there is one.
|
|
26
|
+
*/
|
|
27
|
+
export function AdditionalFieldValue<M extends Record<string, unknown>>({
|
|
28
|
+
field,
|
|
29
|
+
entity,
|
|
30
|
+
context
|
|
31
|
+
}: AdditionalFieldValueProps<M>) {
|
|
32
|
+
|
|
33
|
+
const [resolved, setResolved] = useState<string | number | undefined>(() => {
|
|
34
|
+
const value = field.value?.({ entity, context });
|
|
35
|
+
// A synchronous value is available on the first render, so it never
|
|
36
|
+
// flashes empty on its way in.
|
|
37
|
+
return isPromise(value) ? undefined : value;
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
useEffect(() => {
|
|
41
|
+
const value = field.value?.({ entity, context });
|
|
42
|
+
if (!isPromise(value)) {
|
|
43
|
+
setResolved(value);
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// The row may scroll out, or the record change, before this lands.
|
|
48
|
+
let live = true;
|
|
49
|
+
value.then(
|
|
50
|
+
result => {
|
|
51
|
+
if (live) setResolved(result);
|
|
52
|
+
},
|
|
53
|
+
error => {
|
|
54
|
+
if (live) setResolved(undefined);
|
|
55
|
+
console.warn(`[rebase] Additional field "${field.key}" failed to resolve:`, error);
|
|
56
|
+
}
|
|
57
|
+
);
|
|
58
|
+
return () => {
|
|
59
|
+
live = false;
|
|
60
|
+
};
|
|
61
|
+
}, [field, entity, context]);
|
|
62
|
+
|
|
63
|
+
return <>{resolved === undefined || resolved === null ? null : String(resolved)}</>;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function isPromise(value: unknown): value is Promise<string | number | undefined> {
|
|
67
|
+
return typeof (value as Promise<unknown> | undefined)?.then === "function";
|
|
68
|
+
}
|
|
@@ -1,13 +1,20 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { FilterXIcon, iconSize, IconButton, Tooltip } from "@rebasepro/ui";
|
|
2
2
|
import { EntityTableController } from "@rebasepro/admin-types";
|
|
3
3
|
import { useTranslation } from "@rebasepro/app";
|
|
4
4
|
|
|
5
5
|
export function ClearFilterSortButton({
|
|
6
6
|
tableController,
|
|
7
|
-
enabled
|
|
7
|
+
enabled,
|
|
8
|
+
compact
|
|
8
9
|
}: {
|
|
9
10
|
enabled: boolean;
|
|
10
|
-
tableController: EntityTableController
|
|
11
|
+
tableController: EntityTableController;
|
|
12
|
+
/**
|
|
13
|
+
* Toolbar showing icon-only controls (split view, or a layout too narrow
|
|
14
|
+
* for the filters button's label). Only decides the icon size, which
|
|
15
|
+
* follows that button — see `CollectionViewStartActions`.
|
|
16
|
+
*/
|
|
17
|
+
compact?: boolean;
|
|
11
18
|
}) {
|
|
12
19
|
const { t } = useTranslation();
|
|
13
20
|
|
|
@@ -29,17 +36,19 @@ export function ClearFilterSortButton({
|
|
|
29
36
|
}
|
|
30
37
|
return (
|
|
31
38
|
<Tooltip title={label}>
|
|
32
|
-
|
|
39
|
+
{/* An `IconButton`, like every other icon-only control in this
|
|
40
|
+
toolbar. As a `Button` it was a rounded-md box with text
|
|
41
|
+
padding sitting between two circular icon buttons. */}
|
|
42
|
+
<IconButton
|
|
33
43
|
size={"small"}
|
|
34
|
-
variant={"text"}
|
|
35
44
|
aria-label={label}
|
|
36
45
|
onClick={() => {
|
|
37
46
|
tableController.clearFilter?.();
|
|
38
47
|
tableController.setSortBy?.(undefined);
|
|
39
48
|
}}
|
|
40
49
|
>
|
|
41
|
-
<FilterXIcon size={iconSize.small}/>
|
|
42
|
-
</
|
|
50
|
+
<FilterXIcon size={compact ? iconSize.smallest : iconSize.small}/>
|
|
51
|
+
</IconButton>
|
|
43
52
|
</Tooltip>
|
|
44
53
|
);
|
|
45
54
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { AdditionalFieldDelegate, AdminCollection } from "@rebasepro/admin-types";
|
|
2
|
+
import { AdditionalFieldValue } from "../AdditionalFieldValue";
|
|
2
3
|
import React, { useCallback, useMemo, useRef } from "react";
|
|
3
4
|
import { Entity, User, Property } from "@rebasepro/types";
|
|
4
5
|
import { CollectionSize, RebaseContext } from "@rebasepro/admin-types";
|
|
@@ -16,7 +17,7 @@ import { propertiesToColumns } from "./column_utils";
|
|
|
16
17
|
import { ErrorView } from "@rebasepro/app";
|
|
17
18
|
import { SelectableTable } from "../SelectableTable/SelectableTable";
|
|
18
19
|
import { cls } from "@rebasepro/ui";
|
|
19
|
-
import { getRowHeight } from "@rebasepro/app";
|
|
20
|
+
import { getRowHeight, isDisabled } from "@rebasepro/app";
|
|
20
21
|
import { getValueInPath } from "@rebasepro/utils";
|
|
21
22
|
|
|
22
23
|
/**
|
|
@@ -146,7 +147,7 @@ export const CollectionTableBinding = function CollectionTableBinding<M extends
|
|
|
146
147
|
propertyKey,
|
|
147
148
|
entity
|
|
148
149
|
}) ?? columnCustom?.resolvedProperty;
|
|
149
|
-
if (!property
|
|
150
|
+
if (!property || !isDisabled(property)) {
|
|
150
151
|
disabled = false;
|
|
151
152
|
}
|
|
152
153
|
|
|
@@ -214,12 +215,10 @@ export const CollectionTableBinding = function CollectionTableBinding<M extends
|
|
|
214
215
|
|
|
215
216
|
const child: React.ReactNode = Builder
|
|
216
217
|
? <Builder entity={entity} context={context}/>
|
|
217
|
-
:
|
|
218
|
-
{additionalField
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
})?.toString()}
|
|
222
|
-
</>;
|
|
218
|
+
: <AdditionalFieldValue
|
|
219
|
+
field={additionalField}
|
|
220
|
+
entity={entity}
|
|
221
|
+
context={context as RebaseContext}/>;
|
|
223
222
|
|
|
224
223
|
return (
|
|
225
224
|
<EntityTableCell
|
|
@@ -18,7 +18,7 @@ import { EntityTableCellActions } from "./internal/EntityTableCellActions";
|
|
|
18
18
|
import { useSelectableTableController } from "../SelectableTable/SelectableTableContext";
|
|
19
19
|
import { useClearRestoreValue } from "../../form/useClearRestoreValue";
|
|
20
20
|
import { getRowHeight } from "@rebasepro/app";
|
|
21
|
-
import { isReadOnly } from "@rebasepro/app";
|
|
21
|
+
import { isDisabled, isReadOnly } from "@rebasepro/app";
|
|
22
22
|
import { TableRelationField } from "./fields/TableRelationField";
|
|
23
23
|
import { TableRelationSelectorField } from "./fields/TableRelationSelectorField";
|
|
24
24
|
|
|
@@ -95,7 +95,7 @@ export const PropertyTableCell = React.memo<PropertyTableCellProps<any>>(
|
|
|
95
95
|
const customPreview = Boolean(property.admin?.Preview);
|
|
96
96
|
const readOnlyProperty = isReadOnly(property);
|
|
97
97
|
const disabledTooltip: string | undefined = typeof property.admin?.disabled === "object" ? property.admin?.disabled.disabledMessage : undefined;
|
|
98
|
-
const disabled = readonly || disabledProp ||
|
|
98
|
+
const disabled = readonly || disabledProp || isDisabled(property);
|
|
99
99
|
|
|
100
100
|
const validation = useMemo(() => mapPropertyToZod({
|
|
101
101
|
property,
|
|
@@ -39,15 +39,24 @@ export function propertiesToColumns<M extends Record<string, unknown>>({ propert
|
|
|
39
39
|
const disabledFilter = Boolean(fixedFilter);
|
|
40
40
|
return Object.entries<Property>(properties)
|
|
41
41
|
.flatMap(([key, property]) => getColumnKeysForProperty(property, key))
|
|
42
|
-
.
|
|
42
|
+
.flatMap(({
|
|
43
43
|
key,
|
|
44
44
|
disabled
|
|
45
45
|
}) => {
|
|
46
|
-
const property = getResolvedPropertyInPath(properties, key) as Property;
|
|
47
|
-
|
|
48
|
-
|
|
46
|
+
const property = getResolvedPropertyInPath(properties, key) as Property | undefined;
|
|
47
|
+
// One unresolvable key used to throw, and this runs inside the
|
|
48
|
+
// memo that builds the table's columns — so a single bad column
|
|
49
|
+
// took down the header, the rows and the empty state together,
|
|
50
|
+
// leaving a blank pane with nothing to attribute it to. A column
|
|
51
|
+
// that cannot be resolved is one column the table cannot offer;
|
|
52
|
+
// say so and carry the rest. Same choice, and now the same
|
|
53
|
+
// behaviour, as `getSortablePropertyOptions` below.
|
|
54
|
+
if (!property) {
|
|
55
|
+
console.warn(`No property found in path "${key}" — skipping that column.`);
|
|
56
|
+
return [];
|
|
57
|
+
}
|
|
49
58
|
const filterable = filterableProperty(property, false, engine);
|
|
50
|
-
return {
|
|
59
|
+
return [{
|
|
51
60
|
key: key as string,
|
|
52
61
|
align: getTableCellAlignment(property),
|
|
53
62
|
icon: getIconForProperty(property, "small"),
|
|
@@ -63,7 +72,7 @@ export function propertiesToColumns<M extends Record<string, unknown>>({ propert
|
|
|
63
72
|
AdditionalHeaderWidget: AdditionalHeaderWidget
|
|
64
73
|
? ({ onHover }: { onHover: boolean }) => <AdditionalHeaderWidget property={property} propertyKey={key} onHover={onHover}/>
|
|
65
74
|
: undefined
|
|
66
|
-
} satisfies VirtualTableColumn;
|
|
75
|
+
} satisfies VirtualTableColumn];
|
|
67
76
|
});
|
|
68
77
|
}
|
|
69
78
|
|
|
@@ -16,7 +16,7 @@ import { getPropertyInPath } from "../../../../util/property_utils";
|
|
|
16
16
|
import { PropertyFieldBinding, zodToFormErrors } from "../../../../form";
|
|
17
17
|
import { useAuthController, useCustomizationController, useData, useRebaseContext } from "@rebasepro/app";
|
|
18
18
|
import type { OnCellValueChangeParams } from "@rebasepro/app";
|
|
19
|
-
import { isReadOnly } from "@rebasepro/app";
|
|
19
|
+
import { isDisabled, isReadOnly } from "@rebasepro/app";
|
|
20
20
|
import type { AdminCollection } from "@rebasepro/admin-types";
|
|
21
21
|
|
|
22
22
|
interface PopupFormFieldProps<M extends Record<string, unknown>> {
|
|
@@ -295,7 +295,7 @@ export function PopupFormFieldInternal<M extends Record<string, unknown>>({
|
|
|
295
295
|
const fieldProps: PropertyFieldBindingProps<M> | undefined = propertyKey && property
|
|
296
296
|
? {
|
|
297
297
|
propertyKey: propertyKey as string,
|
|
298
|
-
disabled: isSubmitting || isReadOnly(property) ||
|
|
298
|
+
disabled: isSubmitting || isReadOnly(property) || isDisabled(property),
|
|
299
299
|
property,
|
|
300
300
|
includeDescription: false,
|
|
301
301
|
underlyingValueHasChanged: false,
|
|
@@ -240,7 +240,13 @@ export function getTableBindingForProperty(
|
|
|
240
240
|
Component: RelationDialogBindingComponent,
|
|
241
241
|
allowScroll: false
|
|
242
242
|
};
|
|
243
|
-
} else {
|
|
243
|
+
} else if (selected) {
|
|
244
|
+
// Gated on `selected`, like the number and string editors above.
|
|
245
|
+
// This one mounts a live picker against the *target* collection,
|
|
246
|
+
// and the cell is rendered once per visible row: ungated, opening
|
|
247
|
+
// a table with one relation column fired a request — or, on the
|
|
248
|
+
// realtime path, a subscription — per row, for data nobody had
|
|
249
|
+
// asked to see. Unselected cells fall through to PropertyPreview.
|
|
244
250
|
return {
|
|
245
251
|
Component: RelationSelectorBindingComponent,
|
|
246
252
|
allowScroll: false
|
|
@@ -6,7 +6,8 @@ import { PropertyPreview } from "../../preview";
|
|
|
6
6
|
import { useAuthController, useCustomizationController } from "@rebasepro/app";
|
|
7
7
|
import { IconForView } from "@rebasepro/app";
|
|
8
8
|
import { BoardItemViewProps } from "@rebasepro/ui";
|
|
9
|
-
import { useCollectionSlotKeys,
|
|
9
|
+
import { useCollectionSlotKeys, useEntitySlots } from "./usePreviewSlots";
|
|
10
|
+
import { SlotValue, TagChips } from "./SlotValue";
|
|
10
11
|
import type { AdminCollection } from "@rebasepro/admin-types";
|
|
11
12
|
|
|
12
13
|
export type BoardCardBindingProps<M extends Record<string, unknown> = Record<string, unknown>> = BoardItemViewProps<Entity<M>> & {
|
|
@@ -46,13 +47,10 @@ function BoardCardBindingInner<M extends Record<string, unknown> = Record<string
|
|
|
46
47
|
customizationController.propertyConfigs
|
|
47
48
|
);
|
|
48
49
|
|
|
49
|
-
const slots =
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
slotKeys
|
|
54
|
-
),
|
|
55
|
-
[entity, collection, slotKeys]
|
|
50
|
+
const slots = useEntitySlots(
|
|
51
|
+
entity as Entity<Record<string, unknown>>,
|
|
52
|
+
collection as AdminCollection<Record<string, unknown>>,
|
|
53
|
+
slotKeys
|
|
56
54
|
);
|
|
57
55
|
|
|
58
56
|
const handleClick = useCallback((e: React.MouseEvent) => {
|
|
@@ -126,13 +124,7 @@ function BoardCardBindingInner<M extends Record<string, unknown> = Record<string
|
|
|
126
124
|
selectionEnabled && "group-hover/card:opacity-30",
|
|
127
125
|
selected && "opacity-0"
|
|
128
126
|
)}>
|
|
129
|
-
<
|
|
130
|
-
property={slots.image.property}
|
|
131
|
-
propertyKey={slots.image.propertyKey}
|
|
132
|
-
size="small"
|
|
133
|
-
value={slots.image.value}
|
|
134
|
-
fill={true}
|
|
135
|
-
/>
|
|
127
|
+
<SlotValue slot={slots.image} size="small" fill={true}/>
|
|
136
128
|
</div>
|
|
137
129
|
) : (
|
|
138
130
|
<div className={cls(
|
|
@@ -177,12 +169,7 @@ function BoardCardBindingInner<M extends Record<string, unknown> = Record<string
|
|
|
177
169
|
{/* Title slot */}
|
|
178
170
|
<div className="line-clamp-2 text-sm font-medium">
|
|
179
171
|
{slots.title ? (
|
|
180
|
-
<
|
|
181
|
-
propertyKey={slots.title.propertyKey}
|
|
182
|
-
value={slots.title.value}
|
|
183
|
-
property={slots.title.property}
|
|
184
|
-
size="small"
|
|
185
|
-
/>
|
|
172
|
+
<SlotValue slot={slots.title} size="small"/>
|
|
186
173
|
) : (
|
|
187
174
|
<span className="text-surface-500">{entity.id}</span>
|
|
188
175
|
)}
|
|
@@ -193,12 +180,7 @@ function BoardCardBindingInner<M extends Record<string, unknown> = Record<string
|
|
|
193
180
|
{typeof slots.subtitle.value === "string" ? (
|
|
194
181
|
<Markdown source={slots.subtitle.value} size="small" />
|
|
195
182
|
) : (
|
|
196
|
-
<
|
|
197
|
-
propertyKey={slots.subtitle.propertyKey}
|
|
198
|
-
value={slots.subtitle.value}
|
|
199
|
-
property={slots.subtitle.property}
|
|
200
|
-
size="small"
|
|
201
|
-
/>
|
|
183
|
+
<SlotValue slot={slots.subtitle} size="small"/>
|
|
202
184
|
)}
|
|
203
185
|
</div>
|
|
204
186
|
) : (
|
|
@@ -228,6 +210,23 @@ function BoardCardBindingInner<M extends Record<string, unknown> = Record<string
|
|
|
228
210
|
)}
|
|
229
211
|
</div>
|
|
230
212
|
)}
|
|
213
|
+
|
|
214
|
+
{/* Tags slot */}
|
|
215
|
+
{slots.tags && (
|
|
216
|
+
<div className="flex items-center gap-1 mt-1 flex-wrap">
|
|
217
|
+
<TagChips slot={slots.tags} max={3}/>
|
|
218
|
+
</div>
|
|
219
|
+
)}
|
|
220
|
+
|
|
221
|
+
{/* Date slot — a board card is a list row without the image, and
|
|
222
|
+
the row shows it. The status is deliberately not repeated
|
|
223
|
+
here: on a board grouped by it, the column header is already
|
|
224
|
+
saying it. */}
|
|
225
|
+
{slots.date && (
|
|
226
|
+
<div className="text-[10px] text-surface-500 dark:text-surface-400 mt-1">
|
|
227
|
+
{slots.date.formatted}
|
|
228
|
+
</div>
|
|
229
|
+
)}
|
|
231
230
|
</div>
|
|
232
231
|
</div>
|
|
233
232
|
</div>
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
|
|
2
2
|
import React, { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
3
|
-
import { Entity, EnumValueConfig } from "@rebasepro/types";
|
|
3
|
+
import { Entity, EnumValueConfig, MAX_LIST_LIMIT } from "@rebasepro/types";
|
|
4
4
|
import { EntityTableController, SelectionController, AdminCollection } from "@rebasepro/admin-types";
|
|
5
5
|
import { BoardCardBinding } from "./BoardCardBinding";
|
|
6
6
|
import {
|
|
@@ -376,13 +376,19 @@ parentEntityIds,
|
|
|
376
376
|
setBackfillLoading(true);
|
|
377
377
|
|
|
378
378
|
try {
|
|
379
|
-
// Fetch
|
|
380
|
-
//
|
|
381
|
-
//
|
|
382
|
-
//
|
|
383
|
-
//
|
|
379
|
+
// Fetch the documents to key (not relying on loaded entities). No
|
|
380
|
+
// `orderBy`: with no usable order key there is nothing to order by,
|
|
381
|
+
// and this is the same query the columns themselves run, so the
|
|
382
|
+
// keys are handed out in the order the board is showing — which is
|
|
383
|
+
// what the dialog promises.
|
|
384
|
+
//
|
|
385
|
+
// `MAX_LIST_LIMIT`, not a made-up 10 000: that is the most a single
|
|
386
|
+
// read serves, and asking for more is now refused outright rather
|
|
387
|
+
// than quietly answered with the first thousand. A board past the
|
|
388
|
+
// ceiling still gets only its first page backfilled — the same rows
|
|
389
|
+
// as before this became explicit — and wants a paged walk.
|
|
384
390
|
const allDocsRes = await dataClient.collection(fullPath).find({
|
|
385
|
-
limit:
|
|
391
|
+
limit: MAX_LIST_LIMIT
|
|
386
392
|
});
|
|
387
393
|
const allDocs = allDocsRes.data as Entity<M>[];
|
|
388
394
|
|
|
@@ -145,6 +145,7 @@ export function CollectionCardViewBinding<M extends Record<string, unknown> = Re
|
|
|
145
145
|
key={`${entity.path}_${entity.id}`}
|
|
146
146
|
entity={entity}
|
|
147
147
|
collection={collection}
|
|
148
|
+
searchString={tableController.searchString}
|
|
148
149
|
onClick={onClick as any}
|
|
149
150
|
selected={selected}
|
|
150
151
|
highlighted={highlighted}
|
|
@@ -152,7 +153,7 @@ export function CollectionCardViewBinding<M extends Record<string, unknown> = Re
|
|
|
152
153
|
selectionEnabled={selectionEnabled}
|
|
153
154
|
size={size}
|
|
154
155
|
/>
|
|
155
|
-
), [collection, selectionEnabled, size, handleRowSelectionChange, ResolvedEntityCard])}
|
|
156
|
+
), [collection, selectionEnabled, size, handleRowSelectionChange, ResolvedEntityCard, tableController.searchString])}
|
|
156
157
|
/>
|
|
157
158
|
);
|
|
158
159
|
}
|