slice-machine-ui 2.16.2-beta.17 → 2.16.2-beta.3
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/out/404.html +1 -1
- package/out/_next/static/WcTCr5PY9CMXQCakT6VQd/_buildManifest.js +1 -0
- package/out/_next/static/chunks/268-6d5fc7642a1b87c8.js +1 -0
- package/out/_next/static/chunks/34-2911c905c8a6e9d9.js +1 -0
- package/out/_next/static/chunks/630-93339694ef30b82d.js +1 -0
- package/out/_next/static/chunks/867-8164160c810122c6.js +1 -0
- package/out/_next/static/chunks/882-28837678beff7e51.js +1 -0
- package/out/_next/static/chunks/895-8c214ba470a4e23c.js +1 -0
- package/out/_next/static/chunks/{main-c46f4dcf6e3174bd.js → main-1c0e53194ff2e726.js} +1 -1
- package/out/_next/static/chunks/pages/_app-3fd13526dea9f1d4.js +657 -0
- package/out/_next/static/chunks/pages/changelog-80a618708f44f25f.js +1 -0
- package/out/_next/static/chunks/pages/changes-d40c17939854b984.js +1 -0
- package/out/_next/static/chunks/pages/custom-types/{[customTypeId]-a408f5a660e096a6.js → [customTypeId]-389d1b7a492fb3e7.js} +1 -1
- package/out/_next/static/chunks/pages/{custom-types-5acd56959b60346f.js → custom-types-2a5fd94ee42ba593.js} +1 -1
- package/out/_next/static/chunks/pages/{index-0d8cb369de720a35.js → index-02dd147957c8b40f.js} +1 -1
- package/out/_next/static/chunks/pages/labs-c6df252ea5d8fb6f.js +1 -0
- package/out/_next/static/chunks/pages/page-types/{[pageTypeId]-f5e851ebe35049a8.js → [pageTypeId]-3589bd1f9138a97b.js} +1 -1
- package/out/_next/static/chunks/pages/settings-170379902605f38a.js +1 -0
- package/out/_next/static/chunks/pages/slices/[lib]/[sliceName]/[variation]/simulator-063fa88ba75f483e.js +1 -0
- package/out/_next/static/chunks/pages/slices/[lib]/[sliceName]/[variation]-6de02b8ed13b680d.js +1 -0
- package/out/_next/static/chunks/pages/slices-071bb494907adf0f.js +1 -0
- package/out/_next/static/chunks/webpack-2f903acb0cccbf9e.js +1 -0
- package/out/_next/static/css/9c9a7de81f9ac811.css +1 -0
- package/out/changelog.html +1 -1
- package/out/changes.html +1 -1
- package/out/custom-types/[customTypeId].html +1 -1
- package/out/custom-types.html +1 -1
- package/out/index.html +1 -1
- package/out/labs.html +1 -1
- package/out/page-types/[pageTypeId].html +1 -1
- package/out/settings.html +1 -1
- package/out/slices/[lib]/[sliceName]/[variation]/simulator.html +1 -1
- package/out/slices/[lib]/[sliceName]/[variation].html +1 -1
- package/out/slices.html +1 -1
- package/package.json +8 -8
- package/src/features/customTypes/customTypesTable/CustomTypesTable.tsx +4 -2
- package/src/features/customTypes/customTypesTable/{useCustomTypesAutoRevalidation.tsx → useCustomTypes.ts} +34 -1
- package/src/legacy/lib/builders/common/Zone/Card/components/Hints/index.tsx +1 -12
- package/src/legacy/lib/models/common/widgets/ContentRelationship/Form.tsx +77 -35
- package/src/legacy/lib/models/common/widgets/ContentRelationship/index.ts +15 -46
- package/src/legacy/lib/models/common/widgets/Link/index.ts +1 -1
- package/src/utils/tracking/trackFieldAdded.ts +5 -2
- package/src/utils/tracking/trackFieldUpdated.ts +5 -2
- package/out/_next/static/YlBfAcqDSP3Tctj3WT7xv/_buildManifest.js +0 -1
- package/out/_next/static/chunks/248-03446cd9e9f13730.js +0 -1
- package/out/_next/static/chunks/268-6a9214b97195af9c.js +0 -1
- package/out/_next/static/chunks/33641354.3864aefb6106ae71.js +0 -28
- package/out/_next/static/chunks/34-e684c5fd75cc9dd0.js +0 -1
- package/out/_next/static/chunks/630-a8b2c2d022cc9450.js +0 -1
- package/out/_next/static/chunks/647-7b9b5aa9468f9e4b.js +0 -1
- package/out/_next/static/chunks/882-151468121d542ed6.js +0 -1
- package/out/_next/static/chunks/pages/_app-e3fa2a0a57c571ce.js +0 -708
- package/out/_next/static/chunks/pages/changelog-063c5e11dfc8fd55.js +0 -1
- package/out/_next/static/chunks/pages/changes-564336edb0ed18b0.js +0 -1
- package/out/_next/static/chunks/pages/labs-9630bfb1005be02b.js +0 -1
- package/out/_next/static/chunks/pages/settings-01f4aeb9112a1f87.js +0 -1
- package/out/_next/static/chunks/pages/slices/[lib]/[sliceName]/[variation]/simulator-5008e29008aa04f4.js +0 -1
- package/out/_next/static/chunks/pages/slices/[lib]/[sliceName]/[variation]-bd5e45632c419567.js +0 -1
- package/out/_next/static/chunks/pages/slices-4a60cd5f2c71327e.js +0 -1
- package/out/_next/static/chunks/webpack-b3522fdebabf510a.js +0 -1
- package/out/_next/static/css/cc9b10286400c2b9.css +0 -1
- package/src/features/builder/fields/contentRelationship/ContentRelationshipFieldPicker.tsx +0 -1279
- package/src/features/builder/fields/contentRelationship/Hint.tsx +0 -28
- package/src/features/builder/fields/contentRelationship/__tests__/ContentRelationshipFieldPicker.test.ts +0 -265
- package/src/features/customTypes/useCustomTypes.ts +0 -48
- package/src/utils/isValidObject.ts +0 -32
- package/src/utils/tracking/getLinkTrackingProperties.ts +0 -26
- /package/out/_next/static/{YlBfAcqDSP3Tctj3WT7xv → WcTCr5PY9CMXQCakT6VQd}/_ssgManifest.js +0 -0
|
@@ -1,1279 +0,0 @@
|
|
|
1
|
-
import { pluralize } from "@prismicio/editor-support/String";
|
|
2
|
-
import {
|
|
3
|
-
Alert,
|
|
4
|
-
AnimatedSuspense,
|
|
5
|
-
Badge,
|
|
6
|
-
Box,
|
|
7
|
-
Button,
|
|
8
|
-
DropdownMenu,
|
|
9
|
-
DropdownMenuContent,
|
|
10
|
-
DropdownMenuItem,
|
|
11
|
-
DropdownMenuLabel,
|
|
12
|
-
DropdownMenuTrigger,
|
|
13
|
-
Icon,
|
|
14
|
-
IconButton,
|
|
15
|
-
Skeleton,
|
|
16
|
-
Text,
|
|
17
|
-
TextOverflow,
|
|
18
|
-
Tooltip,
|
|
19
|
-
TreeView,
|
|
20
|
-
TreeViewCheckbox,
|
|
21
|
-
TreeViewSection,
|
|
22
|
-
} from "@prismicio/editor-ui";
|
|
23
|
-
import {
|
|
24
|
-
CustomType,
|
|
25
|
-
Group,
|
|
26
|
-
Link,
|
|
27
|
-
LinkConfig,
|
|
28
|
-
NestableWidget,
|
|
29
|
-
} from "@prismicio/types-internal/lib/customtypes";
|
|
30
|
-
import { useEffect } from "react";
|
|
31
|
-
|
|
32
|
-
import { ErrorBoundary } from "@/ErrorBoundary";
|
|
33
|
-
import {
|
|
34
|
-
revalidateGetCustomTypes,
|
|
35
|
-
useCustomTypes as useCustomTypesRequest,
|
|
36
|
-
} from "@/features/customTypes/useCustomTypes";
|
|
37
|
-
import { isValidObject } from "@/utils/isValidObject";
|
|
38
|
-
|
|
39
|
-
type NonReadonly<T> = { -readonly [P in keyof T]: T[P] };
|
|
40
|
-
|
|
41
|
-
/**
|
|
42
|
-
* Picker fields check map types. Used internally to keep track of the checked
|
|
43
|
-
* fields in the TreeView, as it's easier to handle objects than arrays and
|
|
44
|
-
* also ensures field uniqueness.
|
|
45
|
-
*
|
|
46
|
-
* @example
|
|
47
|
-
* {
|
|
48
|
-
* author: {
|
|
49
|
-
* fullName: {
|
|
50
|
-
* type: "checkbox",
|
|
51
|
-
* value: true,
|
|
52
|
-
* },
|
|
53
|
-
* awards: {
|
|
54
|
-
* type: "group",
|
|
55
|
-
* value: {
|
|
56
|
-
* date: {
|
|
57
|
-
* type: "checkbox",
|
|
58
|
-
* value: true,
|
|
59
|
-
* },
|
|
60
|
-
* awardsCr: {
|
|
61
|
-
* type: "contentRelationship",
|
|
62
|
-
* value: {
|
|
63
|
-
* award: {
|
|
64
|
-
* title: {
|
|
65
|
-
* type: "checkbox",
|
|
66
|
-
* value: true,
|
|
67
|
-
* },
|
|
68
|
-
* issuer: {
|
|
69
|
-
* type: "group",
|
|
70
|
-
* value: {
|
|
71
|
-
* name: {
|
|
72
|
-
* type: "checkbox",
|
|
73
|
-
* value: true,
|
|
74
|
-
* },
|
|
75
|
-
* },
|
|
76
|
-
* },
|
|
77
|
-
* },
|
|
78
|
-
* },
|
|
79
|
-
* },
|
|
80
|
-
* },
|
|
81
|
-
* },
|
|
82
|
-
* professionCr: {
|
|
83
|
-
* type: "contentRelationship",
|
|
84
|
-
* value: {
|
|
85
|
-
* profession: {
|
|
86
|
-
* name: {
|
|
87
|
-
* type: "checkbox",
|
|
88
|
-
* value: true,
|
|
89
|
-
* },
|
|
90
|
-
* areas: {
|
|
91
|
-
* type: "group",
|
|
92
|
-
* value: {
|
|
93
|
-
* name: {
|
|
94
|
-
* type: "checkbox",
|
|
95
|
-
* value: true,
|
|
96
|
-
* },
|
|
97
|
-
* },
|
|
98
|
-
* },
|
|
99
|
-
* },
|
|
100
|
-
* },
|
|
101
|
-
* },
|
|
102
|
-
* },
|
|
103
|
-
* }
|
|
104
|
-
**/
|
|
105
|
-
interface PickerCustomTypes {
|
|
106
|
-
[customTypeId: string]: PickerCustomType;
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
interface PickerCustomType {
|
|
110
|
-
[fieldId: string]: PickerCustomTypeValue;
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
type PickerCustomTypeValue =
|
|
114
|
-
| PickerCheckboxField
|
|
115
|
-
| PickerFirstLevelGroupField
|
|
116
|
-
| PickerContentRelationshipField;
|
|
117
|
-
|
|
118
|
-
interface PickerCheckboxField {
|
|
119
|
-
type: "checkbox";
|
|
120
|
-
value: boolean;
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
interface PickerFirstLevelGroupField {
|
|
124
|
-
type: "group";
|
|
125
|
-
value: PickerFirstLevelGroupFieldValue;
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
interface PickerLeafGroupField {
|
|
129
|
-
type: "group";
|
|
130
|
-
value: PickerLeafGroupFieldValue;
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
interface PickerLeafGroupFieldValue {
|
|
134
|
-
[fieldId: string]: PickerCheckboxField;
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
interface PickerFirstLevelGroupFieldValue {
|
|
138
|
-
[fieldId: string]: PickerCheckboxField | PickerContentRelationshipField;
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
interface PickerContentRelationshipField {
|
|
142
|
-
type: "contentRelationship";
|
|
143
|
-
value: PickerContentRelationshipFieldValue;
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
interface PickerContentRelationshipFieldValue {
|
|
147
|
-
[customTypeId: string]: PickerNestedCustomTypeValue;
|
|
148
|
-
}
|
|
149
|
-
interface PickerNestedCustomTypeValue {
|
|
150
|
-
[fieldId: string]: PickerCheckboxField | PickerLeafGroupField;
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
/**
|
|
154
|
-
* Content relationship Link customtypes property structure.
|
|
155
|
-
*
|
|
156
|
-
* @example
|
|
157
|
-
* [
|
|
158
|
-
* {
|
|
159
|
-
* id: "author",
|
|
160
|
-
* fields: [
|
|
161
|
-
* "fullName",
|
|
162
|
-
* {
|
|
163
|
-
* id: "awards",
|
|
164
|
-
* fields: [
|
|
165
|
-
* "date",
|
|
166
|
-
* {
|
|
167
|
-
* id: "awardsCr",
|
|
168
|
-
* customtypes: [
|
|
169
|
-
* {
|
|
170
|
-
* id: "award",
|
|
171
|
-
* fields: [
|
|
172
|
-
* "title",
|
|
173
|
-
* {
|
|
174
|
-
* id: "issuer",
|
|
175
|
-
* fields: ["name"],
|
|
176
|
-
* },
|
|
177
|
-
* ],
|
|
178
|
-
* },
|
|
179
|
-
* ],
|
|
180
|
-
* },
|
|
181
|
-
* ],
|
|
182
|
-
* },
|
|
183
|
-
* {
|
|
184
|
-
* id: "professionCr",
|
|
185
|
-
* customtypes: [
|
|
186
|
-
* {
|
|
187
|
-
* id: "profession",
|
|
188
|
-
* fields: [
|
|
189
|
-
* "name",
|
|
190
|
-
* {
|
|
191
|
-
* id: "areas",
|
|
192
|
-
* fields: ["name"],
|
|
193
|
-
* },
|
|
194
|
-
* ],
|
|
195
|
-
* },
|
|
196
|
-
* ],
|
|
197
|
-
* },
|
|
198
|
-
* ],
|
|
199
|
-
* },
|
|
200
|
-
* ]
|
|
201
|
-
*/
|
|
202
|
-
type LinkCustomtypes = NonNullable<LinkConfig["customtypes"]>;
|
|
203
|
-
|
|
204
|
-
type LinkCustomtypesFields = Exclude<
|
|
205
|
-
LinkCustomtypes[number],
|
|
206
|
-
string
|
|
207
|
-
>["fields"][number];
|
|
208
|
-
|
|
209
|
-
type LinkCustomtypesContentRelationshipFieldValue = Exclude<
|
|
210
|
-
LinkCustomtypesFields,
|
|
211
|
-
string | { fields: unknown }
|
|
212
|
-
>;
|
|
213
|
-
|
|
214
|
-
type LinkCustomtypesGroupFieldValue = Exclude<
|
|
215
|
-
LinkCustomtypesFields,
|
|
216
|
-
string | { customtypes: unknown }
|
|
217
|
-
>;
|
|
218
|
-
|
|
219
|
-
interface ContentRelationshipFieldPickerProps {
|
|
220
|
-
value: LinkCustomtypes | undefined;
|
|
221
|
-
onChange: (fields: LinkCustomtypes) => void;
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
export function ContentRelationshipFieldPicker(
|
|
225
|
-
props: ContentRelationshipFieldPickerProps,
|
|
226
|
-
) {
|
|
227
|
-
return (
|
|
228
|
-
<ErrorBoundary
|
|
229
|
-
renderError={() => (
|
|
230
|
-
<Box alignItems="center" gap={8}>
|
|
231
|
-
<Icon name="alert" size="small" color="tomato10" />
|
|
232
|
-
<Text color="tomato10">Error loading your types</Text>
|
|
233
|
-
</Box>
|
|
234
|
-
)}
|
|
235
|
-
>
|
|
236
|
-
<AnimatedSuspense
|
|
237
|
-
fallback={
|
|
238
|
-
<Box flexDirection="column" position="relative">
|
|
239
|
-
<Skeleton height={240} />
|
|
240
|
-
<Box
|
|
241
|
-
position="absolute"
|
|
242
|
-
top="50%"
|
|
243
|
-
left="50%"
|
|
244
|
-
transform="translate(-50%, -50%)"
|
|
245
|
-
alignItems="center"
|
|
246
|
-
gap={8}
|
|
247
|
-
>
|
|
248
|
-
<Icon name="autorenew" size="small" color="grey11" />
|
|
249
|
-
<Text color="grey11">Loading your types...</Text>
|
|
250
|
-
</Box>
|
|
251
|
-
</Box>
|
|
252
|
-
}
|
|
253
|
-
>
|
|
254
|
-
<ContentRelationshipFieldPickerContent {...props} />
|
|
255
|
-
</AnimatedSuspense>
|
|
256
|
-
</ErrorBoundary>
|
|
257
|
-
);
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
function ContentRelationshipFieldPickerContent(
|
|
261
|
-
props: ContentRelationshipFieldPickerProps,
|
|
262
|
-
) {
|
|
263
|
-
const { value, onChange } = props;
|
|
264
|
-
const { allCustomTypes, pickedCustomTypes } = useCustomTypes(value);
|
|
265
|
-
|
|
266
|
-
const fieldCheckMap = value
|
|
267
|
-
? convertLinkCustomtypesToFieldCheckMap(value)
|
|
268
|
-
: {};
|
|
269
|
-
|
|
270
|
-
function onCustomTypesChange(id: string, newCustomType: PickerCustomType) {
|
|
271
|
-
// The picker does not handle strings (custom type ids), as it's only meant
|
|
272
|
-
// to pick fields from custom types (objects). So we need to merge it with
|
|
273
|
-
// the existing value, which can have strings in the first level that
|
|
274
|
-
// represent new types added without any picked fields.
|
|
275
|
-
onChange(
|
|
276
|
-
mergeAndConvertCheckMapToLinkCustomtypes({
|
|
277
|
-
existingLinkCustomtypes: value,
|
|
278
|
-
previousPickerCustomtypes: fieldCheckMap,
|
|
279
|
-
customTypeId: id,
|
|
280
|
-
newCustomType,
|
|
281
|
-
}),
|
|
282
|
-
);
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
function addCustomType(id: string) {
|
|
286
|
-
const newFields = value ? [...value, id] : [id];
|
|
287
|
-
onChange(newFields);
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
function removeCustomType(id: string) {
|
|
291
|
-
if (value) {
|
|
292
|
-
onChange(value.filter((existingCt) => getId(existingCt) !== id));
|
|
293
|
-
}
|
|
294
|
-
}
|
|
295
|
-
|
|
296
|
-
return (
|
|
297
|
-
<Box
|
|
298
|
-
overflow="hidden"
|
|
299
|
-
flexDirection="column"
|
|
300
|
-
border
|
|
301
|
-
borderRadius={6}
|
|
302
|
-
width="100%"
|
|
303
|
-
>
|
|
304
|
-
<Box
|
|
305
|
-
border={{ bottom: true }}
|
|
306
|
-
padding={{ inline: 16, bottom: 16, top: 12 }}
|
|
307
|
-
flexDirection="column"
|
|
308
|
-
gap={8}
|
|
309
|
-
>
|
|
310
|
-
{pickedCustomTypes.length > 0 ? (
|
|
311
|
-
<>
|
|
312
|
-
<Box flexDirection="column">
|
|
313
|
-
<Text variant="h4" color="grey12">
|
|
314
|
-
Allowed type
|
|
315
|
-
</Text>
|
|
316
|
-
<Text color="grey11">
|
|
317
|
-
Select a single type that editors can link to in the Page
|
|
318
|
-
Builder.
|
|
319
|
-
<br />
|
|
320
|
-
For the selected type, choose which fields to include in the API
|
|
321
|
-
response.
|
|
322
|
-
</Text>
|
|
323
|
-
{pickedCustomTypes.length > 1 && (
|
|
324
|
-
<Box margin={{ block: 12 }}>
|
|
325
|
-
<Alert
|
|
326
|
-
color="warn"
|
|
327
|
-
icon="alert"
|
|
328
|
-
subtitle={
|
|
329
|
-
<>
|
|
330
|
-
<Text color="inherit" variant="bold">
|
|
331
|
-
Legacy mode. Keep only one type to enable the improved
|
|
332
|
-
Content Relationship feature.
|
|
333
|
-
</Text>
|
|
334
|
-
<br />
|
|
335
|
-
<a
|
|
336
|
-
href="https://prismic.io/docs/fields/content-relationship"
|
|
337
|
-
target="_blank"
|
|
338
|
-
rel="noopener noreferrer"
|
|
339
|
-
style={{
|
|
340
|
-
color: "inherit",
|
|
341
|
-
textDecoration: "none",
|
|
342
|
-
fontWeight: "bold",
|
|
343
|
-
display: "flex",
|
|
344
|
-
alignItems: "center",
|
|
345
|
-
gap: 4,
|
|
346
|
-
}}
|
|
347
|
-
>
|
|
348
|
-
<Text color="inherit" variant="bold">
|
|
349
|
-
See documentation
|
|
350
|
-
</Text>
|
|
351
|
-
<Icon
|
|
352
|
-
name="arrowForward"
|
|
353
|
-
size="small"
|
|
354
|
-
color="inherit"
|
|
355
|
-
/>
|
|
356
|
-
</a>
|
|
357
|
-
</>
|
|
358
|
-
}
|
|
359
|
-
/>
|
|
360
|
-
</Box>
|
|
361
|
-
)}
|
|
362
|
-
</Box>
|
|
363
|
-
{pickedCustomTypes.map((customType) => (
|
|
364
|
-
<Box
|
|
365
|
-
key={customType.id}
|
|
366
|
-
gap={4}
|
|
367
|
-
padding={8}
|
|
368
|
-
border
|
|
369
|
-
borderRadius={6}
|
|
370
|
-
borderColor="grey6"
|
|
371
|
-
backgroundColor="white"
|
|
372
|
-
justifyContent="space-between"
|
|
373
|
-
>
|
|
374
|
-
{pickedCustomTypes.length > 1 ? (
|
|
375
|
-
<Text>{customType.id}</Text>
|
|
376
|
-
) : (
|
|
377
|
-
<TreeView>
|
|
378
|
-
<TreeViewCustomType
|
|
379
|
-
customType={customType}
|
|
380
|
-
onChange={(value) =>
|
|
381
|
-
onCustomTypesChange(customType.id, value)
|
|
382
|
-
}
|
|
383
|
-
fieldCheckMap={fieldCheckMap[customType.id] ?? {}}
|
|
384
|
-
allCustomTypes={allCustomTypes}
|
|
385
|
-
/>
|
|
386
|
-
</TreeView>
|
|
387
|
-
)}
|
|
388
|
-
|
|
389
|
-
<IconButton
|
|
390
|
-
icon="close"
|
|
391
|
-
size="small"
|
|
392
|
-
onClick={() => removeCustomType(customType.id)}
|
|
393
|
-
sx={{ height: 24, width: 24 }}
|
|
394
|
-
hiddenLabel="Remove type"
|
|
395
|
-
/>
|
|
396
|
-
</Box>
|
|
397
|
-
))}
|
|
398
|
-
</>
|
|
399
|
-
) : (
|
|
400
|
-
<EmptyView onSelect={addCustomType} allCustomTypes={allCustomTypes} />
|
|
401
|
-
)}
|
|
402
|
-
</Box>
|
|
403
|
-
<Box backgroundColor="white" flexDirection="column" padding={12}>
|
|
404
|
-
<Text variant="normal" color="grey11">
|
|
405
|
-
Have ideas for improving this field?{" "}
|
|
406
|
-
<a
|
|
407
|
-
href="https://community.prismic.io/t/content-relationship-share-your-requests-and-feedback/19843"
|
|
408
|
-
target="_blank"
|
|
409
|
-
rel="noopener noreferrer"
|
|
410
|
-
style={{ color: "inherit", textDecoration: "underline" }}
|
|
411
|
-
>
|
|
412
|
-
Please provide your feedback here.
|
|
413
|
-
</a>
|
|
414
|
-
</Text>
|
|
415
|
-
</Box>
|
|
416
|
-
</Box>
|
|
417
|
-
);
|
|
418
|
-
}
|
|
419
|
-
|
|
420
|
-
type EmptyViewProps = {
|
|
421
|
-
onSelect: (customTypeId: string) => void;
|
|
422
|
-
allCustomTypes: CustomType[];
|
|
423
|
-
};
|
|
424
|
-
|
|
425
|
-
function EmptyView(props: EmptyViewProps) {
|
|
426
|
-
const { allCustomTypes, onSelect } = props;
|
|
427
|
-
|
|
428
|
-
return (
|
|
429
|
-
<Box
|
|
430
|
-
flexDirection="column"
|
|
431
|
-
gap={8}
|
|
432
|
-
alignItems="center"
|
|
433
|
-
padding={{ block: 24 }}
|
|
434
|
-
>
|
|
435
|
-
<Box flexDirection="column" alignItems="center" gap={4}>
|
|
436
|
-
<Text variant="h5" color="grey12">
|
|
437
|
-
No type selected
|
|
438
|
-
</Text>
|
|
439
|
-
<Text color="grey11" component="p" align="center">
|
|
440
|
-
Select the type editors can link to.
|
|
441
|
-
<br />
|
|
442
|
-
Then, choose which fields to return in the API.
|
|
443
|
-
</Text>
|
|
444
|
-
</Box>
|
|
445
|
-
<Box>
|
|
446
|
-
<AddTypeButton allCustomTypes={allCustomTypes} onSelect={onSelect} />
|
|
447
|
-
</Box>
|
|
448
|
-
</Box>
|
|
449
|
-
);
|
|
450
|
-
}
|
|
451
|
-
|
|
452
|
-
type AddTypeButtonProps = {
|
|
453
|
-
onSelect: (customTypeId: string) => void;
|
|
454
|
-
allCustomTypes: CustomType[];
|
|
455
|
-
};
|
|
456
|
-
|
|
457
|
-
function AddTypeButton(props: AddTypeButtonProps) {
|
|
458
|
-
const { allCustomTypes, onSelect } = props;
|
|
459
|
-
|
|
460
|
-
const triggerButton = (
|
|
461
|
-
<Button startIcon="add" color="grey" disabled={allCustomTypes.length === 0}>
|
|
462
|
-
Add type
|
|
463
|
-
</Button>
|
|
464
|
-
);
|
|
465
|
-
|
|
466
|
-
const disabledButton = (
|
|
467
|
-
<Box>
|
|
468
|
-
<Tooltip
|
|
469
|
-
content="No type available"
|
|
470
|
-
side="bottom"
|
|
471
|
-
align="start"
|
|
472
|
-
disableHoverableContent
|
|
473
|
-
>
|
|
474
|
-
{triggerButton}
|
|
475
|
-
</Tooltip>
|
|
476
|
-
</Box>
|
|
477
|
-
);
|
|
478
|
-
|
|
479
|
-
if (allCustomTypes.length === 0) return disabledButton;
|
|
480
|
-
|
|
481
|
-
return (
|
|
482
|
-
<Box>
|
|
483
|
-
<DropdownMenu>
|
|
484
|
-
<DropdownMenuTrigger>{triggerButton}</DropdownMenuTrigger>
|
|
485
|
-
<DropdownMenuContent maxHeight={400} minWidth={256} align="center">
|
|
486
|
-
<DropdownMenuLabel>
|
|
487
|
-
<Text color="grey11">Types</Text>
|
|
488
|
-
</DropdownMenuLabel>
|
|
489
|
-
{allCustomTypes.map((customType) => (
|
|
490
|
-
<DropdownMenuItem
|
|
491
|
-
key={customType.id}
|
|
492
|
-
onSelect={() => onSelect(customType.id)}
|
|
493
|
-
>
|
|
494
|
-
<Box alignItems="center" justifyContent="space-between" gap={8}>
|
|
495
|
-
<TextOverflow>
|
|
496
|
-
<Text>{customType.id}</Text>
|
|
497
|
-
</TextOverflow>
|
|
498
|
-
<Badge
|
|
499
|
-
title={
|
|
500
|
-
<Text variant="extraSmall" color="purple11">
|
|
501
|
-
{getTypeFormatLabel(customType.format)}
|
|
502
|
-
</Text>
|
|
503
|
-
}
|
|
504
|
-
color="purple"
|
|
505
|
-
size="small"
|
|
506
|
-
/>
|
|
507
|
-
</Box>
|
|
508
|
-
</DropdownMenuItem>
|
|
509
|
-
))}
|
|
510
|
-
</DropdownMenuContent>
|
|
511
|
-
</DropdownMenu>
|
|
512
|
-
</Box>
|
|
513
|
-
);
|
|
514
|
-
}
|
|
515
|
-
|
|
516
|
-
interface TreeViewCustomTypeProps {
|
|
517
|
-
customType: CustomType;
|
|
518
|
-
fieldCheckMap: PickerCustomType;
|
|
519
|
-
onChange: (newValue: PickerCustomType) => void;
|
|
520
|
-
allCustomTypes: CustomType[];
|
|
521
|
-
}
|
|
522
|
-
|
|
523
|
-
function TreeViewCustomType(props: TreeViewCustomTypeProps) {
|
|
524
|
-
const {
|
|
525
|
-
customType,
|
|
526
|
-
fieldCheckMap: customTypeFieldsCheckMap,
|
|
527
|
-
onChange: onCustomTypeChange,
|
|
528
|
-
allCustomTypes,
|
|
529
|
-
} = props;
|
|
530
|
-
|
|
531
|
-
const renderedFields = getCustomTypeStaticFields(customType).map(
|
|
532
|
-
({ fieldId, field }) => {
|
|
533
|
-
// Group field
|
|
534
|
-
|
|
535
|
-
if (field.type === "Group") {
|
|
536
|
-
const onGroupFieldChange = (
|
|
537
|
-
newGroupFields: PickerFirstLevelGroupFieldValue,
|
|
538
|
-
) => {
|
|
539
|
-
onCustomTypeChange({
|
|
540
|
-
...customTypeFieldsCheckMap,
|
|
541
|
-
[fieldId]: { type: "group", value: newGroupFields },
|
|
542
|
-
});
|
|
543
|
-
};
|
|
544
|
-
|
|
545
|
-
const groupFieldCheckMap = customTypeFieldsCheckMap[fieldId] ?? {};
|
|
546
|
-
|
|
547
|
-
return (
|
|
548
|
-
<TreeViewFirstLevelGroupField
|
|
549
|
-
key={fieldId}
|
|
550
|
-
group={field}
|
|
551
|
-
groupId={fieldId}
|
|
552
|
-
onChange={onGroupFieldChange}
|
|
553
|
-
fieldCheckMap={
|
|
554
|
-
groupFieldCheckMap.type === "group"
|
|
555
|
-
? groupFieldCheckMap.value
|
|
556
|
-
: {}
|
|
557
|
-
}
|
|
558
|
-
allCustomTypes={allCustomTypes}
|
|
559
|
-
/>
|
|
560
|
-
);
|
|
561
|
-
}
|
|
562
|
-
|
|
563
|
-
// Content relationship field with custom types
|
|
564
|
-
|
|
565
|
-
if (isContentRelationshipFieldWithSingleCustomtype(field)) {
|
|
566
|
-
const onContentRelationshipFieldChange = (
|
|
567
|
-
newCrFields: PickerContentRelationshipFieldValue,
|
|
568
|
-
) => {
|
|
569
|
-
onCustomTypeChange({
|
|
570
|
-
...customTypeFieldsCheckMap,
|
|
571
|
-
[fieldId]: {
|
|
572
|
-
type: "contentRelationship",
|
|
573
|
-
value: newCrFields,
|
|
574
|
-
},
|
|
575
|
-
});
|
|
576
|
-
};
|
|
577
|
-
|
|
578
|
-
const crFieldCheckMap = customTypeFieldsCheckMap[fieldId] ?? {};
|
|
579
|
-
|
|
580
|
-
return (
|
|
581
|
-
<TreeViewContentRelationshipField
|
|
582
|
-
key={fieldId}
|
|
583
|
-
field={field}
|
|
584
|
-
fieldId={fieldId}
|
|
585
|
-
onChange={onContentRelationshipFieldChange}
|
|
586
|
-
fieldCheckMap={
|
|
587
|
-
crFieldCheckMap.type === "contentRelationship"
|
|
588
|
-
? crFieldCheckMap.value
|
|
589
|
-
: {}
|
|
590
|
-
}
|
|
591
|
-
allCustomTypes={allCustomTypes}
|
|
592
|
-
/>
|
|
593
|
-
);
|
|
594
|
-
}
|
|
595
|
-
|
|
596
|
-
// Regular field
|
|
597
|
-
|
|
598
|
-
const onCheckedChange = (newValue: boolean) => {
|
|
599
|
-
onCustomTypeChange({
|
|
600
|
-
...customTypeFieldsCheckMap,
|
|
601
|
-
[fieldId]: { type: "checkbox", value: newValue },
|
|
602
|
-
});
|
|
603
|
-
};
|
|
604
|
-
|
|
605
|
-
return (
|
|
606
|
-
<TreeViewCheckbox
|
|
607
|
-
key={fieldId}
|
|
608
|
-
title={fieldId}
|
|
609
|
-
checked={customTypeFieldsCheckMap[fieldId]?.value === true}
|
|
610
|
-
onCheckedChange={onCheckedChange}
|
|
611
|
-
/>
|
|
612
|
-
);
|
|
613
|
-
},
|
|
614
|
-
);
|
|
615
|
-
|
|
616
|
-
const exposedFieldsCount = countPickedFields(customTypeFieldsCheckMap);
|
|
617
|
-
|
|
618
|
-
return (
|
|
619
|
-
<TreeViewSection
|
|
620
|
-
key={customType.id}
|
|
621
|
-
title={customType.id}
|
|
622
|
-
subtitle={
|
|
623
|
-
exposedFieldsCount.pickedFields > 0
|
|
624
|
-
? getPickedFieldsLabel(
|
|
625
|
-
exposedFieldsCount.pickedFields,
|
|
626
|
-
"returned in the API",
|
|
627
|
-
)
|
|
628
|
-
: "(No fields returned in the API)"
|
|
629
|
-
}
|
|
630
|
-
badge={getTypeFormatLabel(customType.format)}
|
|
631
|
-
defaultOpen
|
|
632
|
-
>
|
|
633
|
-
{renderedFields.length > 0 ? renderedFields : <NoFieldsAvailable />}
|
|
634
|
-
</TreeViewSection>
|
|
635
|
-
);
|
|
636
|
-
}
|
|
637
|
-
|
|
638
|
-
interface TreeViewContentRelationshipFieldProps {
|
|
639
|
-
fieldId: string;
|
|
640
|
-
field: Link;
|
|
641
|
-
fieldCheckMap: PickerContentRelationshipFieldValue;
|
|
642
|
-
onChange: (newValue: PickerContentRelationshipFieldValue) => void;
|
|
643
|
-
allCustomTypes: CustomType[];
|
|
644
|
-
}
|
|
645
|
-
|
|
646
|
-
function TreeViewContentRelationshipField(
|
|
647
|
-
props: TreeViewContentRelationshipFieldProps,
|
|
648
|
-
) {
|
|
649
|
-
const {
|
|
650
|
-
field,
|
|
651
|
-
fieldId,
|
|
652
|
-
fieldCheckMap: crFieldsCheckMap,
|
|
653
|
-
onChange: onCrFieldChange,
|
|
654
|
-
allCustomTypes,
|
|
655
|
-
} = props;
|
|
656
|
-
|
|
657
|
-
if (!field.config?.customtypes) return null;
|
|
658
|
-
|
|
659
|
-
const resolvedCustomTypes = resolveContentRelationshipCustomTypes(
|
|
660
|
-
field.config.customtypes,
|
|
661
|
-
allCustomTypes,
|
|
662
|
-
);
|
|
663
|
-
|
|
664
|
-
if (resolvedCustomTypes.length === 0) return null;
|
|
665
|
-
|
|
666
|
-
return (
|
|
667
|
-
<TreeViewSection
|
|
668
|
-
title={fieldId}
|
|
669
|
-
subtitle={getPickedFieldsLabel(
|
|
670
|
-
countPickedFields(crFieldsCheckMap).pickedFields,
|
|
671
|
-
)}
|
|
672
|
-
>
|
|
673
|
-
{resolvedCustomTypes.map((customType) => {
|
|
674
|
-
if (typeof customType === "string") return null;
|
|
675
|
-
|
|
676
|
-
const onNestedCustomTypeChange = (
|
|
677
|
-
newNestedCustomTypeFields: PickerNestedCustomTypeValue,
|
|
678
|
-
) => {
|
|
679
|
-
onCrFieldChange({
|
|
680
|
-
...crFieldsCheckMap,
|
|
681
|
-
[customType.id]: newNestedCustomTypeFields,
|
|
682
|
-
});
|
|
683
|
-
};
|
|
684
|
-
|
|
685
|
-
const nestedCtFieldsCheckMap = crFieldsCheckMap[customType.id] ?? {};
|
|
686
|
-
|
|
687
|
-
const renderedFields = getCustomTypeStaticFields(customType).map(
|
|
688
|
-
({ fieldId, field }) => {
|
|
689
|
-
// Group field
|
|
690
|
-
|
|
691
|
-
if (field.type === "Group") {
|
|
692
|
-
const onGroupFieldsChange = (
|
|
693
|
-
newGroupFields: PickerLeafGroupFieldValue,
|
|
694
|
-
) => {
|
|
695
|
-
onNestedCustomTypeChange({
|
|
696
|
-
...nestedCtFieldsCheckMap,
|
|
697
|
-
[fieldId]: { type: "group", value: newGroupFields },
|
|
698
|
-
});
|
|
699
|
-
};
|
|
700
|
-
|
|
701
|
-
const groupFieldCheckMap = nestedCtFieldsCheckMap[fieldId] ?? {};
|
|
702
|
-
|
|
703
|
-
return (
|
|
704
|
-
<TreeViewLeafGroupField
|
|
705
|
-
key={fieldId}
|
|
706
|
-
group={field}
|
|
707
|
-
groupId={fieldId}
|
|
708
|
-
onChange={onGroupFieldsChange}
|
|
709
|
-
fieldCheckMap={
|
|
710
|
-
groupFieldCheckMap.type === "group"
|
|
711
|
-
? groupFieldCheckMap.value
|
|
712
|
-
: {}
|
|
713
|
-
}
|
|
714
|
-
/>
|
|
715
|
-
);
|
|
716
|
-
}
|
|
717
|
-
|
|
718
|
-
// Regular field
|
|
719
|
-
|
|
720
|
-
const onCheckedChange = (newChecked: boolean) => {
|
|
721
|
-
onNestedCustomTypeChange({
|
|
722
|
-
...nestedCtFieldsCheckMap,
|
|
723
|
-
[fieldId]: { type: "checkbox", value: newChecked },
|
|
724
|
-
});
|
|
725
|
-
};
|
|
726
|
-
|
|
727
|
-
return (
|
|
728
|
-
<TreeViewCheckbox
|
|
729
|
-
key={fieldId}
|
|
730
|
-
title={fieldId}
|
|
731
|
-
checked={nestedCtFieldsCheckMap[fieldId]?.value === true}
|
|
732
|
-
onCheckedChange={onCheckedChange}
|
|
733
|
-
/>
|
|
734
|
-
);
|
|
735
|
-
},
|
|
736
|
-
);
|
|
737
|
-
|
|
738
|
-
return (
|
|
739
|
-
<TreeViewSection
|
|
740
|
-
key={customType.id}
|
|
741
|
-
title={customType.id}
|
|
742
|
-
subtitle={getPickedFieldsLabel(
|
|
743
|
-
countPickedFields(nestedCtFieldsCheckMap).pickedFields,
|
|
744
|
-
)}
|
|
745
|
-
badge={getTypeFormatLabel(customType.format)}
|
|
746
|
-
>
|
|
747
|
-
{renderedFields.length > 0 ? renderedFields : <NoFieldsAvailable />}
|
|
748
|
-
</TreeViewSection>
|
|
749
|
-
);
|
|
750
|
-
})}
|
|
751
|
-
</TreeViewSection>
|
|
752
|
-
);
|
|
753
|
-
}
|
|
754
|
-
|
|
755
|
-
function NoFieldsAvailable() {
|
|
756
|
-
return <Text color="grey11">No available fields to select</Text>;
|
|
757
|
-
}
|
|
758
|
-
|
|
759
|
-
interface TreeViewLeafGroupFieldProps {
|
|
760
|
-
group: Group;
|
|
761
|
-
groupId: string;
|
|
762
|
-
fieldCheckMap: PickerLeafGroupFieldValue;
|
|
763
|
-
onChange: (newValue: PickerLeafGroupFieldValue) => void;
|
|
764
|
-
}
|
|
765
|
-
|
|
766
|
-
function TreeViewLeafGroupField(props: TreeViewLeafGroupFieldProps) {
|
|
767
|
-
const {
|
|
768
|
-
group,
|
|
769
|
-
groupId,
|
|
770
|
-
fieldCheckMap: groupFieldsCheckMap,
|
|
771
|
-
onChange: onGroupFieldChange,
|
|
772
|
-
} = props;
|
|
773
|
-
|
|
774
|
-
if (!group.config?.fields) return null;
|
|
775
|
-
|
|
776
|
-
const renderedFields = getGroupFields(group).map(({ fieldId }) => {
|
|
777
|
-
const onCheckedChange = (newChecked: boolean) => {
|
|
778
|
-
onGroupFieldChange({
|
|
779
|
-
...groupFieldsCheckMap,
|
|
780
|
-
[fieldId]: { type: "checkbox", value: newChecked },
|
|
781
|
-
});
|
|
782
|
-
};
|
|
783
|
-
|
|
784
|
-
return (
|
|
785
|
-
<TreeViewCheckbox
|
|
786
|
-
key={fieldId}
|
|
787
|
-
title={fieldId}
|
|
788
|
-
checked={groupFieldsCheckMap[fieldId]?.value === true}
|
|
789
|
-
onCheckedChange={onCheckedChange}
|
|
790
|
-
/>
|
|
791
|
-
);
|
|
792
|
-
});
|
|
793
|
-
|
|
794
|
-
return (
|
|
795
|
-
<TreeViewSection
|
|
796
|
-
key={groupId}
|
|
797
|
-
title={groupId}
|
|
798
|
-
subtitle={getPickedFieldsLabel(
|
|
799
|
-
countPickedFields(groupFieldsCheckMap).pickedFields,
|
|
800
|
-
)}
|
|
801
|
-
badge="Group"
|
|
802
|
-
>
|
|
803
|
-
{renderedFields.length > 0 ? renderedFields : <NoFieldsAvailable />}
|
|
804
|
-
</TreeViewSection>
|
|
805
|
-
);
|
|
806
|
-
}
|
|
807
|
-
|
|
808
|
-
interface TreeViewFirstLevelGroupFieldProps {
|
|
809
|
-
group: Group;
|
|
810
|
-
groupId: string;
|
|
811
|
-
fieldCheckMap: PickerFirstLevelGroupFieldValue;
|
|
812
|
-
onChange: (newValue: PickerFirstLevelGroupFieldValue) => void;
|
|
813
|
-
allCustomTypes: CustomType[];
|
|
814
|
-
}
|
|
815
|
-
|
|
816
|
-
function TreeViewFirstLevelGroupField(
|
|
817
|
-
props: TreeViewFirstLevelGroupFieldProps,
|
|
818
|
-
) {
|
|
819
|
-
const {
|
|
820
|
-
group,
|
|
821
|
-
groupId,
|
|
822
|
-
fieldCheckMap: groupFieldsCheckMap,
|
|
823
|
-
onChange: onGroupFieldChange,
|
|
824
|
-
allCustomTypes,
|
|
825
|
-
} = props;
|
|
826
|
-
|
|
827
|
-
const renderedFields = getGroupFields(group).map(({ fieldId, field }) => {
|
|
828
|
-
// Content relationship field with custom types
|
|
829
|
-
|
|
830
|
-
if (isContentRelationshipFieldWithSingleCustomtype(field)) {
|
|
831
|
-
const onContentRelationshipFieldChange = (
|
|
832
|
-
newCrFields: PickerContentRelationshipFieldValue,
|
|
833
|
-
) => {
|
|
834
|
-
onGroupFieldChange({
|
|
835
|
-
...groupFieldsCheckMap,
|
|
836
|
-
[fieldId]: {
|
|
837
|
-
type: "contentRelationship",
|
|
838
|
-
value: newCrFields,
|
|
839
|
-
},
|
|
840
|
-
});
|
|
841
|
-
};
|
|
842
|
-
|
|
843
|
-
const crFieldCheckMap = groupFieldsCheckMap[fieldId] ?? {};
|
|
844
|
-
|
|
845
|
-
return (
|
|
846
|
-
<TreeViewContentRelationshipField
|
|
847
|
-
key={fieldId}
|
|
848
|
-
field={field}
|
|
849
|
-
fieldId={fieldId}
|
|
850
|
-
fieldCheckMap={
|
|
851
|
-
crFieldCheckMap.type === "contentRelationship"
|
|
852
|
-
? crFieldCheckMap.value
|
|
853
|
-
: {}
|
|
854
|
-
}
|
|
855
|
-
onChange={onContentRelationshipFieldChange}
|
|
856
|
-
allCustomTypes={allCustomTypes}
|
|
857
|
-
/>
|
|
858
|
-
);
|
|
859
|
-
}
|
|
860
|
-
|
|
861
|
-
// Regular field
|
|
862
|
-
|
|
863
|
-
const onCheckedChange = (newChecked: boolean) => {
|
|
864
|
-
onGroupFieldChange({
|
|
865
|
-
...groupFieldsCheckMap,
|
|
866
|
-
[fieldId]: { type: "checkbox", value: newChecked },
|
|
867
|
-
});
|
|
868
|
-
};
|
|
869
|
-
|
|
870
|
-
return (
|
|
871
|
-
<TreeViewCheckbox
|
|
872
|
-
key={fieldId}
|
|
873
|
-
title={fieldId}
|
|
874
|
-
checked={groupFieldsCheckMap[fieldId]?.value === true}
|
|
875
|
-
onCheckedChange={onCheckedChange}
|
|
876
|
-
/>
|
|
877
|
-
);
|
|
878
|
-
});
|
|
879
|
-
|
|
880
|
-
return (
|
|
881
|
-
<TreeViewSection
|
|
882
|
-
key={groupId}
|
|
883
|
-
title={groupId}
|
|
884
|
-
subtitle={getPickedFieldsLabel(
|
|
885
|
-
countPickedFields(groupFieldsCheckMap).pickedFields,
|
|
886
|
-
)}
|
|
887
|
-
badge="Group"
|
|
888
|
-
>
|
|
889
|
-
{renderedFields.length > 0 ? renderedFields : <NoFieldsAvailable />}
|
|
890
|
-
</TreeViewSection>
|
|
891
|
-
);
|
|
892
|
-
}
|
|
893
|
-
|
|
894
|
-
function getPickedFieldsLabel(count: number, suffix = "selected") {
|
|
895
|
-
if (count === 0) return undefined;
|
|
896
|
-
return `(${count} ${pluralize(count, "field", "fields")} ${suffix})`;
|
|
897
|
-
}
|
|
898
|
-
|
|
899
|
-
function getTypeFormatLabel(format: CustomType["format"]) {
|
|
900
|
-
return format === "page" ? "Page type" : "Custom type";
|
|
901
|
-
}
|
|
902
|
-
|
|
903
|
-
/** Retrieves all existing page & custom types. */
|
|
904
|
-
function useCustomTypes(value: LinkCustomtypes | undefined): {
|
|
905
|
-
/** Every existing custom type, used to discover nested custom types down the tree and the add type dropdown. */
|
|
906
|
-
allCustomTypes: CustomType[];
|
|
907
|
-
/** The custom types that are already picked. */
|
|
908
|
-
pickedCustomTypes: CustomType[];
|
|
909
|
-
} {
|
|
910
|
-
const { customTypes: allCustomTypes } = useCustomTypesRequest();
|
|
911
|
-
|
|
912
|
-
useEffect(() => {
|
|
913
|
-
void revalidateGetCustomTypes();
|
|
914
|
-
}, []);
|
|
915
|
-
|
|
916
|
-
if (!value) {
|
|
917
|
-
return {
|
|
918
|
-
allCustomTypes,
|
|
919
|
-
pickedCustomTypes: [],
|
|
920
|
-
};
|
|
921
|
-
}
|
|
922
|
-
|
|
923
|
-
const pickedCustomTypes = value.flatMap(
|
|
924
|
-
(pickedCt) => allCustomTypes.find((ct) => ct.id === getId(pickedCt)) ?? [],
|
|
925
|
-
);
|
|
926
|
-
|
|
927
|
-
return {
|
|
928
|
-
allCustomTypes,
|
|
929
|
-
pickedCustomTypes,
|
|
930
|
-
};
|
|
931
|
-
}
|
|
932
|
-
|
|
933
|
-
function resolveContentRelationshipCustomTypes(
|
|
934
|
-
linkCustomtypes: LinkCustomtypes,
|
|
935
|
-
localCustomTypes: CustomType[],
|
|
936
|
-
): CustomType[] {
|
|
937
|
-
return linkCustomtypes.flatMap((linkCustomtype) => {
|
|
938
|
-
return localCustomTypes.find((ct) => ct.id === getId(linkCustomtype)) ?? [];
|
|
939
|
-
});
|
|
940
|
-
}
|
|
941
|
-
|
|
942
|
-
/**
|
|
943
|
-
* Converts a Link config `customtypes` ({@link LinkCustomtypes}) structure into
|
|
944
|
-
* picker fields check map ({@link PickerCustomTypes}).
|
|
945
|
-
*/
|
|
946
|
-
export function convertLinkCustomtypesToFieldCheckMap(
|
|
947
|
-
customTypes: LinkCustomtypes,
|
|
948
|
-
): PickerCustomTypes {
|
|
949
|
-
return customTypes.reduce<PickerCustomTypes>((customTypes, customType) => {
|
|
950
|
-
if (typeof customType === "string") return customTypes;
|
|
951
|
-
|
|
952
|
-
customTypes[customType.id] = customType.fields.reduce<PickerCustomType>(
|
|
953
|
-
(customTypeFields, field) => {
|
|
954
|
-
if (typeof field === "string") {
|
|
955
|
-
// Regular field
|
|
956
|
-
customTypeFields[field] = { type: "checkbox", value: true };
|
|
957
|
-
} else if ("fields" in field && field.fields !== undefined) {
|
|
958
|
-
// Group field
|
|
959
|
-
customTypeFields[field.id] = createGroupFieldCheckMap(field);
|
|
960
|
-
} else if ("customtypes" in field && field.customtypes !== undefined) {
|
|
961
|
-
// Content relationship field
|
|
962
|
-
customTypeFields[field.id] =
|
|
963
|
-
createContentRelationshipFieldCheckMap(field);
|
|
964
|
-
}
|
|
965
|
-
|
|
966
|
-
return customTypeFields;
|
|
967
|
-
},
|
|
968
|
-
{},
|
|
969
|
-
);
|
|
970
|
-
return customTypes;
|
|
971
|
-
}, {});
|
|
972
|
-
}
|
|
973
|
-
|
|
974
|
-
function createGroupFieldCheckMap(
|
|
975
|
-
group: LinkCustomtypesGroupFieldValue,
|
|
976
|
-
): PickerFirstLevelGroupField {
|
|
977
|
-
return {
|
|
978
|
-
type: "group",
|
|
979
|
-
value: group.fields.reduce<PickerFirstLevelGroupFieldValue>(
|
|
980
|
-
(fields, field) => {
|
|
981
|
-
if (typeof field === "string") {
|
|
982
|
-
// Regular field
|
|
983
|
-
fields[field] = { type: "checkbox", value: true };
|
|
984
|
-
} else if ("customtypes" in field && field.customtypes !== undefined) {
|
|
985
|
-
// Content relationship field
|
|
986
|
-
fields[field.id] = createContentRelationshipFieldCheckMap(field);
|
|
987
|
-
}
|
|
988
|
-
|
|
989
|
-
return fields;
|
|
990
|
-
},
|
|
991
|
-
{},
|
|
992
|
-
),
|
|
993
|
-
};
|
|
994
|
-
}
|
|
995
|
-
|
|
996
|
-
function createContentRelationshipFieldCheckMap(
|
|
997
|
-
field: LinkCustomtypesContentRelationshipFieldValue,
|
|
998
|
-
): PickerContentRelationshipField {
|
|
999
|
-
const crField: PickerContentRelationshipField = {
|
|
1000
|
-
type: "contentRelationship",
|
|
1001
|
-
value: {},
|
|
1002
|
-
};
|
|
1003
|
-
const crFieldCustomTypes = crField.value;
|
|
1004
|
-
|
|
1005
|
-
for (const customType of field.customtypes) {
|
|
1006
|
-
if (typeof customType === "string") continue;
|
|
1007
|
-
|
|
1008
|
-
crFieldCustomTypes[customType.id] ??= {};
|
|
1009
|
-
const customTypeFields = crFieldCustomTypes[customType.id];
|
|
1010
|
-
|
|
1011
|
-
for (const nestedField of customType.fields) {
|
|
1012
|
-
if (typeof nestedField === "string") {
|
|
1013
|
-
// Regular field
|
|
1014
|
-
customTypeFields[nestedField] = { type: "checkbox", value: true };
|
|
1015
|
-
} else {
|
|
1016
|
-
// Group field
|
|
1017
|
-
const groupFieldsEntries = nestedField.fields.map(
|
|
1018
|
-
(field) => [field, { type: "checkbox", value: true }] as const,
|
|
1019
|
-
);
|
|
1020
|
-
|
|
1021
|
-
if (groupFieldsEntries.length > 0) {
|
|
1022
|
-
customTypeFields[nestedField.id] = {
|
|
1023
|
-
type: "group",
|
|
1024
|
-
value: Object.fromEntries(groupFieldsEntries),
|
|
1025
|
-
};
|
|
1026
|
-
}
|
|
1027
|
-
}
|
|
1028
|
-
}
|
|
1029
|
-
}
|
|
1030
|
-
|
|
1031
|
-
return crField;
|
|
1032
|
-
}
|
|
1033
|
-
|
|
1034
|
-
/**
|
|
1035
|
-
* Merges the existing Link `customtypes` array with the picker state, ensuring
|
|
1036
|
-
* that conversions from to string (custom type id) to object and vice versa are
|
|
1037
|
-
* made correctly and that the order is preserved.
|
|
1038
|
-
*/
|
|
1039
|
-
function mergeAndConvertCheckMapToLinkCustomtypes(args: {
|
|
1040
|
-
existingLinkCustomtypes: LinkCustomtypes | undefined;
|
|
1041
|
-
previousPickerCustomtypes: PickerCustomTypes;
|
|
1042
|
-
newCustomType: PickerCustomType;
|
|
1043
|
-
customTypeId: string;
|
|
1044
|
-
}): LinkCustomtypes {
|
|
1045
|
-
const {
|
|
1046
|
-
existingLinkCustomtypes,
|
|
1047
|
-
previousPickerCustomtypes,
|
|
1048
|
-
newCustomType,
|
|
1049
|
-
customTypeId,
|
|
1050
|
-
} = args;
|
|
1051
|
-
|
|
1052
|
-
const result: NonReadonly<LinkCustomtypes> = [];
|
|
1053
|
-
const pickerLinkCustomtypes = convertFieldCheckMapToLinkCustomtypes({
|
|
1054
|
-
...previousPickerCustomtypes,
|
|
1055
|
-
[customTypeId]: newCustomType,
|
|
1056
|
-
});
|
|
1057
|
-
|
|
1058
|
-
if (!existingLinkCustomtypes) return pickerLinkCustomtypes;
|
|
1059
|
-
|
|
1060
|
-
for (const existingLinkCt of existingLinkCustomtypes) {
|
|
1061
|
-
const existingPickerLinkCt = pickerLinkCustomtypes.find((ct) => {
|
|
1062
|
-
return getId(ct) === getId(existingLinkCt);
|
|
1063
|
-
});
|
|
1064
|
-
|
|
1065
|
-
if (existingPickerLinkCt !== undefined) {
|
|
1066
|
-
// Custom type with exposed fields, keep the customtypes object
|
|
1067
|
-
result.push(existingPickerLinkCt);
|
|
1068
|
-
} else if (getId(existingLinkCt) === customTypeId) {
|
|
1069
|
-
// Custom type that had exposed fields, but now has none, change to string
|
|
1070
|
-
result.push(getId(existingLinkCt));
|
|
1071
|
-
} else {
|
|
1072
|
-
// Custom type without exposed fields, keep the string
|
|
1073
|
-
result.push(existingLinkCt);
|
|
1074
|
-
}
|
|
1075
|
-
}
|
|
1076
|
-
|
|
1077
|
-
return result;
|
|
1078
|
-
}
|
|
1079
|
-
|
|
1080
|
-
/**
|
|
1081
|
-
* Converts a picker fields check map structure ({@link PickerCustomTypes}) into
|
|
1082
|
-
* Link config `customtypes` ({@link LinkCustomtypes}) and filter out empty Custom
|
|
1083
|
-
* types.
|
|
1084
|
-
*/
|
|
1085
|
-
function convertFieldCheckMapToLinkCustomtypes(
|
|
1086
|
-
checkMap: PickerCustomTypes,
|
|
1087
|
-
): LinkCustomtypes {
|
|
1088
|
-
return Object.entries(checkMap).flatMap<LinkCustomtypes[number]>(
|
|
1089
|
-
([ctId, ctFields]) => {
|
|
1090
|
-
const fields = Object.entries(ctFields).flatMap<
|
|
1091
|
-
| string
|
|
1092
|
-
| LinkCustomtypesContentRelationshipFieldValue
|
|
1093
|
-
| LinkCustomtypesGroupFieldValue
|
|
1094
|
-
>(([fieldId, fieldValue]) => {
|
|
1095
|
-
// First level group field
|
|
1096
|
-
if (fieldValue.type === "group") {
|
|
1097
|
-
const fields = Object.entries(fieldValue.value).flatMap<
|
|
1098
|
-
string | LinkCustomtypesContentRelationshipFieldValue
|
|
1099
|
-
>(([fieldId, fieldValue]) => {
|
|
1100
|
-
if (fieldValue.type === "checkbox") {
|
|
1101
|
-
return fieldValue.value ? fieldId : [];
|
|
1102
|
-
}
|
|
1103
|
-
|
|
1104
|
-
const customTypes = createContentRelationshipLinkCustomtypes(
|
|
1105
|
-
fieldValue.value,
|
|
1106
|
-
);
|
|
1107
|
-
|
|
1108
|
-
return customTypes.length > 0
|
|
1109
|
-
? { id: fieldId, customtypes: customTypes }
|
|
1110
|
-
: [];
|
|
1111
|
-
});
|
|
1112
|
-
|
|
1113
|
-
return fields.length > 0 ? { id: fieldId, fields } : [];
|
|
1114
|
-
}
|
|
1115
|
-
|
|
1116
|
-
// Content relationship field
|
|
1117
|
-
if (fieldValue.type === "contentRelationship") {
|
|
1118
|
-
const customTypes = createContentRelationshipLinkCustomtypes(
|
|
1119
|
-
fieldValue.value,
|
|
1120
|
-
);
|
|
1121
|
-
|
|
1122
|
-
return customTypes.length > 0
|
|
1123
|
-
? { id: fieldId, customtypes: customTypes }
|
|
1124
|
-
: [];
|
|
1125
|
-
}
|
|
1126
|
-
|
|
1127
|
-
// Regular field
|
|
1128
|
-
return fieldValue.value ? fieldId : [];
|
|
1129
|
-
});
|
|
1130
|
-
|
|
1131
|
-
return fields.length > 0 ? { id: ctId, fields } : [];
|
|
1132
|
-
},
|
|
1133
|
-
);
|
|
1134
|
-
}
|
|
1135
|
-
|
|
1136
|
-
function createContentRelationshipLinkCustomtypes(
|
|
1137
|
-
value: PickerContentRelationshipFieldValue,
|
|
1138
|
-
): LinkCustomtypesContentRelationshipFieldValue["customtypes"] {
|
|
1139
|
-
return Object.entries(value).flatMap(
|
|
1140
|
-
([nestedCustomTypeId, nestedCustomTypeFields]) => {
|
|
1141
|
-
const fields = Object.entries(nestedCustomTypeFields).flatMap(
|
|
1142
|
-
([nestedFieldId, nestedFieldValue]) => {
|
|
1143
|
-
// Leaf group field
|
|
1144
|
-
if (nestedFieldValue.type === "group") {
|
|
1145
|
-
const nestedGroupFields = Object.entries(
|
|
1146
|
-
nestedFieldValue.value,
|
|
1147
|
-
).flatMap<string>(([fieldId, fieldValue]) => {
|
|
1148
|
-
// Regular field
|
|
1149
|
-
return fieldValue.type === "checkbox" && fieldValue.value
|
|
1150
|
-
? fieldId
|
|
1151
|
-
: [];
|
|
1152
|
-
});
|
|
1153
|
-
|
|
1154
|
-
return nestedGroupFields.length > 0
|
|
1155
|
-
? { id: nestedFieldId, fields: nestedGroupFields }
|
|
1156
|
-
: [];
|
|
1157
|
-
}
|
|
1158
|
-
|
|
1159
|
-
return nestedFieldValue.value ? nestedFieldId : [];
|
|
1160
|
-
},
|
|
1161
|
-
);
|
|
1162
|
-
|
|
1163
|
-
return fields.length > 0 ? { id: nestedCustomTypeId, fields } : [];
|
|
1164
|
-
},
|
|
1165
|
-
);
|
|
1166
|
-
}
|
|
1167
|
-
|
|
1168
|
-
type CountPickedFieldsResult = {
|
|
1169
|
-
pickedFields: number;
|
|
1170
|
-
nestedPickedFields: number;
|
|
1171
|
-
};
|
|
1172
|
-
|
|
1173
|
-
/**
|
|
1174
|
-
* Generic recursive function that goes down the fields check map and counts all
|
|
1175
|
-
* the properties that are set to true, which correspond to selected fields.
|
|
1176
|
-
*
|
|
1177
|
-
* Distinguishes between all picked fields and nested picked fields within a
|
|
1178
|
-
* content relationship field.
|
|
1179
|
-
*
|
|
1180
|
-
* It's not type safe, but checks the type of the values at runtime so that
|
|
1181
|
-
* it only recurses into valid objects, and only counts checkbox fields.
|
|
1182
|
-
*/
|
|
1183
|
-
export function countPickedFields(
|
|
1184
|
-
fields: Record<string, unknown> | undefined,
|
|
1185
|
-
isNested = false,
|
|
1186
|
-
): CountPickedFieldsResult {
|
|
1187
|
-
if (!fields) return { pickedFields: 0, nestedPickedFields: 0 };
|
|
1188
|
-
|
|
1189
|
-
return Object.values(fields).reduce<CountPickedFieldsResult>(
|
|
1190
|
-
(result, value) => {
|
|
1191
|
-
if (!isValidObject(value)) return result;
|
|
1192
|
-
|
|
1193
|
-
if ("type" in value && value.type === "checkbox") {
|
|
1194
|
-
const isChecked = Boolean(value.value);
|
|
1195
|
-
if (!isChecked) return result;
|
|
1196
|
-
|
|
1197
|
-
return {
|
|
1198
|
-
pickedFields: result.pickedFields + 1,
|
|
1199
|
-
nestedPickedFields: result.nestedPickedFields + (isNested ? 1 : 0),
|
|
1200
|
-
};
|
|
1201
|
-
}
|
|
1202
|
-
|
|
1203
|
-
if ("type" in value && value.type === "contentRelationship") {
|
|
1204
|
-
const { pickedFields, nestedPickedFields } = countPickedFields(
|
|
1205
|
-
value,
|
|
1206
|
-
true,
|
|
1207
|
-
);
|
|
1208
|
-
|
|
1209
|
-
return {
|
|
1210
|
-
pickedFields: result.pickedFields + pickedFields,
|
|
1211
|
-
nestedPickedFields: result.nestedPickedFields + nestedPickedFields,
|
|
1212
|
-
};
|
|
1213
|
-
}
|
|
1214
|
-
|
|
1215
|
-
const { pickedFields, nestedPickedFields } = countPickedFields(
|
|
1216
|
-
value,
|
|
1217
|
-
isNested,
|
|
1218
|
-
);
|
|
1219
|
-
|
|
1220
|
-
return {
|
|
1221
|
-
pickedFields: result.pickedFields + pickedFields,
|
|
1222
|
-
nestedPickedFields: result.nestedPickedFields + nestedPickedFields,
|
|
1223
|
-
};
|
|
1224
|
-
},
|
|
1225
|
-
{
|
|
1226
|
-
pickedFields: 0,
|
|
1227
|
-
nestedPickedFields: 0,
|
|
1228
|
-
},
|
|
1229
|
-
);
|
|
1230
|
-
}
|
|
1231
|
-
|
|
1232
|
-
/**
|
|
1233
|
-
* Check if the field is a Content Relationship Link with a **single** custom
|
|
1234
|
-
* type. CRs with multiple custom types are not currently supported (legacy).
|
|
1235
|
-
*/
|
|
1236
|
-
function isContentRelationshipFieldWithSingleCustomtype(
|
|
1237
|
-
field: NestableWidget | Group,
|
|
1238
|
-
): field is Link {
|
|
1239
|
-
return !!(
|
|
1240
|
-
field.type === "Link" &&
|
|
1241
|
-
field.config?.select === "document" &&
|
|
1242
|
-
field.config?.customtypes &&
|
|
1243
|
-
field.config.customtypes.length === 1
|
|
1244
|
-
);
|
|
1245
|
-
}
|
|
1246
|
-
|
|
1247
|
-
function getCustomTypeStaticFields(customType: CustomType) {
|
|
1248
|
-
return Object.values(customType.json).flatMap((tabFields) => {
|
|
1249
|
-
return Object.entries(tabFields).flatMap(([fieldId, field]) => {
|
|
1250
|
-
if (
|
|
1251
|
-
field.type !== "Slices" &&
|
|
1252
|
-
field.type !== "Choice" &&
|
|
1253
|
-
// Filter out uid fields because it's a special field returned by the
|
|
1254
|
-
// API and is not part of the data object in the document.
|
|
1255
|
-
// We also filter by key "uid", because (as of the time of writing
|
|
1256
|
-
// this), creating any field with that API id will result in it being
|
|
1257
|
-
// used for metadata.
|
|
1258
|
-
(field.type !== "UID" || fieldId !== "uid")
|
|
1259
|
-
) {
|
|
1260
|
-
return { fieldId, field: field as NestableWidget | Group };
|
|
1261
|
-
}
|
|
1262
|
-
|
|
1263
|
-
return [];
|
|
1264
|
-
});
|
|
1265
|
-
});
|
|
1266
|
-
}
|
|
1267
|
-
|
|
1268
|
-
function getGroupFields(group: Group) {
|
|
1269
|
-
if (!group.config?.fields) return [];
|
|
1270
|
-
return Object.entries(group.config.fields).map(([fieldId, field]) => {
|
|
1271
|
-
return { fieldId, field: field as NestableWidget };
|
|
1272
|
-
});
|
|
1273
|
-
}
|
|
1274
|
-
|
|
1275
|
-
/** If it's a string, return it, otherwise return the `id` property. */
|
|
1276
|
-
function getId<T extends string | { id: string }>(customType: T): string {
|
|
1277
|
-
if (typeof customType === "string") return customType;
|
|
1278
|
-
return customType.id;
|
|
1279
|
-
}
|