slice-machine-ui 2.17.0 → 2.17.1-alpha.jp-cr-ui-nested-ct-remove-section.2

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.
@@ -22,6 +22,7 @@ import {
22
22
  } from "@prismicio/editor-ui";
23
23
  import {
24
24
  CustomType,
25
+ DynamicWidget,
25
26
  Group,
26
27
  Link,
27
28
  LinkConfig,
@@ -260,11 +261,11 @@ export function ContentRelationshipFieldPicker(
260
261
  function ContentRelationshipFieldPickerContent(
261
262
  props: ContentRelationshipFieldPickerProps,
262
263
  ) {
263
- const { value, onChange } = props;
264
- const { allCustomTypes, pickedCustomTypes } = useCustomTypes(value);
264
+ const { value: linkCustomtypes, onChange } = props;
265
+ const { allCustomTypes, pickedCustomTypes } = useCustomTypes(linkCustomtypes);
265
266
 
266
- const fieldCheckMap = value
267
- ? convertLinkCustomtypesToFieldCheckMap(value)
267
+ const fieldCheckMap = linkCustomtypes
268
+ ? convertLinkCustomtypesToFieldCheckMap({ linkCustomtypes, allCustomTypes })
268
269
  : {};
269
270
 
270
271
  function onCustomTypesChange(id: string, newCustomType: PickerCustomType) {
@@ -274,22 +275,24 @@ function ContentRelationshipFieldPickerContent(
274
275
  // represent new types added without any picked fields.
275
276
  onChange(
276
277
  mergeAndConvertCheckMapToLinkCustomtypes({
277
- existingLinkCustomtypes: value,
278
- previousPickerCustomtypes: fieldCheckMap,
279
- customTypeId: id,
278
+ fieldCheckMap,
280
279
  newCustomType,
280
+ linkCustomtypes,
281
+ customTypeId: id,
281
282
  }),
282
283
  );
283
284
  }
284
285
 
285
286
  function addCustomType(id: string) {
286
- const newFields = value ? [...value, id] : [id];
287
+ const newFields = linkCustomtypes ? [...linkCustomtypes, id] : [id];
287
288
  onChange(newFields);
288
289
  }
289
290
 
290
291
  function removeCustomType(id: string) {
291
- if (value) {
292
- onChange(value.filter((existingCt) => getId(existingCt) !== id));
292
+ if (linkCustomtypes) {
293
+ onChange(
294
+ linkCustomtypes.filter((existingCt) => getId(existingCt) !== id),
295
+ );
293
296
  }
294
297
  }
295
298
 
@@ -409,8 +412,9 @@ function ContentRelationshipFieldPickerContent(
409
412
  rel="noopener noreferrer"
410
413
  style={{ color: "inherit", textDecoration: "underline" }}
411
414
  >
412
- Please provide your feedback here.
415
+ Please provide your feedback here
413
416
  </a>
417
+ .
414
418
  </Text>
415
419
  </Box>
416
420
  </Box>
@@ -463,20 +467,20 @@ function AddTypeButton(props: AddTypeButtonProps) {
463
467
  </Button>
464
468
  );
465
469
 
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;
470
+ if (allCustomTypes.length === 0) {
471
+ return (
472
+ <Box>
473
+ <Tooltip
474
+ content="No type available"
475
+ side="bottom"
476
+ align="start"
477
+ disableHoverableContent
478
+ >
479
+ {triggerButton}
480
+ </Tooltip>
481
+ </Box>
482
+ );
483
+ }
480
484
 
481
485
  return (
482
486
  <Box>
@@ -529,7 +533,7 @@ function TreeViewCustomType(props: TreeViewCustomTypeProps) {
529
533
  } = props;
530
534
 
531
535
  const renderedFields = getCustomTypeStaticFields(customType).map(
532
- ({ fieldId, field }) => {
536
+ ([fieldId, field]) => {
533
537
  // Group field
534
538
 
535
539
  if (field.type === "Group") {
@@ -661,93 +665,90 @@ function TreeViewContentRelationshipField(
661
665
  allCustomTypes,
662
666
  );
663
667
 
664
- if (resolvedCustomTypes.length === 0) return null;
668
+ if (resolvedCustomTypes.length !== 1) return null;
665
669
 
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;
670
+ const [customType] = resolvedCustomTypes;
671
+
672
+ if (typeof customType === "string") return null;
673
+
674
+ const onNestedCustomTypeChange = (
675
+ newNestedCustomTypeFields: PickerNestedCustomTypeValue,
676
+ ) => {
677
+ onCrFieldChange({
678
+ ...crFieldsCheckMap,
679
+ [customType.id]: newNestedCustomTypeFields,
680
+ });
681
+ };
682
+
683
+ const nestedCtFieldsCheckMap = crFieldsCheckMap[customType.id] ?? {};
675
684
 
676
- const onNestedCustomTypeChange = (
677
- newNestedCustomTypeFields: PickerNestedCustomTypeValue,
685
+ const renderedFields = getCustomTypeStaticFields(customType).map(
686
+ ([fieldId, field]) => {
687
+ // Group field
688
+
689
+ if (field.type === "Group") {
690
+ const onGroupFieldsChange = (
691
+ newGroupFields: PickerLeafGroupFieldValue,
678
692
  ) => {
679
- onCrFieldChange({
680
- ...crFieldsCheckMap,
681
- [customType.id]: newNestedCustomTypeFields,
693
+ onNestedCustomTypeChange({
694
+ ...nestedCtFieldsCheckMap,
695
+ [fieldId]: { type: "group", value: newGroupFields },
682
696
  });
683
697
  };
684
698
 
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
- );
699
+ const groupFieldCheckMap = nestedCtFieldsCheckMap[fieldId] ?? {};
700
+
701
+ return (
702
+ <TreeViewLeafGroupField
703
+ key={fieldId}
704
+ group={field}
705
+ groupId={fieldId}
706
+ onChange={onGroupFieldsChange}
707
+ fieldCheckMap={
708
+ groupFieldCheckMap.type === "group"
709
+ ? groupFieldCheckMap.value
710
+ : {}
716
711
  }
712
+ />
713
+ );
714
+ }
717
715
 
718
- // Regular field
716
+ // Regular field
719
717
 
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
- );
718
+ const onCheckedChange = (newChecked: boolean) => {
719
+ onNestedCustomTypeChange({
720
+ ...nestedCtFieldsCheckMap,
721
+ [fieldId]: { type: "checkbox", value: newChecked },
722
+ });
723
+ };
737
724
 
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
- })}
725
+ return (
726
+ <TreeViewCheckbox
727
+ key={fieldId}
728
+ title={fieldId}
729
+ checked={nestedCtFieldsCheckMap[fieldId]?.value === true}
730
+ onCheckedChange={onCheckedChange}
731
+ />
732
+ );
733
+ },
734
+ );
735
+
736
+ return (
737
+ <TreeViewSection
738
+ key={customType.id}
739
+ // https://linear.app/prismic/issue/DT-2736/
740
+ // @ts-expect-error - TODO: Fix this when we are able to release editor packages
741
+ title={
742
+ <Text>
743
+ {fieldId} <Text color="grey11">→ {customType.id}</Text>
744
+ </Text>
745
+ }
746
+ subtitle={getPickedFieldsLabel(
747
+ countPickedFields(nestedCtFieldsCheckMap).pickedFields,
748
+ )}
749
+ badge={getTypeFormatLabel(customType.format)}
750
+ >
751
+ {renderedFields.length > 0 ? renderedFields : <NoFieldsAvailable />}
751
752
  </TreeViewSection>
752
753
  );
753
754
  }
@@ -901,7 +902,7 @@ function getTypeFormatLabel(format: CustomType["format"]) {
901
902
  }
902
903
 
903
904
  /** Retrieves all existing page & custom types. */
904
- function useCustomTypes(value: LinkCustomtypes | undefined): {
905
+ function useCustomTypes(linkCustomtypes: LinkCustomtypes | undefined): {
905
906
  /** Every existing custom type, used to discover nested custom types down the tree and the add type dropdown. */
906
907
  allCustomTypes: CustomType[];
907
908
  /** The custom types that are already picked. */
@@ -913,14 +914,14 @@ function useCustomTypes(value: LinkCustomtypes | undefined): {
913
914
  void revalidateGetCustomTypes();
914
915
  }, []);
915
916
 
916
- if (!value) {
917
+ if (!linkCustomtypes) {
917
918
  return {
918
919
  allCustomTypes,
919
920
  pickedCustomTypes: [],
920
921
  };
921
922
  }
922
923
 
923
- const pickedCustomTypes = value.flatMap(
924
+ const pickedCustomTypes = linkCustomtypes.flatMap(
924
925
  (pickedCt) => allCustomTypes.find((ct) => ct.id === getId(pickedCt)) ?? [],
925
926
  );
926
927
 
@@ -932,10 +933,10 @@ function useCustomTypes(value: LinkCustomtypes | undefined): {
932
933
 
933
934
  function resolveContentRelationshipCustomTypes(
934
935
  linkCustomtypes: LinkCustomtypes,
935
- localCustomTypes: CustomType[],
936
+ allCustomTypes: CustomType[],
936
937
  ): CustomType[] {
937
938
  return linkCustomtypes.flatMap((linkCustomtype) => {
938
- return localCustomTypes.find((ct) => ct.id === getId(linkCustomtype)) ?? [];
939
+ return allCustomTypes.find((ct) => ct.id === getId(linkCustomtype)) ?? [];
939
940
  });
940
941
  }
941
942
 
@@ -943,92 +944,281 @@ function resolveContentRelationshipCustomTypes(
943
944
  * Converts a Link config `customtypes` ({@link LinkCustomtypes}) structure into
944
945
  * picker fields check map ({@link PickerCustomTypes}).
945
946
  */
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") {
947
+ export function convertLinkCustomtypesToFieldCheckMap(args: {
948
+ linkCustomtypes: LinkCustomtypes;
949
+ allCustomTypes?: CustomType[];
950
+ }): PickerCustomTypes {
951
+ const { linkCustomtypes, allCustomTypes } = args;
952
+
953
+ // If allCustomTypes is undefined, avoid checking if the fields exist.
954
+ const shouldValidate = allCustomTypes !== undefined;
955
+
956
+ const checkMap = linkCustomtypes.reduce<PickerCustomTypes>(
957
+ (customTypes, customType) => {
958
+ if (typeof customType === "string") return customTypes;
959
+
960
+ let ctFlatFieldMap: Record<string, NestableWidget | Group> = {};
961
+
962
+ if (shouldValidate) {
963
+ const existingCt = allCustomTypes.find((c) => c.id === customType.id);
964
+ // Exit early if the custom type doesn't exist
965
+ if (!existingCt) return customTypes;
966
+
967
+ ctFlatFieldMap = getCustomTypeStaticFieldsMap(existingCt);
968
+ }
969
+
970
+ const customTypeFields = customType.fields.reduce<PickerCustomType>(
971
+ (fields, field) => {
972
+ // Check if the field exists (only if validating)
973
+ const existingField = ctFlatFieldMap[getId(field)];
974
+ if (shouldValidate && existingField === undefined) return fields;
975
+
955
976
  // Regular field
956
- customTypeFields[field] = { type: "checkbox", value: true };
957
- } else if ("fields" in field && field.fields !== undefined) {
977
+ if (typeof field === "string") {
978
+ // Check if the field matched the existing one in the custom type (only if validating)
979
+ if (
980
+ shouldValidate &&
981
+ existingField !== undefined &&
982
+ existingField.type === "Group"
983
+ ) {
984
+ return fields;
985
+ }
986
+
987
+ fields[field] = { type: "checkbox", value: true };
988
+ return fields;
989
+ }
990
+
958
991
  // Group field
959
- customTypeFields[field.id] = createGroupFieldCheckMap(field);
960
- } else if ("customtypes" in field && field.customtypes !== undefined) {
992
+ if ("fields" in field && field.fields !== undefined) {
993
+ // Check if the field matched the existing one in the custom type (only if validating)
994
+ if (
995
+ shouldValidate &&
996
+ existingField !== undefined &&
997
+ existingField.type !== "Group"
998
+ ) {
999
+ return fields;
1000
+ }
1001
+
1002
+ const groupFieldCheckMap = createGroupFieldCheckMap({
1003
+ group: field,
1004
+ allCustomTypes,
1005
+ ctFlatFieldMap,
1006
+ });
1007
+
1008
+ if (groupFieldCheckMap) {
1009
+ fields[field.id] = groupFieldCheckMap;
1010
+ }
1011
+
1012
+ return fields;
1013
+ }
1014
+
961
1015
  // Content relationship field
962
- customTypeFields[field.id] =
963
- createContentRelationshipFieldCheckMap(field);
964
- }
1016
+ if ("customtypes" in field && field.customtypes !== undefined) {
1017
+ // Check if the field matched the existing one in the custom type (only if validating)
1018
+ if (
1019
+ shouldValidate &&
1020
+ existingField !== undefined &&
1021
+ !isContentRelationshipField(existingField)
1022
+ ) {
1023
+ return fields;
1024
+ }
965
1025
 
966
- return customTypeFields;
967
- },
968
- {},
969
- );
970
- return customTypes;
971
- }, {});
1026
+ const crFieldCheckMap = createContentRelationshipFieldCheckMap({
1027
+ field,
1028
+ allCustomTypes,
1029
+ });
1030
+
1031
+ if (crFieldCheckMap) {
1032
+ fields[field.id] = crFieldCheckMap;
1033
+ }
1034
+
1035
+ return fields;
1036
+ }
1037
+
1038
+ return fields;
1039
+ },
1040
+ {},
1041
+ );
1042
+
1043
+ if (Object.keys(customTypeFields).length > 0) {
1044
+ customTypes[customType.id] = customTypeFields;
1045
+ }
1046
+
1047
+ return customTypes;
1048
+ },
1049
+ {},
1050
+ );
1051
+
1052
+ return checkMap;
972
1053
  }
973
1054
 
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);
1055
+ function createGroupFieldCheckMap(args: {
1056
+ group: LinkCustomtypesGroupFieldValue;
1057
+ allCustomTypes?: CustomType[];
1058
+ ctFlatFieldMap: Record<string, NestableWidget | Group>;
1059
+ }): PickerFirstLevelGroupField | undefined {
1060
+ const { group, ctFlatFieldMap, allCustomTypes } = args;
1061
+
1062
+ // If allCustomTypes is undefined, avoid checking if the fields exist.
1063
+ const shouldValidate = allCustomTypes !== undefined;
1064
+
1065
+ const fieldEntries = group.fields.reduce<PickerFirstLevelGroupFieldValue>(
1066
+ (fields, field) => {
1067
+ // Check if the field exists (only if validating)
1068
+ const existingField = getGroupFieldFromMap(
1069
+ ctFlatFieldMap,
1070
+ group.id,
1071
+ getId(field),
1072
+ );
1073
+ if (shouldValidate && !existingField) return fields;
1074
+
1075
+ // Regular field
1076
+ if (typeof field === "string") {
1077
+ // Check if the field matched the existing one in the custom type (only if validating)
1078
+ if (
1079
+ shouldValidate &&
1080
+ existingField !== undefined &&
1081
+ existingField.type === "Group"
1082
+ ) {
1083
+ return fields;
987
1084
  }
988
1085
 
1086
+ fields[field] = { type: "checkbox", value: true };
989
1087
  return fields;
990
- },
991
- {},
992
- ),
1088
+ }
1089
+
1090
+ // Content relationship field
1091
+ if ("customtypes" in field && field.customtypes !== undefined) {
1092
+ // Check if the field matched the existing one in the custom type (only if validating)
1093
+ if (
1094
+ shouldValidate &&
1095
+ existingField !== undefined &&
1096
+ !isContentRelationshipField(existingField)
1097
+ ) {
1098
+ return fields;
1099
+ }
1100
+
1101
+ const crFieldCheckMap = createContentRelationshipFieldCheckMap({
1102
+ field,
1103
+ allCustomTypes,
1104
+ });
1105
+
1106
+ if (crFieldCheckMap) {
1107
+ fields[field.id] = crFieldCheckMap;
1108
+ }
1109
+
1110
+ return fields;
1111
+ }
1112
+
1113
+ return fields;
1114
+ },
1115
+ {},
1116
+ );
1117
+
1118
+ if (Object.keys(fieldEntries).length === 0) return undefined;
1119
+
1120
+ return {
1121
+ type: "group",
1122
+ value: fieldEntries,
993
1123
  };
994
1124
  }
995
1125
 
996
- function createContentRelationshipFieldCheckMap(
997
- field: LinkCustomtypesContentRelationshipFieldValue,
998
- ): PickerContentRelationshipField {
999
- const crField: PickerContentRelationshipField = {
1000
- type: "contentRelationship",
1001
- value: {},
1002
- };
1003
- const crFieldCustomTypes = crField.value;
1126
+ function createContentRelationshipFieldCheckMap(args: {
1127
+ field: LinkCustomtypesContentRelationshipFieldValue;
1128
+ allCustomTypes?: CustomType[];
1129
+ }): PickerContentRelationshipField | undefined {
1130
+ const { field, allCustomTypes } = args;
1004
1131
 
1005
- for (const customType of field.customtypes) {
1006
- if (typeof customType === "string") continue;
1132
+ // If allCustomTypes is undefined, avoid checking if the fields exists.
1133
+ const shouldValidate = allCustomTypes !== undefined;
1007
1134
 
1008
- crFieldCustomTypes[customType.id] ??= {};
1009
- const customTypeFields = crFieldCustomTypes[customType.id];
1135
+ const fieldEntries =
1136
+ field.customtypes.reduce<PickerContentRelationshipFieldValue>(
1137
+ (customTypes, customType) => {
1138
+ if (typeof customType === "string") return customTypes;
1010
1139
 
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,
1140
+ let ctFlatFieldMap: Record<string, NestableWidget | Group> = {};
1141
+
1142
+ if (shouldValidate) {
1143
+ const existingCt = allCustomTypes.find((c) => c.id === customType.id);
1144
+ // Exit early if the custom type doesn't exist
1145
+ if (!existingCt) return customTypes;
1146
+
1147
+ ctFlatFieldMap = getCustomTypeStaticFieldsMap(existingCt);
1148
+ }
1149
+
1150
+ const ctFields = customType.fields.reduce<PickerNestedCustomTypeValue>(
1151
+ (nestedFields, nestedField) => {
1152
+ // Regular field
1153
+ if (typeof nestedField === "string") {
1154
+ const existingField = ctFlatFieldMap[nestedField];
1155
+
1156
+ // Check if the field matched the existing one in the custom type (only if validating)
1157
+ if (
1158
+ shouldValidate &&
1159
+ (existingField === undefined || existingField.type === "Group")
1160
+ ) {
1161
+ return nestedFields;
1162
+ }
1163
+
1164
+ nestedFields[nestedField] = { type: "checkbox", value: true };
1165
+ return nestedFields;
1166
+ }
1167
+
1168
+ if ("fields" in nestedField && nestedField.fields !== undefined) {
1169
+ // Group field
1170
+ const groupFields =
1171
+ nestedField.fields.reduce<PickerLeafGroupFieldValue>(
1172
+ (groupFields, groupField) => {
1173
+ const existingField = getGroupFieldFromMap(
1174
+ ctFlatFieldMap,
1175
+ nestedField.id,
1176
+ groupField,
1177
+ );
1178
+
1179
+ // Check if the field matched the existing one in the custom type (only if validating)
1180
+ if (
1181
+ shouldValidate &&
1182
+ (existingField === undefined ||
1183
+ existingField.type === "Group")
1184
+ ) {
1185
+ return groupFields;
1186
+ }
1187
+
1188
+ groupFields[groupField] = { type: "checkbox", value: true };
1189
+ return groupFields;
1190
+ },
1191
+ {},
1192
+ );
1193
+
1194
+ if (Object.keys(groupFields).length > 0) {
1195
+ nestedFields[nestedField.id] = {
1196
+ type: "group",
1197
+ value: groupFields,
1198
+ };
1199
+ }
1200
+ }
1201
+
1202
+ return nestedFields;
1203
+ },
1204
+ {},
1019
1205
  );
1020
1206
 
1021
- if (groupFieldsEntries.length > 0) {
1022
- customTypeFields[nestedField.id] = {
1023
- type: "group",
1024
- value: Object.fromEntries(groupFieldsEntries),
1025
- };
1207
+ if (Object.keys(ctFields).length > 0) {
1208
+ customTypes[customType.id] = ctFields;
1026
1209
  }
1027
- }
1028
- }
1029
- }
1030
1210
 
1031
- return crField;
1211
+ return customTypes;
1212
+ },
1213
+ {},
1214
+ );
1215
+
1216
+ if (Object.keys(fieldEntries).length === 0) return undefined;
1217
+
1218
+ return {
1219
+ type: "contentRelationship",
1220
+ value: fieldEntries,
1221
+ };
1032
1222
  }
1033
1223
 
1034
1224
  /**
@@ -1037,27 +1227,22 @@ function createContentRelationshipFieldCheckMap(
1037
1227
  * made correctly and that the order is preserved.
1038
1228
  */
1039
1229
  function mergeAndConvertCheckMapToLinkCustomtypes(args: {
1040
- existingLinkCustomtypes: LinkCustomtypes | undefined;
1041
- previousPickerCustomtypes: PickerCustomTypes;
1230
+ linkCustomtypes: LinkCustomtypes | undefined;
1231
+ fieldCheckMap: PickerCustomTypes;
1042
1232
  newCustomType: PickerCustomType;
1043
1233
  customTypeId: string;
1044
1234
  }): LinkCustomtypes {
1045
- const {
1046
- existingLinkCustomtypes,
1047
- previousPickerCustomtypes,
1048
- newCustomType,
1049
- customTypeId,
1050
- } = args;
1235
+ const { linkCustomtypes, fieldCheckMap, newCustomType, customTypeId } = args;
1051
1236
 
1052
1237
  const result: NonReadonly<LinkCustomtypes> = [];
1053
1238
  const pickerLinkCustomtypes = convertFieldCheckMapToLinkCustomtypes({
1054
- ...previousPickerCustomtypes,
1239
+ ...fieldCheckMap,
1055
1240
  [customTypeId]: newCustomType,
1056
1241
  });
1057
1242
 
1058
- if (!existingLinkCustomtypes) return pickerLinkCustomtypes;
1243
+ if (!linkCustomtypes) return pickerLinkCustomtypes;
1059
1244
 
1060
- for (const existingLinkCt of existingLinkCustomtypes) {
1245
+ for (const existingLinkCt of linkCustomtypes) {
1061
1246
  const existingPickerLinkCt = pickerLinkCustomtypes.find((ct) => {
1062
1247
  return getId(ct) === getId(existingLinkCt);
1063
1248
  });
@@ -1229,6 +1414,10 @@ export function countPickedFields(
1229
1414
  );
1230
1415
  }
1231
1416
 
1417
+ function isContentRelationshipField(field: DynamicWidget): field is Link {
1418
+ return field.type === "Link" && field.config?.select === "document";
1419
+ }
1420
+
1232
1421
  /**
1233
1422
  * Check if the field is a Content Relationship Link with a **single** custom
1234
1423
  * type. CRs with multiple custom types are not currently supported (legacy).
@@ -1237,34 +1426,65 @@ function isContentRelationshipFieldWithSingleCustomtype(
1237
1426
  field: NestableWidget | Group,
1238
1427
  ): field is Link {
1239
1428
  return !!(
1240
- field.type === "Link" &&
1241
- field.config?.select === "document" &&
1429
+ isContentRelationshipField(field) &&
1242
1430
  field.config?.customtypes &&
1243
1431
  field.config.customtypes.length === 1
1244
1432
  );
1245
1433
  }
1246
1434
 
1247
- function getCustomTypeStaticFields(customType: CustomType) {
1435
+ /**
1436
+ * Flattens all custom type tabs and fields into an array of [fieldId, field] tuples.
1437
+ * Also filters out invalid fields.
1438
+ */
1439
+ function getCustomTypeStaticFields(
1440
+ customType: CustomType,
1441
+ ): [fieldId: string, field: NestableWidget | Group][] {
1248
1442
  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
- });
1443
+ return Object.entries(tabFields).flatMap<[string, NestableWidget | Group]>(
1444
+ ([fieldId, field]) => {
1445
+ return isValidField(fieldId, field) ? [[fieldId, field]] : [];
1446
+ },
1447
+ );
1265
1448
  });
1266
1449
  }
1267
1450
 
1451
+ /**
1452
+ * Flattens all custom type tabs and fields into a map of field ids to fields.
1453
+ * Also filters out invalid fields.
1454
+ */
1455
+ function getCustomTypeStaticFieldsMap(
1456
+ customType: CustomType,
1457
+ ): Record<string, NestableWidget | Group> {
1458
+ return Object.fromEntries(getCustomTypeStaticFields(customType));
1459
+ }
1460
+
1461
+ function getGroupFieldFromMap(
1462
+ flattenFields: Record<string, NestableWidget | Group>,
1463
+ groupId: string,
1464
+ fieldId: string,
1465
+ ) {
1466
+ const group = flattenFields[groupId];
1467
+ if (group === undefined || group.type !== "Group") return undefined;
1468
+ return group.config?.fields?.[fieldId];
1469
+ }
1470
+
1471
+ function isValidField(
1472
+ fieldId: string,
1473
+ field: DynamicWidget,
1474
+ ): field is NestableWidget | Group {
1475
+ return (
1476
+ field.type !== "Slices" &&
1477
+ field.type !== "Choice" &&
1478
+ // We don't display uid fields because they're a special field returned by
1479
+ // the API and they're not included in the document data object.
1480
+ // We also filter by key "uid", because (as of the time of writing this)
1481
+ // creating any field with that API id will result in it being used for
1482
+ // metadata, regardless of its type.
1483
+ field.type !== "UID" &&
1484
+ fieldId !== "uid"
1485
+ );
1486
+ }
1487
+
1268
1488
  function getGroupFields(group: Group) {
1269
1489
  if (!group.config?.fields) return [];
1270
1490
  return Object.entries(group.config.fields).map(([fieldId, field]) => {