@cosmicdrift/kumiko-renderer-web 0.131.0 → 0.132.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.132.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.132.0",
|
|
20
|
+
"@cosmicdrift/kumiko-headless": "0.132.0",
|
|
21
|
+
"@cosmicdrift/kumiko-renderer": "0.132.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",
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
//
|
|
2
2
|
// ToastProvider + useToast pinnt: toast() rendert Title+Description in
|
|
3
|
-
// einem Radix-Toast; mehrere toasts stapeln; Variant=
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
//
|
|
3
|
+
// einem Radix-Toast; mehrere toasts stapeln; Variant=bad setzt die
|
|
4
|
+
// StatusTone-Klasse; useToast außerhalb des Providers ist no-op (kein
|
|
5
|
+
// crash); IDs sind kollisionsfrei auch bei zwei Calls im selben Tick
|
|
6
|
+
// (Counter-Race-Bug).
|
|
7
7
|
|
|
8
8
|
import { describe, expect, test } from "bun:test";
|
|
9
9
|
import { act, fireEvent, screen } from "@testing-library/react";
|
|
@@ -49,7 +49,7 @@ describe("ToastProvider + useToast", () => {
|
|
|
49
49
|
options={[
|
|
50
50
|
{
|
|
51
51
|
title: "Konflikt",
|
|
52
|
-
variant: "
|
|
52
|
+
variant: "bad",
|
|
53
53
|
docsUrl: "https://docs.kumiko.rocks/errors/stale_state",
|
|
54
54
|
},
|
|
55
55
|
]}
|
|
@@ -88,18 +88,18 @@ describe("ToastProvider + useToast", () => {
|
|
|
88
88
|
expect(screen.queryByRole("link")).toBeNull();
|
|
89
89
|
});
|
|
90
90
|
|
|
91
|
-
test("variant=
|
|
91
|
+
test("variant=bad: setzt die status-bad StatusTone-Klasse auf den Root", () => {
|
|
92
92
|
render(
|
|
93
93
|
<ToastProvider>
|
|
94
|
-
<ToastTrigger options={[{ title: "Failed", variant: "
|
|
94
|
+
<ToastTrigger options={[{ title: "Failed", variant: "bad" }]} />
|
|
95
95
|
</ToastProvider>,
|
|
96
96
|
);
|
|
97
97
|
// Class-Mapping ist der Public-Vertrag mit Tailwind-Tokens. Wir
|
|
98
98
|
// suchen den nearest Ancestor des Title-Knotens dessen Klassen-
|
|
99
|
-
// String "
|
|
99
|
+
// String "status-bad" enthält — Radix-Toast.Root rendert in einem
|
|
100
100
|
// <li>, aber die genaue Role wechselt je nach priority/type.
|
|
101
101
|
let node: HTMLElement | null = screen.getByText("Failed");
|
|
102
|
-
while (node !== null && !node.className.includes("
|
|
102
|
+
while (node !== null && !node.className.includes("status-bad")) {
|
|
103
103
|
node = node.parentElement;
|
|
104
104
|
}
|
|
105
105
|
expect(node).not.toBeNull();
|
package/src/primitives/index.tsx
CHANGED
|
@@ -778,7 +778,7 @@ function useRowActionTrigger(row: ListRowViewModel) {
|
|
|
778
778
|
toast({
|
|
779
779
|
title: t("kumiko.rowAction.failed"),
|
|
780
780
|
description: e instanceof Error ? e.message : String(e),
|
|
781
|
-
variant: "
|
|
781
|
+
variant: "bad",
|
|
782
782
|
...(docsUrl !== undefined && { docsUrl }),
|
|
783
783
|
});
|
|
784
784
|
} finally {
|
package/src/primitives/toast.tsx
CHANGED
|
@@ -3,9 +3,9 @@
|
|
|
3
3
|
// kann. Kein Context-Boilerplate für Konsumenten — der Hook returned
|
|
4
4
|
// einen `toast()`-Trigger, mehr Public-API gibt's nicht.
|
|
5
5
|
//
|
|
6
|
-
//
|
|
7
|
-
// Auto-dismiss nach 5s, manuell schließbar via X-Button.
|
|
8
|
-
// Live-Region-Setup kommt komplett aus Radix.
|
|
6
|
+
// Variant = StatusTone (dieselbe Farb-Familie wie StatusBadge), Default
|
|
7
|
+
// "muted" (neutral). Auto-dismiss nach 5s, manuell schließbar via X-Button.
|
|
8
|
+
// Der ARIA-Live-Region-Setup kommt komplett aus Radix.
|
|
9
9
|
|
|
10
10
|
import { useTranslation } from "@cosmicdrift/kumiko-renderer";
|
|
11
11
|
import * as Primitive from "@radix-ui/react-toast";
|
|
@@ -21,8 +21,9 @@ import {
|
|
|
21
21
|
useState,
|
|
22
22
|
} from "react";
|
|
23
23
|
import { cn } from "../lib/cn";
|
|
24
|
+
import type { StatusTone } from "../widgets/status-badge";
|
|
24
25
|
|
|
25
|
-
export type ToastVariant =
|
|
26
|
+
export type ToastVariant = StatusTone;
|
|
26
27
|
|
|
27
28
|
export type ToastOptions = {
|
|
28
29
|
readonly title: string;
|
|
@@ -111,6 +112,14 @@ const rootClass =
|
|
|
111
112
|
"data-[state=closed]:fade-out-80 data-[state=closed]:slide-out-to-right-full " +
|
|
112
113
|
"data-[state=open]:slide-in-from-top-full data-[state=open]:sm:slide-in-from-bottom-full";
|
|
113
114
|
|
|
115
|
+
const TONE_CLASS: Record<StatusTone, string> = {
|
|
116
|
+
ok: "border-status-ok/30 bg-status-ok/10 text-status-ok",
|
|
117
|
+
warn: "border-status-warn/30 bg-status-warn/10 text-status-warn",
|
|
118
|
+
bad: "border-status-bad/30 bg-status-bad/10 text-status-bad",
|
|
119
|
+
critical: "border-status-critical/40 bg-status-critical/15 text-status-critical",
|
|
120
|
+
muted: "border bg-background text-foreground",
|
|
121
|
+
};
|
|
122
|
+
|
|
114
123
|
function ToastItem({
|
|
115
124
|
entry,
|
|
116
125
|
onClose,
|
|
@@ -120,10 +129,7 @@ function ToastItem({
|
|
|
120
129
|
}): ReactNode {
|
|
121
130
|
const t = useTranslation();
|
|
122
131
|
const learnMore = entry.docsLinkLabel ?? t("kumiko.toast.learn-more");
|
|
123
|
-
const variantClass =
|
|
124
|
-
entry.variant === "destructive"
|
|
125
|
-
? "destructive group border-destructive bg-destructive text-destructive-foreground"
|
|
126
|
-
: "border bg-background text-foreground";
|
|
132
|
+
const variantClass = TONE_CLASS[entry.variant ?? "muted"];
|
|
127
133
|
return (
|
|
128
134
|
<Primitive.Root
|
|
129
135
|
className={cn(rootClass, variantClass)}
|
|
@@ -159,7 +165,6 @@ function ToastItem({
|
|
|
159
165
|
"absolute right-2 top-2 rounded-md p-1 text-foreground/50 opacity-0 transition-opacity",
|
|
160
166
|
"hover:text-foreground focus:opacity-100 focus:outline-none focus:ring-1",
|
|
161
167
|
"group-hover:opacity-100",
|
|
162
|
-
"group-[.destructive]:text-red-300 group-[.destructive]:hover:text-red-50",
|
|
163
168
|
)}
|
|
164
169
|
aria-label={t("kumiko.dialog.close")}
|
|
165
170
|
>
|