@cosmicdrift/kumiko-renderer-web 0.220.0 → 0.220.1
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.220.
|
|
3
|
+
"version": "0.220.1",
|
|
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.220.
|
|
20
|
-
"@cosmicdrift/kumiko-headless": "0.220.
|
|
21
|
-
"@cosmicdrift/kumiko-renderer": "0.220.
|
|
19
|
+
"@cosmicdrift/kumiko-dispatcher-live": "0.220.1",
|
|
20
|
+
"@cosmicdrift/kumiko-headless": "0.220.1",
|
|
21
|
+
"@cosmicdrift/kumiko-renderer": "0.220.1",
|
|
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.220.
|
|
67
|
+
"@cosmicdrift/kumiko-locale-de": "0.220.1"
|
|
68
68
|
},
|
|
69
69
|
"repository": {
|
|
70
70
|
"type": "git",
|
package/src/primitives/index.tsx
CHANGED
|
@@ -673,8 +673,8 @@ function DefaultDataTable({
|
|
|
673
673
|
// scrollen typischerweise mit dem Page-Container, nicht intern.
|
|
674
674
|
// Sticky würde mit der Topbar konkurrieren.
|
|
675
675
|
const hasTableActions = rowActions !== undefined && rowActions.length > 0;
|
|
676
|
-
|
|
677
|
-
rows.length === 0 ? (
|
|
676
|
+
function tableInner(): ReactNode {
|
|
677
|
+
return rows.length === 0 ? (
|
|
678
678
|
<div
|
|
679
679
|
data-testid={testId !== undefined ? `${testId}-empty` : "render-list-empty"}
|
|
680
680
|
className="flex flex-col items-center justify-center rounded-md border border-dashed p-12 text-sm text-muted-foreground gap-3"
|
|
@@ -777,6 +777,8 @@ function DefaultDataTable({
|
|
|
777
777
|
</Table>
|
|
778
778
|
</div>
|
|
779
779
|
);
|
|
780
|
+
}
|
|
781
|
+
const tableContent = tableInner();
|
|
780
782
|
|
|
781
783
|
// Pager wird IMMER unter der Tabelle gerendert (auch bei rows=[]),
|
|
782
784
|
// damit der User bei einem Filter-Hit-of-Zero zurückblättern kann
|
|
@@ -1701,7 +1703,6 @@ function DefaultForm({
|
|
|
1701
1703
|
export type FormScreenShellWidth = FormWidth;
|
|
1702
1704
|
|
|
1703
1705
|
const formScreenShellWidth: Record<FormScreenShellWidth, string> = {
|
|
1704
|
-
sm: "max-w-sm mx-auto",
|
|
1705
1706
|
"3xl": "max-w-3xl mx-auto",
|
|
1706
1707
|
"4xl": "max-w-4xl mx-auto",
|
|
1707
1708
|
full: "max-w-full",
|
|
@@ -20,12 +20,6 @@ export type InfinityListProps<TData = unknown, TRow = Readonly<Record<string, un
|
|
|
20
20
|
/** Must derive from row content (e.g. `row.id`), not from `index` — a
|
|
21
21
|
* live refresh reorders rows (new/changed rows move to the front). */
|
|
22
22
|
readonly rowId: (row: TRow, index: number) => string;
|
|
23
|
-
/** Pass this when `rowId` depends on list position — the live-merge path
|
|
24
|
-
* needs a position-independent identity. Without it, an index-based
|
|
25
|
-
* `rowId` (legitimate when live-merge isn't used) can collide between
|
|
26
|
-
* the freshly-fetched first page's local indices and the accumulated
|
|
27
|
-
* list's indices, silently dropping an unrelated mid-list row on merge. */
|
|
28
|
-
readonly rowKey?: (row: TRow) => string;
|
|
29
23
|
readonly renderRow: (row: TRow) => ReactNode;
|
|
30
24
|
readonly emptyState?: ReactNode;
|
|
31
25
|
readonly className?: string;
|
|
@@ -55,7 +49,6 @@ export function InfinityList<TData = unknown, TRow = Readonly<Record<string, unk
|
|
|
55
49
|
rows,
|
|
56
50
|
nextCursor,
|
|
57
51
|
rowId,
|
|
58
|
-
rowKey,
|
|
59
52
|
renderRow,
|
|
60
53
|
emptyState,
|
|
61
54
|
className,
|
|
@@ -80,16 +73,6 @@ export function InfinityList<TData = unknown, TRow = Readonly<Record<string, unk
|
|
|
80
73
|
nextCursorRef.current = nextCursor;
|
|
81
74
|
const rowIdRef = useRef(rowId);
|
|
82
75
|
rowIdRef.current = rowId;
|
|
83
|
-
const rowKeyRef = useRef(rowKey);
|
|
84
|
-
rowKeyRef.current = rowKey;
|
|
85
|
-
// Position-independent when rowKey is supplied, otherwise falls back to
|
|
86
|
-
// the (possibly position-dependent) rowId — same identity function used
|
|
87
|
-
// for both sides of the live-merge diff below so a position-based rowId
|
|
88
|
-
// can no longer collide across the fresh page's local indices and the
|
|
89
|
-
// accumulated list's indices (fw#1829).
|
|
90
|
-
const identifyRef = useRef((row: TRow, index: number): string =>
|
|
91
|
-
rowKeyRef.current !== undefined ? rowKeyRef.current(row) : rowIdRef.current(row, index),
|
|
92
|
-
);
|
|
93
76
|
|
|
94
77
|
// Discards a response whose request was superseded by a newer one before
|
|
95
78
|
// it resolved (e.g. two searches fired in quick succession) — without
|
|
@@ -121,9 +104,8 @@ export function InfinityList<TData = unknown, TRow = Readonly<Record<string, unk
|
|
|
121
104
|
if (cursor === null) {
|
|
122
105
|
// Same identity as refreshFirstPage's freshIds/staleRows diff below
|
|
123
106
|
// — this ref is what that diff's previousFirstPageIds reads, so it
|
|
124
|
-
// must agree on rowKey vs rowId or the two id spaces never overlap.
|
|
125
107
|
firstPageIdsRef.current = new Set(
|
|
126
|
-
nextRows.map((row, index) =>
|
|
108
|
+
nextRows.map((row, index) => rowIdRef.current(row, index)),
|
|
127
109
|
);
|
|
128
110
|
}
|
|
129
111
|
setState((prev) => {
|
|
@@ -174,7 +156,7 @@ export function InfinityList<TData = unknown, TRow = Readonly<Record<string, unk
|
|
|
174
156
|
// skip: background live refresh failed, keep showing the current rows
|
|
175
157
|
if (!res.isSuccess) return;
|
|
176
158
|
const freshRows = rowsRef.current(res.data);
|
|
177
|
-
const freshIds = new Set(freshRows.map((row, index) =>
|
|
159
|
+
const freshIds = new Set(freshRows.map((row, index) => rowIdRef.current(row, index)));
|
|
178
160
|
// A row absent from freshIds is stale for one of two reasons: it was
|
|
179
161
|
// dropped from page 1 (deleted, or filtered out elsewhere) and must be
|
|
180
162
|
// pruned, or it belongs to an already-accumulated later page and must
|
|
@@ -194,7 +176,7 @@ export function InfinityList<TData = unknown, TRow = Readonly<Record<string, unk
|
|
|
194
176
|
return { kind: "ready", rows: freshRows, cursor: nextCursorRef.current(res.data) };
|
|
195
177
|
}
|
|
196
178
|
const staleRows = prev.rows.filter((row, index) => {
|
|
197
|
-
const id =
|
|
179
|
+
const id = rowIdRef.current(row, index);
|
|
198
180
|
return !freshIds.has(id) && !previousFirstPageIds.has(id);
|
|
199
181
|
});
|
|
200
182
|
return { kind: "ready", rows: [...freshRows, ...staleRows], cursor: prev.cursor };
|