@firecms/core 3.0.0-alpha.58 → 3.0.0-alpha.59
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/components/ReferenceWidget.d.ts +15 -12
- package/dist/index.es.js +2812 -2900
- package/dist/index.es.js.map +1 -1
- package/dist/index.umd.js +5 -5
- package/dist/index.umd.js.map +1 -1
- package/package.json +3 -3
- package/src/components/ReferenceWidget.tsx +87 -199
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@firecms/core",
|
|
3
|
-
"version": "3.0.0-alpha.
|
|
3
|
+
"version": "3.0.0-alpha.59",
|
|
4
4
|
"description": "Awesome Firebase/Firestore-based headless open-source CMS",
|
|
5
5
|
"funding": {
|
|
6
6
|
"url": "https://github.com/sponsors/firecmsco"
|
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
"generateIcons": "ts-node --esm src/icons/generateIcons.ts"
|
|
46
46
|
},
|
|
47
47
|
"dependencies": {
|
|
48
|
-
"@firecms/ui": "^3.0.0-alpha.
|
|
48
|
+
"@firecms/ui": "^3.0.0-alpha.59",
|
|
49
49
|
"@fontsource/ibm-plex-mono": "^5.0.8",
|
|
50
50
|
"@fontsource/roboto": "^5.0.8",
|
|
51
51
|
"@hello-pangea/dnd": "^16.5.0",
|
|
@@ -116,7 +116,7 @@
|
|
|
116
116
|
"dist",
|
|
117
117
|
"src"
|
|
118
118
|
],
|
|
119
|
-
"gitHead": "
|
|
119
|
+
"gitHead": "9d82460b4b28c7b31d097ff3e0e28f77c05b1023",
|
|
120
120
|
"publishConfig": {
|
|
121
121
|
"access": "public"
|
|
122
122
|
}
|
|
@@ -1,52 +1,38 @@
|
|
|
1
1
|
import React, { useCallback, useMemo } from "react";
|
|
2
2
|
|
|
3
|
-
import { Entity, EntityCollection, EntityReference, FilterValues
|
|
4
|
-
import { getReferenceFrom
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
|
|
8
|
-
useFireCMSContext,
|
|
9
|
-
useNavigationController,
|
|
10
|
-
useReferenceDialog,
|
|
11
|
-
useSideEntityController
|
|
12
|
-
} from "../hooks";
|
|
13
|
-
import { LabelWithIcon } from "../components";
|
|
14
|
-
import {
|
|
15
|
-
Button,
|
|
16
|
-
ClearIcon,
|
|
17
|
-
cn,
|
|
18
|
-
ErrorIcon,
|
|
19
|
-
IconButton,
|
|
20
|
-
KeyboardTabIcon,
|
|
21
|
-
LinkIcon,
|
|
22
|
-
Tooltip,
|
|
23
|
-
Typography
|
|
24
|
-
} from "@firecms/ui";
|
|
25
|
-
import { ErrorBoundary, ErrorView } from "../components";
|
|
3
|
+
import { Entity, EntityCollection, EntityReference, FilterValues } from "../types";
|
|
4
|
+
import { getReferenceFrom } from "../util";
|
|
5
|
+
import { PreviewSize, ReferencePreview } from "../preview";
|
|
6
|
+
import { useNavigationController, useReferenceDialog } from "../hooks";
|
|
7
|
+
import { Button, cn } from "@firecms/ui";
|
|
26
8
|
|
|
27
9
|
/**
|
|
28
10
|
* This field allows selecting reference/s.
|
|
29
|
-
* @param name
|
|
30
|
-
* @param path
|
|
31
|
-
* @param disabled
|
|
32
|
-
* @param value
|
|
33
|
-
* @param setValue
|
|
34
|
-
* @param previewProperties
|
|
35
|
-
* @param forceFilter
|
|
36
|
-
* @constructor
|
|
37
11
|
*/
|
|
38
12
|
export function ReferenceWidget<M extends Record<string, any>>({
|
|
39
13
|
name,
|
|
14
|
+
multiselect = false,
|
|
40
15
|
path,
|
|
41
16
|
disabled,
|
|
42
17
|
value,
|
|
43
|
-
|
|
18
|
+
onReferenceSelected,
|
|
19
|
+
onMultipleReferenceSelected,
|
|
44
20
|
previewProperties,
|
|
45
|
-
forceFilter
|
|
21
|
+
forceFilter,
|
|
22
|
+
size,
|
|
23
|
+
className
|
|
46
24
|
}: {
|
|
47
25
|
name?: string,
|
|
48
|
-
|
|
49
|
-
|
|
26
|
+
multiselect?: boolean,
|
|
27
|
+
value: EntityReference | EntityReference[] | null,
|
|
28
|
+
onReferenceSelected?: (params: {
|
|
29
|
+
reference: EntityReference | null,
|
|
30
|
+
entity: Entity<M> | null
|
|
31
|
+
}) => void,
|
|
32
|
+
onMultipleReferenceSelected?: (params: {
|
|
33
|
+
references: EntityReference[] | null,
|
|
34
|
+
entities: Entity<M>[] | null
|
|
35
|
+
}) => void,
|
|
50
36
|
path: string,
|
|
51
37
|
disabled?: boolean,
|
|
52
38
|
previewProperties?: string[];
|
|
@@ -54,11 +40,11 @@ export function ReferenceWidget<M extends Record<string, any>>({
|
|
|
54
40
|
* Allow selection of entities that pass the given filter only.
|
|
55
41
|
*/
|
|
56
42
|
forceFilter?: FilterValues<string>;
|
|
43
|
+
size: PreviewSize;
|
|
44
|
+
className?: string;
|
|
57
45
|
}) {
|
|
58
46
|
|
|
59
|
-
const fireCMSContext = useFireCMSContext();
|
|
60
47
|
const navigationController = useNavigationController();
|
|
61
|
-
const sideEntityController = useSideEntityController();
|
|
62
48
|
|
|
63
49
|
const collection: EntityCollection | undefined = useMemo(() => {
|
|
64
50
|
return navigationController.getCollection(path);
|
|
@@ -68,188 +54,90 @@ export function ReferenceWidget<M extends Record<string, any>>({
|
|
|
68
54
|
throw Error(`Couldn't find the corresponding collection for the path: ${path}`);
|
|
69
55
|
}
|
|
70
56
|
|
|
71
|
-
const
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
entityId: validValue ? value.id : undefined,
|
|
80
|
-
collection,
|
|
81
|
-
useCache: true
|
|
82
|
-
});
|
|
57
|
+
const onSingleEntitySelected = useCallback((entity: Entity<M> | null) => {
|
|
58
|
+
if (disabled)
|
|
59
|
+
return;
|
|
60
|
+
if (onReferenceSelected) {
|
|
61
|
+
const reference = entity ? getReferenceFrom(entity) : null;
|
|
62
|
+
onReferenceSelected?.({ reference, entity });
|
|
63
|
+
}
|
|
64
|
+
}, [disabled, onReferenceSelected]);
|
|
83
65
|
|
|
84
|
-
const
|
|
66
|
+
const onMultipleEntitiesSelected = useCallback((entities: Entity<M>[]) => {
|
|
85
67
|
if (disabled)
|
|
86
68
|
return;
|
|
87
|
-
|
|
88
|
-
|
|
69
|
+
if (onMultipleReferenceSelected) {
|
|
70
|
+
const references = entities ? entities.map(e => getReferenceFrom(e)) : null;
|
|
71
|
+
onMultipleReferenceSelected({ references, entities });
|
|
72
|
+
}
|
|
73
|
+
}, [disabled, onReferenceSelected]);
|
|
89
74
|
|
|
90
75
|
const referenceDialogController = useReferenceDialog({
|
|
91
|
-
multiselect
|
|
76
|
+
multiselect,
|
|
92
77
|
path,
|
|
93
78
|
collection,
|
|
94
79
|
onSingleEntitySelected,
|
|
80
|
+
onMultipleEntitiesSelected,
|
|
95
81
|
forceFilter
|
|
96
82
|
}
|
|
97
83
|
);
|
|
98
84
|
|
|
99
|
-
const handleClickOpen = useCallback(() => {
|
|
100
|
-
referenceDialogController.open();
|
|
101
|
-
}, [referenceDialogController]);
|
|
102
|
-
|
|
103
85
|
const clearValue = useCallback((e: React.MouseEvent) => {
|
|
104
86
|
e.stopPropagation();
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
const seeEntityDetails = useCallback((e: React.MouseEvent) => {
|
|
109
|
-
e.stopPropagation();
|
|
110
|
-
if (entity) {
|
|
111
|
-
fireCMSContext.onAnalyticsEvent?.("entity_click_from_reference", {
|
|
112
|
-
path: entity.path,
|
|
113
|
-
entityId: entity.id
|
|
114
|
-
});
|
|
115
|
-
sideEntityController.open({
|
|
116
|
-
entityId: entity.id,
|
|
117
|
-
path,
|
|
118
|
-
updateUrl: true
|
|
119
|
-
});
|
|
120
|
-
}
|
|
121
|
-
}, [entity, path, sideEntityController]);
|
|
122
|
-
|
|
123
|
-
const buildEntityView = (collection?: EntityCollection<any>) => {
|
|
124
|
-
|
|
125
|
-
const missingEntity = entity && !entity.values;
|
|
126
|
-
|
|
127
|
-
let body: React.ReactNode;
|
|
128
|
-
if (!collection) {
|
|
129
|
-
body = (
|
|
130
|
-
<ErrorView
|
|
131
|
-
error={"The specified collection does not exist. Check console"}/>
|
|
132
|
-
);
|
|
133
|
-
} else if (missingEntity) {
|
|
134
|
-
body = (
|
|
135
|
-
<Tooltip title={value && value.path}>
|
|
136
|
-
<div className="flex items-center p-4 flex-grow">
|
|
137
|
-
<ErrorIcon size={"small"} color={"error"}/>
|
|
138
|
-
<div className="ml-4">Missing
|
|
139
|
-
reference {entity && entity.id}</div>
|
|
140
|
-
</div>
|
|
141
|
-
</Tooltip>
|
|
142
|
-
);
|
|
87
|
+
if (multiselect) {
|
|
88
|
+
onMultipleEntitiesSelected([]);
|
|
143
89
|
} else {
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
const listProperties = getReferencePreviewKeys(collection, fireCMSContext.propertyConfigs, previewProperties, 3);
|
|
147
|
-
|
|
148
|
-
body = (
|
|
149
|
-
<div className="flex flex-col flex-grow ml-4 mr-4">
|
|
150
|
-
|
|
151
|
-
{listProperties && listProperties.map((key) => {
|
|
152
|
-
const property = collection.properties[key as string];
|
|
153
|
-
if (!property) return null;
|
|
154
|
-
return (
|
|
155
|
-
<div
|
|
156
|
-
key={`reference_previews_${key as string}`}
|
|
157
|
-
className="mt-1 mb-1">
|
|
158
|
-
<ErrorBoundary>{
|
|
159
|
-
entity
|
|
160
|
-
? <PropertyPreview
|
|
161
|
-
propertyKey={key as string}
|
|
162
|
-
value={(entity.values)[key]}
|
|
163
|
-
property={property as ResolvedProperty}
|
|
164
|
-
entity={entity}
|
|
165
|
-
size={"tiny"}/>
|
|
166
|
-
: <SkeletonPropertyComponent
|
|
167
|
-
property={property as ResolvedProperty}
|
|
168
|
-
size={"tiny"}/>}
|
|
169
|
-
</ErrorBoundary>
|
|
170
|
-
</div>
|
|
171
|
-
);
|
|
172
|
-
})}
|
|
173
|
-
</div>
|
|
174
|
-
);
|
|
175
|
-
} else {
|
|
176
|
-
body = <div className="p-4 flex justify-center"
|
|
177
|
-
onClick={disabled ? undefined : handleClickOpen}>
|
|
178
|
-
<Typography variant={"label"}
|
|
179
|
-
className="flex-grow text-center">No value
|
|
180
|
-
set</Typography>
|
|
181
|
-
{!disabled && <Button variant="outlined"
|
|
182
|
-
color="primary">
|
|
183
|
-
Set
|
|
184
|
-
</Button>}
|
|
185
|
-
</div>;
|
|
186
|
-
}
|
|
90
|
+
onSingleEntitySelected(null);
|
|
187
91
|
}
|
|
92
|
+
}, [onReferenceSelected]);
|
|
188
93
|
|
|
189
|
-
|
|
190
|
-
<div
|
|
191
|
-
onClick={disabled ? undefined : handleClickOpen}
|
|
192
|
-
className="flex">
|
|
193
|
-
|
|
194
|
-
<div className="flex flex-col flex-grow">
|
|
195
|
-
|
|
196
|
-
<div className="flex flex-col flex-grow">
|
|
197
|
-
<LabelWithIcon icon={<LinkIcon color={"inherit"}
|
|
198
|
-
/>}
|
|
199
|
-
className="text-text-secondary dark:text-text-secondary-dark ml-3.5"
|
|
200
|
-
title={name}
|
|
201
|
-
/>
|
|
202
|
-
|
|
203
|
-
{entity &&
|
|
204
|
-
<div className="self-center m-4">
|
|
205
|
-
<Tooltip title={value && value.path}>
|
|
206
|
-
<Typography variant={"caption"}
|
|
207
|
-
className={"font-mono"}>
|
|
208
|
-
{entity.id}
|
|
209
|
-
</Typography>
|
|
210
|
-
</Tooltip>
|
|
211
|
-
</div>}
|
|
212
|
-
|
|
213
|
-
{!missingEntity && entity && value &&
|
|
214
|
-
<Tooltip title={`See details for ${entity.id}`}>
|
|
215
|
-
<span>
|
|
216
|
-
<IconButton
|
|
217
|
-
onClick={seeEntityDetails}
|
|
218
|
-
size="large">
|
|
219
|
-
<KeyboardTabIcon/>
|
|
220
|
-
</IconButton>
|
|
221
|
-
</span>
|
|
222
|
-
</Tooltip>}
|
|
223
|
-
|
|
224
|
-
{value && <div>
|
|
225
|
-
<Tooltip title=" Clear">
|
|
226
|
-
<span>
|
|
227
|
-
<IconButton
|
|
228
|
-
disabled={disabled}
|
|
229
|
-
onClick={disabled ? undefined : clearValue}
|
|
230
|
-
size="large">
|
|
231
|
-
<ClearIcon/>
|
|
232
|
-
</IconButton>
|
|
233
|
-
</span>
|
|
234
|
-
</Tooltip>
|
|
235
|
-
</div>}
|
|
236
|
-
|
|
237
|
-
</div>
|
|
94
|
+
let child: React.ReactNode;
|
|
238
95
|
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
);
|
|
96
|
+
const onEntryClick = () => {
|
|
97
|
+
if (disabled)
|
|
98
|
+
return;
|
|
99
|
+
referenceDialogController.open();
|
|
244
100
|
};
|
|
245
101
|
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
102
|
+
if (Array.isArray(value)) {
|
|
103
|
+
child = <div className={"flex flex-col gap-4"}>
|
|
104
|
+
{value.map((ref, index) => {
|
|
105
|
+
return <ReferencePreview
|
|
106
|
+
key={`reference_preview_${index}`}
|
|
107
|
+
onClick={onEntryClick}
|
|
108
|
+
reference={ref}
|
|
109
|
+
disabled={disabled}
|
|
110
|
+
previewProperties={previewProperties}
|
|
111
|
+
size={size}/>
|
|
112
|
+
})}
|
|
113
|
+
</div>
|
|
114
|
+
} else if (value instanceof EntityReference) {
|
|
115
|
+
child = <ReferencePreview
|
|
116
|
+
reference={value}
|
|
117
|
+
onClick={onEntryClick}
|
|
118
|
+
disabled={disabled}
|
|
119
|
+
previewProperties={previewProperties}
|
|
120
|
+
size={size}/>
|
|
251
121
|
|
|
252
|
-
|
|
122
|
+
}
|
|
123
|
+
return <div className={cn("text-sm font-medium text-gray-500",
|
|
124
|
+
"min-w-80 flex flex-col gap-4",
|
|
125
|
+
"relative transition-colors duration-200 ease-in rounded font-medium",
|
|
126
|
+
disabled ? "bg-opacity-50" : "hover:bg-opacity-75",
|
|
127
|
+
"text-opacity-50 dark:text-white dark:text-opacity-50",
|
|
128
|
+
className
|
|
129
|
+
)}
|
|
130
|
+
>
|
|
253
131
|
|
|
254
|
-
|
|
132
|
+
{child}
|
|
133
|
+
{!value && <div className="justify-center text-left">
|
|
134
|
+
<Button variant="outlined"
|
|
135
|
+
color="primary"
|
|
136
|
+
disabled={disabled}
|
|
137
|
+
onClick={onEntryClick}>
|
|
138
|
+
Edit {name}
|
|
139
|
+
</Button>
|
|
140
|
+
</div>}
|
|
141
|
+
|
|
142
|
+
</div>;
|
|
255
143
|
}
|