slice-machine-ui 2.16.2-alpha.jp-cr-ui-multiple-improvements-rendering-logic.6 → 2.16.2-alpha.jp-cr-ui-types-internal-version.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 → bjRIqA2BKLEQ0oCFi096L}/_buildManifest.js +1 -1
  3. package/out/_next/static/chunks/{268-b9f569b2a5b4e1c4.js → 268-046fd0c9d105117f.js} +1 -1
  4. package/out/_next/static/chunks/{34-a89c34b9365e2fa3.js → 34-7e02d3123542d645.js} +1 -1
  5. package/out/_next/static/chunks/{630-e151ae4c0afa9897.js → 630-6b2adc6e86debe3d.js} +1 -1
  6. package/out/_next/static/chunks/{867-2756f347bcd416f3.js → 867-3f1b306bb3e20dec.js} +1 -1
  7. package/out/_next/static/chunks/{882-de1be3aece0cc601.js → 882-34af0755031a0763.js} +1 -1
  8. package/out/_next/static/chunks/{895-566153447e052c73.js → 895-63d0e67d0014ea28.js} +1 -1
  9. package/out/_next/static/chunks/e1b0e87a.19cd118baf725e50.js +28 -0
  10. package/out/_next/static/chunks/pages/{_app-3d7d86878ccba2a2.js → _app-80da54270aab8938.js} +268 -219
  11. package/out/_next/static/chunks/pages/{changelog-1e3c6a1e687e5d63.js → changelog-1f45bbf9399626c7.js} +1 -1
  12. package/out/_next/static/chunks/pages/{changes-12afd5211a55c133.js → changes-f331fca898fc0c3a.js} +1 -1
  13. package/out/_next/static/chunks/pages/{labs-e6978d96b28c5621.js → labs-7cf1a8180d15bc81.js} +1 -1
  14. package/out/_next/static/chunks/pages/{settings-df9b029574887ea1.js → settings-9d6e23720fd95d41.js} +1 -1
  15. package/out/_next/static/chunks/pages/slices/[lib]/[sliceName]/[variation]/{simulator-6bf879f9bdaf8164.js → simulator-35c5ab99351f266a.js} +1 -1
  16. package/out/_next/static/chunks/pages/slices/[lib]/[sliceName]/{[variation]-11950d263994762e.js → [variation]-b89f6513d4bdea03.js} +1 -1
  17. package/out/_next/static/chunks/pages/{slices-aef1683623e9583c.js → slices-097e95abd417dd2d.js} +1 -1
  18. package/out/_next/static/chunks/{webpack-db4c6e9b826ac694.js → webpack-a75559e2f94885b4.js} +1 -1
  19. package/out/_next/static/css/7393c1f29ee22bf7.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 +355 -379
  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 → bjRIqA2BKLEQ0oCFi096L}/_ssgManifest.js +0 -0
@@ -6,23 +6,18 @@ import {
6
6
  TreeViewCheckbox,
7
7
  TreeViewSection,
8
8
  } from "@prismicio/editor-ui";
9
- import {
10
- CustomType,
11
- Group,
12
- Link,
13
- LinkConfig,
14
- NestableWidget,
15
- } from "@prismicio/types-internal/lib/customtypes";
9
+ import { UID } from "@prismicio/types-internal/lib/customtypes";
10
+ import { useMemo } from "react";
16
11
  import { useSelector } from "react-redux";
17
12
 
18
- import { CustomTypes } from "@/legacy/lib/models/common/CustomType";
13
+ import { CustomTypeSM, TabFields } from "@/legacy/lib/models/common/CustomType";
19
14
  import { selectAllCustomTypes } from "@/modules/availableCustomTypes";
20
15
  import { isValidObject } from "@/utils/isValidObject";
21
16
 
22
17
  /**
23
18
  * Picker fields check map types. Used internally to keep track of the checked
24
19
  * fields in the TreeView, as it's easier to handle objects than arrays and
25
- * also ensures field uniqueness.
20
+ * also ensure field uniqueness.
26
21
  *
27
22
  * @example
28
23
  * {
@@ -132,7 +127,7 @@ interface PickerNestedCustomTypeValue {
132
127
  }
133
128
 
134
129
  /**
135
- * Content relationship Link customtypes property structure.
130
+ * Copy of types-internal Link customtypes.
136
131
  *
137
132
  * @example
138
133
  * [
@@ -180,26 +175,40 @@ interface PickerNestedCustomTypeValue {
180
175
  * },
181
176
  * ]
182
177
  */
183
- type LinkCustomtypes = NonNullable<LinkConfig["customtypes"]>;
178
+ type TICustomTypes = readonly (string | TICustomType)[];
179
+
180
+ interface TICustomType {
181
+ id: string;
182
+ fields: readonly (
183
+ | string
184
+ | TIContentRelationshipFieldValue
185
+ | TIGroupFieldValues
186
+ )[];
187
+ }
188
+
189
+ interface TICustomTypeRegularFieldValues {
190
+ id: string;
191
+ fields: readonly string[];
192
+ }
184
193
 
185
- type LinkCustomtypesFields = Exclude<
186
- LinkCustomtypes[number],
187
- string
188
- >["fields"][number];
194
+ interface TIGroupFieldValues {
195
+ id: string;
196
+ fields: readonly (string | TIContentRelationshipFieldValue)[];
197
+ }
189
198
 
190
- type LinkCustomtypesContentRelationshipFieldValue = Exclude<
191
- LinkCustomtypesFields,
192
- string | { fields: unknown }
193
- >;
199
+ interface TIContentRelationshipFieldValue {
200
+ id: string;
201
+ customtypes: readonly (string | TICustomTypeFieldValues)[];
202
+ }
194
203
 
195
- type LinkCustomtypesGroupFieldValue = Exclude<
196
- LinkCustomtypesFields,
197
- string | { customtypes: unknown }
198
- >;
204
+ interface TICustomTypeFieldValues {
205
+ id: string;
206
+ fields: readonly (string | TICustomTypeRegularFieldValues)[];
207
+ }
199
208
 
200
209
  interface ContentRelationshipFieldPickerProps {
201
- value: LinkCustomtypes | undefined;
202
- onChange: (fields: LinkCustomtypes) => void;
210
+ value: TICustomTypes | undefined;
211
+ onChange: (fields: TICustomTypes) => void;
203
212
  }
204
213
 
205
214
  export function ContentRelationshipFieldPicker(
@@ -207,14 +216,11 @@ export function ContentRelationshipFieldPicker(
207
216
  ) {
208
217
  const { value, onChange } = props;
209
218
  const customTypes = useCustomTypes();
210
-
211
- const fieldCheckMap = value
212
- ? convertLinkCustomtypesToFieldCheckMap(value)
213
- : {};
219
+ const fieldCheckMap = value ? convertCustomTypesToFieldCheckMap(value) : {};
214
220
 
215
221
  function onCustomTypesChange(customTypeId: string, value: PickerCustomType) {
216
222
  onChange(
217
- convertFieldCheckMapToLinkCustomtypes({
223
+ convertFieldCheckMapToCustomTypes({
218
224
  ...fieldCheckMap,
219
225
  [customTypeId]: value,
220
226
  }),
@@ -247,7 +253,6 @@ export function ContentRelationshipFieldPicker(
247
253
  customType={customType}
248
254
  onChange={(value) => onCustomTypesChange(customType.id, value)}
249
255
  fieldCheckMap={fieldCheckMap[customType.id] ?? {}}
250
- customTypes={customTypes}
251
256
  />
252
257
  ))}
253
258
  </TreeView>
@@ -271,157 +276,131 @@ export function ContentRelationshipFieldPicker(
271
276
  }
272
277
 
273
278
  interface TreeViewCustomTypeProps {
274
- customType: CustomType;
279
+ customType: TICustomType;
275
280
  fieldCheckMap: PickerCustomType;
276
281
  onChange: (newValue: PickerCustomType) => void;
277
- customTypes: CustomType[];
278
282
  }
279
283
 
280
284
  function TreeViewCustomType(props: TreeViewCustomTypeProps) {
281
285
  const {
282
286
  customType,
283
- fieldCheckMap: customTypeFieldsCheckMap,
287
+ fieldCheckMap: customTypeFieldCheckMap,
284
288
  onChange: onCustomTypeChange,
285
- customTypes,
286
289
  } = props;
287
290
 
288
- const renderedFields = getCustomTypeStaticFields(customType).map(
289
- ({ fieldId, field }) => {
290
- // Group field
291
+ return (
292
+ <TreeViewSection
293
+ key={customType.id}
294
+ title={customType.id}
295
+ subtitle={getExposedFieldsLabel(
296
+ countPickedFields(customTypeFieldCheckMap),
297
+ )}
298
+ badge="Custom type"
299
+ >
300
+ {customType.fields.map((field) => {
301
+ // Checkbox field
291
302
 
292
- if (isGroupField(field)) {
293
- const onGroupFieldChange = (
294
- newGroupFields: PickerFirstLevelGroupFieldValue,
295
- ) => {
296
- onCustomTypeChange({
297
- ...customTypeFieldsCheckMap,
298
- [fieldId]: { type: "group", value: newGroupFields },
299
- });
300
- };
303
+ if (typeof field === "string") {
304
+ const checked = customTypeFieldCheckMap[field]?.value ?? false;
301
305
 
302
- const groupFieldCheckMap = customTypeFieldsCheckMap[fieldId] ?? {};
306
+ const onCheckedChange = (newValue: boolean) => {
307
+ onCustomTypeChange({
308
+ ...customTypeFieldCheckMap,
309
+ [field]: { type: "checkbox", value: newValue },
310
+ });
311
+ };
303
312
 
304
- return (
305
- <TreeViewFirstLevelGroupField
306
- key={fieldId}
307
- group={field}
308
- groupId={fieldId}
309
- onChange={onGroupFieldChange}
310
- fieldCheckMap={
311
- groupFieldCheckMap.type === "group"
312
- ? groupFieldCheckMap.value
313
- : {}
314
- }
315
- customTypes={customTypes}
316
- />
317
- );
318
- }
313
+ return (
314
+ <TreeViewCheckbox
315
+ key={field}
316
+ title={field}
317
+ checked={checked === true}
318
+ onCheckedChange={onCheckedChange}
319
+ />
320
+ );
321
+ }
319
322
 
320
- // Content relationship field
323
+ const crOrGroupFieldCheckMap = customTypeFieldCheckMap[field.id] ?? {};
324
+
325
+ // Group field
326
+
327
+ if ("fields" in field) {
328
+ const onGroupFieldChange = (
329
+ newGroupFields: PickerFirstLevelGroupFieldValue,
330
+ ) => {
331
+ onCustomTypeChange({
332
+ ...customTypeFieldCheckMap,
333
+ [field.id]: { type: "group", value: newGroupFields },
334
+ });
335
+ };
336
+
337
+ return (
338
+ <TreeViewGroupField
339
+ key={field.id}
340
+ group={field}
341
+ onChange={onGroupFieldChange}
342
+ fieldCheckMap={
343
+ crOrGroupFieldCheckMap.type === "group"
344
+ ? crOrGroupFieldCheckMap.value
345
+ : {}
346
+ }
347
+ />
348
+ );
349
+ }
350
+
351
+ // Content relationship field
321
352
 
322
- if (isContentRelationshipField(field)) {
323
353
  const onContentRelationshipFieldChange = (
324
354
  newCrFields: PickerContentRelationshipFieldValue,
325
355
  ) => {
326
356
  onCustomTypeChange({
327
- ...customTypeFieldsCheckMap,
328
- [fieldId]: {
329
- type: "contentRelationship",
330
- value: newCrFields,
331
- },
357
+ ...customTypeFieldCheckMap,
358
+ [field.id]: { type: "contentRelationship", value: newCrFields },
332
359
  });
333
360
  };
334
361
 
335
- const crFieldCheckMap = customTypeFieldsCheckMap[fieldId] ?? {};
336
-
337
362
  return (
338
363
  <TreeViewContentRelationshipField
339
- key={fieldId}
364
+ key={field.id}
340
365
  field={field}
341
- fieldId={fieldId}
342
366
  onChange={onContentRelationshipFieldChange}
343
367
  fieldCheckMap={
344
- crFieldCheckMap.type === "contentRelationship"
345
- ? crFieldCheckMap.value
368
+ crOrGroupFieldCheckMap.type === "contentRelationship"
369
+ ? crOrGroupFieldCheckMap.value
346
370
  : {}
347
371
  }
348
- customTypes={customTypes}
349
372
  />
350
373
  );
351
- }
352
-
353
- // Regular field
354
-
355
- const onCheckedChange = (newValue: boolean) => {
356
- onCustomTypeChange({
357
- ...customTypeFieldsCheckMap,
358
- [fieldId]: { type: "checkbox", value: newValue },
359
- });
360
- };
361
-
362
- return (
363
- <TreeViewCheckbox
364
- key={fieldId}
365
- title={fieldId}
366
- checked={customTypeFieldsCheckMap[fieldId]?.value === true}
367
- onCheckedChange={onCheckedChange}
368
- />
369
- );
370
- },
371
- );
372
-
373
- if (renderedFields.length === 0) return null;
374
-
375
- return (
376
- <TreeViewSection
377
- key={customType.id}
378
- title={customType.id}
379
- subtitle={getExposedFieldsLabel(
380
- countPickedFields(customTypeFieldsCheckMap),
381
- )}
382
- badge={customType.format === "page" ? "Page type" : "Custom type"}
383
- >
384
- {renderedFields}
374
+ })}
385
375
  </TreeViewSection>
386
376
  );
387
377
  }
388
378
 
389
379
  interface TreeViewContentRelationshipFieldProps {
390
- field: Link;
391
- fieldId: string;
380
+ field: TIContentRelationshipFieldValue;
392
381
  fieldCheckMap: PickerContentRelationshipFieldValue;
393
382
  onChange: (newValue: PickerContentRelationshipFieldValue) => void;
394
- customTypes: CustomType[];
395
383
  }
396
384
 
397
385
  function TreeViewContentRelationshipField(
398
386
  props: TreeViewContentRelationshipFieldProps,
399
387
  ) {
400
388
  const {
401
- field,
402
- fieldId,
389
+ field: crField,
403
390
  fieldCheckMap: crFieldsCheckMap,
404
391
  onChange: onCrFieldChange,
405
- customTypes,
406
392
  } = props;
407
393
 
408
- if (!field.config?.customtypes) return null;
409
-
410
- const resolvedCustomTypes = resolveContentRelationshipCustomTypes(
411
- field.config.customtypes,
412
- customTypes,
413
- );
414
-
415
- if (resolvedCustomTypes.length === 0) return null;
416
-
417
394
  return (
418
395
  <TreeViewSection
419
- title={fieldId}
396
+ title={crField.id}
420
397
  subtitle={getExposedFieldsLabel(countPickedFields(crFieldsCheckMap))}
421
398
  >
422
- {resolvedCustomTypes.map((customType) => {
399
+ {crField.customtypes.map((customType) => {
423
400
  if (typeof customType === "string") return null;
424
401
 
402
+ const nestedCtFieldsCheckMap = crFieldsCheckMap[customType.id] ?? {};
403
+
425
404
  const onNestedCustomTypeChange = (
426
405
  newNestedCustomTypeFields: PickerNestedCustomTypeValue,
427
406
  ) => {
@@ -431,29 +410,51 @@ function TreeViewContentRelationshipField(
431
410
  });
432
411
  };
433
412
 
434
- const nestedCtFieldsCheckMap = crFieldsCheckMap[customType.id] ?? {};
413
+ return (
414
+ <TreeViewSection
415
+ key={customType.id}
416
+ title={customType.id}
417
+ subtitle={getExposedFieldsLabel(
418
+ countPickedFields(nestedCtFieldsCheckMap),
419
+ )}
420
+ badge="Custom type"
421
+ >
422
+ {customType.fields.map((field) => {
423
+ if (typeof field === "string") {
424
+ const checked = nestedCtFieldsCheckMap[field]?.value === true;
425
+
426
+ const onCheckedChange = (newChecked: boolean) => {
427
+ onNestedCustomTypeChange({
428
+ ...nestedCtFieldsCheckMap,
429
+ [field]: { type: "checkbox", value: newChecked },
430
+ });
431
+ };
432
+
433
+ return (
434
+ <TreeViewCheckbox
435
+ key={field}
436
+ title={field}
437
+ checked={checked}
438
+ onCheckedChange={onCheckedChange}
439
+ />
440
+ );
441
+ }
435
442
 
436
- const renderedFields = getCustomTypeStaticFields(customType).map(
437
- ({ fieldId, field }) => {
438
- // Group field
443
+ const groupFieldCheckMap = nestedCtFieldsCheckMap[field.id] ?? {};
439
444
 
440
- if (isGroupField(field)) {
441
445
  const onGroupFieldsChange = (
442
446
  newGroupFields: PickerLeafGroupFieldValue,
443
447
  ) => {
444
448
  onNestedCustomTypeChange({
445
449
  ...nestedCtFieldsCheckMap,
446
- [fieldId]: { type: "group", value: newGroupFields },
450
+ [field.id]: { type: "group", value: newGroupFields },
447
451
  });
448
452
  };
449
453
 
450
- const groupFieldCheckMap = nestedCtFieldsCheckMap[fieldId] ?? {};
451
-
452
454
  return (
453
- <TreeViewLeafGroupField
454
- key={fieldId}
455
+ <TreeViewContentRelationshipFieldNestedGroup
456
+ key={field.id}
455
457
  group={field}
456
- groupId={fieldId}
457
458
  onChange={onGroupFieldsChange}
458
459
  fieldCheckMap={
459
460
  groupFieldCheckMap.type === "group"
@@ -462,40 +463,7 @@ function TreeViewContentRelationshipField(
462
463
  }
463
464
  />
464
465
  );
465
- }
466
-
467
- // Regular field
468
-
469
- const onCheckedChange = (newChecked: boolean) => {
470
- onNestedCustomTypeChange({
471
- ...nestedCtFieldsCheckMap,
472
- [fieldId]: { type: "checkbox", value: newChecked },
473
- });
474
- };
475
-
476
- return (
477
- <TreeViewCheckbox
478
- key={fieldId}
479
- title={fieldId}
480
- checked={nestedCtFieldsCheckMap[fieldId]?.value === true}
481
- onCheckedChange={onCheckedChange}
482
- />
483
- );
484
- },
485
- );
486
-
487
- if (renderedFields.length === 0) return null;
488
-
489
- return (
490
- <TreeViewSection
491
- key={customType.id}
492
- title={customType.id}
493
- subtitle={getExposedFieldsLabel(
494
- countPickedFields(nestedCtFieldsCheckMap),
495
- )}
496
- badge={customType.format === "page" ? "Page type" : "Custom type"}
497
- >
498
- {renderedFields}
466
+ })}
499
467
  </TreeViewSection>
500
468
  );
501
469
  })}
@@ -503,128 +471,108 @@ function TreeViewContentRelationshipField(
503
471
  );
504
472
  }
505
473
 
506
- interface TreeViewLeafGroupFieldProps {
507
- group: Group;
508
- groupId: string;
474
+ interface TreeViewContentRelationshipFieldGroupProps {
475
+ group: TICustomTypeRegularFieldValues;
509
476
  fieldCheckMap: PickerLeafGroupFieldValue;
510
477
  onChange: (newValue: PickerLeafGroupFieldValue) => void;
511
478
  }
512
479
 
513
- function TreeViewLeafGroupField(props: TreeViewLeafGroupFieldProps) {
480
+ function TreeViewContentRelationshipFieldNestedGroup(
481
+ props: TreeViewContentRelationshipFieldGroupProps,
482
+ ) {
514
483
  const {
515
484
  group,
516
- groupId,
517
485
  fieldCheckMap: groupFieldsCheckMap,
518
486
  onChange: onGroupFieldChange,
519
487
  } = props;
520
488
 
521
- if (!group.config?.fields) return null;
522
-
523
- const renderedFields = getGroupFields(group).map(({ fieldId }) => {
524
- const onCheckedChange = (newChecked: boolean) => {
525
- onGroupFieldChange({
526
- ...groupFieldsCheckMap,
527
- [fieldId]: { type: "checkbox", value: newChecked },
528
- });
529
- };
530
-
531
- return (
532
- <TreeViewCheckbox
533
- key={fieldId}
534
- title={fieldId}
535
- checked={groupFieldsCheckMap[fieldId]?.value === true}
536
- onCheckedChange={onCheckedChange}
537
- />
538
- );
539
- });
540
-
541
- if (renderedFields.length === 0) return null;
542
-
543
489
  return (
544
490
  <TreeViewSection
545
- key={groupId}
546
- title={groupId}
491
+ key={group.id}
492
+ title={group.id}
547
493
  subtitle={getExposedFieldsLabel(countPickedFields(groupFieldsCheckMap))}
548
494
  badge="Group"
549
495
  >
550
- {renderedFields}
496
+ {group.fields.map((field) => {
497
+ const checked = groupFieldsCheckMap[field]?.value ?? false;
498
+
499
+ const onCheckedChange = (newChecked: boolean) => {
500
+ onGroupFieldChange({
501
+ ...groupFieldsCheckMap,
502
+ [field]: { type: "checkbox", value: newChecked },
503
+ });
504
+ };
505
+
506
+ return (
507
+ <TreeViewCheckbox
508
+ key={field}
509
+ title={field}
510
+ checked={checked === true}
511
+ onCheckedChange={onCheckedChange}
512
+ />
513
+ );
514
+ })}
551
515
  </TreeViewSection>
552
516
  );
553
517
  }
554
518
 
555
- interface TreeViewFirstLevelGroupFieldProps {
556
- group: Group;
557
- groupId: string;
519
+ interface TreeViewGroupFieldProps {
520
+ group: TIGroupFieldValues;
558
521
  fieldCheckMap: PickerFirstLevelGroupFieldValue;
559
522
  onChange: (newValue: PickerFirstLevelGroupFieldValue) => void;
560
- customTypes: CustomType[];
561
523
  }
562
524
 
563
- function TreeViewFirstLevelGroupField(
564
- props: TreeViewFirstLevelGroupFieldProps,
565
- ) {
525
+ function TreeViewGroupField(props: TreeViewGroupFieldProps) {
566
526
  const {
567
527
  group,
568
- groupId,
569
528
  fieldCheckMap: groupFieldsCheckMap,
570
529
  onChange: onGroupFieldChange,
571
- customTypes,
572
530
  } = props;
573
531
 
574
- if (!group.config?.fields) return null;
575
-
576
532
  return (
577
- <TreeViewSection
578
- key={groupId}
579
- title={groupId}
580
- subtitle={getExposedFieldsLabel(countPickedFields(groupFieldsCheckMap))}
581
- badge="Group"
582
- >
583
- {getGroupFields(group).map(({ fieldId, field }) => {
584
- if (isContentRelationshipField(field)) {
585
- const onContentRelationshipFieldChange = (
586
- newCrFields: PickerContentRelationshipFieldValue,
587
- ) => {
533
+ <TreeViewSection key={group.id} title={group.id} badge="Group">
534
+ {group.fields.map((field) => {
535
+ if (typeof field === "string") {
536
+ const checked = groupFieldsCheckMap[field]?.value ?? false;
537
+
538
+ const onCheckedChange = (newChecked: boolean) => {
588
539
  onGroupFieldChange({
589
540
  ...groupFieldsCheckMap,
590
- [fieldId]: {
591
- type: "contentRelationship",
592
- value: newCrFields,
593
- },
541
+ [field]: { type: "checkbox", value: newChecked },
594
542
  });
595
543
  };
596
544
 
597
- const crFieldCheckMap = groupFieldsCheckMap[fieldId] ?? {};
598
-
599
545
  return (
600
- <TreeViewContentRelationshipField
601
- key={fieldId}
602
- field={field}
603
- fieldId={fieldId}
604
- fieldCheckMap={
605
- crFieldCheckMap.type === "contentRelationship"
606
- ? crFieldCheckMap.value
607
- : {}
608
- }
609
- onChange={onContentRelationshipFieldChange}
610
- customTypes={customTypes}
546
+ <TreeViewCheckbox
547
+ key={field}
548
+ title={field}
549
+ checked={checked === true}
550
+ onCheckedChange={onCheckedChange}
611
551
  />
612
552
  );
613
553
  }
614
554
 
615
- const onCheckedChange = (newChecked: boolean) => {
555
+ const crFieldCheckMap = groupFieldsCheckMap[field.id] ?? {};
556
+
557
+ const onContentRelationshipFieldChange = (
558
+ newCrFields: PickerContentRelationshipFieldValue,
559
+ ) => {
616
560
  onGroupFieldChange({
617
561
  ...groupFieldsCheckMap,
618
- [fieldId]: { type: "checkbox", value: newChecked },
562
+ [field.id]: { type: "contentRelationship", value: newCrFields },
619
563
  });
620
564
  };
621
565
 
622
566
  return (
623
- <TreeViewCheckbox
624
- key={fieldId}
625
- title={fieldId}
626
- checked={groupFieldsCheckMap[fieldId]?.value === true}
627
- onCheckedChange={onCheckedChange}
567
+ <TreeViewContentRelationshipField
568
+ key={field.id}
569
+ field={field}
570
+ fieldCheckMap={
571
+ crFieldCheckMap.type === "contentRelationship"
572
+ ? crFieldCheckMap.value
573
+ : {}
574
+ }
575
+ onChange={onContentRelationshipFieldChange}
628
576
  />
629
577
  );
630
578
  })}
@@ -638,42 +586,120 @@ function getExposedFieldsLabel(count: number) {
638
586
  }
639
587
 
640
588
  /**
641
- * Gets all the existing local custom types from the store, filters and sorts
642
- * them.
589
+ * Get all the existing local custom types from the store and process them into
590
+ * a single array to be rendered by the picker. For this we use the same as the
591
+ * Link config `customtypes` structure {@link TICustomTypes}.
643
592
  */
644
- function useCustomTypes(): CustomType[] {
593
+ function useCustomTypes() {
645
594
  const allCustomTypes = useSelector(selectAllCustomTypes);
646
- const localCustomTypes = allCustomTypes.flatMap<CustomType>((ct) => {
595
+ const localCustomTypes = allCustomTypes.flatMap((ct) => {
647
596
  // In the store we have remote and local custom types, we want to show
648
597
  // the local ones, so that the user is able to create a content
649
598
  // relationship with custom types present on the user's computer (pushed
650
599
  // or not).
651
- return "local" in ct ? CustomTypes.fromSM(ct.local) : [];
600
+ return "local" in ct ? ct.local : [];
652
601
  });
653
602
 
654
- localCustomTypes.sort((a, b) => a.id.localeCompare(b.id));
603
+ return useMemo(() => {
604
+ const customTypes = localCustomTypes.flatMap<TICustomType>((customType) => {
605
+ const tabFields = customType.tabs.flatMap((tab) => {
606
+ return tab.value.flatMap((field) => {
607
+ if (isUidField(field)) return [];
608
+
609
+ // Check if it's a content relationship link/field
610
+ if (
611
+ field.value.type === "Link" &&
612
+ field.value.config?.select === "document" &&
613
+ field.value.config.customtypes
614
+ ) {
615
+ const resolvedFields = resolveContentRelationshipFields(
616
+ field.value.config.customtypes,
617
+ localCustomTypes,
618
+ );
619
+
620
+ return resolvedFields.length > 0
621
+ ? { id: field.key, customtypes: resolvedFields }
622
+ : [];
623
+ }
624
+
625
+ if (field.value.type === "Group" && field.value.config?.fields) {
626
+ return {
627
+ id: field.key,
628
+ fields: field.value.config.fields.map((field) => field.key),
629
+ };
630
+ }
655
631
 
656
- return localCustomTypes;
632
+ return field.key;
633
+ });
634
+ });
635
+
636
+ return tabFields.length > 0
637
+ ? { id: customType.id, fields: tabFields }
638
+ : [];
639
+ });
640
+
641
+ customTypes.sort((a, b) => a.id.localeCompare(b.id));
642
+
643
+ return customTypes;
644
+ }, [localCustomTypes]);
657
645
  }
658
646
 
659
- function resolveContentRelationshipCustomTypes(
660
- customTypes: LinkCustomtypes,
661
- localCustomTypes: CustomType[],
662
- ): CustomType[] {
663
- const fields = customTypes.flatMap<CustomType>((customType) => {
664
- if (typeof customType === "string") return [];
665
- return localCustomTypes.find((ct) => ct.id === customType.id) ?? [];
666
- });
647
+ function resolveContentRelationshipFields(
648
+ customTypesArray: TICustomTypes,
649
+ localCustomTypes: CustomTypeSM[],
650
+ ): TICustomTypeFieldValues[] {
651
+ const fields = customTypesArray.flatMap<TICustomTypeFieldValues>(
652
+ (customType) => {
653
+ if (typeof customType === "string") return [];
654
+
655
+ // Didn't find a custom type with a matching id in the store
656
+ const matchingCustomType = localCustomTypes.find(
657
+ (ct) => ct.id === customType.id,
658
+ );
659
+ if (!matchingCustomType) return [];
660
+
661
+ const tabFields = matchingCustomType.tabs.flatMap((tab) => {
662
+ return tab.value.flatMap((field) => {
663
+ if (isUidField(field)) return [];
664
+
665
+ // Group inside content relationship field
666
+ if (field.value.type === "Group" && field.value.config?.fields) {
667
+ return {
668
+ id: field.key,
669
+ fields: field.value.config.fields.map((field) => field.key),
670
+ };
671
+ }
672
+
673
+ return field.key;
674
+ });
675
+ });
676
+
677
+ return tabFields.length > 0
678
+ ? { id: customType.id, fields: tabFields }
679
+ : [];
680
+ },
681
+ );
667
682
 
668
683
  return fields;
669
684
  }
670
685
 
686
+ function isUidField(
687
+ field: TabFields[number],
688
+ ): field is { key: string; value: UID } {
689
+ // Filter out uid fields because it's a special field returned by the
690
+ // API and is not part of the data object in the document.
691
+ // We also filter by key "uid", because (as of the time of writing
692
+ // this), creating any field with that API id will result in it being
693
+ // used for metadata.
694
+ return field.key === "uid" && field.value.type === "UID";
695
+ }
696
+
671
697
  /**
672
- * Converts a Link config `customtypes` ({@link LinkCustomtypes}) structure into
698
+ * Converts a Link config `customtypes` ({@link TICustomTypes}) structure into
673
699
  * picker fields check map ({@link PickerCustomTypes}).
674
700
  */
675
- function convertLinkCustomtypesToFieldCheckMap(
676
- customTypes: LinkCustomtypes,
701
+ function convertCustomTypesToFieldCheckMap(
702
+ customTypes: TICustomTypes,
677
703
  ): PickerCustomTypes {
678
704
  return customTypes.reduce<PickerCustomTypes>((customTypes, customType) => {
679
705
  if (typeof customType === "string") return customTypes;
@@ -685,11 +711,10 @@ function convertLinkCustomtypesToFieldCheckMap(
685
711
  customTypeFields[field] = { type: "checkbox", value: true };
686
712
  } else if ("fields" in field && field.fields !== undefined) {
687
713
  // Group field
688
- customTypeFields[field.id] = createGroupFieldCheckMap(field);
714
+ customTypeFields[field.id] = createGroupField(field);
689
715
  } else if ("customtypes" in field && field.customtypes !== undefined) {
690
716
  // Content relationship field
691
- customTypeFields[field.id] =
692
- createContentRelationshipFieldCheckMap(field);
717
+ customTypeFields[field.id] = createNestedCustomTypeField(field);
693
718
  }
694
719
 
695
720
  return customTypeFields;
@@ -700,8 +725,8 @@ function convertLinkCustomtypesToFieldCheckMap(
700
725
  }, {});
701
726
  }
702
727
 
703
- function createGroupFieldCheckMap(
704
- group: LinkCustomtypesGroupFieldValue,
728
+ function createGroupField(
729
+ group: TIGroupFieldValues,
705
730
  ): PickerFirstLevelGroupField {
706
731
  return {
707
732
  type: "group",
@@ -712,7 +737,7 @@ function createGroupFieldCheckMap(
712
737
  fields[field] = { type: "checkbox", value: true };
713
738
  } else if ("customtypes" in field && field.customtypes !== undefined) {
714
739
  // Content relationship field
715
- fields[field.id] = createContentRelationshipFieldCheckMap(field);
740
+ fields[field.id] = createNestedCustomTypeField(field);
716
741
  }
717
742
 
718
743
  return fields;
@@ -722,8 +747,8 @@ function createGroupFieldCheckMap(
722
747
  };
723
748
  }
724
749
 
725
- function createContentRelationshipFieldCheckMap(
726
- field: LinkCustomtypesContentRelationshipFieldValue,
750
+ function createNestedCustomTypeField(
751
+ field: TIContentRelationshipFieldValue,
727
752
  ): PickerContentRelationshipField {
728
753
  const crField: PickerContentRelationshipField = {
729
754
  type: "contentRelationship",
@@ -762,84 +787,76 @@ function createContentRelationshipFieldCheckMap(
762
787
 
763
788
  /**
764
789
  * Converts a picker fields check map structure ({@link PickerCustomTypes}) into
765
- * Link config `customtypes` ({@link LinkCustomtypes}) and filter out empty Custom
790
+ * Link config `customtypes` ({@link TICustomTypes}) and filter out empty Custom
766
791
  * types.
767
792
  */
768
- function convertFieldCheckMapToLinkCustomtypes(map: PickerCustomTypes) {
769
- return Object.entries(map).flatMap<LinkCustomtypes[number]>(
770
- ([ctId, ctFields]) => {
771
- const fields = Object.entries(ctFields).flatMap<
772
- | string
773
- | LinkCustomtypesContentRelationshipFieldValue
774
- | LinkCustomtypesGroupFieldValue
775
- >(([fieldId, fieldValue]) => {
776
- // First level group field
777
- if (fieldValue.type === "group") {
778
- const fields = Object.entries(fieldValue.value).flatMap<
779
- string | LinkCustomtypesContentRelationshipFieldValue
780
- >(([fieldId, fieldValue]) => {
781
- if (fieldValue.type === "checkbox") {
782
- return fieldValue.value ? fieldId : [];
783
- }
784
-
785
- const customTypes = createContentRelationshipLinkCustomtypes(
786
- fieldValue.value,
787
- );
788
-
789
- return customTypes.length > 0
790
- ? { id: fieldId, customtypes: customTypes }
791
- : [];
792
- });
793
+ function convertFieldCheckMapToCustomTypes(map: PickerCustomTypes) {
794
+ return Object.entries(map).flatMap<TICustomType>(([ctId, ctFields]) => {
795
+ const fields = Object.entries(ctFields).flatMap<
796
+ string | TIContentRelationshipFieldValue | TIGroupFieldValues
797
+ >(([fieldId, fieldValue]) => {
798
+ if (fieldValue.type === "checkbox") {
799
+ return fieldValue.value ? fieldId : [];
800
+ }
793
801
 
794
- return fields.length > 0 ? { id: fieldId, fields } : [];
795
- }
802
+ if (fieldValue.type === "group") {
803
+ const fields = Object.entries(fieldValue.value).flatMap<
804
+ string | TIContentRelationshipFieldValue
805
+ >(([fieldId, fieldValue]) => {
806
+ if (fieldValue.type === "checkbox") {
807
+ return fieldValue.value ? fieldId : [];
808
+ }
796
809
 
797
- // Content relationship field
798
- if (fieldValue.type === "contentRelationship") {
799
- const customTypes = createContentRelationshipLinkCustomtypes(
810
+ const customTypes = convertContentRelationshipFieldValueToCustomTypes(
800
811
  fieldValue.value,
801
812
  );
802
813
 
803
814
  return customTypes.length > 0
804
815
  ? { id: fieldId, customtypes: customTypes }
805
816
  : [];
806
- }
817
+ });
807
818
 
808
- // Regular field
809
- return fieldValue.value ? fieldId : [];
810
- });
819
+ return fields.length > 0 ? { id: fieldId, fields } : [];
820
+ }
811
821
 
812
- return fields.length > 0 ? { id: ctId, fields } : [];
813
- },
814
- );
822
+ // Content relationship field
823
+ const customTypes = convertContentRelationshipFieldValueToCustomTypes(
824
+ fieldValue.value,
825
+ );
826
+
827
+ return customTypes.length > 0
828
+ ? { id: fieldId, customtypes: customTypes }
829
+ : [];
830
+ });
831
+
832
+ return fields.length > 0 ? { id: ctId, fields } : [];
833
+ });
815
834
  }
816
835
 
817
- function createContentRelationshipLinkCustomtypes(
836
+ function convertContentRelationshipFieldValueToCustomTypes(
818
837
  value: PickerContentRelationshipFieldValue,
819
- ): LinkCustomtypesContentRelationshipFieldValue["customtypes"] {
820
- return Object.entries(value).flatMap(
838
+ ): TICustomTypeFieldValues[] {
839
+ return Object.entries(value).flatMap<TICustomTypeFieldValues>(
821
840
  ([nestedCustomTypeId, nestedCustomTypeFields]) => {
822
- const fields = Object.entries(nestedCustomTypeFields).flatMap(
823
- ([nestedFieldId, nestedFieldValue]) => {
824
- // Leaf group field
825
- if (nestedFieldValue.type === "group") {
826
- const nestedGroupFields = Object.entries(
827
- nestedFieldValue.value,
828
- ).flatMap<string>(([fieldId, fieldValue]) => {
829
- // Regular field
830
- return fieldValue.type === "checkbox" && fieldValue.value
831
- ? fieldId
832
- : [];
833
- });
834
-
835
- return nestedGroupFields.length > 0
836
- ? { id: nestedFieldId, fields: nestedGroupFields }
841
+ const fields = Object.entries(nestedCustomTypeFields).flatMap<
842
+ string | TICustomTypeRegularFieldValues
843
+ >(([nestedFieldId, nestedFieldValue]) => {
844
+ if (nestedFieldValue.type === "group") {
845
+ const nestedGroupFields = Object.entries(
846
+ nestedFieldValue.value,
847
+ ).flatMap<string>(([fieldId, fieldValue]) => {
848
+ return fieldValue.type === "checkbox" && fieldValue.value
849
+ ? fieldId
837
850
  : [];
838
- }
851
+ });
839
852
 
840
- return nestedFieldValue.value ? nestedFieldId : [];
841
- },
842
- );
853
+ return nestedGroupFields.length > 0
854
+ ? { id: nestedFieldId, fields: nestedGroupFields }
855
+ : [];
856
+ }
857
+
858
+ return nestedFieldValue.value ? nestedFieldId : [];
859
+ });
843
860
 
844
861
  return fields.length > 0 ? { id: nestedCustomTypeId, fields } : [];
845
862
  },
@@ -859,53 +876,12 @@ function countPickedFields(
859
876
  if (!fields) return 0;
860
877
  return Object.values(fields).reduce<number>((count, value) => {
861
878
  if (!isValidObject(value)) return count;
862
- if (isCheckboxValue(value)) return count + (value.value ? 1 : 0);
879
+ if (isCheckboxField(value)) return count + (value.value ? 1 : 0);
863
880
  return count + countPickedFields(value);
864
881
  }, 0);
865
882
  }
866
- function isCheckboxValue(value: unknown): value is PickerCheckboxField {
883
+
884
+ function isCheckboxField(value: unknown): value is PickerCheckboxField {
867
885
  if (!isValidObject(value)) return false;
868
886
  return "type" in value && value.type === "checkbox";
869
887
  }
870
-
871
- function isGroupField(field: NestableWidget | Group): field is Group {
872
- return field.type === "Group";
873
- }
874
-
875
- function isContentRelationshipField(
876
- field: NestableWidget | Group,
877
- ): field is Link {
878
- return (
879
- field.type === "Link" &&
880
- field.config?.select === "document" &&
881
- field.config?.customtypes !== undefined
882
- );
883
- }
884
-
885
- function getCustomTypeStaticFields(customType: CustomType) {
886
- return Object.values(customType.json).flatMap((tabFields) => {
887
- return Object.entries(tabFields).flatMap(([fieldId, field]) => {
888
- if (
889
- field.type !== "Slices" &&
890
- field.type !== "Choice" &&
891
- // Filter out uid fields because it's a special field returned by the
892
- // API and is not part of the data object in the document.
893
- // We also filter by key "uid", because (as of the time of writing
894
- // this), creating any field with that API id will result in it being
895
- // used for metadata.
896
- (field.type !== "UID" || fieldId !== "uid")
897
- ) {
898
- return { fieldId, field: field as NestableWidget | Group };
899
- }
900
-
901
- return [];
902
- });
903
- });
904
- }
905
-
906
- function getGroupFields(group: Group) {
907
- if (!group.config?.fields) return [];
908
- return Object.entries(group.config.fields).map(([fieldId, field]) => {
909
- return { fieldId, field: field as NestableWidget };
910
- });
911
- }