@rebasepro/admin 0.13.0 → 0.13.1-canary.g18cfeb7
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-CUFXevVg.js} +3 -3
- package/dist/{CollectionEditorDialog-CjXkWZ64.js.map → CollectionEditorDialog-CUFXevVg.js.map} +1 -1
- package/dist/{PropertyEditView-W8I3YV89.js → PropertyEditView-DDUC7giL.js} +2 -2
- package/dist/{PropertyEditView-W8I3YV89.js.map → PropertyEditView-DDUC7giL.js.map} +1 -1
- package/dist/{RouterCollectionsStudioView-BsLTNoqX.js → RouterCollectionsStudioView-vzS848Gf.js} +9 -13
- package/dist/RouterCollectionsStudioView-vzS848Gf.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/CollectionViewBinding/CollectionViewBinding.d.ts +16 -0
- package/dist/components/CollectionViewBinding/SlotValue.d.ts +63 -0
- 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/{export-J79rMoNj.js → export-5bl2EoSs.js} +2 -2
- package/dist/{export-J79rMoNj.js.map → export-5bl2EoSs.js.map} +1 -1
- 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-BuKKAhoY.js} +4 -4
- package/dist/{history-D7L_JlLO.js.map → history-BuKKAhoY.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-BhdWs34H.js} +3 -3
- package/dist/{import-BvYl--U4.js.map → import-BhdWs34H.js.map} +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +152 -57
- 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-GiwPgxnL.js → util-DNG7iK0w.js} +3756 -2300
- package/dist/util-DNG7iK0w.js.map +1 -0
- package/package.json +9 -9
- package/src/collection_editor/serializable_types.ts +1 -0
- package/src/collection_editor/ui/collection_editor/CollectionsStudioView.tsx +9 -11
- package/src/components/AdditionalFieldValue.tsx +68 -0
- 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/CollectionViewBinding/BoardCardBinding.tsx +26 -27
- package/src/components/CollectionViewBinding/CollectionListViewBinding.tsx +723 -348
- package/src/components/CollectionViewBinding/CollectionViewActions.tsx +1 -1
- package/src/components/CollectionViewBinding/CollectionViewBinding.tsx +38 -8
- package/src/components/CollectionViewBinding/EntityCardBinding.tsx +23 -36
- package/src/components/CollectionViewBinding/SlotValue.tsx +156 -0
- package/src/components/CollectionViewBinding/SplitListCloseButton.tsx +23 -7
- package/src/components/CollectionViewBinding/SplitListShowButton.tsx +28 -0
- package/src/components/CollectionViewBinding/SplitListView.tsx +33 -2
- package/src/components/CollectionViewBinding/useBoardDataController.tsx +12 -3
- 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 +93 -56
- package/src/components/DrawerNavigationGroup.tsx +16 -39
- package/src/components/DrawerNavigationItem.tsx +44 -16
- package/src/components/EditViewBinding.tsx +215 -42
- 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 +342 -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 +15 -4
- package/src/components/SideDialogs.tsx +15 -3
- package/src/components/SidePanelBinding.tsx +122 -66
- package/src/components/UserSelector.tsx +15 -4
- 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_import/utils/data.ts +9 -1
- package/src/form/EntityForm.tsx +64 -52
- 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 +7 -1
- 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/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/number_format.ts +46 -0
- package/src/util/previews.ts +8 -55
- package/src/util/property_utils.tsx +6 -1
- package/dist/RouterCollectionsStudioView-BsLTNoqX.js.map +0 -1
- package/dist/util-GiwPgxnL.js.map +0 -1
|
@@ -10,11 +10,11 @@ import { CollectionViewBinding } from "./CollectionViewBinding/CollectionViewBin
|
|
|
10
10
|
import { EntityViewBinding } from "./EntityViewBinding";
|
|
11
11
|
import { EntityIdentityBar } from "./EntityIdentityBar";
|
|
12
12
|
import { SplitListCloseButton } from "./CollectionViewBinding/SplitListCloseButton";
|
|
13
|
+
import { SplitListShowButton } from "./CollectionViewBinding/SplitListShowButton";
|
|
13
14
|
import { EntityInspector, InspectorTab } from "./EntityInspector";
|
|
14
15
|
import { CircularProgressCenter, iconSize } from "@rebasepro/ui";
|
|
15
16
|
import {
|
|
16
17
|
Alert,
|
|
17
|
-
ArrowLeftIcon,
|
|
18
18
|
Button,
|
|
19
19
|
CenteredView,
|
|
20
20
|
cls,
|
|
@@ -34,6 +34,7 @@ import { ErrorBoundary } from "@rebasepro/ui";
|
|
|
34
34
|
import { ErrorView, createFormexStub, usePermissions, useTranslation, getIcon } from "@rebasepro/app";
|
|
35
35
|
|
|
36
36
|
import { removeInitialAndTrailingSlashes, resolveDefaultSelectedView } from "@rebasepro/app";
|
|
37
|
+
import { withViewMode } from "../util/view_mode";
|
|
37
38
|
import { resolvedSelectedEntityView } from "../util/resolutions";
|
|
38
39
|
import {
|
|
39
40
|
useCustomizationController,
|
|
@@ -45,9 +46,7 @@ import {
|
|
|
45
46
|
import { useUrlController } from "../hooks/navigation/contexts/UrlContext";
|
|
46
47
|
import { useCollectionRegistryController } from "../hooks/navigation/contexts/CollectionRegistryContext";
|
|
47
48
|
import { useNavigate } from "react-router";
|
|
48
|
-
import {
|
|
49
|
-
import { getEntityTitlePropertyKeyForEntity, isUserSelectProperty, resolveTitleToString } from "../util/previews";
|
|
50
|
-
import { getUserLabel, useResolvedUser } from "../hooks/useResolvedUsers";
|
|
49
|
+
import { useEntityDisplayTitle } from "../hooks/useEntityDisplayTitle";
|
|
51
50
|
|
|
52
51
|
|
|
53
52
|
import { MAIN_TAB_VALUE, JSON_TAB_VALUE, HISTORY_TAB_VALUE } from "../util/view_constants";
|
|
@@ -57,6 +56,8 @@ export type BarActionsParams = {
|
|
|
57
56
|
status: EntityStatus,
|
|
58
57
|
path: string,
|
|
59
58
|
entityId?: string | number;
|
|
59
|
+
/** Always false here — this view reads a record, it does not edit one. */
|
|
60
|
+
dirty: boolean;
|
|
60
61
|
};
|
|
61
62
|
|
|
62
63
|
export type OnTabChangeParams<M extends Record<string, unknown>> = {
|
|
@@ -75,6 +76,13 @@ export interface DetailViewBindingProps<M extends Record<string, unknown> = Reco
|
|
|
75
76
|
parentEntityIds: string[];
|
|
76
77
|
onTabChange?: (props: OnTabChangeParams<M>) => void;
|
|
77
78
|
onEditClick?: () => void;
|
|
79
|
+
/**
|
|
80
|
+
* Dismiss this record — the ✕ at the identity bar's trailing edge.
|
|
81
|
+
*
|
|
82
|
+
* Set by the layouts that own the record's place on screen but are not side
|
|
83
|
+
* dialogs: the split, where dismissing is navigating back to the collection.
|
|
84
|
+
*/
|
|
85
|
+
onCloseRequest?: () => void;
|
|
78
86
|
layout?: "side_panel" | "full_screen" | "split" | "dialog";
|
|
79
87
|
barActions?: (params: BarActionsParams) => React.ReactNode;
|
|
80
88
|
/**
|
|
@@ -82,6 +90,12 @@ export interface DetailViewBindingProps<M extends Record<string, unknown> = Reco
|
|
|
82
90
|
* of the breadcrumb — the side panel's close button.
|
|
83
91
|
*/
|
|
84
92
|
barActionsStart?: React.ReactNode;
|
|
93
|
+
/**
|
|
94
|
+
* Full screen only: bring the list back beside this record. Set when the
|
|
95
|
+
* record was opened out of a split collection, so folding the list away is
|
|
96
|
+
* reversible; it takes the place of the back arrow when present.
|
|
97
|
+
*/
|
|
98
|
+
onShowList?: () => void;
|
|
85
99
|
}
|
|
86
100
|
|
|
87
101
|
export function DetailViewBinding<M extends Record<string, unknown>>({
|
|
@@ -137,11 +151,13 @@ function DetailViewBindingInner<M extends Record<string, unknown>>({
|
|
|
137
151
|
parentEntityIds,
|
|
138
152
|
onTabChange,
|
|
139
153
|
onEditClick,
|
|
154
|
+
onCloseRequest,
|
|
140
155
|
entity,
|
|
141
156
|
dataLoading,
|
|
142
157
|
layout = "full_screen",
|
|
143
158
|
barActions,
|
|
144
|
-
barActionsStart
|
|
159
|
+
barActionsStart,
|
|
160
|
+
onShowList
|
|
145
161
|
}: DetailViewBindingProps<M> & {
|
|
146
162
|
entity?: Entity<M>,
|
|
147
163
|
dataLoading: boolean,
|
|
@@ -189,7 +205,12 @@ entityId }
|
|
|
189
205
|
}
|
|
190
206
|
}, [selectedTabProp, defaultSelectedView]);
|
|
191
207
|
|
|
192
|
-
|
|
208
|
+
// `hideFromEntityViews`, not `hideFromNavigation`: the latter governs the
|
|
209
|
+
// drawer. Filtering tabs on it defeated the introspection generator, which
|
|
210
|
+
// sets `hideFromNavigation` on owned-child tables precisely *because* "every
|
|
211
|
+
// inbound foreign key renders as a tab on the parent" — so the rows it
|
|
212
|
+
// deliberately kept out of the drawer became reachable from nowhere.
|
|
213
|
+
const childViews = getAdminEntityChildViews(collection).filter(v => !v.collection.hideFromEntityViews);
|
|
193
214
|
const subcollections = childViews.map(v => v.collection);
|
|
194
215
|
const subcollectionsCount = subcollections?.length ?? 0;
|
|
195
216
|
const customViews = collection.entityViews ?? [];
|
|
@@ -283,18 +304,13 @@ entityId }
|
|
|
283
304
|
const formViewConfig = (collection as AdminCollection<M> & { formView?: FormViewConfig<M> }).formView;
|
|
284
305
|
const FormViewBuilder = formViewConfig?.Builder ? resolveComponentRef<EntityCustomViewParams>(formViewConfig.Builder as ComponentRef<EntityCustomViewParams>) : null;
|
|
285
306
|
|
|
286
|
-
//
|
|
287
|
-
|
|
288
|
-
const
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
const title = titleUser
|
|
294
|
-
? getUserLabel(titleUser)
|
|
295
|
-
: (rawTitle !== undefined && rawTitle !== null
|
|
296
|
-
? resolveTitleToString(rawTitle)
|
|
297
|
-
: (collection.singularName ?? collection.name));
|
|
307
|
+
// The heading, resolved the one way — this used to be a copy of
|
|
308
|
+
// `useEntityDisplayTitle`'s body, and the two had already drifted.
|
|
309
|
+
const title = useEntityDisplayTitle({
|
|
310
|
+
collection,
|
|
311
|
+
entity: usedEntity,
|
|
312
|
+
status: "existing"
|
|
313
|
+
});
|
|
298
314
|
|
|
299
315
|
// Non-action custom views
|
|
300
316
|
const nonActionCustomViews = useMemo(() =>
|
|
@@ -380,20 +396,31 @@ entityId }
|
|
|
380
396
|
}, [onTabChange, path, entityId, collection]);
|
|
381
397
|
|
|
382
398
|
const propertyDetailView = () => {
|
|
383
|
-
// formView.Builder replaces the default property display
|
|
399
|
+
// formView.Builder replaces the default property display. It gets the
|
|
400
|
+
// centred column the record used to be given here; the default display
|
|
401
|
+
// now lays itself out, rail included.
|
|
384
402
|
if (FormViewBuilder && usedEntity) {
|
|
385
|
-
return <
|
|
386
|
-
<
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
403
|
+
return <div className={"flex-1 min-w-0 overflow-y-auto flex justify-center items-start"}>
|
|
404
|
+
<div className={cls(
|
|
405
|
+
"w-full max-w-3xl 2xl:max-w-4xl flex flex-col",
|
|
406
|
+
layout === "dialog"
|
|
407
|
+
? "pt-5 pb-12 px-6 sm:px-8"
|
|
408
|
+
: "pt-6 pb-16 px-5 sm:px-8"
|
|
409
|
+
)}>
|
|
410
|
+
<ErrorBoundary>
|
|
411
|
+
<Suspense fallback={<CircularProgressCenter />}>
|
|
412
|
+
<FormViewBuilder
|
|
413
|
+
collection={collection}
|
|
414
|
+
parentCollectionSlugs={parentCollectionSlugs}
|
|
415
|
+
parentEntityIds={parentEntityIds}
|
|
416
|
+
entity={usedEntity}
|
|
417
|
+
modifiedValues={usedEntity?.values}
|
|
418
|
+
formContext={readOnlyFormContext as FormContext<Record<string, unknown>>}
|
|
419
|
+
/>
|
|
420
|
+
</Suspense>
|
|
421
|
+
</ErrorBoundary>
|
|
422
|
+
</div>
|
|
423
|
+
</div>;
|
|
397
424
|
}
|
|
398
425
|
|
|
399
426
|
return (
|
|
@@ -402,6 +429,9 @@ entityId }
|
|
|
402
429
|
entity={usedEntity}
|
|
403
430
|
collection={collection}
|
|
404
431
|
path={path}
|
|
432
|
+
asPage
|
|
433
|
+
openEntityMode={layout}
|
|
434
|
+
formContext={readOnlyFormContext}
|
|
405
435
|
/>}
|
|
406
436
|
</>
|
|
407
437
|
);
|
|
@@ -458,24 +488,27 @@ entityId }
|
|
|
458
488
|
);
|
|
459
489
|
});
|
|
460
490
|
|
|
491
|
+
// The breadcrumb's destination, and the back arrow's when it renders. Both
|
|
492
|
+
// keep the view mode: a collection reached from one of its own records must
|
|
493
|
+
// come back as the user left it.
|
|
494
|
+
const collectionUrl = withViewMode(urlController.buildUrlCollectionPath(path));
|
|
495
|
+
|
|
461
496
|
// Full screen is the one layout with no way out of its own: a side panel and a
|
|
462
497
|
// dialog can be dismissed, and a split keeps the list beside it, but opening an
|
|
463
498
|
// entity full screen replaces the collection entirely and left browser Back as
|
|
464
499
|
// the only route back to it — which is not an affordance the page shows, and is
|
|
465
500
|
// wrong anyway once the user has moved between tabs inside the entity.
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
const shouldShowTopBar = Boolean(barActions) || hasAdditionalViews || layout === "side_panel" || layout === "dialog" || Boolean(backToCollectionButton);
|
|
501
|
+
//
|
|
502
|
+
// Unless the list can be unfolded back beside the record: that chevron
|
|
503
|
+
// leads to the same collection while keeping the record open, so it stands
|
|
504
|
+
// alone rather than beside an arrow going almost the same place.
|
|
505
|
+
//
|
|
506
|
+
// It goes to the bar's leading edge — where a back control reads as back,
|
|
507
|
+
// and where the edit view has always put the same arrow — rather than to
|
|
508
|
+
// the trailing edge among the record's own actions.
|
|
509
|
+
const backToCollection = layout === "full_screen" && !onShowList
|
|
510
|
+
? () => navigate(collectionUrl)
|
|
511
|
+
: undefined;
|
|
479
512
|
|
|
480
513
|
const fullScreenButton = !barActions && // Not in split: the list panel beside it carries the close control,
|
|
481
514
|
// and an expand button there competes with it.
|
|
@@ -508,49 +541,53 @@ entityId }
|
|
|
508
541
|
// Main content view. The title, the `path/id` chip and the empty band under
|
|
509
542
|
// them have moved into the identity bar, and the `w-80 2xl:w-96` rail that
|
|
510
543
|
// held a single Edit button is gone — the bar holds it now.
|
|
544
|
+
//
|
|
545
|
+
// The scroll now lives on the record's own column rather than on this row,
|
|
546
|
+
// so the metadata rail beside it stays put while the record scrolls — the
|
|
547
|
+
// same arrangement the form uses.
|
|
511
548
|
const mainView = <div
|
|
512
549
|
className={cls(
|
|
513
|
-
"flex-1 flex flex-row w-full
|
|
550
|
+
"flex-1 flex flex-row w-full min-h-0",
|
|
514
551
|
!mainViewVisible ? "hidden" : ""
|
|
515
552
|
)}>
|
|
516
|
-
|
|
517
|
-
"w-full max-w-3xl 2xl:max-w-4xl flex flex-col",
|
|
518
|
-
layout === "dialog"
|
|
519
|
-
? "pt-5 pb-12 px-6 sm:px-8"
|
|
520
|
-
: "pt-6 pb-16 px-5 sm:px-8"
|
|
521
|
-
)}>
|
|
522
|
-
{propertyDetailView()}
|
|
523
|
-
</div>
|
|
553
|
+
{propertyDetailView()}
|
|
524
554
|
</div>;
|
|
525
555
|
|
|
526
556
|
let result = <div className="relative flex flex-col h-full w-full bg-white dark:bg-surface-800">
|
|
527
557
|
|
|
528
558
|
<EntityIdentityBar
|
|
529
559
|
collection={collection as AdminCollection}
|
|
560
|
+
collectionUrl={layout === "full_screen" || layout === "split" ? collectionUrl : undefined}
|
|
530
561
|
title={title}
|
|
531
562
|
entityId={entityId}
|
|
532
563
|
status={"existing"}
|
|
533
564
|
dirty={false}
|
|
534
565
|
saving={false}
|
|
566
|
+
onBack={backToCollection}
|
|
567
|
+
onClose={onCloseRequest}
|
|
535
568
|
onInspect={includeJsonView ? () => setInspectorTab("json") : undefined}
|
|
536
569
|
onViewHistory={includeHistoryView ? () => setInspectorTab("history") : undefined}
|
|
570
|
+
externalLink={usedEntity
|
|
571
|
+
? customizationController?.entityLinkBuilder?.({ entity: usedEntity })
|
|
572
|
+
: undefined}
|
|
537
573
|
leading={<>
|
|
538
574
|
{barActionsStart}
|
|
539
575
|
{/* Split view: closing the list is opening this record
|
|
540
576
|
full screen, and the control for it belongs at this
|
|
541
|
-
bar's leading edge. */}
|
|
577
|
+
bar's leading edge. Full screen carries its mirror. */}
|
|
542
578
|
{layout === "split" && <SplitListCloseButton/>}
|
|
579
|
+
{layout === "full_screen" && onShowList && <SplitListShowButton onClick={onShowList}/>}
|
|
543
580
|
</>}
|
|
544
581
|
trailing={<>
|
|
545
582
|
{editButton}
|
|
546
583
|
{pluginActionsTop}
|
|
547
|
-
{backToCollectionButton}
|
|
548
584
|
{fullScreenButton}
|
|
549
585
|
{barActions?.({
|
|
550
586
|
path,
|
|
551
587
|
entityId,
|
|
552
588
|
values: usedEntity?.values ?? {},
|
|
553
|
-
status: "existing"
|
|
589
|
+
status: "existing",
|
|
590
|
+
dirty: false
|
|
554
591
|
})}
|
|
555
592
|
</>}
|
|
556
593
|
/>
|
|
@@ -28,11 +28,12 @@ export interface DrawerNavigationGroupProps {
|
|
|
28
28
|
*/
|
|
29
29
|
drawerOpen: boolean;
|
|
30
30
|
/**
|
|
31
|
-
*
|
|
31
|
+
* @deprecated No longer read. Entry tooltips are uncontrolled — they follow the
|
|
32
|
+
* pointer and focus, and are suppressed wherever the label is already visible.
|
|
32
33
|
*/
|
|
33
|
-
tooltipsOpen
|
|
34
|
+
tooltipsOpen?: boolean;
|
|
34
35
|
/**
|
|
35
|
-
*
|
|
36
|
+
* @deprecated No longer read. See {@link tooltipsOpen}.
|
|
36
37
|
*/
|
|
37
38
|
adminMenuOpen?: boolean;
|
|
38
39
|
/**
|
|
@@ -50,8 +51,10 @@ export interface DrawerNavigationGroupProps {
|
|
|
50
51
|
/**
|
|
51
52
|
* Lucide icon name for the group header, from `NavigationGroupMapping.icon`.
|
|
52
53
|
*
|
|
53
|
-
*
|
|
54
|
-
*
|
|
54
|
+
* It decorates the header and nothing else: the entries below are untouched and
|
|
55
|
+
* keep their own icons. An app that wants the categorised look — rows giving up
|
|
56
|
+
* their icons to indent under the group — builds it in its own drawer, by
|
|
57
|
+
* overriding `Shell.DrawerNavigationItem` and passing `indented`.
|
|
55
58
|
*/
|
|
56
59
|
icon?: string;
|
|
57
60
|
}
|
|
@@ -66,8 +69,6 @@ export function DrawerNavigationGroup({
|
|
|
66
69
|
collapsed,
|
|
67
70
|
onToggleCollapsed,
|
|
68
71
|
drawerOpen,
|
|
69
|
-
tooltipsOpen,
|
|
70
|
-
adminMenuOpen,
|
|
71
72
|
headerActions,
|
|
72
73
|
onItemClick,
|
|
73
74
|
hideHeader,
|
|
@@ -76,17 +77,13 @@ export function DrawerNavigationGroup({
|
|
|
76
77
|
const { t } = useTranslation();
|
|
77
78
|
const ResolvedDrawerNavigationItem = useComponentOverride("Shell.DrawerNavigationItem", DrawerNavigationItem);
|
|
78
79
|
|
|
79
|
-
//
|
|
80
|
-
//
|
|
81
|
-
//
|
|
82
|
-
//
|
|
83
|
-
//
|
|
84
|
-
//
|
|
85
|
-
// nothing to indent under, and collapsed to a rail the entry icons are the only
|
|
86
|
-
// affordance left to click.
|
|
80
|
+
// The icon decorates the header, and stops there. It used to also strip the
|
|
81
|
+
// entries of theirs and indent them, which made a per-app styling choice into
|
|
82
|
+
// framework behaviour: every project that labelled a group with an icon lost the
|
|
83
|
+
// icons on its rows, with nothing to turn it off. An app that wants that look
|
|
84
|
+
// now opts into it in its own drawer — see `indented` on
|
|
85
|
+
// {@link DrawerNavigationItem}.
|
|
87
86
|
const groupIcon = !hideHeader && drawerOpen ? getIcon(icon, undefined, undefined, "small") : undefined;
|
|
88
|
-
const categorised = Boolean(groupIcon);
|
|
89
|
-
const indentEntries = categorised;
|
|
90
87
|
return (
|
|
91
88
|
<div
|
|
92
89
|
className={"my-2 mx-2 flex flex-col"}
|
|
@@ -103,10 +100,7 @@ export function DrawerNavigationGroup({
|
|
|
103
100
|
<ChevronDownIcon
|
|
104
101
|
size={iconSize.small}
|
|
105
102
|
className={cls(
|
|
106
|
-
|
|
107
|
-
? "text-surface-500 dark:text-surface-400"
|
|
108
|
-
: "text-surface-400 dark:text-surface-400",
|
|
109
|
-
"transition-transform duration-200 mr-1",
|
|
103
|
+
"text-surface-400 dark:text-surface-400 transition-transform duration-200 mr-1",
|
|
110
104
|
collapsed ? "-rotate-90" : "rotate-0"
|
|
111
105
|
)}
|
|
112
106
|
/>
|
|
@@ -118,21 +112,7 @@ export function DrawerNavigationGroup({
|
|
|
118
112
|
<Typography
|
|
119
113
|
variant={"caption"}
|
|
120
114
|
color={"secondary"}
|
|
121
|
-
|
|
122
|
-
// itself. Without an icon this is byte-for-byte the original: the
|
|
123
|
-
// drawer is a flat list of same-weight rows, and a louder header
|
|
124
|
-
// would just compete with them.
|
|
125
|
-
//
|
|
126
|
-
// With one, the group becomes the thing you scan — its rows have
|
|
127
|
-
// given up their icons to indent beneath it — so the label steps up
|
|
128
|
-
// to match. At 11px in surface-400 it sat *below* the contrast of
|
|
129
|
-
// the rows it labels, which is backwards once it is the anchor.
|
|
130
|
-
className={cls(
|
|
131
|
-
"font-semibold uppercase flex-grow line-clamp-1",
|
|
132
|
-
categorised
|
|
133
|
-
? "text-[12px] tracking-wide text-surface-600 dark:text-surface-300"
|
|
134
|
-
: "text-[11px] tracking-wider text-surface-400 dark:text-surface-400"
|
|
135
|
-
)}
|
|
115
|
+
className="font-semibold text-[11px] uppercase tracking-wider flex-grow line-clamp-1 text-surface-400 dark:text-surface-400"
|
|
136
116
|
>
|
|
137
117
|
{(group || t("views_group"))}
|
|
138
118
|
</Typography>
|
|
@@ -158,13 +138,10 @@ export function DrawerNavigationGroup({
|
|
|
158
138
|
<ResolvedDrawerNavigationItem
|
|
159
139
|
key={entry.id}
|
|
160
140
|
icon={<IconForView collectionOrView={entry.collection ?? entry.view} size={"small"}/>}
|
|
161
|
-
tooltipsOpen={!collapsed && tooltipsOpen}
|
|
162
|
-
adminMenuOpen={adminMenuOpen}
|
|
163
141
|
drawerOpen={drawerOpen}
|
|
164
142
|
onClick={() => onItemClick?.(entry)}
|
|
165
143
|
url={entry.url}
|
|
166
144
|
name={entry.name}
|
|
167
|
-
indented={indentEntries}
|
|
168
145
|
/>
|
|
169
146
|
))}
|
|
170
147
|
</div>
|
|
@@ -3,30 +3,39 @@ import React from "react";
|
|
|
3
3
|
import { NavLink } from "react-router";
|
|
4
4
|
import { cls, Tooltip } from "@rebasepro/ui";
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
tooltipsOpen,
|
|
12
|
-
url,
|
|
13
|
-
onClick,
|
|
14
|
-
indented = false
|
|
15
|
-
}: {
|
|
6
|
+
/**
|
|
7
|
+
* Props of a drawer navigation row, named so an app overriding
|
|
8
|
+
* `Shell.DrawerNavigationItem` can type its wrapper against them.
|
|
9
|
+
*/
|
|
10
|
+
export type DrawerNavigationItemProps = {
|
|
16
11
|
icon: React.ReactElement,
|
|
17
12
|
name: string,
|
|
18
|
-
|
|
13
|
+
/** @deprecated No longer read: the tooltip is uncontrolled. */
|
|
14
|
+
tooltipsOpen?: boolean,
|
|
19
15
|
drawerOpen: boolean,
|
|
16
|
+
/** @deprecated No longer read: the tooltip is uncontrolled. */
|
|
20
17
|
adminMenuOpen?: boolean,
|
|
21
18
|
url: string,
|
|
22
19
|
onClick?: () => void,
|
|
23
20
|
/**
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
21
|
+
* Drop the row's icon and indent the label into the space it occupied, so the
|
|
22
|
+
* row reads as a child of the group header above it.
|
|
23
|
+
*
|
|
24
|
+
* Nothing in the framework sets this: the stock drawer always passes `false`,
|
|
25
|
+
* and an app that wants the categorised look opts into it by overriding
|
|
26
|
+
* `Shell.DrawerNavigationItem` and deciding for itself which rows get it.
|
|
27
27
|
*/
|
|
28
28
|
indented?: boolean,
|
|
29
|
-
}
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
export function DrawerNavigationItem({
|
|
32
|
+
name,
|
|
33
|
+
icon,
|
|
34
|
+
drawerOpen,
|
|
35
|
+
url,
|
|
36
|
+
onClick,
|
|
37
|
+
indented = false
|
|
38
|
+
}: DrawerNavigationItemProps) {
|
|
30
39
|
|
|
31
40
|
// Indented rows give up the icon rather than keeping it *and* indenting: a
|
|
32
41
|
// per-row icon repeated down a group is what flattens the hierarchy, since
|
|
@@ -40,6 +49,15 @@ export function DrawerNavigationItem({
|
|
|
40
49
|
{icon}
|
|
41
50
|
</div>;
|
|
42
51
|
|
|
52
|
+
const [wantsTooltip, setWantsTooltip] = React.useState(false);
|
|
53
|
+
|
|
54
|
+
// Radix will not report a close it never noticed: while the tooltip is masked
|
|
55
|
+
// the pointer can leave the row without `onOpenChange(false)` ever firing. So
|
|
56
|
+
// the stale `true` is dropped on the way *into* the masked state — otherwise
|
|
57
|
+
// it surfaces the moment the mask lifts, as a tooltip hanging over a rail the
|
|
58
|
+
// pointer left long ago.
|
|
59
|
+
if (drawerOpen && wantsTooltip) setWantsTooltip(false);
|
|
60
|
+
|
|
43
61
|
const listItem = <div>
|
|
44
62
|
<NavLink
|
|
45
63
|
onClick={onClick}
|
|
@@ -70,8 +88,18 @@ export function DrawerNavigationItem({
|
|
|
70
88
|
</NavLink>
|
|
71
89
|
</div>;
|
|
72
90
|
|
|
91
|
+
// Per row, and always controlled. The old shared flag could only say "every
|
|
92
|
+
// tooltip" or "none", and the state it needed was unreachable, so no entry
|
|
93
|
+
// tooltip ever opened — leaving a bare rail of unlabelled icons. Radix decides
|
|
94
|
+
// when this one wants to be open, from the pointer and from focus; the drawer
|
|
95
|
+
// only masks it once the label says the same thing.
|
|
96
|
+
//
|
|
97
|
+
// Masked rather than withheld: dropping `open`, or the title, would move the
|
|
98
|
+
// tooltip between controlled and uncontrolled as the drawer opens, which
|
|
99
|
+
// strands the old state — a tooltip left up over a pointer nowhere near it.
|
|
73
100
|
return <Tooltip
|
|
74
|
-
open={drawerOpen
|
|
101
|
+
open={!drawerOpen && wantsTooltip}
|
|
102
|
+
onOpenChange={setWantsTooltip}
|
|
75
103
|
side="right"
|
|
76
104
|
title={name}>
|
|
77
105
|
{listItem}
|