@jgengine/react 0.2.0 → 0.4.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/dist/components.d.ts +22 -1
- package/dist/components.js +44 -1
- package/dist/hooks.d.ts +1 -0
- package/dist/hooks.js +9 -0
- package/package.json +4 -4
package/dist/components.d.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type ReactNode } from "react";
|
|
2
2
|
import type { InventorySlot } from "@jgengine/core/inventory/inventoryModel";
|
|
3
3
|
import type { ProximityPrompt as ProximityPromptDef } from "@jgengine/core/interaction/proximityPrompt";
|
|
4
|
+
import type { FeedEntry } from "@jgengine/core/game/feed";
|
|
5
|
+
import type { StatLevelUpEvent } from "@jgengine/core/game/events";
|
|
4
6
|
export declare function SlotGrid({ inventoryId, className, renderSlot, }: {
|
|
5
7
|
inventoryId: string;
|
|
6
8
|
className?: string;
|
|
@@ -53,3 +55,22 @@ export declare function DialogueBox({ dialogue, onChoice, className, }: {
|
|
|
53
55
|
onChoice?: (choice: DialogueChoice) => void;
|
|
54
56
|
className?: string;
|
|
55
57
|
}): import("react").JSX.Element;
|
|
58
|
+
export declare function ToastStack({ action, limit, className, renderToast, }: {
|
|
59
|
+
action: string;
|
|
60
|
+
limit?: number;
|
|
61
|
+
className?: string;
|
|
62
|
+
renderToast?: (entry: FeedEntry, index: number) => ReactNode;
|
|
63
|
+
}): import("react").JSX.Element | null;
|
|
64
|
+
export declare function DeathScreen({ statId, open, className, children, }: {
|
|
65
|
+
statId?: string;
|
|
66
|
+
open?: boolean;
|
|
67
|
+
className?: string;
|
|
68
|
+
children?: ReactNode;
|
|
69
|
+
}): import("react").JSX.Element;
|
|
70
|
+
export declare function LevelUpFlash({ stat, durationMs, className, children, renderFlash, }: {
|
|
71
|
+
stat?: string;
|
|
72
|
+
durationMs?: number;
|
|
73
|
+
className?: string;
|
|
74
|
+
children?: ReactNode;
|
|
75
|
+
renderFlash?: (event: StatLevelUpEvent) => ReactNode;
|
|
76
|
+
}): import("react").JSX.Element | null;
|
package/dist/components.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import {
|
|
2
|
+
import { useEffect, useRef, useState } from "react";
|
|
3
|
+
import { useGameContext } from "./provider.js";
|
|
4
|
+
import { useCurrency, useEntityStat, useFeed, useInventory, useLocalPlayerDead } from "./hooks.js";
|
|
3
5
|
export function SlotGrid({ inventoryId, className, renderSlot, }) {
|
|
4
6
|
const slots = useInventory(inventoryId);
|
|
5
7
|
return (_jsx("div", { className: className, "data-inventory": inventoryId, children: slots.map((slot, index) => (_jsx("div", { "data-slot": index, children: renderSlot !== undefined
|
|
@@ -41,3 +43,44 @@ export function KeybindRow({ action, keys, className, }) {
|
|
|
41
43
|
export function DialogueBox({ dialogue, onChoice, className, }) {
|
|
42
44
|
return (_jsx("div", { className: className, "data-dialogue": dialogue.id, children: dialogue.lines.map((line, index) => "choices" in line ? (_jsx("div", { "data-choices": true, children: line.choices.map((choice) => (_jsx("button", { type: "button", onClick: () => onChoice?.(choice), children: choice.label }, choice.label))) }, index)) : (_jsxs("p", { children: [_jsx("span", { "data-speaker": true, children: line.speaker }), _jsx("span", { children: line.text })] }, index))) }));
|
|
43
45
|
}
|
|
46
|
+
function defaultToast(entry) {
|
|
47
|
+
const data = entry.data;
|
|
48
|
+
if (data?.drops !== undefined) {
|
|
49
|
+
return data.drops.map((drop) => `${drop.item ?? drop.currency ?? "item"} ×${drop.count}`).join(" ");
|
|
50
|
+
}
|
|
51
|
+
return typeof entry.data === "string" ? entry.data : JSON.stringify(entry.data);
|
|
52
|
+
}
|
|
53
|
+
export function ToastStack({ action, limit = 4, className, renderToast, }) {
|
|
54
|
+
const entries = useFeed({ action, limit });
|
|
55
|
+
if (entries.length === 0)
|
|
56
|
+
return null;
|
|
57
|
+
const newestFirst = [...entries].reverse();
|
|
58
|
+
return (_jsx("div", { className: className, "data-toast-stack": action, children: newestFirst.map((entry, index) => (_jsx("div", { "data-toast": true, children: renderToast !== undefined ? renderToast(entry, index) : defaultToast(entry) }, `${entry.at}-${index}`))) }));
|
|
59
|
+
}
|
|
60
|
+
export function DeathScreen({ statId = "health", open, className, children, }) {
|
|
61
|
+
const dead = useLocalPlayerDead(statId);
|
|
62
|
+
return (_jsx(Screen, { id: "death", open: open ?? dead, className: className, children: children }));
|
|
63
|
+
}
|
|
64
|
+
export function LevelUpFlash({ stat, durationMs = 1600, className, children, renderFlash, }) {
|
|
65
|
+
const ctx = useGameContext();
|
|
66
|
+
const [flash, setFlash] = useState(null);
|
|
67
|
+
const timerRef = useRef(null);
|
|
68
|
+
useEffect(() => {
|
|
69
|
+
const unsubscribe = ctx.game.events.on("stat.levelUp", (event) => {
|
|
70
|
+
if (stat !== undefined && event.stat !== stat)
|
|
71
|
+
return;
|
|
72
|
+
setFlash(event);
|
|
73
|
+
if (timerRef.current !== null)
|
|
74
|
+
clearTimeout(timerRef.current);
|
|
75
|
+
timerRef.current = setTimeout(() => setFlash(null), durationMs);
|
|
76
|
+
});
|
|
77
|
+
return () => {
|
|
78
|
+
unsubscribe();
|
|
79
|
+
if (timerRef.current !== null)
|
|
80
|
+
clearTimeout(timerRef.current);
|
|
81
|
+
};
|
|
82
|
+
}, [ctx, stat, durationMs]);
|
|
83
|
+
if (flash === null)
|
|
84
|
+
return null;
|
|
85
|
+
return (_jsx("div", { className: className, "data-levelup": flash.level, children: renderFlash !== undefined ? renderFlash(flash) : children ?? `Level ${flash.level}` }));
|
|
86
|
+
}
|
package/dist/hooks.d.ts
CHANGED
|
@@ -39,5 +39,6 @@ export declare function useLeaderboard(stat: string, options: {
|
|
|
39
39
|
userId: string;
|
|
40
40
|
value: number;
|
|
41
41
|
}[];
|
|
42
|
+
export declare function useLocalPlayerDead(healthStatId?: string): boolean;
|
|
42
43
|
export declare function localPlayerEntity(ctx: GameContext): SceneEntity | null;
|
|
43
44
|
export declare function useActivePrompt<T extends PositionedPrompt>(prompts?: readonly T[]): T | null;
|
package/dist/hooks.js
CHANGED
|
@@ -50,6 +50,15 @@ export function usePresence(userId) {
|
|
|
50
50
|
export function useLeaderboard(stat, options) {
|
|
51
51
|
return useGameStore((ctx) => ctx.game.leaderboard.getTop(stat, options));
|
|
52
52
|
}
|
|
53
|
+
export function useLocalPlayerDead(healthStatId = "health") {
|
|
54
|
+
return useGameStore((ctx) => {
|
|
55
|
+
const player = localPlayerEntity(ctx);
|
|
56
|
+
if (player === null)
|
|
57
|
+
return false;
|
|
58
|
+
const health = ctx.scene.entity.stats.get(player.id, healthStatId);
|
|
59
|
+
return health !== null && health.current <= health.min;
|
|
60
|
+
});
|
|
61
|
+
}
|
|
53
62
|
export function localPlayerEntity(ctx) {
|
|
54
63
|
return (ctx.scene.entity.get(ctx.player.userId) ??
|
|
55
64
|
ctx.scene.entity.list().find((entity) => entity.role === "player") ??
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jgengine/react",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "React UI layer for JGengine: GameProvider, hooks, and headless primitives over @jgengine/core.",
|
|
5
5
|
"license": "AGPL-3.0-only",
|
|
6
6
|
"type": "module",
|
|
@@ -18,11 +18,11 @@
|
|
|
18
18
|
}
|
|
19
19
|
},
|
|
20
20
|
"scripts": {
|
|
21
|
-
"build": "
|
|
22
|
-
"check-types": "
|
|
21
|
+
"build": "tsgo -p tsconfig.build.json && bun ../../scripts/fix-extensions.ts dist",
|
|
22
|
+
"check-types": "tsgo --noEmit -p tsconfig.json"
|
|
23
23
|
},
|
|
24
24
|
"dependencies": {
|
|
25
|
-
"@jgengine/core": "^0.
|
|
25
|
+
"@jgengine/core": "^0.4.0"
|
|
26
26
|
},
|
|
27
27
|
"peerDependencies": {
|
|
28
28
|
"react": "^19.0.0"
|