@cosmicdrift/kumiko-headless 0.354.0 → 0.355.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/dist/index.d.ts
CHANGED
|
@@ -10,5 +10,5 @@ export type { NavDefinition, NavNode, NavRegistrySlice, NavTree, ResolveNavigati
|
|
|
10
10
|
export { resolveNavigation } from "./nav/index.js";
|
|
11
11
|
export type { Store, WritableStore } from "./store/index.js";
|
|
12
12
|
export { createStore, shallowEqual } from "./store/index.js";
|
|
13
|
-
export type { ComputeEditViewModelInput, ComputeListViewModelInput, DerivedCellRoundingTarget, EditExtensionSectionViewModel, EditFieldSpec, EditFieldsSectionViewModel, EditFieldViewModel, EditRelatedListSectionViewModel, EditSectionSpec, EditSectionViewModel, EditViewModel, EditWriteFormSectionViewModel, EmbeddedDerivedOp, EmbeddedListCellViewModel, EmbeddedListIssueGroups, FieldConditionCtx, FieldRenderer, ListColumnSpec, ListColumnViewModel, ListRowViewModel, ListViewModel, RuntimeRenderer, ScreenSlots, Translate, } from "./view-model/index.js";
|
|
13
|
+
export type { ComputeEditViewModelInput, ComputeListViewModelInput, DerivedCellRoundingTarget, EditExtensionSectionViewModel, EditFieldSpec, EditFieldsSectionViewModel, EditFieldViewModel, EditFormMode, EditRelatedListSectionViewModel, EditSectionSpec, EditSectionViewModel, EditViewModel, EditWriteFormSectionViewModel, EmbeddedDerivedOp, EmbeddedListCellViewModel, EmbeddedListIssueGroups, FieldConditionCtx, FieldRenderer, ListColumnSpec, ListColumnViewModel, ListRowViewModel, ListViewModel, RuntimeRenderer, ScreenSlots, Translate, } from "./view-model/index.js";
|
|
14
14
|
export { buildOptionLabels, computeDerivedCellValue, computeEditViewModel, computeListViewModel, computeRelatedListSectionViewModel, embeddedCellLabelKey, embeddedCellOptionLabelKey, fieldLabelKey, fieldOptionLabelKey, fieldOptionLabelKeyPrefix, groupEmbeddedListIssues, roundDerivedCellValue, sumEmbeddedListColumn, } from "./view-model/index.js";
|
|
@@ -6,6 +6,11 @@ export type ComputeEditViewModelInput<TValues extends Readonly<Record<string, un
|
|
|
6
6
|
readonly values: TValues;
|
|
7
7
|
readonly translate: Translate;
|
|
8
8
|
readonly featureName: string;
|
|
9
|
+
/** Which write the form drives. Applies the screen's `writeExcludedFields`
|
|
10
|
+
* for that verb (create hides them, update shows them read-only); omitted
|
|
11
|
+
* means no exclusion applies. */
|
|
12
|
+
readonly mode?: EditFormMode;
|
|
9
13
|
};
|
|
14
|
+
export type EditFormMode = "create" | "update";
|
|
10
15
|
export declare function computeRelatedListSectionViewModel(sectionSpec: EditRelatedListSection, translate: Translate): EditRelatedListSectionViewModel;
|
|
11
16
|
export declare function computeEditViewModel<TValues extends Readonly<Record<string, unknown>> = Readonly<Record<string, unknown>>>(input: ComputeEditViewModelInput<TValues>): EditViewModel;
|
package/dist/view-model/edit.js
CHANGED
|
@@ -42,6 +42,9 @@ function computeWriteFormSectionViewModel(sectionSpec, screenId, values, transla
|
|
|
42
42
|
...(sectionSpec.actions !== undefined && { actions: sectionSpec.actions }),
|
|
43
43
|
};
|
|
44
44
|
}
|
|
45
|
+
function wasEmptiedByWriteExclusion(declaredFieldSpecs, remainingFields) {
|
|
46
|
+
return declaredFieldSpecs.length > 0 && remainingFields.length === 0;
|
|
47
|
+
}
|
|
45
48
|
// relatedList runs its own query — nothing here to resolve against
|
|
46
49
|
// entity/values, the spec passes through verbatim (fw#2166).
|
|
47
50
|
export function computeRelatedListSectionViewModel(sectionSpec, translate) {
|
|
@@ -304,7 +307,9 @@ function buildExtensionSectionViewModel(sectionSpec, translate) {
|
|
|
304
307
|
// section/field tree the renderer draws. FieldConditions are evaluated here
|
|
305
308
|
// so the renderer never re-runs them during React render.
|
|
306
309
|
export function computeEditViewModel(input) {
|
|
307
|
-
const { screen, entity, values, translate, featureName } = input;
|
|
310
|
+
const { screen, entity, values, translate, featureName, mode } = input;
|
|
311
|
+
const excludedFields = new Set(mode === undefined ? [] : screen.writeExcludedFields?.[mode]);
|
|
312
|
+
const isHiddenByExclusion = (fieldSpec) => mode === "create" && excludedFields.has(normalizeEditField(fieldSpec).field);
|
|
308
313
|
const sections = screen.layout.sections.map((sectionSpec) => {
|
|
309
314
|
if (isExtensionEditSection(sectionSpec)) {
|
|
310
315
|
return buildExtensionSectionViewModel(sectionSpec, translate);
|
|
@@ -317,7 +322,10 @@ export function computeEditViewModel(input) {
|
|
|
317
322
|
}
|
|
318
323
|
// `groups` flattens into the same per-field pipeline as plain `fields`;
|
|
319
324
|
// the group structure below just re-groups the computed views by name.
|
|
320
|
-
const
|
|
325
|
+
const declaredFieldSpecs = sectionFieldSpecs(sectionSpec);
|
|
326
|
+
const fields = declaredFieldSpecs
|
|
327
|
+
.filter((fieldSpec) => !isHiddenByExclusion(fieldSpec))
|
|
328
|
+
.map((fieldSpec) => {
|
|
321
329
|
const normalized = normalizeEditField(fieldSpec);
|
|
322
330
|
const fieldDef = entity.fields[normalized.field];
|
|
323
331
|
if (!fieldDef) {
|
|
@@ -343,7 +351,8 @@ export function computeEditViewModel(input) {
|
|
|
343
351
|
// `readOnly` (camelCase) is the name on both sides: EditFieldSpec
|
|
344
352
|
// in the engine, and the view-model emitted here. One convention
|
|
345
353
|
// through the stack beats translating at the boundary.
|
|
346
|
-
const readOnly =
|
|
354
|
+
const readOnly = (mode === "update" && excludedFields.has(normalized.field)) ||
|
|
355
|
+
evalCondition(normalized.readOnly, false, values);
|
|
347
356
|
// `required` on the field-spec overrides the entity-default. A
|
|
348
357
|
// field that's required at the entity-level but marked required:
|
|
349
358
|
// false on the screen (e.g. a soft-onboarding wizard that
|
|
@@ -383,14 +392,17 @@ export function computeEditViewModel(input) {
|
|
|
383
392
|
};
|
|
384
393
|
return view;
|
|
385
394
|
});
|
|
386
|
-
//
|
|
387
|
-
//
|
|
395
|
+
// The boot-validator rejects declared-empty sections (screens.ts), but a
|
|
396
|
+
// create-excluded field list can still empty one here: then `visible` is false.
|
|
388
397
|
const visible = fields.some((field) => field.visible);
|
|
389
398
|
// Tabs mode renders this section as its own panel, where two columns reads
|
|
390
399
|
// better than the single-column default a stacked form keeps. Applies to
|
|
391
400
|
// entityEdit tabs as well since fw#3134, not just projectionDetail.
|
|
392
401
|
const defaultColumns = screen.layout.mode === "tabs" ? 2 : 1;
|
|
393
|
-
const groups = sectionSpec.groups
|
|
402
|
+
const groups = sectionSpec.groups
|
|
403
|
+
?.map((group) => ({ ...group, fields: group.fields.filter((f) => !isHiddenByExclusion(f)) }))
|
|
404
|
+
.filter((group) => group.fields.length > 0)
|
|
405
|
+
.map((group) => ({
|
|
394
406
|
title: translate(group.title),
|
|
395
407
|
columns: group.columns ?? 2,
|
|
396
408
|
fields: group.fields.map((fieldSpec) => {
|
|
@@ -407,6 +419,7 @@ export function computeEditViewModel(input) {
|
|
|
407
419
|
kind: "fields",
|
|
408
420
|
...(sectionSpec.id !== undefined && { id: sectionSpec.id }),
|
|
409
421
|
visible,
|
|
422
|
+
emptiedByWriteExclusion: wasEmptiedByWriteExclusion(declaredFieldSpecs, fields),
|
|
410
423
|
// Titellose Section (flache Form) → kein h3; nur übersetzen wenn gesetzt.
|
|
411
424
|
...(sectionSpec.title !== undefined && { title: translate(sectionSpec.title) }),
|
|
412
425
|
...(sectionSpec.description !== undefined && {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export type { ComputeEditViewModelInput } from "./edit.js";
|
|
1
|
+
export type { ComputeEditViewModelInput, EditFormMode } from "./edit.js";
|
|
2
2
|
export { computeEditViewModel, computeRelatedListSectionViewModel } from "./edit.js";
|
|
3
3
|
export type { DerivedCellRoundingTarget, EmbeddedDerivedOp, EmbeddedListIssueGroups, } from "./embedded-list.js";
|
|
4
4
|
export { computeDerivedCellValue, groupEmbeddedListIssues, roundDerivedCellValue, sumEmbeddedListColumn, } from "./embedded-list.js";
|
|
@@ -228,6 +228,10 @@ export type EditFieldsSectionViewModel = {
|
|
|
228
228
|
* The renderer then draws nothing, but the section stays in the array
|
|
229
229
|
* (key stability). */
|
|
230
230
|
readonly visible: boolean;
|
|
231
|
+
/** Set when every declared field was hidden by the create handler's
|
|
232
|
+
* `excludeFields`. Unlike a declared-empty section (review step), the
|
|
233
|
+
* renderer must not give it a tab or wizard step. */
|
|
234
|
+
readonly emptiedByWriteExclusion?: boolean;
|
|
231
235
|
/** Optional — eine titellose Section rendert nur ihre Felder (flache Form). */
|
|
232
236
|
readonly title?: string;
|
|
233
237
|
/** Translated help text under the block heading, from
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cosmicdrift/kumiko-headless",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.355.0",
|
|
4
4
|
"description": "Headless UI logic for Kumiko — Dispatcher contract, Form-Controller, View-Model, Nav-Resolver. Plattform- und React-frei; jeder Renderer (renderer, renderer-web, renderer-native, …) komponiert darauf.",
|
|
5
5
|
"license": "BUSL-1.1",
|
|
6
6
|
"author": "Marc Frost <marc@cosmicdriftgamestudio.com>",
|
|
@@ -36,8 +36,8 @@
|
|
|
36
36
|
}
|
|
37
37
|
},
|
|
38
38
|
"dependencies": {
|
|
39
|
-
"@cosmicdrift/kumiko-framework": "0.
|
|
40
|
-
"@cosmicdrift/kumiko-types": "0.
|
|
39
|
+
"@cosmicdrift/kumiko-framework": "0.355.0",
|
|
40
|
+
"@cosmicdrift/kumiko-types": "0.355.0",
|
|
41
41
|
"temporal-polyfill": "^0.3.2",
|
|
42
42
|
"zod": "^4.4.3"
|
|
43
43
|
},
|