@cosmicdrift/kumiko-headless 0.326.1 → 0.328.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 +2 -2
- package/src/changes.json +7 -0
- package/src/format/__tests__/format.test.ts +4 -0
- package/src/format/index.ts +5 -1
- package/src/view-model/__tests__/list.test.ts +11 -0
- package/src/view-model/edit.ts +4 -0
- package/src/view-model/list.ts +3 -0
- package/src/view-model/types.ts +8 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cosmicdrift/kumiko-headless",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.328.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,7 +36,7 @@
|
|
|
36
36
|
}
|
|
37
37
|
},
|
|
38
38
|
"dependencies": {
|
|
39
|
-
"@cosmicdrift/kumiko-framework": "0.
|
|
39
|
+
"@cosmicdrift/kumiko-framework": "0.328.0",
|
|
40
40
|
"temporal-polyfill": "^0.3.2",
|
|
41
41
|
"zod": "^4.4.3"
|
|
42
42
|
},
|
package/src/changes.json
CHANGED
|
@@ -1,4 +1,11 @@
|
|
|
1
1
|
[
|
|
2
|
+
{
|
|
3
|
+
"version": "0.328.0",
|
|
4
|
+
"type": "breaking",
|
|
5
|
+
"title": "Dates format with two-digit day and month",
|
|
6
|
+
"detail": "`formatValue` for date values passes `day: \"2-digit\"` and `month: \"2-digit\"`, so German output is 01.10.2026 instead of 1.10.2026 in lists, details and related lists. List view models carry `hideOnNarrow` onto columns.",
|
|
7
|
+
"migration": "Update tests and snapshots that expect single-digit day or month."
|
|
8
|
+
},
|
|
2
9
|
{
|
|
3
10
|
"version": "0.308.0",
|
|
4
11
|
"type": "improvement",
|
|
@@ -79,6 +79,10 @@ describe("applyFormatSpec — timestamp/date (formatDateCell-Pfad)", () => {
|
|
|
79
79
|
expect(out).toContain("2026");
|
|
80
80
|
});
|
|
81
81
|
|
|
82
|
+
test("date ohne dateStyle rendert Tag und Monat zweistellig", () => {
|
|
83
|
+
expect(applyFormatSpec({ format: "date", locale: "de-DE" }, "2026-03-05")).toBe("05.03.2026");
|
|
84
|
+
});
|
|
85
|
+
|
|
82
86
|
test("timestamp ohne Optionen nutzt das kompakte Default-Format", () => {
|
|
83
87
|
const out = applyFormatSpec({ format: "timestamp", locale: "en-US" }, instant);
|
|
84
88
|
expect(out).toContain("2026");
|
package/src/format/index.ts
CHANGED
|
@@ -53,7 +53,11 @@ function formatDateCell(
|
|
|
53
53
|
});
|
|
54
54
|
}
|
|
55
55
|
if (type === "date") {
|
|
56
|
-
return toPlainDate(raw).toLocaleString(locale
|
|
56
|
+
return toPlainDate(raw).toLocaleString(locale, {
|
|
57
|
+
year: "numeric",
|
|
58
|
+
month: "2-digit",
|
|
59
|
+
day: "2-digit",
|
|
60
|
+
});
|
|
57
61
|
}
|
|
58
62
|
return toInstant(raw).toLocaleString(locale, {
|
|
59
63
|
year: "numeric",
|
|
@@ -425,4 +425,15 @@ describe("computeListViewModel", () => {
|
|
|
425
425
|
});
|
|
426
426
|
expect(vm.slots).toBe(slots);
|
|
427
427
|
});
|
|
428
|
+
test("hideOnNarrow is carried onto the column view model only when set", () => {
|
|
429
|
+
const vm = computeListViewModel({
|
|
430
|
+
screen: listScreen(["title", { field: "priority", hideOnNarrow: true }]),
|
|
431
|
+
entity: taskEntity,
|
|
432
|
+
rows: [],
|
|
433
|
+
translate,
|
|
434
|
+
featureName: "tasks",
|
|
435
|
+
});
|
|
436
|
+
expect(vm.columns[0]?.hideOnNarrow).toBeUndefined();
|
|
437
|
+
expect(vm.columns[1]?.hideOnNarrow).toBe(true);
|
|
438
|
+
});
|
|
428
439
|
});
|
package/src/view-model/edit.ts
CHANGED
|
@@ -130,6 +130,10 @@ function computeRelatedListSectionViewModel(
|
|
|
130
130
|
...(sectionSpec.parentParam !== undefined && { parentParam: sectionSpec.parentParam }),
|
|
131
131
|
...(sectionSpec.parentFilter !== undefined && { parentFilter: sectionSpec.parentFilter }),
|
|
132
132
|
columns: sectionSpec.columns,
|
|
133
|
+
...(sectionSpec.description !== undefined && {
|
|
134
|
+
description: translate(sectionSpec.description),
|
|
135
|
+
}),
|
|
136
|
+
...(sectionSpec.itemNoun !== undefined && { itemNoun: sectionSpec.itemNoun }),
|
|
133
137
|
...(sectionSpec.pageSize !== undefined && { pageSize: sectionSpec.pageSize }),
|
|
134
138
|
...(sectionSpec.defaultSort !== undefined && { defaultSort: sectionSpec.defaultSort }),
|
|
135
139
|
...(sectionSpec.searchable !== undefined && { searchable: sectionSpec.searchable }),
|
package/src/view-model/list.ts
CHANGED
|
@@ -164,6 +164,7 @@ export function computeListViewModel(input: ComputeListViewModelInput): ListView
|
|
|
164
164
|
(fieldDef as unknown as { options?: readonly string[] }).options ?? [],
|
|
165
165
|
)
|
|
166
166
|
: undefined;
|
|
167
|
+
const optionTones = fieldDef.type === "select" ? fieldDef.optionTones : undefined;
|
|
167
168
|
const grouping = fieldDef.type === "number" ? fieldDef.grouping : undefined;
|
|
168
169
|
const column: ListColumnViewModel = {
|
|
169
170
|
field: normalized.field,
|
|
@@ -172,10 +173,12 @@ export function computeListViewModel(input: ComputeListViewModelInput): ListView
|
|
|
172
173
|
sortable: fieldIsSortable(fieldDef),
|
|
173
174
|
...(normalized.renderer !== undefined && { renderer: normalized.renderer }),
|
|
174
175
|
...(optionLabels !== undefined && { optionLabels }),
|
|
176
|
+
...(optionTones !== undefined && { optionTones }),
|
|
175
177
|
...(refEntity !== undefined && { refEntity }),
|
|
176
178
|
...(refFeature !== undefined && { refFeature }),
|
|
177
179
|
...(refLabelField !== undefined && { refLabelField }),
|
|
178
180
|
...(grouping !== undefined && { grouping }),
|
|
181
|
+
...(normalized.hideOnNarrow === true && { hideOnNarrow: true }),
|
|
179
182
|
};
|
|
180
183
|
columns.push(column);
|
|
181
184
|
}
|
package/src/view-model/types.ts
CHANGED
|
@@ -11,6 +11,7 @@ import type {
|
|
|
11
11
|
RelatedListToolbarAction,
|
|
12
12
|
RowAction,
|
|
13
13
|
ScreenSlots,
|
|
14
|
+
SelectOptionTone,
|
|
14
15
|
} from "@cosmicdrift/kumiko-framework/ui-types";
|
|
15
16
|
|
|
16
17
|
// Runtime-only renderer — function form allowed here because the renderer
|
|
@@ -53,6 +54,8 @@ export type ListColumnViewModel = {
|
|
|
53
54
|
* instead of humanizeSlug when present. Convention key:
|
|
54
55
|
* `<feature>:entity:<entity>:field:<field>:option:<value>`. */
|
|
55
56
|
readonly optionLabels?: Readonly<Record<string, string>>;
|
|
57
|
+
/** Only for `type: "select"` — declared tone per raw option value. */
|
|
58
|
+
readonly optionTones?: Readonly<Partial<Record<string, SelectOptionTone>>>;
|
|
56
59
|
/** Nur bei `type: "reference"` — referenced Entity-Name für Bulk-
|
|
57
60
|
* Lookup im Renderer (`<refFeature>:query:<refEntity>:list`). */
|
|
58
61
|
readonly refEntity?: string;
|
|
@@ -68,6 +71,8 @@ export type ListColumnViewModel = {
|
|
|
68
71
|
* multi-year statement grid. DataTable renders it with a distinct
|
|
69
72
|
* header/cell background. */
|
|
70
73
|
readonly highlighted?: boolean;
|
|
74
|
+
/** Mirrors `ListColumnSpec.hideOnNarrow`: card layout skips the column. */
|
|
75
|
+
readonly hideOnNarrow?: boolean;
|
|
71
76
|
/** Only for `type: "number"` — mirrors `NumberFieldDef.grouping` (default
|
|
72
77
|
* `true`). `false` renders without thousands-separators (e.g. a model
|
|
73
78
|
* year). */
|
|
@@ -328,6 +333,9 @@ export type EditRelatedListSectionViewModel = {
|
|
|
328
333
|
readonly parentParam?: string;
|
|
329
334
|
readonly parentFilter?: { readonly field: string };
|
|
330
335
|
readonly columns: readonly ListColumnSpec[];
|
|
336
|
+
readonly description?: string;
|
|
337
|
+
/** Untranslated plural-forms key, resolved with the row count by the list footer. */
|
|
338
|
+
readonly itemNoun?: string;
|
|
331
339
|
readonly pageSize?: number;
|
|
332
340
|
readonly defaultSort?: ListSortSpec;
|
|
333
341
|
readonly searchable?: boolean;
|