@rebasepro/admin 0.13.0 → 0.13.1-canary.g394d868
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-D78xsXgL.js} +3 -3
- package/dist/{CollectionEditorDialog-CjXkWZ64.js.map → CollectionEditorDialog-D78xsXgL.js.map} +1 -1
- package/dist/{PropertyEditView-W8I3YV89.js → PropertyEditView-B88EC-1f.js} +2 -2
- package/dist/{PropertyEditView-W8I3YV89.js.map → PropertyEditView-B88EC-1f.js.map} +1 -1
- package/dist/{RouterCollectionsStudioView-BsLTNoqX.js → RouterCollectionsStudioView-B1Ws7h9l.js} +4 -4
- package/dist/{RouterCollectionsStudioView-BsLTNoqX.js.map → RouterCollectionsStudioView-B1Ws7h9l.js.map} +1 -1
- 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-BZhdWhkB.js} +2 -2
- package/dist/{export-J79rMoNj.js.map → export-BZhdWhkB.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-BPpt68CL.js} +4 -4
- package/dist/{history-D7L_JlLO.js.map → history-BPpt68CL.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-gRfemegK.js} +3 -3
- package/dist/{import-BvYl--U4.js.map → import-gRfemegK.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-BHq3VOjH.js} +9080 -7861
- package/dist/util-BHq3VOjH.js.map +1 -0
- package/package.json +9 -9
- package/src/collection_editor/serializable_types.ts +1 -0
- 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 +172 -112
- 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 +177 -54
- package/src/components/DefaultDrawer.tsx +19 -23
- package/src/components/DetailViewBinding.tsx +87 -55
- package/src/components/DrawerNavigationGroup.tsx +16 -39
- package/src/components/DrawerNavigationItem.tsx +44 -16
- package/src/components/EditViewBinding.tsx +211 -41
- package/src/components/EntityDisplayHeader.tsx +170 -0
- package/src/components/EntityIdentityBar.tsx +149 -23
- 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/index.ts +3 -0
- 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/util-GiwPgxnL.js.map +0 -1
|
@@ -1,14 +1,17 @@
|
|
|
1
1
|
import type { AdminCollection } from "@rebasepro/admin-types";
|
|
2
2
|
import type { EntityStatus } from "@rebasepro/types";
|
|
3
3
|
import React, { useState } from "react";
|
|
4
|
+
import { Link } from "react-router";
|
|
4
5
|
import { useTranslation } from "@rebasepro/app";
|
|
5
6
|
import {
|
|
6
7
|
Button,
|
|
7
8
|
CheckIcon,
|
|
9
|
+
ChevronDownIcon,
|
|
8
10
|
cls,
|
|
9
11
|
CodeIcon,
|
|
10
12
|
CopyIcon,
|
|
11
13
|
defaultBorderMixin,
|
|
14
|
+
ExternalLinkIcon,
|
|
12
15
|
HistoryIcon,
|
|
13
16
|
IconButton,
|
|
14
17
|
iconSize,
|
|
@@ -19,12 +22,22 @@ import {
|
|
|
19
22
|
MoreVerticalIcon,
|
|
20
23
|
Separator,
|
|
21
24
|
Tooltip,
|
|
22
|
-
Typography
|
|
25
|
+
Typography,
|
|
26
|
+
XIcon
|
|
23
27
|
} from "@rebasepro/ui";
|
|
24
28
|
|
|
25
29
|
export interface EntityIdentityBarProps {
|
|
26
30
|
/** Plural collection name, shown as the breadcrumb ahead of the title. */
|
|
27
31
|
collection: AdminCollection;
|
|
32
|
+
/**
|
|
33
|
+
* Where the breadcrumb points. Without it the collection name is inert text
|
|
34
|
+
* that still reads as a breadcrumb — worse than no breadcrumb at all, and
|
|
35
|
+
* the only way back once a layout drops its back arrow.
|
|
36
|
+
*
|
|
37
|
+
* Left unset by the overlays (side panel, dialog), where the collection is
|
|
38
|
+
* already underneath and navigating would dismiss the record instead.
|
|
39
|
+
*/
|
|
40
|
+
collectionUrl?: string;
|
|
28
41
|
/** Resolved record title, already guarded against showing a field default. */
|
|
29
42
|
title: string;
|
|
30
43
|
entityId?: string | number;
|
|
@@ -44,11 +57,47 @@ export interface EntityIdentityBarProps {
|
|
|
44
57
|
*/
|
|
45
58
|
onSaveAndClose?: () => void;
|
|
46
59
|
|
|
60
|
+
/**
|
|
61
|
+
* Where {@link onSaveAndClose} is offered.
|
|
62
|
+
*
|
|
63
|
+
* `"button"` — its own filled button, and Save demotes to text. For the
|
|
64
|
+
* overlays, where you opened the record to do one thing to it and closing is
|
|
65
|
+
* the dominant intent.
|
|
66
|
+
*
|
|
67
|
+
* `"menu"` — a `▾` welded to the Save button. For the split, where the list
|
|
68
|
+
* is right there and `j`/`k` walk from record to record: there, saving and
|
|
69
|
+
* *staying* is the common case, so it keeps the filled button and dismissing
|
|
70
|
+
* is one step further in. The gesture still exists, so muscle memory carried
|
|
71
|
+
* over from the side panel finds it.
|
|
72
|
+
*/
|
|
73
|
+
saveAndClosePlacement?: "button" | "menu";
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Dismiss this record, from the ✕ at the bar's trailing edge.
|
|
77
|
+
*
|
|
78
|
+
* Fenced off from Save by a separator: it is chrome, not the last item in
|
|
79
|
+
* the action row. The panel that supplies it decides what dismissing means —
|
|
80
|
+
* in the split it is a navigation, which the layout's unsaved-changes
|
|
81
|
+
* blocker already guards, so the ✕ cannot drop an edit without asking.
|
|
82
|
+
*/
|
|
83
|
+
onClose?: () => void;
|
|
84
|
+
|
|
47
85
|
onBack?: () => void;
|
|
48
86
|
onInspect?: () => void;
|
|
49
87
|
/** Opens the inspector straight on its history tab. */
|
|
50
88
|
onViewHistory?: () => void;
|
|
51
89
|
|
|
90
|
+
/**
|
|
91
|
+
* Where this record appears on the live site, from `entityLinkBuilder`.
|
|
92
|
+
*
|
|
93
|
+
* Here rather than above the record, where it used to be a single unlabelled
|
|
94
|
+
* icon alone on its own right-aligned row — the only thing between the bar
|
|
95
|
+
* and the first field, and the only control in the view that did not say
|
|
96
|
+
* what it was. It is a thing you do *to* a record, so it belongs with the
|
|
97
|
+
* others.
|
|
98
|
+
*/
|
|
99
|
+
externalLink?: string;
|
|
100
|
+
|
|
52
101
|
/**
|
|
53
102
|
* Actions performed *on* the record — copy, delete, whatever the collection
|
|
54
103
|
* adds — as menu items under the overflow button. They live here rather
|
|
@@ -81,6 +130,7 @@ export interface EntityIdentityBarProps {
|
|
|
81
130
|
*/
|
|
82
131
|
export function EntityIdentityBar({
|
|
83
132
|
collection,
|
|
133
|
+
collectionUrl,
|
|
84
134
|
title,
|
|
85
135
|
entityId,
|
|
86
136
|
status,
|
|
@@ -91,9 +141,12 @@ export function EntityIdentityBar({
|
|
|
91
141
|
saveDisabled,
|
|
92
142
|
hasErrors,
|
|
93
143
|
onSaveAndClose,
|
|
144
|
+
saveAndClosePlacement = "button",
|
|
145
|
+
onClose,
|
|
94
146
|
onBack,
|
|
95
147
|
onInspect,
|
|
96
148
|
onViewHistory,
|
|
149
|
+
externalLink,
|
|
97
150
|
recordActions,
|
|
98
151
|
pluginActions,
|
|
99
152
|
trailing,
|
|
@@ -108,7 +161,17 @@ export function EntityIdentityBar({
|
|
|
108
161
|
const closeLabel = status === "existing"
|
|
109
162
|
? t("save_and_close")
|
|
110
163
|
: status === "copy" ? t("create_copy_and_close") : t("create_and_close");
|
|
111
|
-
const hasMenu = Boolean(recordActions) || Boolean(onInspect) || Boolean(onViewHistory)
|
|
164
|
+
const hasMenu = Boolean(recordActions) || Boolean(onInspect) || Boolean(onViewHistory)
|
|
165
|
+
|| Boolean(externalLink);
|
|
166
|
+
|
|
167
|
+
// The two shapes "save and close" takes, so the Save button next to it knows
|
|
168
|
+
// whether it is still the primary action.
|
|
169
|
+
const saveAndCloseButton = Boolean(onSaveAndClose) && saveAndClosePlacement === "button";
|
|
170
|
+
const saveAndCloseMenu = Boolean(onSaveAndClose) && saveAndClosePlacement === "menu";
|
|
171
|
+
|
|
172
|
+
const saveTooltip = hasErrors
|
|
173
|
+
? (t("fix_errors_before_saving") ?? "Fix highlighted errors before saving")
|
|
174
|
+
: undefined;
|
|
112
175
|
|
|
113
176
|
return (
|
|
114
177
|
<div className={cls(
|
|
@@ -126,10 +189,21 @@ export function EntityIdentityBar({
|
|
|
126
189
|
</Tooltip>
|
|
127
190
|
)}
|
|
128
191
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
192
|
+
{collectionUrl
|
|
193
|
+
? <Link to={collectionUrl}
|
|
194
|
+
className={"visited:text-inherit dark:visited:text-inherit hidden sm:block"}>
|
|
195
|
+
<Typography variant={"caption"}
|
|
196
|
+
className={cls(
|
|
197
|
+
"text-text-disabled dark:text-text-disabled-dark whitespace-nowrap",
|
|
198
|
+
"hover:text-text-primary dark:hover:text-text-primary-dark transition-colors"
|
|
199
|
+
)}>
|
|
200
|
+
{collection.name} /
|
|
201
|
+
</Typography>
|
|
202
|
+
</Link>
|
|
203
|
+
: <Typography variant={"caption"}
|
|
204
|
+
className={"text-text-disabled dark:text-text-disabled-dark whitespace-nowrap hidden sm:block"}>
|
|
205
|
+
{collection.name} /
|
|
206
|
+
</Typography>}
|
|
133
207
|
|
|
134
208
|
<span className={"font-headers font-semibold text-[15px] tracking-tight truncate min-w-0"}
|
|
135
209
|
title={title}>
|
|
@@ -154,24 +228,52 @@ export function EntityIdentityBar({
|
|
|
154
228
|
)}
|
|
155
229
|
|
|
156
230
|
{onSave && (
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
231
|
+
// `rounded-lg overflow-hidden`: the two halves of the split
|
|
232
|
+
// button are square inside and the group carries the radius, so
|
|
233
|
+
// there is one control with a seam rather than two buttons that
|
|
234
|
+
// happen to touch. Their borders are `border-primary` over
|
|
235
|
+
// `bg-primary` — the same colour — so the seam has to be drawn.
|
|
236
|
+
<div className={"flex items-stretch rounded-lg overflow-hidden"}>
|
|
237
|
+
<Tooltip title={saveTooltip}>
|
|
238
|
+
<LoadingButton
|
|
239
|
+
// Where a separate close button carries it, closing
|
|
240
|
+
// is the dominant intent and takes the filled
|
|
241
|
+
// treatment. Under the `▾` the record is staying
|
|
242
|
+
// open, so Save keeps it.
|
|
243
|
+
variant={saveAndCloseButton ? "text" : "filled"}
|
|
244
|
+
color={"primary"}
|
|
245
|
+
size={"small"}
|
|
246
|
+
loading={saving && !saveAndCloseButton}
|
|
247
|
+
disabled={saveDisabled}
|
|
248
|
+
onClick={onSave}
|
|
249
|
+
className={saveAndCloseMenu ? "rounded-none" : undefined}>
|
|
250
|
+
{saveLabel}
|
|
251
|
+
</LoadingButton>
|
|
252
|
+
</Tooltip>
|
|
253
|
+
|
|
254
|
+
{saveAndCloseMenu && <>
|
|
255
|
+
<span className={"w-px self-stretch bg-white/25 z-10"} aria-hidden={true}/>
|
|
256
|
+
<Menu align={"end"}
|
|
257
|
+
trigger={
|
|
258
|
+
<Button variant={"filled"}
|
|
259
|
+
color={"primary"}
|
|
260
|
+
size={"small"}
|
|
261
|
+
disabled={saveDisabled}
|
|
262
|
+
aria-label={closeLabel}
|
|
263
|
+
className={"rounded-none px-1.5"}>
|
|
264
|
+
<ChevronDownIcon size={iconSize.smallest}/>
|
|
265
|
+
</Button>
|
|
266
|
+
}>
|
|
267
|
+
<MenuItem onClick={onSaveAndClose}>
|
|
268
|
+
<CheckIcon size={iconSize.smallest}/>
|
|
269
|
+
{closeLabel}
|
|
270
|
+
</MenuItem>
|
|
271
|
+
</Menu>
|
|
272
|
+
</>}
|
|
273
|
+
</div>
|
|
172
274
|
)}
|
|
173
275
|
|
|
174
|
-
{
|
|
276
|
+
{saveAndCloseButton && (
|
|
175
277
|
<LoadingButton variant={"filled"}
|
|
176
278
|
color={"primary"}
|
|
177
279
|
size={"small"}
|
|
@@ -189,6 +291,14 @@ export function EntityIdentityBar({
|
|
|
189
291
|
<MoreVerticalIcon size={iconSize.smallest}/>
|
|
190
292
|
</IconButton>
|
|
191
293
|
}>
|
|
294
|
+
{externalLink && (
|
|
295
|
+
<MenuItem onClick={() => window.open(externalLink, "_blank", "noopener,noreferrer")}>
|
|
296
|
+
<ExternalLinkIcon size={iconSize.smallest}/>
|
|
297
|
+
{t("open_in_live_site") ?? "Open in the live site"}
|
|
298
|
+
</MenuItem>
|
|
299
|
+
)}
|
|
300
|
+
{externalLink && (recordActions || onInspect || onViewHistory) &&
|
|
301
|
+
<Separator orientation={"horizontal"}/>}
|
|
192
302
|
{recordActions}
|
|
193
303
|
{recordActions && (onInspect || onViewHistory) && <Separator orientation={"horizontal"}/>}
|
|
194
304
|
{onViewHistory && (
|
|
@@ -207,6 +317,18 @@ export function EntityIdentityBar({
|
|
|
207
317
|
)}
|
|
208
318
|
|
|
209
319
|
{trailing}
|
|
320
|
+
|
|
321
|
+
{/* Last, and behind a rule. A ✕ that sits flush against Save reads
|
|
322
|
+
as the next item in the action row, and the two adjacent controls
|
|
323
|
+
are then "commit this edit" and "abandon it". */}
|
|
324
|
+
{onClose && <>
|
|
325
|
+
<Separator orientation={"vertical"} className={"h-5 mx-1 shrink-0"}/>
|
|
326
|
+
<Tooltip title={`${t("close")} · Esc`}>
|
|
327
|
+
<IconButton size={"small"} onClick={onClose} aria-label={t("close")}>
|
|
328
|
+
<XIcon size={iconSize.smallest}/>
|
|
329
|
+
</IconButton>
|
|
330
|
+
</Tooltip>
|
|
331
|
+
</>}
|
|
210
332
|
</div>
|
|
211
333
|
);
|
|
212
334
|
}
|
|
@@ -275,7 +397,11 @@ function IdChip({ value }: { value: string }) {
|
|
|
275
397
|
onClick={copy}
|
|
276
398
|
aria-label={`${t("copy_id")} ${value}`}
|
|
277
399
|
className={cls(
|
|
278
|
-
|
|
400
|
+
// `whitespace-nowrap`: the middle of the id is elided with a
|
|
401
|
+
// `…`, and a line break is allowed after one. In the dialog,
|
|
402
|
+
// where the bar is narrow and every button is on it, the
|
|
403
|
+
// chip broke across two lines inside a 52px row.
|
|
404
|
+
"hidden md:inline-flex items-center gap-1.5 shrink-0 whitespace-nowrap px-2 py-0.5 rounded-md",
|
|
279
405
|
"font-mono text-[11px] text-text-secondary dark:text-text-secondary-dark",
|
|
280
406
|
"bg-surface-accent-200/50 dark:bg-white/[0.055]",
|
|
281
407
|
"hover:bg-surface-accent-200/75 dark:hover:bg-white/[0.09] transition-colors"
|
|
@@ -25,7 +25,8 @@ import {
|
|
|
25
25
|
import { useAnalyticsController } from "@rebasepro/app";
|
|
26
26
|
import { IconForView } from "@rebasepro/app";
|
|
27
27
|
import { getPropertyInPath } from "../util/property_utils";
|
|
28
|
-
import {
|
|
28
|
+
import { resolveCollectionSlotKeys } from "./CollectionViewBinding/usePreviewSlots";
|
|
29
|
+
import { useEntityTitle } from "../hooks/useEntityDisplay";
|
|
29
30
|
import { getValueInPath } from "@rebasepro/utils";
|
|
30
31
|
import { useCollectionRegistryController } from "../hooks/navigation/contexts/CollectionRegistryContext";
|
|
31
32
|
import { useSidePanel } from "../hooks/useSidePanel";
|
|
@@ -87,10 +88,33 @@ export function EntityPreviewBindingData({
|
|
|
87
88
|
|
|
88
89
|
const collection = collectionProp ?? collectionRegistryController.getCollection(entity.path);
|
|
89
90
|
|
|
91
|
+
// Above the `!collection` return, because hooks are. `useEntityTitle`
|
|
92
|
+
// tolerates a missing collection for exactly this reason.
|
|
93
|
+
const title = useEntityTitle({
|
|
94
|
+
collection,
|
|
95
|
+
entity
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
// What the card says under the title.
|
|
99
|
+
//
|
|
100
|
+
// A stated list wins outright — `previewProperties` is the one place a
|
|
101
|
+
// developer says "show these", and nothing here is allowed to second-guess
|
|
102
|
+
// it. Failing that, the card fills the same slots as every other surface
|
|
103
|
+
// that renders one record in a row: a supporting line and a status. It used
|
|
104
|
+
// to take the first few properties in declaration order instead, which is
|
|
105
|
+
// how an author card ended up rendering an entire Markdown biography.
|
|
106
|
+
const statedKeys = previewKeys ?? (collection?.previewProperties as string[] | undefined);
|
|
90
107
|
const listProperties = useMemo(() => {
|
|
91
108
|
if (!collection) return [];
|
|
92
|
-
|
|
93
|
-
|
|
109
|
+
if (statedKeys && statedKeys.length > 0) {
|
|
110
|
+
return statedKeys.filter(key => getPropertyInPath(collection.properties, key));
|
|
111
|
+
}
|
|
112
|
+
const slotKeys = resolveCollectionSlotKeys(
|
|
113
|
+
collection as AdminCollection<Record<string, unknown>>,
|
|
114
|
+
authController,
|
|
115
|
+
customizationController.propertyConfigs);
|
|
116
|
+
return [slotKeys.subtitleKey, slotKeys.statusKey].filter(Boolean) as string[];
|
|
117
|
+
}, [statedKeys, collection, authController, customizationController.propertyConfigs]);
|
|
94
118
|
|
|
95
119
|
if (!collection) {
|
|
96
120
|
return (
|
|
@@ -100,7 +124,7 @@ export function EntityPreviewBindingData({
|
|
|
100
124
|
);
|
|
101
125
|
}
|
|
102
126
|
|
|
103
|
-
const titleProperty = includeTitle ?
|
|
127
|
+
const titleProperty = includeTitle ? title.source?.key : undefined;
|
|
104
128
|
const imagePropertyKey = includeImage ? getEntityImagePreviewPropertyKey(collection) : undefined;
|
|
105
129
|
const imageProperty = imagePropertyKey ? collection.properties[imagePropertyKey] : undefined;
|
|
106
130
|
const ofProp = imageProperty && "of" in imageProperty ? imageProperty.of : undefined;
|
|
@@ -119,7 +143,10 @@ export function EntityPreviewBindingData({
|
|
|
119
143
|
// relation among the preview properties collapses to one line instead
|
|
120
144
|
// of nesting another bordered card with its own id and action button.
|
|
121
145
|
<NestedEntityPreviewBoundary>
|
|
122
|
-
|
|
146
|
+
{/* `overflow-hidden`: the image slot is a fixed box, and a thumbnail
|
|
147
|
+
that fails to load renders a "file not found" message sized for a
|
|
148
|
+
full preview — which spilled across the title beside it. */}
|
|
149
|
+
<div className={cls("flex shrink-0 overflow-hidden", {
|
|
123
150
|
"w-6 h-6 mx-1 my-0.5": size === "small" || size === "smallest",
|
|
124
151
|
"w-8 h-8 ml-1 mr-2 m-2 self-start": size === "medium",
|
|
125
152
|
"w-10 h-10 ml-2 mr-2 m-2 self-start": size === "large"
|
|
@@ -148,20 +175,24 @@ export function EntityPreviewBindingData({
|
|
|
148
175
|
</div>
|
|
149
176
|
: <Skeleton/>)}
|
|
150
177
|
|
|
151
|
-
{
|
|
178
|
+
{/* A title that came from a property keeps that property's own
|
|
179
|
+
rendering — an enum stays its coloured chip, a relation stays
|
|
180
|
+
a link. A computed one is text, because that is all a
|
|
181
|
+
resolver returns. */}
|
|
182
|
+
{includeTitle && (titleProperty || title.value) && (
|
|
152
183
|
<div
|
|
153
184
|
className={"truncate my-0.5 text-sm font-medium text-text-primary dark:text-text-primary-dark"}>
|
|
154
|
-
{
|
|
155
|
-
|
|
185
|
+
{!entity && titleProperty
|
|
186
|
+
? <SkeletonPropertyComponent
|
|
187
|
+
property={getPropertyInPath(collection.properties, titleProperty) as Property}
|
|
188
|
+
size={"medium"}/>
|
|
189
|
+
: titleProperty
|
|
156
190
|
? <PropertyPreview
|
|
157
|
-
propertyKey={titleProperty
|
|
158
|
-
value={
|
|
159
|
-
property={collection.properties
|
|
160
|
-
size={"medium"}/>
|
|
161
|
-
: <SkeletonPropertyComponent
|
|
162
|
-
property={collection.properties[titleProperty as string] as Property}
|
|
191
|
+
propertyKey={titleProperty}
|
|
192
|
+
value={title.source?.value as never}
|
|
193
|
+
property={getPropertyInPath(collection.properties, titleProperty) as Property}
|
|
163
194
|
size={"medium"}/>
|
|
164
|
-
|
|
195
|
+
: title.value}
|
|
165
196
|
</div>
|
|
166
197
|
)}
|
|
167
198
|
|
|
@@ -171,8 +202,14 @@ export function EntityPreviewBindingData({
|
|
|
171
202
|
|
|
172
203
|
const valueInPath = getValueInPath(entity.values, key);
|
|
173
204
|
return (
|
|
205
|
+
// `truncate` clamps a line of text; it does nothing to a
|
|
206
|
+
// preview that renders blocks. Custom `admin.Preview`
|
|
207
|
+
// components can render anything at all, so the height
|
|
208
|
+
// is capped here as well — a card that loses the tail of
|
|
209
|
+
// an unusual value beats a card the size of a document.
|
|
174
210
|
<div key={"ref_prev_" + key}
|
|
175
|
-
className={cls("truncate
|
|
211
|
+
className={cls("truncate min-w-0 max-h-8 overflow-hidden",
|
|
212
|
+
restProperties.length > 1 ? "my-0.5" : "my-0")}>
|
|
176
213
|
{
|
|
177
214
|
entity
|
|
178
215
|
? <PropertyPreview
|
|
@@ -362,9 +399,12 @@ export const EntityPreviewContainer = React.forwardRef<HTMLDivElement, EntityPre
|
|
|
362
399
|
style={style}
|
|
363
400
|
className={cls(
|
|
364
401
|
"bg-white dark:bg-surface-900",
|
|
365
|
-
|
|
402
|
+
// On the shared 28/32/40/48 control scale. `44px` was between two
|
|
403
|
+
// steps of it, so a reference in a form was 4px short of the field
|
|
404
|
+
// next to it and their boxes never quite agreed.
|
|
405
|
+
size === "small" ? "min-h-[32px]" : "min-h-[48px]",
|
|
366
406
|
fullwidth ? "w-full" : "",
|
|
367
|
-
"items-center",
|
|
407
|
+
"items-center overflow-hidden",
|
|
368
408
|
hover ? "hover:bg-surface-accent-50 dark:hover:bg-surface-800 group-hover:bg-surface-accent-50 dark:group-hover:bg-surface-800" : "",
|
|
369
409
|
size === "small" ? "p-1" : "px-2 py-1",
|
|
370
410
|
"flex border rounded-lg",
|