@cosmicdrift/kumiko-renderer-web 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-renderer-web",
3
- "version": "0.222.0",
3
+ "version": "0.224.0",
4
4
  "description": "Web-platform bindings for @cosmicdrift/kumiko-renderer. HTML default-primitives, browser history-based navigation, EventSource-backed live events, and a one-call createKumikoApp that mounts the whole stack via react-dom.",
5
5
  "license": "BUSL-1.1",
6
6
  "author": "Marc Frost <marc@cosmicdriftgamestudio.com>",
@@ -16,9 +16,9 @@
16
16
  "./styles.css": "./src/styles.css"
17
17
  },
18
18
  "dependencies": {
19
- "@cosmicdrift/kumiko-dispatcher-live": "0.222.0",
20
- "@cosmicdrift/kumiko-headless": "0.222.0",
21
- "@cosmicdrift/kumiko-renderer": "0.222.0",
19
+ "@cosmicdrift/kumiko-dispatcher-live": "0.224.0",
20
+ "@cosmicdrift/kumiko-headless": "0.224.0",
21
+ "@cosmicdrift/kumiko-renderer": "0.224.0",
22
22
  "@radix-ui/react-dialog": "^1.1.15",
23
23
  "@radix-ui/react-dropdown-menu": "^2.1.16",
24
24
  "@radix-ui/react-label": "^2.1.8",
@@ -64,7 +64,7 @@
64
64
  "@types/react-dom": "^19.2.3",
65
65
  "jsdom": "^29.1.1",
66
66
  "tailwindcss": "^4.3.0",
67
- "@cosmicdrift/kumiko-locale-de": "0.222.0"
67
+ "@cosmicdrift/kumiko-locale-de": "0.224.0"
68
68
  },
69
69
  "repository": {
70
70
  "type": "git",
@@ -0,0 +1,64 @@
1
+ // DefaultGrid's `maxRows` clips the grid to roughly N rows once the option
2
+ // count (given `columns`) actually exceeds that many rows — style-only until
3
+ // then, no extra wrapper element. Height derives from the
4
+ // `--kumiko-grid-row-h` CSS var (same var `grid-auto-rows`'s minmax floor
5
+ // uses), never a hardcoded pixel/rem constant, so a theme redefining the var
6
+ // scales both together. `grid-auto-rows` is a minmax MIN, not a fixed
7
+ // height, so a wrapped label grows its row instead of being clipped.
8
+
9
+ import { describe, expect, test } from "bun:test";
10
+ import { render, screen } from "@testing-library/react";
11
+ import { defaultPrimitives } from "../index";
12
+
13
+ const { Grid } = defaultPrimitives;
14
+
15
+ describe("DefaultGrid — maxRows", () => {
16
+ test("fewer options than maxRows rows can hold → no scroll container", () => {
17
+ render(
18
+ <Grid columns={2} maxRows={3} testId="grid">
19
+ <div>a</div>
20
+ <div>b</div>
21
+ </Grid>,
22
+ );
23
+ const el = screen.getByTestId("grid");
24
+ expect(el.style.overflowY).toBe("");
25
+ expect(el.style.maxHeight).toBe("");
26
+ });
27
+
28
+ test("more options than maxRows rows can hold → scroll container with height derived from maxRows", () => {
29
+ render(
30
+ <Grid columns={2} maxRows={2} testId="grid">
31
+ <div>a</div>
32
+ <div>b</div>
33
+ <div>c</div>
34
+ <div>d</div>
35
+ <div>e</div>
36
+ </Grid>,
37
+ );
38
+ const el = screen.getByTestId("grid");
39
+ expect(el.style.overflowY).toBe("auto");
40
+ expect(el.style.maxHeight).toContain("var(--kumiko-grid-row-h");
41
+ expect(el.style.maxHeight).toContain("2 *");
42
+ // minmax(..., auto), not a fixed height — a row taller than the CSS var
43
+ // (a wrapped label) grows instead of getting clipped mid-row.
44
+ const gridAutoRows = el.style.getPropertyValue("grid-auto-rows");
45
+ expect(gridAutoRows).toContain("var(--kumiko-grid-row-h");
46
+ expect(gridAutoRows).toContain("minmax(");
47
+ expect(gridAutoRows).toContain("auto");
48
+ });
49
+
50
+ test("no maxRows at all → never a scroll container regardless of option count", () => {
51
+ render(
52
+ <Grid columns={2} testId="grid">
53
+ <div>a</div>
54
+ <div>b</div>
55
+ <div>c</div>
56
+ <div>d</div>
57
+ <div>e</div>
58
+ </Grid>,
59
+ );
60
+ const el = screen.getByTestId("grid");
61
+ expect(el.style.overflowY).toBe("");
62
+ expect(el.style.maxHeight).toBe("");
63
+ });
64
+ });
@@ -70,6 +70,7 @@ import {
70
70
  } from "lucide-react";
71
71
  import {
72
72
  type ChangeEvent,
73
+ Children,
73
74
  type CSSProperties,
74
75
  createContext,
75
76
  type MouseEvent,
@@ -1848,18 +1849,38 @@ function DefaultSection({
1848
1849
  );
1849
1850
  }
1850
1851
 
1851
- function DefaultGrid({ columns, children, testId }: GridProps): ReactNode {
1852
+ function DefaultGrid({ columns, children, testId, maxRows }: GridProps): ReactNode {
1852
1853
  // Responsive: Mobile (< sm = 640px) bleibt 1-spaltig, ab sm: greift
1853
1854
  // die Author-deklarierte Spaltenzahl. Inline-style schreibt
1854
1855
  // CSS-Variable; Tailwind-Klasse liest die Variable mit
1855
1856
  // `grid-template-columns: var(--grid-cols)`. Saubere Lösung weil
1856
1857
  // Tailwind JIT keinen dynamischen `grid-cols-${N}` auflösen kann.
1858
+ const rowCount = Math.ceil(Children.count(children) / columns);
1859
+ const clipped = maxRows !== undefined && rowCount > maxRows;
1860
+ // gridAutoRows is a minmax MIN, not a fixed height, so wrapped labels grow
1861
+ // their row instead of being clipped mid-row. maxHeight is therefore only
1862
+ // an approximation of "maxRows rows tall" — a row taller than the minimum
1863
+ // means fewer than maxRows rows end up visible before scrolling kicks in.
1864
+ // Themes may override --kumiko-grid-row-h (default 2.5rem, set in
1865
+ // styles.css) to change that minimum.
1866
+ // Mobile note: the grid collapses to 1 column below `sm` (see className),
1867
+ // but rowCount above is always computed against the desktop `columns` —
1868
+ // inline styles can't express a media query, so the mobile row count is
1869
+ // an under-count. Known, accepted.
1870
+ const style: CSSProperties = {
1871
+ "--grid-cols": `repeat(${columns}, minmax(0, 1fr))`,
1872
+ ...(clipped && {
1873
+ gridAutoRows: "minmax(var(--kumiko-grid-row-h, 2.5rem), auto)",
1874
+ maxHeight: `calc(${maxRows} * var(--kumiko-grid-row-h, 2.5rem) + ${maxRows - 1} * 1rem)`,
1875
+ overflowY: "auto",
1876
+ }),
1877
+ // workaround: duplicate @types/react instances break direct CSSProperties cast
1878
+ } as unknown as CSSProperties;
1857
1879
  return (
1858
1880
  <div
1859
1881
  data-testid={testId}
1860
1882
  className="grid gap-4 grid-cols-1 sm:[grid-template-columns:var(--grid-cols)]"
1861
- // workaround: duplicate @types/react instances break direct CSSProperties cast
1862
- style={{ "--grid-cols": `repeat(${columns}, minmax(0, 1fr))` } as unknown as CSSProperties}
1883
+ style={style}
1863
1884
  >
1864
1885
  {children}
1865
1886
  </div>
package/src/styles.css CHANGED
@@ -99,6 +99,7 @@
99
99
  --card-padding: 1.5rem;
100
100
  --card-radius: var(--radius-xl);
101
101
  --card-shadow: 0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1);
102
+ --kumiko-grid-row-h: 2.5rem;
102
103
  }
103
104
 
104
105
  /* Light-Mode-Overrides — aktiv wenn `<html>` KEINE `.dark` Klasse