@cosmicdrift/kumiko-renderer-web 0.222.0 → 0.223.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.
|
|
3
|
+
"version": "0.223.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.
|
|
20
|
-
"@cosmicdrift/kumiko-headless": "0.
|
|
21
|
-
"@cosmicdrift/kumiko-renderer": "0.
|
|
19
|
+
"@cosmicdrift/kumiko-dispatcher-live": "0.223.0",
|
|
20
|
+
"@cosmicdrift/kumiko-headless": "0.223.0",
|
|
21
|
+
"@cosmicdrift/kumiko-renderer": "0.223.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.
|
|
67
|
+
"@cosmicdrift/kumiko-locale-de": "0.223.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
|
+
});
|
package/src/primitives/index.tsx
CHANGED
|
@@ -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
|
-
|
|
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
|