@gogitcms/design-system 0.16.0-next.1 → 0.16.0-next.11

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 (31) hide show
  1. package/package.json +1 -1
  2. package/src/__tests__/ApplyChangesModal.test.tsx +48 -0
  3. package/src/__tests__/BranchDeletedModal.test.tsx +42 -0
  4. package/src/__tests__/ContentBrowser.fieldfocus.test.tsx +109 -0
  5. package/src/__tests__/ContentBrowser.forms.test.tsx +47 -1
  6. package/src/__tests__/ContentBrowser.history.test.tsx +385 -0
  7. package/src/__tests__/ContentBrowser.historycollab.test.tsx +433 -0
  8. package/src/__tests__/ContentBrowser.references.test.tsx +179 -0
  9. package/src/__tests__/ContentBrowser.slotmedia.test.tsx +150 -0
  10. package/src/__tests__/MediaField.test.tsx +18 -0
  11. package/src/__tests__/ProtectedBranchModal.test.tsx +68 -0
  12. package/src/components/ApplyChangesModal.tsx +70 -21
  13. package/src/components/BranchDeletedModal.tsx +111 -0
  14. package/src/components/ChangeDetail.tsx +94 -11
  15. package/src/components/CollabField.tsx +71 -0
  16. package/src/components/ContentBrowser.tsx +494 -77
  17. package/src/components/DocumentDetails.tsx +0 -0
  18. package/src/components/DocumentHistory.tsx +408 -0
  19. package/src/components/MediaField.tsx +62 -0
  20. package/src/components/MediaPreview.tsx +14 -1
  21. package/src/components/MediaPreview.web.tsx +18 -3
  22. package/src/components/Notifications.tsx +146 -28
  23. package/src/components/ProtectedBranchModal.tsx +136 -0
  24. package/src/components/ReferenceField.tsx +0 -0
  25. package/src/components/primitives.tsx +46 -1
  26. package/src/fieldComponents.ts +2 -2
  27. package/src/history.ts +82 -0
  28. package/src/icons.ts +1 -0
  29. package/src/index.ts +28 -3
  30. package/src/media.ts +15 -0
  31. package/src/references.ts +97 -0
@@ -386,3 +386,74 @@ export function CollabTextarea({
386
386
  </PresenceField>
387
387
  );
388
388
  }
389
+
390
+ // ---- writing a whole value into the shared document ------------------------
391
+
392
+ /**
393
+ * Where one leaf of a document value lives in the shared doc.
394
+ *
395
+ * The split mirrors what the server seeds (collab.go's collectLeaves), because
396
+ * that is what decides which binding a control actually reads: a string is a
397
+ * Y.Text, and everything else — numbers, booleans, null, and whole lists — is a
398
+ * key in the "reg" map. Writing to the other one is not an error anyone sees;
399
+ * it is a value that silently lands where nothing is looking.
400
+ */
401
+ type CollabLeaf = { kind: "text"; value: string } | { kind: "reg"; value: unknown };
402
+
403
+ /**
404
+ * Flattens a value into the leaves the shared document holds it as, keyed by
405
+ * dotted path — `seo.title` for a group's child, exactly as the server does.
406
+ *
407
+ * `undefined` yields no leaves at all: it means the field is absent, which is
408
+ * expressed by clearing whatever was there rather than by writing something.
409
+ */
410
+ function collabLeaves(path: string, value: unknown, into = new Map<string, CollabLeaf>()): Map<string, CollabLeaf> {
411
+ if (value === undefined) return into;
412
+ if (typeof value === "string") {
413
+ into.set(path, { kind: "text", value });
414
+ return into;
415
+ }
416
+ // A list is a whole-value register, matching CollabArrayField — its items are
417
+ // not addressable leaves, so it is written and cleared in one piece.
418
+ if (Array.isArray(value) || value === null || typeof value !== "object") {
419
+ into.set(path, { kind: "reg", value });
420
+ return into;
421
+ }
422
+ for (const [k, v] of Object.entries(value as Record<string, unknown>)) {
423
+ collabLeaves(path ? `${path}.${k}` : k, v, into);
424
+ }
425
+ return into;
426
+ }
427
+
428
+ /**
429
+ * Replaces a field's value in the shared document, so a value written by
430
+ * something other than typing — restoring a field from a past version — reaches
431
+ * the room instead of only the local form.
432
+ *
433
+ * In a live session the shared document is the source of truth: every collab
434
+ * control reads its binding in preference to the host's value, so a write that
435
+ * only touches local state is displayed for an instant and then overwritten by
436
+ * the CRDT's unchanged value. It has to be written here to be written at all.
437
+ *
438
+ * `previous` is what the field held, and is needed rather than inferable: the
439
+ * new value's leaves say what to write, but only the old value's leaves say
440
+ * what to CLEAR — a group that loses a child, or a field being cleared outright,
441
+ * would otherwise keep the shared value nobody can see any more.
442
+ */
443
+ export function writeCollabValue(collab: CollabApi, path: string, previous: unknown, next: unknown): void {
444
+ const before = collabLeaves(path, previous);
445
+ const after = collabLeaves(path, next);
446
+
447
+ for (const [leafPath, leaf] of before) {
448
+ if (after.has(leafPath)) continue;
449
+ // Clearing is per shared type: a Y.Text empties, a register goes back to
450
+ // unset (which is what an absent key reads as, and what makes the control
451
+ // fall back to the document's own value).
452
+ if (leaf.kind === "text") collab.text(leafPath).set("");
453
+ else collab.register(leafPath).set(undefined);
454
+ }
455
+ for (const [leafPath, leaf] of after) {
456
+ if (leaf.kind === "text") collab.text(leafPath).set(leaf.value);
457
+ else collab.register(leafPath).set(leaf.value);
458
+ }
459
+ }