@riducms/admin 0.4.0 → 0.5.0

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 CHANGED
@@ -21,11 +21,11 @@
21
21
  "@iconify/json": "^2.2.509",
22
22
  "@internationalized/date": "^3.12.3",
23
23
  "@lezer/highlight": "^1.2.3",
24
- "@riducms/plugin": "0.4.0",
25
- "@riducms/protocol": "0.4.0",
26
- "@riducms/sdk": "0.4.0",
27
- "@riducms/translations": "0.4.0",
28
- "@riducms/ui": "0.4.0",
24
+ "@riducms/plugin": "0.5.0",
25
+ "@riducms/protocol": "0.5.0",
26
+ "@riducms/sdk": "0.5.0",
27
+ "@riducms/translations": "0.5.0",
28
+ "@riducms/ui": "0.5.0",
29
29
  "@twinkleplop/bash": "0.1.5",
30
30
  "@twinkleplop/go": "0.1.5",
31
31
  "@twinkleplop/typescript": "0.1.5",
@@ -35,7 +35,7 @@
35
35
  "description": "Framework-owned Svelte 5 authoring application embedded by Ridu servers.",
36
36
  "devDependencies": {
37
37
  "@hvniel/lexical-svelte": "^0.1.1",
38
- "@riducms/build": "0.4.0",
38
+ "@riducms/build": "0.5.0",
39
39
  "svelte": "^5.57.0"
40
40
  },
41
41
  "exports": {
@@ -74,5 +74,5 @@
74
74
  "test:unit": "bun test tests"
75
75
  },
76
76
  "type": "module",
77
- "version": "0.4.0"
77
+ "version": "0.5.0"
78
78
  }
@@ -66,6 +66,16 @@ export function submissionFormValues(
66
66
  return submissionRecord(fields, input, "", include);
67
67
  }
68
68
 
69
+ /** Project an update onto caller edits, retaining row membership and stable identities. */
70
+ export function changedFormValues(
71
+ fields: readonly SchemaField[],
72
+ input: FormValues,
73
+ original: FormValues,
74
+ isInherited?: (path: string) => boolean
75
+ ): FormValues {
76
+ return submissionRecord(fields, input, "", () => true, original, isInherited);
77
+ }
78
+
69
79
  /** Initialize a new record, retaining supplied values and container metadata. */
70
80
  export function initialFormValues(
71
81
  fields: readonly SchemaField[],
@@ -100,9 +110,12 @@ function submissionRecord(
100
110
  fields: readonly SchemaField[],
101
111
  input: unknown,
102
112
  prefix: string,
103
- include: (path: string, canonicalPath: string) => boolean
113
+ include: (path: string, canonicalPath: string) => boolean,
114
+ original?: unknown,
115
+ isInherited?: (path: string) => boolean
104
116
  ): FormValues {
105
117
  const values = isRecord(input) ? input : {};
118
+ const before = isRecord(original) ? original : {};
106
119
  const submitted: FormValues = {};
107
120
  for (const field of fields) {
108
121
  const path = joinFormPath(prefix, field.name);
@@ -113,23 +126,47 @@ function submissionRecord(
113
126
  )
114
127
  continue;
115
128
  const value = values[field.name];
116
- if (field.type === "group") {
117
- submitted[field.name] = submissionRecord(field.nested?.fields ?? [], value, path, include);
118
- } else if (field.type === "array" && Array.isArray(value)) {
119
- submitted[field.name] = value.map((row, index) =>
120
- submissionRecord(field.nested?.fields ?? [], row, `${path}.${index}`, include)
129
+ const displayedPrevious = before[field.name];
130
+ if (Object.hasOwn(before, field.name) && deepEqual(value, displayedPrevious)) continue;
131
+ // An edited fallback establishes a new locale value, without retained children to merge.
132
+ const previous = isInherited?.(path) === true ? undefined : displayedPrevious;
133
+
134
+ if (field.type === "group" && isRecord(value)) {
135
+ submitted[field.name] = submissionRecord(
136
+ field.nested?.fields ?? [],
137
+ value,
138
+ path,
139
+ include,
140
+ previous,
141
+ isInherited
121
142
  );
122
- } else if (field.type === "blocks" && Array.isArray(value)) {
143
+ } else if ((field.type === "array" || field.type === "blocks") && Array.isArray(value)) {
144
+ const previousRows = new Map(
145
+ (Array.isArray(previous) ? previous : []).flatMap((row) =>
146
+ isRecord(row) && typeof row._key === "string" ? [[row._key, row] as const] : []
147
+ )
148
+ );
149
+ const blocks = resolveBlockTypes(field.blocks);
123
150
  submitted[field.name] = value.map((row, index) => {
124
- if (!isRecord(row) || typeof row.blockType !== "string") return cloneFormValue(row);
125
- const block = resolveBlockTypes(field.blocks).find(
126
- (candidate) => candidate.slug === row.blockType
151
+ if (!isRecord(row)) return cloneFormValue(row);
152
+ const children =
153
+ field.type === "array"
154
+ ? (field.nested?.fields ?? [])
155
+ : blocks.find((block) => block.slug === row.blockType)?.fields;
156
+ if (children === undefined) return cloneFormValue(row);
157
+
158
+ const prior = typeof row._key === "string" ? previousRows.get(row._key) : undefined;
159
+ return submissionRecord(
160
+ children,
161
+ row,
162
+ `${path}.${index}`,
163
+ include,
164
+ field.type === "blocks" && prior?.blockType !== row.blockType ? undefined : prior,
165
+ isInherited
127
166
  );
128
- return block === undefined
129
- ? cloneFormValue(row)
130
- : submissionRecord(block.fields, row, `${path}.${index}`, include);
131
167
  });
132
168
  } else if (field.type === "plugin" && field.plugin?.embeddedTrees !== undefined) {
169
+ const priorOccurrences = embeddedOccurrences(field, previous, path).occurrences;
133
170
  submitted[field.name] = transformEmbeddedPayloads(
134
171
  field,
135
172
  cloneFormValue(value),
@@ -139,7 +176,17 @@ function submissionRecord(
139
176
  occurrence.block.fields,
140
177
  occurrence.payload,
141
178
  occurrence.path,
142
- include
179
+ include,
180
+ occurrence.identity === undefined
181
+ ? undefined
182
+ : priorOccurrences.find(
183
+ (prior) =>
184
+ prior.tree.key === occurrence.tree.key &&
185
+ prior.case.tagValue === occurrence.case.tagValue &&
186
+ prior.block.slug === occurrence.block.slug &&
187
+ prior.identity === occurrence.identity
188
+ )?.payload,
189
+ isInherited
143
190
  ),
144
191
  [occurrence.case.discriminator]: occurrence.block.slug,
145
192
  ...(occurrence.identity === undefined
@@ -11,7 +11,11 @@ import {
11
11
  saveFormDraft,
12
12
  takeFormDraft,
13
13
  } from "@admin/core/forms/form-draft-recovery";
14
- import { documentFormValues, initialFormValues } from "@admin/core/forms/form-schema";
14
+ import {
15
+ changedFormValues,
16
+ documentFormValues,
17
+ initialFormValues,
18
+ } from "@admin/core/forms/form-schema";
15
19
  import { invalidFieldLabels } from "@admin/core/forms/form-validation";
16
20
  import type { NotificationCenter } from "@admin/core/notifications/notification-center.svelte";
17
21
  import { samePreparedCreateValues } from "@admin/core/bootstrap/admin-bootstrap";
@@ -501,6 +505,10 @@ export class DocumentController {
501
505
  const wasCreating = this.creating;
502
506
  try {
503
507
  const saved = await this.form.submit(this.validationFields, this.creating, async (values) => {
508
+ if (!wasCreating)
509
+ values = changedFormValues(this.validationFields, values, this.form.original, (path) =>
510
+ this.form.isInherited(path)
511
+ );
504
512
  if (this.globalResource) {
505
513
  if (publishingChanges) {
506
514
  return this.options.runtime.client.publishGlobalChanges(collectionSlug, values, {
@@ -7,7 +7,11 @@ import { RiduError } from "@riducms/sdk";
7
7
 
8
8
  import type { AdminDocument } from "@admin/core/api/admin-client";
9
9
  import { FormController, FormValidationError } from "@admin/core/forms/form-controller.svelte";
10
- import { documentFormValues, initialFormValues } from "@admin/core/forms/form-schema";
10
+ import {
11
+ changedFormValues,
12
+ documentFormValues,
13
+ initialFormValues,
14
+ } from "@admin/core/forms/form-schema";
11
15
  import { invalidFieldLabels } from "@admin/core/forms/form-validation";
12
16
  import type { NotificationCenter } from "@admin/core/notifications/notification-center.svelte";
13
17
  import type { AdminRuntime } from "@admin/core/runtime/admin-runtime.svelte";
@@ -450,6 +454,9 @@ export class ReferenceBrowserWorkflow {
450
454
  const saved = await this.form.submit(this.validationFields, this.creating, async (values) => {
451
455
  if (this.options.readOnly) throw new Error("This field is read-only.");
452
456
  if (this.editorDocument !== undefined) {
457
+ values = changedFormValues(this.validationFields, values, this.form.original, (path) =>
458
+ this.form.isInherited(path)
459
+ );
453
460
  if (this.collection.capabilities.upload) {
454
461
  return this.options.runtime.client.updateUpload(
455
462
  this.collection.slug,