@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.
- package/package.json +1 -1
- package/src/__tests__/ApplyChangesModal.test.tsx +48 -0
- package/src/__tests__/BranchDeletedModal.test.tsx +42 -0
- package/src/__tests__/ContentBrowser.fieldfocus.test.tsx +109 -0
- package/src/__tests__/ContentBrowser.forms.test.tsx +47 -1
- package/src/__tests__/ContentBrowser.history.test.tsx +385 -0
- package/src/__tests__/ContentBrowser.historycollab.test.tsx +433 -0
- package/src/__tests__/ContentBrowser.references.test.tsx +179 -0
- package/src/__tests__/ContentBrowser.slotmedia.test.tsx +150 -0
- package/src/__tests__/MediaField.test.tsx +18 -0
- package/src/__tests__/ProtectedBranchModal.test.tsx +68 -0
- package/src/components/ApplyChangesModal.tsx +70 -21
- package/src/components/BranchDeletedModal.tsx +111 -0
- package/src/components/ChangeDetail.tsx +94 -11
- package/src/components/CollabField.tsx +71 -0
- package/src/components/ContentBrowser.tsx +494 -77
- package/src/components/DocumentDetails.tsx +0 -0
- package/src/components/DocumentHistory.tsx +408 -0
- package/src/components/MediaField.tsx +62 -0
- package/src/components/MediaPreview.tsx +14 -1
- package/src/components/MediaPreview.web.tsx +18 -3
- package/src/components/Notifications.tsx +146 -28
- package/src/components/ProtectedBranchModal.tsx +136 -0
- package/src/components/ReferenceField.tsx +0 -0
- package/src/components/primitives.tsx +46 -1
- package/src/fieldComponents.ts +2 -2
- package/src/history.ts +82 -0
- package/src/icons.ts +1 -0
- package/src/index.ts +28 -3
- package/src/media.ts +15 -0
- 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
|
+
}
|