slice-machine-ui 2.16.2-alpha.jp-cr-ui-multiple-improvements-rendering-logic.6 → 2.16.2-alpha.jp-cr-ui-legacy-and-new-picker.1

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.
Files changed (35) hide show
  1. package/out/404.html +1 -1
  2. package/out/_next/static/{brRbaMHv-k9puxAqEHfsy → YCSNkhIsNcvCJJbYQxF12}/_buildManifest.js +1 -1
  3. package/out/_next/static/chunks/{268-b9f569b2a5b4e1c4.js → 268-6a9214b97195af9c.js} +1 -1
  4. package/out/_next/static/chunks/33641354.3864aefb6106ae71.js +28 -0
  5. package/out/_next/static/chunks/{34-a89c34b9365e2fa3.js → 34-e684c5fd75cc9dd0.js} +1 -1
  6. package/out/_next/static/chunks/{630-e151ae4c0afa9897.js → 630-db2634510e265e9a.js} +1 -1
  7. package/out/_next/static/chunks/{867-2756f347bcd416f3.js → 867-bb2db4d365ee7e40.js} +1 -1
  8. package/out/_next/static/chunks/{882-de1be3aece0cc601.js → 882-636039ab926dbc22.js} +1 -1
  9. package/out/_next/static/chunks/{895-566153447e052c73.js → 895-e5bf82a4d5d7c3e1.js} +1 -1
  10. package/out/_next/static/chunks/pages/{_app-3d7d86878ccba2a2.js → _app-ff5b76fbd166df98.js} +268 -219
  11. package/out/_next/static/chunks/pages/{changelog-1e3c6a1e687e5d63.js → changelog-063c5e11dfc8fd55.js} +1 -1
  12. package/out/_next/static/chunks/pages/{changes-12afd5211a55c133.js → changes-564336edb0ed18b0.js} +1 -1
  13. package/out/_next/static/chunks/pages/{labs-e6978d96b28c5621.js → labs-9630bfb1005be02b.js} +1 -1
  14. package/out/_next/static/chunks/pages/{settings-df9b029574887ea1.js → settings-01f4aeb9112a1f87.js} +1 -1
  15. package/out/_next/static/chunks/pages/slices/[lib]/[sliceName]/[variation]/{simulator-6bf879f9bdaf8164.js → simulator-5008e29008aa04f4.js} +1 -1
  16. package/out/_next/static/chunks/pages/slices/[lib]/[sliceName]/{[variation]-11950d263994762e.js → [variation]-85ab73c10f8322e1.js} +1 -1
  17. package/out/_next/static/chunks/pages/{slices-aef1683623e9583c.js → slices-4a60cd5f2c71327e.js} +1 -1
  18. package/out/_next/static/chunks/{webpack-db4c6e9b826ac694.js → webpack-b3522fdebabf510a.js} +1 -1
  19. package/out/_next/static/css/cc9b10286400c2b9.css +1 -0
  20. package/out/changelog.html +1 -1
  21. package/out/changes.html +1 -1
  22. package/out/custom-types/[customTypeId].html +1 -1
  23. package/out/custom-types.html +1 -1
  24. package/out/index.html +1 -1
  25. package/out/labs.html +1 -1
  26. package/out/page-types/[pageTypeId].html +1 -1
  27. package/out/settings.html +1 -1
  28. package/out/slices/[lib]/[sliceName]/[variation]/simulator.html +1 -1
  29. package/out/slices/[lib]/[sliceName]/[variation].html +1 -1
  30. package/out/slices.html +1 -1
  31. package/package.json +6 -6
  32. package/src/features/customTypes/fields/ContentRelationshipFieldPicker.tsx +377 -67
  33. package/out/_next/static/chunks/454bc60f.6a84d718ae50c9e6.js +0 -28
  34. package/out/_next/static/css/5785747ad1838276.css +0 -1
  35. /package/out/_next/static/{brRbaMHv-k9puxAqEHfsy → YCSNkhIsNcvCJJbYQxF12}/_ssgManifest.js +0 -0
@@ -1,7 +1,21 @@
1
1
  import { pluralize } from "@prismicio/editor-support/String";
2
+ import { revalidateData, useRequest } from "@prismicio/editor-support/Suspense";
2
3
  import {
4
+ AnimatedSuspense,
5
+ Badge,
3
6
  Box,
7
+ Button,
8
+ DropdownMenu,
9
+ DropdownMenuContent,
10
+ DropdownMenuItem,
11
+ DropdownMenuLabel,
12
+ DropdownMenuTrigger,
13
+ Icon,
14
+ IconButton,
15
+ Skeleton,
4
16
  Text,
17
+ TextOverflow,
18
+ Tooltip,
5
19
  TreeView,
6
20
  TreeViewCheckbox,
7
21
  TreeViewSection,
@@ -13,11 +27,13 @@ import {
13
27
  LinkConfig,
14
28
  NestableWidget,
15
29
  } from "@prismicio/types-internal/lib/customtypes";
16
- import { useSelector } from "react-redux";
17
30
 
18
- import { CustomTypes } from "@/legacy/lib/models/common/CustomType";
19
- import { selectAllCustomTypes } from "@/modules/availableCustomTypes";
31
+ import { ErrorBoundary } from "@/ErrorBoundary";
32
+ import { managerClient } from "@/managerClient";
20
33
  import { isValidObject } from "@/utils/isValidObject";
34
+ import { useEffect, useState } from "react";
35
+
36
+ type NonReadonly<T> = { -readonly [P in keyof T]: T[P] };
21
37
 
22
38
  /**
23
39
  * Picker fields check map types. Used internally to keep track of the checked
@@ -204,53 +220,150 @@ interface ContentRelationshipFieldPickerProps {
204
220
 
205
221
  export function ContentRelationshipFieldPicker(
206
222
  props: ContentRelationshipFieldPickerProps,
223
+ ) {
224
+ return (
225
+ <ErrorBoundary
226
+ renderError={() => (
227
+ <Box alignItems="center" gap={8}>
228
+ <Icon name="alert" size="small" color="tomato10" />
229
+ <Text color="tomato10">Error loading your types</Text>
230
+ </Box>
231
+ )}
232
+ >
233
+ <AnimatedSuspense
234
+ fallback={
235
+ <Box flexDirection="column" position="relative">
236
+ <Skeleton height={240} />
237
+ <Box
238
+ position="absolute"
239
+ top="50%"
240
+ left="50%"
241
+ transform="translate(-50%, -50%)"
242
+ alignItems="center"
243
+ gap={8}
244
+ >
245
+ <Icon name="autorenew" size="small" color="grey11" />
246
+ <Text color="grey11">Loading your types...</Text>
247
+ </Box>
248
+ </Box>
249
+ }
250
+ >
251
+ <ContentRelationshipFieldPickerContent {...props} />
252
+ </AnimatedSuspense>
253
+ </ErrorBoundary>
254
+ );
255
+ }
256
+
257
+ function ContentRelationshipFieldPickerContent(
258
+ props: ContentRelationshipFieldPickerProps,
207
259
  ) {
208
260
  const { value, onChange } = props;
209
- const customTypes = useCustomTypes();
261
+ const { allCustomTypes, availableCustomTypes, pickedCustomTypes } =
262
+ useCustomTypes(value);
263
+
264
+ const [isNewType, setIsNewType] = useState(false);
210
265
 
211
266
  const fieldCheckMap = value
212
267
  ? convertLinkCustomtypesToFieldCheckMap(value)
213
268
  : {};
214
269
 
215
- function onCustomTypesChange(customTypeId: string, value: PickerCustomType) {
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.
216
275
  onChange(
217
- convertFieldCheckMapToLinkCustomtypes({
218
- ...fieldCheckMap,
219
- [customTypeId]: value,
276
+ mergeAndConvertCheckMapToLinkCustomtypes({
277
+ existingLinkCustomtypes: value,
278
+ previousPickerCustomtypes: fieldCheckMap,
279
+ customTypeId: id,
280
+ newCustomType,
220
281
  }),
221
282
  );
222
283
  }
223
284
 
285
+ function addCustomType(id: string) {
286
+ setIsNewType(true);
287
+ onChange([...(value ?? []), id]);
288
+ }
289
+
290
+ function removeCustomType(id: string) {
291
+ if (value) {
292
+ onChange(value.filter((existingCt) => getId(existingCt) !== id));
293
+ }
294
+ }
295
+
224
296
  return (
225
- <Box overflow="hidden" flexDirection="column" border borderRadius={6}>
297
+ <Box
298
+ overflow="hidden"
299
+ flexDirection="column"
300
+ border
301
+ borderRadius={6}
302
+ width="100%"
303
+ >
226
304
  <Box
227
305
  border={{ bottom: true }}
228
306
  padding={{ inline: 16, bottom: 16, top: 12 }}
229
307
  flexDirection="column"
230
308
  gap={8}
231
309
  >
232
- <Box flexDirection="column">
233
- <Text variant="h4" color="grey12">
234
- Types
235
- </Text>
236
- <Text color="grey12">
237
- Choose which fields you want to expose from the linked document.
238
- </Text>
239
- </Box>
240
- <TreeView
241
- title="Exposed fields"
242
- subtitle={`(${countPickedFields(fieldCheckMap)})`}
243
- >
244
- {customTypes.map((customType) => (
245
- <TreeViewCustomType
246
- key={customType.id}
247
- customType={customType}
248
- onChange={(value) => onCustomTypesChange(customType.id, value)}
249
- fieldCheckMap={fieldCheckMap[customType.id] ?? {}}
250
- customTypes={customTypes}
310
+ {pickedCustomTypes.length > 0 ? (
311
+ <>
312
+ <Box flexDirection="column">
313
+ <Text variant="h4" color="grey12">
314
+ Allowed Types
315
+ </Text>
316
+ <Text color="grey11">
317
+ Restrict the selection to specific types your content editors
318
+ can link to in the Page Builder.
319
+ <br />
320
+ For each type, choose which fields to expose in the API
321
+ response.
322
+ </Text>
323
+ </Box>
324
+ {pickedCustomTypes.map((customType) => (
325
+ <Box
326
+ key={customType.id}
327
+ gap={4}
328
+ padding={8}
329
+ border
330
+ borderRadius={6}
331
+ borderColor="grey6"
332
+ backgroundColor="white"
333
+ justifyContent="space-between"
334
+ >
335
+ <TreeView>
336
+ <TreeViewCustomType
337
+ customType={customType}
338
+ onChange={(value) =>
339
+ onCustomTypesChange(customType.id, value)
340
+ }
341
+ fieldCheckMap={fieldCheckMap[customType.id] ?? {}}
342
+ allCustomTypes={allCustomTypes}
343
+ isNewType={isNewType}
344
+ />
345
+ </TreeView>
346
+ <IconButton
347
+ icon="close"
348
+ size="small"
349
+ onClick={() => removeCustomType(customType.id)}
350
+ sx={{ height: 24, width: 24 }}
351
+ hiddenLabel="Remove type"
352
+ />
353
+ </Box>
354
+ ))}
355
+ <AddTypeButton
356
+ onSelect={addCustomType}
357
+ pickedCustomTypes={pickedCustomTypes}
358
+ availableCustomTypes={availableCustomTypes}
251
359
  />
252
- ))}
253
- </TreeView>
360
+ </>
361
+ ) : (
362
+ <EmptyView
363
+ onSelect={addCustomType}
364
+ availableCustomTypes={availableCustomTypes}
365
+ />
366
+ )}
254
367
  </Box>
255
368
  <Box backgroundColor="white" flexDirection="column" padding={12}>
256
369
  <Text variant="normal" color="grey11">
@@ -270,11 +383,122 @@ export function ContentRelationshipFieldPicker(
270
383
  );
271
384
  }
272
385
 
386
+ type EmptyViewProps = {
387
+ onSelect: (customTypeId: string) => void;
388
+ availableCustomTypes: CustomType[];
389
+ };
390
+
391
+ function EmptyView(props: EmptyViewProps) {
392
+ const { availableCustomTypes, onSelect } = props;
393
+
394
+ return (
395
+ <Box
396
+ flexDirection="column"
397
+ gap={8}
398
+ alignItems="center"
399
+ padding={{ block: 24 }}
400
+ >
401
+ <Box flexDirection="column" alignItems="center" gap={4}>
402
+ <Text variant="h5" color="grey12">
403
+ No types selected yet.
404
+ </Text>
405
+ <Text color="grey11" component="p" align="center">
406
+ Add one or more document types your content editors can link to.
407
+ <br />
408
+ For each type, select the fields to include in the API response (used
409
+ in your frontend queries).
410
+ </Text>
411
+ </Box>
412
+ <Box>
413
+ <AddTypeButton
414
+ availableCustomTypes={availableCustomTypes}
415
+ onSelect={onSelect}
416
+ />
417
+ </Box>
418
+ </Box>
419
+ );
420
+ }
421
+
422
+ type AddTypeButtonProps = {
423
+ onSelect: (customTypeId: string) => void;
424
+ disabled?: boolean;
425
+ availableCustomTypes: CustomType[];
426
+ pickedCustomTypes?: CustomType[];
427
+ };
428
+
429
+ function AddTypeButton(props: AddTypeButtonProps) {
430
+ const { availableCustomTypes, onSelect, pickedCustomTypes = [] } = props;
431
+
432
+ const triggerButton = (
433
+ <Button
434
+ startIcon="add"
435
+ color="grey"
436
+ disabled={availableCustomTypes.length === 0}
437
+ >
438
+ {pickedCustomTypes.length > 0 ? "Add another type" : "Add type"}
439
+ </Button>
440
+ );
441
+
442
+ const disabledButton = (
443
+ <Box>
444
+ <Tooltip
445
+ content="All available types have been added"
446
+ side="bottom"
447
+ align="start"
448
+ disableHoverableContent
449
+ >
450
+ {triggerButton}
451
+ </Tooltip>
452
+ </Box>
453
+ );
454
+
455
+ if (availableCustomTypes.length === 0) return disabledButton;
456
+
457
+ return (
458
+ <Box>
459
+ <DropdownMenu>
460
+ <DropdownMenuTrigger>{triggerButton}</DropdownMenuTrigger>
461
+ <DropdownMenuContent
462
+ maxHeight={400}
463
+ minWidth={256}
464
+ align={pickedCustomTypes.length > 0 ? "start" : "center"}
465
+ >
466
+ <DropdownMenuLabel>
467
+ <Text color="grey11">Types</Text>
468
+ </DropdownMenuLabel>
469
+ {availableCustomTypes.flatMap((customType) => (
470
+ <DropdownMenuItem
471
+ key={customType.id}
472
+ onSelect={() => onSelect(customType.id)}
473
+ >
474
+ <Box alignItems="center" justifyContent="space-between" gap={8}>
475
+ <TextOverflow>
476
+ <Text>{customType.id}</Text>
477
+ </TextOverflow>
478
+ <Badge
479
+ title={
480
+ <Text variant="extraSmall" color="purple11">
481
+ {getTypeFormatLabel(customType.format)}
482
+ </Text>
483
+ }
484
+ color="purple"
485
+ size="small"
486
+ />
487
+ </Box>
488
+ </DropdownMenuItem>
489
+ ))}
490
+ </DropdownMenuContent>
491
+ </DropdownMenu>
492
+ </Box>
493
+ );
494
+ }
495
+
273
496
  interface TreeViewCustomTypeProps {
274
497
  customType: CustomType;
275
498
  fieldCheckMap: PickerCustomType;
276
499
  onChange: (newValue: PickerCustomType) => void;
277
- customTypes: CustomType[];
500
+ allCustomTypes: CustomType[];
501
+ isNewType: boolean;
278
502
  }
279
503
 
280
504
  function TreeViewCustomType(props: TreeViewCustomTypeProps) {
@@ -282,7 +506,8 @@ function TreeViewCustomType(props: TreeViewCustomTypeProps) {
282
506
  customType,
283
507
  fieldCheckMap: customTypeFieldsCheckMap,
284
508
  onChange: onCustomTypeChange,
285
- customTypes,
509
+ allCustomTypes,
510
+ isNewType,
286
511
  } = props;
287
512
 
288
513
  const renderedFields = getCustomTypeStaticFields(customType).map(
@@ -312,7 +537,7 @@ function TreeViewCustomType(props: TreeViewCustomTypeProps) {
312
537
  ? groupFieldCheckMap.value
313
538
  : {}
314
539
  }
315
- customTypes={customTypes}
540
+ allCustomTypes={allCustomTypes}
316
541
  />
317
542
  );
318
543
  }
@@ -345,7 +570,7 @@ function TreeViewCustomType(props: TreeViewCustomTypeProps) {
345
570
  ? crFieldCheckMap.value
346
571
  : {}
347
572
  }
348
- customTypes={customTypes}
573
+ allCustomTypes={allCustomTypes}
349
574
  />
350
575
  );
351
576
  }
@@ -370,28 +595,34 @@ function TreeViewCustomType(props: TreeViewCustomTypeProps) {
370
595
  },
371
596
  );
372
597
 
373
- if (renderedFields.length === 0) return null;
374
-
598
+ const exposedFieldsCount = countPickedFields(customTypeFieldsCheckMap);
375
599
  return (
376
600
  <TreeViewSection
377
601
  key={customType.id}
378
602
  title={customType.id}
379
- subtitle={getExposedFieldsLabel(
380
- countPickedFields(customTypeFieldsCheckMap),
381
- )}
382
- badge={customType.format === "page" ? "Page type" : "Custom type"}
603
+ subtitle={
604
+ exposedFieldsCount > 0
605
+ ? getExposedFieldsLabel(exposedFieldsCount)
606
+ : "(No fields returned in the API)"
607
+ }
608
+ badge={getTypeFormatLabel(customType.format)}
609
+ defaultOpen={isNewType}
383
610
  >
384
- {renderedFields}
611
+ {renderedFields.length > 0 ? (
612
+ renderedFields
613
+ ) : (
614
+ <Text color="grey11">No available fields to select</Text>
615
+ )}
385
616
  </TreeViewSection>
386
617
  );
387
618
  }
388
619
 
389
620
  interface TreeViewContentRelationshipFieldProps {
390
- field: Link;
391
621
  fieldId: string;
622
+ field: Link;
392
623
  fieldCheckMap: PickerContentRelationshipFieldValue;
393
624
  onChange: (newValue: PickerContentRelationshipFieldValue) => void;
394
- customTypes: CustomType[];
625
+ allCustomTypes: CustomType[];
395
626
  }
396
627
 
397
628
  function TreeViewContentRelationshipField(
@@ -402,14 +633,14 @@ function TreeViewContentRelationshipField(
402
633
  fieldId,
403
634
  fieldCheckMap: crFieldsCheckMap,
404
635
  onChange: onCrFieldChange,
405
- customTypes,
636
+ allCustomTypes,
406
637
  } = props;
407
638
 
408
639
  if (!field.config?.customtypes) return null;
409
640
 
410
641
  const resolvedCustomTypes = resolveContentRelationshipCustomTypes(
411
642
  field.config.customtypes,
412
- customTypes,
643
+ allCustomTypes,
413
644
  );
414
645
 
415
646
  if (resolvedCustomTypes.length === 0) return null;
@@ -493,7 +724,7 @@ function TreeViewContentRelationshipField(
493
724
  subtitle={getExposedFieldsLabel(
494
725
  countPickedFields(nestedCtFieldsCheckMap),
495
726
  )}
496
- badge={customType.format === "page" ? "Page type" : "Custom type"}
727
+ badge={getTypeFormatLabel(customType.format)}
497
728
  >
498
729
  {renderedFields}
499
730
  </TreeViewSection>
@@ -557,7 +788,7 @@ interface TreeViewFirstLevelGroupFieldProps {
557
788
  groupId: string;
558
789
  fieldCheckMap: PickerFirstLevelGroupFieldValue;
559
790
  onChange: (newValue: PickerFirstLevelGroupFieldValue) => void;
560
- customTypes: CustomType[];
791
+ allCustomTypes: CustomType[];
561
792
  }
562
793
 
563
794
  function TreeViewFirstLevelGroupField(
@@ -568,7 +799,7 @@ function TreeViewFirstLevelGroupField(
568
799
  groupId,
569
800
  fieldCheckMap: groupFieldsCheckMap,
570
801
  onChange: onGroupFieldChange,
571
- customTypes,
802
+ allCustomTypes,
572
803
  } = props;
573
804
 
574
805
  if (!group.config?.fields) return null;
@@ -607,7 +838,7 @@ function TreeViewFirstLevelGroupField(
607
838
  : {}
608
839
  }
609
840
  onChange={onContentRelationshipFieldChange}
610
- customTypes={customTypes}
841
+ allCustomTypes={allCustomTypes}
611
842
  />
612
843
  );
613
844
  }
@@ -634,26 +865,52 @@ function TreeViewFirstLevelGroupField(
634
865
 
635
866
  function getExposedFieldsLabel(count: number) {
636
867
  if (count === 0) return undefined;
637
- return `(${count} ${pluralize(count, "field", "fields")} exposed)`;
868
+ return `(${count} ${pluralize(
869
+ count,
870
+ "field",
871
+ "fields",
872
+ )} returned in the API)`;
638
873
  }
639
874
 
640
- /**
641
- * Gets all the existing local custom types from the store, filters and sorts
642
- * them.
643
- */
644
- function useCustomTypes(): CustomType[] {
645
- const allCustomTypes = useSelector(selectAllCustomTypes);
646
- const localCustomTypes = allCustomTypes.flatMap<CustomType>((ct) => {
647
- // In the store we have remote and local custom types, we want to show
648
- // the local ones, so that the user is able to create a content
649
- // relationship with custom types present on the user's computer (pushed
650
- // or not).
651
- return "local" in ct ? CustomTypes.fromSM(ct.local) : [];
652
- });
875
+ function getTypeFormatLabel(format: CustomType["format"]) {
876
+ return format === "page" ? "Page type" : "Custom type";
877
+ }
653
878
 
654
- localCustomTypes.sort((a, b) => a.id.localeCompare(b.id));
879
+ /** Retrieves all existing page & custom types. */
880
+ function useCustomTypes(value: LinkCustomtypes | undefined) {
881
+ const allCustomTypes = useRequest(getCustomTypes, []);
655
882
 
656
- return localCustomTypes;
883
+ useEffect(() => {
884
+ void revalidateData(getCustomTypes, []);
885
+ }, []);
886
+
887
+ if (!value) {
888
+ return {
889
+ allCustomTypes,
890
+ availableCustomTypes: allCustomTypes,
891
+ pickedCustomTypes: [],
892
+ };
893
+ }
894
+
895
+ const pickedCustomTypes = value.flatMap(
896
+ (pickedCt) => allCustomTypes.find((ct) => ct.id === getId(pickedCt)) ?? [],
897
+ );
898
+
899
+ return {
900
+ allCustomTypes,
901
+ pickedCustomTypes,
902
+ availableCustomTypes: allCustomTypes.filter(
903
+ (ct) => pickedCustomTypes.some((pct) => pct.id === ct.id) === false,
904
+ ),
905
+ };
906
+ }
907
+
908
+ async function getCustomTypes(): Promise<CustomType[]> {
909
+ const { errors, models } =
910
+ await managerClient.customTypes.readAllCustomTypes();
911
+
912
+ if (errors.length > 0) throw errors;
913
+ return models.map(({ model }) => model);
657
914
  }
658
915
 
659
916
  function resolveContentRelationshipCustomTypes(
@@ -760,13 +1017,61 @@ function createContentRelationshipFieldCheckMap(
760
1017
  return crField;
761
1018
  }
762
1019
 
1020
+ /**
1021
+ * Merges the existing Link `customtypes` array with the picker state, ensuring
1022
+ * that conversions from to string (custom type id) to object and vice versa are
1023
+ * made correctly and that the order is preserved.
1024
+ */
1025
+ function mergeAndConvertCheckMapToLinkCustomtypes(args: {
1026
+ existingLinkCustomtypes: LinkCustomtypes | undefined;
1027
+ previousPickerCustomtypes: PickerCustomTypes;
1028
+ newCustomType: PickerCustomType;
1029
+ customTypeId: string;
1030
+ }): LinkCustomtypes {
1031
+ const {
1032
+ existingLinkCustomtypes,
1033
+ previousPickerCustomtypes,
1034
+ newCustomType,
1035
+ customTypeId,
1036
+ } = args;
1037
+
1038
+ const result: NonReadonly<LinkCustomtypes> = [];
1039
+ const pickerLinkCustomtypes = convertFieldCheckMapToLinkCustomtypes({
1040
+ ...previousPickerCustomtypes,
1041
+ [customTypeId]: newCustomType,
1042
+ });
1043
+
1044
+ if (!existingLinkCustomtypes) return pickerLinkCustomtypes;
1045
+
1046
+ for (const existingLinkCt of existingLinkCustomtypes) {
1047
+ const existingPickerLinkCt = pickerLinkCustomtypes.find((ct) => {
1048
+ return getId(ct) === getId(existingLinkCt);
1049
+ });
1050
+
1051
+ if (existingPickerLinkCt !== undefined) {
1052
+ // Custom type with exposed fields, keep the customtypes object
1053
+ result.push(existingPickerLinkCt);
1054
+ } else if (getId(existingLinkCt) === customTypeId) {
1055
+ // Custom type that had exposed fields, but now has none, change to string
1056
+ result.push(getId(existingLinkCt));
1057
+ } else {
1058
+ // Custom type without exposed fields, keep the string
1059
+ result.push(existingLinkCt);
1060
+ }
1061
+ }
1062
+
1063
+ return result;
1064
+ }
1065
+
763
1066
  /**
764
1067
  * Converts a picker fields check map structure ({@link PickerCustomTypes}) into
765
1068
  * Link config `customtypes` ({@link LinkCustomtypes}) and filter out empty Custom
766
1069
  * types.
767
1070
  */
768
- function convertFieldCheckMapToLinkCustomtypes(map: PickerCustomTypes) {
769
- return Object.entries(map).flatMap<LinkCustomtypes[number]>(
1071
+ function convertFieldCheckMapToLinkCustomtypes(
1072
+ checkMap: PickerCustomTypes,
1073
+ ): LinkCustomtypes {
1074
+ return Object.entries(checkMap).flatMap<LinkCustomtypes[number]>(
770
1075
  ([ctId, ctFields]) => {
771
1076
  const fields = Object.entries(ctFields).flatMap<
772
1077
  | string
@@ -909,3 +1214,8 @@ function getGroupFields(group: Group) {
909
1214
  return { fieldId, field: field as NestableWidget };
910
1215
  });
911
1216
  }
1217
+ /** If it's a string, return it, otherwise return the `id` property. */
1218
+ function getId<T extends string | { id: string }>(customType: T): string {
1219
+ if (typeof customType === "string") return customType;
1220
+ return customType.id;
1221
+ }