@cosmicdrift/kumiko-headless 0.222.0 → 0.224.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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cosmicdrift/kumiko-headless",
3
- "version": "0.222.0",
3
+ "version": "0.224.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.222.0",
39
+ "@cosmicdrift/kumiko-framework": "0.224.0",
40
40
  "temporal-polyfill": "^0.3.2",
41
41
  "zod": "^4.4.3"
42
42
  },
@@ -632,3 +632,47 @@ describe("computeEditViewModel — embedded-list cells (#1835)", () => {
632
632
  expect(field?.capture).toBeUndefined();
633
633
  });
634
634
  });
635
+
636
+ describe("computeEditViewModel — multiSelect display/columns/maxRows passthrough", () => {
637
+ function multiSelectEntity(overrides?: Record<string, unknown>): EntityDefinition {
638
+ return {
639
+ fields: {
640
+ languages: {
641
+ type: "multiSelect",
642
+ options: ["en", "de", "es"],
643
+ ...overrides,
644
+ },
645
+ },
646
+ } as unknown as EntityDefinition;
647
+ }
648
+
649
+ test("display/columns/maxRows carry through when set on the field def", () => {
650
+ const vm = computeEditViewModel({
651
+ screen: editScreen({ sections: [{ title: "x", fields: ["languages"] }] }),
652
+ entity: multiSelectEntity({ display: "checkboxes", columns: 2, maxRows: 3 }),
653
+ values: {},
654
+ translate,
655
+ featureName: "orders",
656
+ });
657
+
658
+ const field = asFields(vm.sections[0]).fields[0];
659
+ expect(field?.display).toBe("checkboxes");
660
+ expect(field?.columns).toBe(2);
661
+ expect(field?.maxRows).toBe(3);
662
+ });
663
+
664
+ test("display/columns/maxRows are absent when not set on the field def (back-compat)", () => {
665
+ const vm = computeEditViewModel({
666
+ screen: editScreen({ sections: [{ title: "x", fields: ["languages"] }] }),
667
+ entity: multiSelectEntity(),
668
+ values: {},
669
+ translate,
670
+ featureName: "orders",
671
+ });
672
+
673
+ const field = asFields(vm.sections[0]).fields[0];
674
+ expect(field?.display).toBeUndefined();
675
+ expect(field?.columns).toBeUndefined();
676
+ expect(field?.maxRows).toBeUndefined();
677
+ });
678
+ });
@@ -128,6 +128,11 @@ export function computeEditViewModel<
128
128
  options,
129
129
  )
130
130
  : undefined;
131
+ // Checkbox-grid rendering hints for `type: "multiSelect"` — pass
132
+ // through unchanged so the renderer can pick the grid layout.
133
+ const display = fieldDef.type === "multiSelect" ? fieldDef.display : undefined;
134
+ const columns = fieldDef.type === "multiSelect" ? fieldDef.columns : undefined;
135
+ const maxRows = fieldDef.type === "multiSelect" ? fieldDef.maxRows : undefined;
131
136
  // Multiline hint for `type: "text"` — the renderer then switches to a
132
137
  // textarea. `type: "longText"` always renders a textarea regardless
133
138
  // of this hint; it's only carried through as an optional `{ rows }`
@@ -275,6 +280,9 @@ export function computeEditViewModel<
275
280
  ...(normalized.renderer !== undefined && { renderer: normalized.renderer }),
276
281
  ...(options !== undefined && { options }),
277
282
  ...(optionLabels !== undefined && { optionLabels }),
283
+ ...(display !== undefined && { display }),
284
+ ...(columns !== undefined && { columns }),
285
+ ...(maxRows !== undefined && { maxRows }),
278
286
  ...(multiline !== undefined && { multiline }),
279
287
  ...(wallClock !== undefined && { wallClock }),
280
288
  ...(min !== undefined && { min }),
@@ -140,6 +140,18 @@ export type EditFieldViewModel = {
140
140
  * raw value. Convention key:
141
141
  * `<feature>:entity:<entity>:field:<field>:option:<value>`. */
142
142
  readonly optionLabels?: Readonly<Record<string, string>>;
143
+ /** Set for `type: "multiSelect"` when MultiSelectFieldDef.display is
144
+ * "checkboxes" — the renderer shows a checkbox grid with a select-all
145
+ * toggle instead of the default combobox dropdown. */
146
+ readonly display?: "checkboxes" | "dropdown";
147
+ /** Set for `type: "multiSelect"` when MultiSelectFieldDef.columns is
148
+ * defined — column count for the checkbox grid at the widest breakpoint.
149
+ * Ignored unless `display` is "checkboxes". */
150
+ readonly columns?: 1 | 2 | 3 | 4;
151
+ /** Set for `type: "multiSelect"` when MultiSelectFieldDef.maxRows is
152
+ * defined — grid rows visible before the checkbox grid becomes
153
+ * scrollable. Ignored unless `display` is "checkboxes". */
154
+ readonly maxRows?: number;
143
155
  /** For `type: "text"`, set when TextFieldDef.multiline is true — the
144
156
  * renderer then renders a textarea instead of a single-line input.
145
157
  * `type: "longText"` always renders a textarea regardless of this