@pkhex-everywhere/react 0.1.0 → 0.2.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/CHANGELOG.md +28 -0
- package/README.md +44 -9
- package/dist/EngineProvider.d.ts +4 -0
- package/dist/RequireGame.d.ts +6 -0
- package/dist/generated/hooks.d.ts +26 -3
- package/dist/index.d.ts +2 -0
- package/dist/index.js +65 -6
- package/dist/useEngineStatus.d.ts +3 -0
- package/dist/usePokemonDetails.d.ts +1 -0
- package/dist/useQuery.d.ts +7 -1
- package/package.json +3 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,33 @@
|
|
|
1
1
|
# @pkhex-everywhere/react
|
|
2
2
|
|
|
3
|
+
## 0.2.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 92c350e: Document the public types, so editors and the API reference describe each type and field.
|
|
8
|
+
- 4fe8568: Require `@pkhex-everywhere/engine` from the matching minor as a peer, instead of any version.
|
|
9
|
+
- 92c350e: Link the READMEs and npm pages to the new documentation site.
|
|
10
|
+
- Updated dependencies [92c350e]
|
|
11
|
+
- Updated dependencies [55b1b68]
|
|
12
|
+
- Updated dependencies [92c350e]
|
|
13
|
+
- @pkhex-everywhere/engine@0.2.1
|
|
14
|
+
|
|
15
|
+
## 0.2.0
|
|
16
|
+
|
|
17
|
+
### Minor Changes
|
|
18
|
+
|
|
19
|
+
- fb0df12: Binary inputs take `Uint8Array`, `ArrayBuffer`, `Blob` or `File`, and binary outputs return `Uint8Array`. `game.load(data, fileName?)` takes the name from a `File`. Generated methods and hooks document whether they need a loaded save or an open draft. A missing draft fails with the new `no-draft` error code instead of `not-found`.
|
|
20
|
+
- 938f28c: `createEngine()` starts downloading the runtime as soon as it's created in a browser (`{ lazy: true }` opts out) and reports `engine.status` with file download progress through `engine.onStatusChange`. `wasmHost()` returns one shared runtime per page, so several engines on a page all get changes and events.
|
|
21
|
+
- eab17a2: `react` adds `useEngineStatus()` and `<RequireGame fallback>`, and `useQuery` rewrites `no-save` and `no-draft` messages to name the call and the fix. `engine` no longer exports `createClient`, `affects`, `queryTopics`, `Invoke`, `AssemblyExports` or `EngineExports`, and `engine.call` is gone.
|
|
22
|
+
|
|
23
|
+
### Patch Changes
|
|
24
|
+
|
|
25
|
+
- Updated dependencies [fb0df12]
|
|
26
|
+
- Updated dependencies [938f28c]
|
|
27
|
+
- Updated dependencies [38e32c9]
|
|
28
|
+
- Updated dependencies [eab17a2]
|
|
29
|
+
- @pkhex-everywhere/engine@0.2.0
|
|
30
|
+
|
|
3
31
|
## 0.1.0
|
|
4
32
|
|
|
5
33
|
### Minor Changes
|
package/README.md
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
React hooks for [`@pkhex-everywhere/engine`](https://www.npmjs.com/package/@pkhex-everywhere/engine). Hooks suspend until their data arrives and refetch when a command changes it.
|
|
4
4
|
|
|
5
|
+
**[Documentation](https://arley-space--docs.wawesome.app/docs/guides/react/)** · [API reference](https://arley-space--docs.wawesome.app/docs/reference/react/)
|
|
6
|
+
|
|
5
7
|
## Install
|
|
6
8
|
|
|
7
9
|
```sh
|
|
@@ -10,38 +12,71 @@ npm install @pkhex-everywhere/engine @pkhex-everywhere/react
|
|
|
10
12
|
|
|
11
13
|
## Example
|
|
12
14
|
|
|
15
|
+
This app opens a save from a file input, lists the party and downloads the edited save:
|
|
16
|
+
|
|
13
17
|
```tsx
|
|
14
|
-
import { Suspense } from 'react'
|
|
18
|
+
import { Suspense, type ChangeEvent } from 'react'
|
|
15
19
|
import { createEngine } from '@pkhex-everywhere/engine'
|
|
16
|
-
import { EngineProvider, useParty } from '@pkhex-everywhere/react'
|
|
20
|
+
import { EngineProvider, RequireGame, useEngineStatus, useLoadedGame, useParty } from '@pkhex-everywhere/react'
|
|
17
21
|
|
|
18
22
|
const engine = createEngine()
|
|
19
23
|
|
|
24
|
+
function Booting() {
|
|
25
|
+
const { loaded, total } = useEngineStatus()
|
|
26
|
+
return <progress value={loaded} max={total || 1} />
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function OpenSave() {
|
|
30
|
+
const { load } = useLoadedGame()
|
|
31
|
+
const open = (event: ChangeEvent<HTMLInputElement>) => {
|
|
32
|
+
const file = event.target.files?.[0]
|
|
33
|
+
if (file) void load(file)
|
|
34
|
+
}
|
|
35
|
+
return <input type="file" onChange={open} />
|
|
36
|
+
}
|
|
37
|
+
|
|
20
38
|
function Party() {
|
|
21
39
|
const { party } = useParty()
|
|
22
40
|
return (
|
|
23
|
-
<ul>
|
|
41
|
+
<ul id="party">
|
|
24
42
|
{party.map((pokemon) => (
|
|
25
|
-
<li key={pokemon.id}>
|
|
26
|
-
{pokemon.species} Lv. {pokemon.level}
|
|
27
|
-
</li>
|
|
43
|
+
<li key={pokemon.id}>{pokemon.species}</li>
|
|
28
44
|
))}
|
|
29
45
|
</ul>
|
|
30
46
|
)
|
|
31
47
|
}
|
|
32
48
|
|
|
49
|
+
function Download() {
|
|
50
|
+
const { export: exportSave } = useLoadedGame()
|
|
51
|
+
const download = async () => {
|
|
52
|
+
const { bytes, fileName } = await exportSave()
|
|
53
|
+
const url = URL.createObjectURL(new Blob([bytes]))
|
|
54
|
+
const link = document.createElement('a')
|
|
55
|
+
link.href = url
|
|
56
|
+
link.download = fileName
|
|
57
|
+
link.click()
|
|
58
|
+
setTimeout(() => URL.revokeObjectURL(url))
|
|
59
|
+
}
|
|
60
|
+
return <button onClick={download}>Download</button>
|
|
61
|
+
}
|
|
62
|
+
|
|
33
63
|
export function App() {
|
|
34
64
|
return (
|
|
35
65
|
<EngineProvider engine={engine}>
|
|
36
|
-
<Suspense fallback=
|
|
37
|
-
<
|
|
66
|
+
<Suspense fallback={<Booting />}>
|
|
67
|
+
<RequireGame fallback={<OpenSave />}>
|
|
68
|
+
<Party />
|
|
69
|
+
<Download />
|
|
70
|
+
</RequireGame>
|
|
38
71
|
</Suspense>
|
|
39
72
|
</EngineProvider>
|
|
40
73
|
)
|
|
41
74
|
}
|
|
42
75
|
```
|
|
43
76
|
|
|
44
|
-
|
|
77
|
+
The Suspense fallback shows the runtime download with `useEngineStatus()`. `<RequireGame>` renders its fallback until a save is loaded, then its children. If the runtime fails to load, the hooks throw the error to the nearest error boundary.
|
|
78
|
+
|
|
79
|
+
CI builds this app from the packed packages and runs it.
|
|
45
80
|
|
|
46
81
|
## License
|
|
47
82
|
|
package/dist/EngineProvider.d.ts
CHANGED
|
@@ -6,11 +6,15 @@ interface EngineContextValue {
|
|
|
6
6
|
cache: QueryCache;
|
|
7
7
|
}
|
|
8
8
|
interface EngineProviderProps {
|
|
9
|
+
/** The engine the hooks below read from, usually created once with `createEngine()`. */
|
|
9
10
|
engine: Engine;
|
|
11
|
+
/** Called with `EngineError` rejections nothing else handled, for example to show a toast. */
|
|
10
12
|
onUnhandledError?: (error: EngineError) => void;
|
|
11
13
|
children?: ReactNode;
|
|
12
14
|
}
|
|
15
|
+
/** Gives the hooks below it an engine and a query cache that refetches when a command changes the save. */
|
|
13
16
|
export declare function EngineProvider({ engine, onUnhandledError, children }: EngineProviderProps): import("react").JSX.Element;
|
|
14
17
|
export declare function useEngineContext(): EngineContextValue;
|
|
18
|
+
/** The engine from the nearest `EngineProvider`, for any call the hooks don't cover. */
|
|
15
19
|
export declare function useEngine(): Engine;
|
|
16
20
|
export {};
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { ReactNode } from 'react';
|
|
2
|
+
/** Renders `fallback` until a save is loaded, then `children`. Wrap hooks that need a save in it. */
|
|
3
|
+
export declare function RequireGame({ fallback, children }: {
|
|
4
|
+
fallback?: ReactNode;
|
|
5
|
+
children?: ReactNode;
|
|
6
|
+
}): ReactNode;
|
|
@@ -1,43 +1,66 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { Binary, ItemHandle, PokemonHandle, PokemonPatch, TrainerGender } from '@pkhex-everywhere/engine';
|
|
2
|
+
/** Requires a loaded save; throws `no-save` otherwise. Wrap in `<RequireGame>`. */
|
|
2
3
|
export declare function useBox(): {
|
|
4
|
+
/** Requires a loaded save; throws `no-save` otherwise. Wrap in `<RequireGame>`. */
|
|
3
5
|
addEncounter: (index: number) => Promise<import("@pkhex-everywhere/engine").AddedPokemon>;
|
|
4
|
-
|
|
6
|
+
/** Requires a loaded save; throws `no-save` otherwise. Wrap in `<RequireGame>`. */
|
|
7
|
+
addFromFile: (bytes: Binary) => Promise<import("@pkhex-everywhere/engine").AddedPokemon>;
|
|
5
8
|
box: import("@pkhex-everywhere/engine").PokemonSummary[];
|
|
6
9
|
};
|
|
10
|
+
/** Requires a loaded save; throws `no-save` otherwise. Wrap in `<RequireGame>`. */
|
|
7
11
|
export declare function useEvents(): {
|
|
12
|
+
/** Requires a loaded save; throws `no-save` otherwise. Wrap in `<RequireGame>`. */
|
|
8
13
|
giveTickets: (includeOldSeaMap: boolean) => Promise<string[]>;
|
|
14
|
+
/** Requires a loaded save; throws `no-save` otherwise. Wrap in `<RequireGame>`. */
|
|
9
15
|
setFlag: (index: number, value: boolean) => Promise<void>;
|
|
16
|
+
/** Requires a loaded save; throws `no-save` otherwise. Wrap in `<RequireGame>`. */
|
|
10
17
|
setWork: (index: number, value: number) => Promise<void>;
|
|
11
18
|
events: import("@pkhex-everywhere/engine").SaveEvents | null;
|
|
12
19
|
};
|
|
13
20
|
export declare function useLoadedGame(): {
|
|
14
21
|
close: () => Promise<void>;
|
|
22
|
+
/** Requires a loaded save; throws `no-save` otherwise. Wrap in `<RequireGame>`. */
|
|
15
23
|
export: () => Promise<import("@pkhex-everywhere/engine").ExportedSave>;
|
|
16
24
|
file: () => Promise<import("@pkhex-everywhere/engine").LoadedSave | null>;
|
|
17
|
-
load: (data:
|
|
25
|
+
load: (data: Binary, fileName?: string | null) => Promise<void>;
|
|
18
26
|
loadBlank: (version: number) => Promise<void>;
|
|
19
27
|
game: import("@pkhex-everywhere/engine").SaveSummary | null;
|
|
20
28
|
};
|
|
29
|
+
/** Requires a loaded save; throws `no-save` otherwise. Wrap in `<RequireGame>`. */
|
|
21
30
|
export declare function useInventory(): {
|
|
31
|
+
/** Requires a loaded save; throws `no-save` otherwise. Wrap in `<RequireGame>`. */
|
|
22
32
|
setItem: (at: ItemHandle, count: number) => Promise<void>;
|
|
23
33
|
inventory: import("@pkhex-everywhere/engine").Pouch[];
|
|
24
34
|
};
|
|
35
|
+
/** Requires a loaded save; throws `no-save` otherwise. Wrap in `<RequireGame>`. */
|
|
25
36
|
export declare function useParty(): {
|
|
26
37
|
party: import("@pkhex-everywhere/engine").PokemonSummary[];
|
|
27
38
|
};
|
|
39
|
+
/** Requires a loaded save; throws `no-save` otherwise. Wrap in `<RequireGame>`. */
|
|
28
40
|
export declare function usePokemon(at: PokemonHandle): {
|
|
41
|
+
/** Requires a loaded save and an open draft; throws `no-save` or `no-draft` otherwise. Wrap in `<RequireGame>`. */
|
|
29
42
|
addToBox: () => Promise<import("@pkhex-everywhere/engine").AddedPokemon>;
|
|
43
|
+
/** Requires a loaded save; throws `no-save` otherwise. Wrap in `<RequireGame>`. */
|
|
30
44
|
clone: () => Promise<void>;
|
|
45
|
+
/** Requires a loaded save and an open draft; throws `no-save` or `no-draft` otherwise. Wrap in `<RequireGame>`. */
|
|
31
46
|
commit: () => Promise<import("@pkhex-everywhere/engine").PokemonId>;
|
|
47
|
+
/** Requires a loaded save; throws `no-save` otherwise. Wrap in `<RequireGame>`. */
|
|
32
48
|
edit: () => Promise<void>;
|
|
49
|
+
/** Requires a loaded save; throws `no-save` otherwise. Wrap in `<RequireGame>`. */
|
|
33
50
|
setLevel: (level: number) => Promise<void>;
|
|
51
|
+
/** Requires a loaded save; throws `no-save` otherwise. Wrap in `<RequireGame>`. */
|
|
34
52
|
update: (patch: PokemonPatch) => Promise<void>;
|
|
35
53
|
pokemon: import("@pkhex-everywhere/engine").PokemonSummary;
|
|
36
54
|
};
|
|
55
|
+
/** Requires a loaded save; throws `no-save` otherwise. Wrap in `<RequireGame>`. */
|
|
37
56
|
export declare function useTrainer(): {
|
|
57
|
+
/** Requires a loaded save; throws `no-save` otherwise. Wrap in `<RequireGame>`. */
|
|
38
58
|
setBattlePoints: (value: number) => Promise<void>;
|
|
59
|
+
/** Requires a loaded save; throws `no-save` otherwise. Wrap in `<RequireGame>`. */
|
|
39
60
|
setGender: (gender: TrainerGender) => Promise<void>;
|
|
61
|
+
/** Requires a loaded save; throws `no-save` otherwise. Wrap in `<RequireGame>`. */
|
|
40
62
|
setMoney: (amount: number) => Promise<void>;
|
|
63
|
+
/** Requires a loaded save; throws `no-save` otherwise. Wrap in `<RequireGame>`. */
|
|
41
64
|
setName: (name: string) => Promise<void>;
|
|
42
65
|
trainer: import("@pkhex-everywhere/engine").TrainerCard;
|
|
43
66
|
};
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
export { EngineProvider, useEngine } from './EngineProvider';
|
|
2
2
|
export { useQuery, type CallArgs, type CallResult } from './useQuery';
|
|
3
|
+
export { useEngineStatus } from './useEngineStatus';
|
|
4
|
+
export { RequireGame } from './RequireGame';
|
|
3
5
|
export * from './generated/hooks';
|
|
4
6
|
export { usePokemonDetails } from './usePokemonDetails';
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { createContext, useContext, useEffect, useMemo, useRef, useState, useSyncExternalStore } from "react";
|
|
2
|
-
import { EngineError
|
|
2
|
+
import { EngineError } from "@pkhex-everywhere/engine";
|
|
3
|
+
import { affects, call, queryTopics } from "@pkhex-everywhere/engine/internal";
|
|
3
4
|
import { jsx } from "react/jsx-runtime";
|
|
4
5
|
//#region src/cache.ts
|
|
5
6
|
var QueryCache = class {
|
|
@@ -55,6 +56,7 @@ var QueryCache = class {
|
|
|
55
56
|
//#region src/EngineProvider.tsx
|
|
56
57
|
var EngineContext = createContext(null);
|
|
57
58
|
var reported = /* @__PURE__ */ new WeakSet();
|
|
59
|
+
/** Gives the hooks below it an engine and a query cache that refetches when a command changes the save. */
|
|
58
60
|
function EngineProvider({ engine, onUnhandledError, children }) {
|
|
59
61
|
const [cache] = useState(() => new QueryCache());
|
|
60
62
|
useEffect(() => engine.subscribe(["*"], cache.invalidate), [engine, cache]);
|
|
@@ -86,38 +88,70 @@ function useEngineContext() {
|
|
|
86
88
|
if (!context) throw new Error("Engine hooks must be used inside an EngineProvider.");
|
|
87
89
|
return context;
|
|
88
90
|
}
|
|
91
|
+
/** The engine from the nearest `EngineProvider`, for any call the hooks don't cover. */
|
|
89
92
|
function useEngine() {
|
|
90
93
|
return useEngineContext().engine;
|
|
91
94
|
}
|
|
92
95
|
//#endregion
|
|
93
96
|
//#region src/useQuery.ts
|
|
94
|
-
|
|
97
|
+
var fixes = {
|
|
98
|
+
"no-save": (name) => `${name} needs a loaded save. Load one with useLoadedGame().load(file) and wrap the component in <RequireGame>.`,
|
|
99
|
+
"no-draft": (name) => `${name} needs an open draft. Open one with usePokemon(at).edit() or .clone() first.`
|
|
100
|
+
};
|
|
101
|
+
function explain(name, error) {
|
|
102
|
+
if (!(error instanceof EngineError)) return error;
|
|
103
|
+
const fix = fixes[error.code];
|
|
104
|
+
return fix ? new EngineError(error.code, fix(name)) : error;
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Runs a query by name and suspends until it resolves. Refetches when a command changes a topic the query reads,
|
|
108
|
+
* and keeps showing the previous value while it does.
|
|
109
|
+
*/
|
|
110
|
+
function useQuery(name, ...args) {
|
|
95
111
|
const { engine, cache } = useEngineContext();
|
|
96
|
-
const key = JSON.stringify([
|
|
97
|
-
const
|
|
112
|
+
const key = JSON.stringify([name, args]);
|
|
113
|
+
const load = () => call(engine, name, args).catch((error) => {
|
|
114
|
+
throw explain(name, error);
|
|
115
|
+
});
|
|
116
|
+
const entry = useSyncExternalStore(cache.subscribe, () => cache.peek(key)) ?? cache.get(key, queryTopics[name], load);
|
|
98
117
|
if (entry.status === "pending") throw entry.promise;
|
|
99
118
|
if (entry.status === "error") throw entry.error;
|
|
100
119
|
return entry.value;
|
|
101
120
|
}
|
|
102
121
|
//#endregion
|
|
122
|
+
//#region src/useEngineStatus.ts
|
|
123
|
+
/** The engine's boot status. Re-renders as the runtime downloads. Works without a save. */
|
|
124
|
+
function useEngineStatus() {
|
|
125
|
+
const engine = useEngine();
|
|
126
|
+
const read = () => engine.status;
|
|
127
|
+
return useSyncExternalStore(engine.onStatusChange, read, read);
|
|
128
|
+
}
|
|
129
|
+
//#endregion
|
|
103
130
|
//#region src/generated/hooks.ts
|
|
131
|
+
/** Requires a loaded save; throws `no-save` otherwise. Wrap in `<RequireGame>`. */
|
|
104
132
|
function useBox() {
|
|
105
133
|
const engine = useEngine();
|
|
106
134
|
return {
|
|
107
135
|
box: useQuery("box.get"),
|
|
108
136
|
...useMemo(() => ({
|
|
137
|
+
/** Requires a loaded save; throws `no-save` otherwise. Wrap in `<RequireGame>`. */
|
|
109
138
|
addEncounter: (index) => engine.box.addEncounter(index),
|
|
139
|
+
/** Requires a loaded save; throws `no-save` otherwise. Wrap in `<RequireGame>`. */
|
|
110
140
|
addFromFile: (bytes) => engine.box.addFromFile(bytes)
|
|
111
141
|
}), [engine])
|
|
112
142
|
};
|
|
113
143
|
}
|
|
144
|
+
/** Requires a loaded save; throws `no-save` otherwise. Wrap in `<RequireGame>`. */
|
|
114
145
|
function useEvents() {
|
|
115
146
|
const engine = useEngine();
|
|
116
147
|
return {
|
|
117
148
|
events: useQuery("events.get"),
|
|
118
149
|
...useMemo(() => ({
|
|
150
|
+
/** Requires a loaded save; throws `no-save` otherwise. Wrap in `<RequireGame>`. */
|
|
119
151
|
giveTickets: (includeOldSeaMap) => engine.events.giveTickets(includeOldSeaMap),
|
|
152
|
+
/** Requires a loaded save; throws `no-save` otherwise. Wrap in `<RequireGame>`. */
|
|
120
153
|
setFlag: (index, value) => engine.events.setFlag(index, value),
|
|
154
|
+
/** Requires a loaded save; throws `no-save` otherwise. Wrap in `<RequireGame>`. */
|
|
121
155
|
setWork: (index, value) => engine.events.setWork(index, value)
|
|
122
156
|
}), [engine])
|
|
123
157
|
};
|
|
@@ -128,6 +162,7 @@ function useLoadedGame() {
|
|
|
128
162
|
game: useQuery("game.get"),
|
|
129
163
|
...useMemo(() => ({
|
|
130
164
|
close: () => engine.game.close(),
|
|
165
|
+
/** Requires a loaded save; throws `no-save` otherwise. Wrap in `<RequireGame>`. */
|
|
131
166
|
export: () => engine.game.export(),
|
|
132
167
|
file: () => engine.game.file(),
|
|
133
168
|
load: (data, fileName) => engine.game.load(data, fileName),
|
|
@@ -135,16 +170,21 @@ function useLoadedGame() {
|
|
|
135
170
|
}), [engine])
|
|
136
171
|
};
|
|
137
172
|
}
|
|
173
|
+
/** Requires a loaded save; throws `no-save` otherwise. Wrap in `<RequireGame>`. */
|
|
138
174
|
function useInventory() {
|
|
139
175
|
const engine = useEngine();
|
|
140
176
|
return {
|
|
141
177
|
inventory: useQuery("inventory.get"),
|
|
142
|
-
...useMemo(() => ({
|
|
178
|
+
...useMemo(() => ({
|
|
179
|
+
/** Requires a loaded save; throws `no-save` otherwise. Wrap in `<RequireGame>`. */
|
|
180
|
+
setItem: (at, count) => engine.inventory.setItem(at, count) }), [engine])
|
|
143
181
|
};
|
|
144
182
|
}
|
|
183
|
+
/** Requires a loaded save; throws `no-save` otherwise. Wrap in `<RequireGame>`. */
|
|
145
184
|
function useParty() {
|
|
146
185
|
return { party: useQuery("party.get") };
|
|
147
186
|
}
|
|
187
|
+
/** Requires a loaded save; throws `no-save` otherwise. Wrap in `<RequireGame>`. */
|
|
148
188
|
function usePokemon(at) {
|
|
149
189
|
const engine = useEngine();
|
|
150
190
|
const pokemon = useQuery("pokemon.get", at);
|
|
@@ -152,29 +192,48 @@ function usePokemon(at) {
|
|
|
152
192
|
return {
|
|
153
193
|
pokemon,
|
|
154
194
|
...useMemo(() => ({
|
|
195
|
+
/** Requires a loaded save and an open draft; throws `no-save` or `no-draft` otherwise. Wrap in `<RequireGame>`. */
|
|
155
196
|
addToBox: () => engine.pokemon.addToBox(),
|
|
197
|
+
/** Requires a loaded save; throws `no-save` otherwise. Wrap in `<RequireGame>`. */
|
|
156
198
|
clone: () => engine.pokemon.clone(at),
|
|
199
|
+
/** Requires a loaded save and an open draft; throws `no-save` or `no-draft` otherwise. Wrap in `<RequireGame>`. */
|
|
157
200
|
commit: () => engine.pokemon.commit(),
|
|
201
|
+
/** Requires a loaded save; throws `no-save` otherwise. Wrap in `<RequireGame>`. */
|
|
158
202
|
edit: () => engine.pokemon.edit(at),
|
|
203
|
+
/** Requires a loaded save; throws `no-save` otherwise. Wrap in `<RequireGame>`. */
|
|
159
204
|
setLevel: (level) => engine.pokemon.setLevel(at, level),
|
|
205
|
+
/** Requires a loaded save; throws `no-save` otherwise. Wrap in `<RequireGame>`. */
|
|
160
206
|
update: (patch) => engine.pokemon.update(at, patch)
|
|
161
207
|
}), [engine, key])
|
|
162
208
|
};
|
|
163
209
|
}
|
|
210
|
+
/** Requires a loaded save; throws `no-save` otherwise. Wrap in `<RequireGame>`. */
|
|
164
211
|
function useTrainer() {
|
|
165
212
|
const engine = useEngine();
|
|
166
213
|
return {
|
|
167
214
|
trainer: useQuery("trainer.get"),
|
|
168
215
|
...useMemo(() => ({
|
|
216
|
+
/** Requires a loaded save; throws `no-save` otherwise. Wrap in `<RequireGame>`. */
|
|
169
217
|
setBattlePoints: (value) => engine.trainer.setBattlePoints(value),
|
|
218
|
+
/** Requires a loaded save; throws `no-save` otherwise. Wrap in `<RequireGame>`. */
|
|
170
219
|
setGender: (gender) => engine.trainer.setGender(gender),
|
|
220
|
+
/** Requires a loaded save; throws `no-save` otherwise. Wrap in `<RequireGame>`. */
|
|
171
221
|
setMoney: (amount) => engine.trainer.setMoney(amount),
|
|
222
|
+
/** Requires a loaded save; throws `no-save` otherwise. Wrap in `<RequireGame>`. */
|
|
172
223
|
setName: (name) => engine.trainer.setName(name)
|
|
173
224
|
}), [engine])
|
|
174
225
|
};
|
|
175
226
|
}
|
|
176
227
|
//#endregion
|
|
228
|
+
//#region src/RequireGame.tsx
|
|
229
|
+
/** Renders `fallback` until a save is loaded, then `children`. Wrap hooks that need a save in it. */
|
|
230
|
+
function RequireGame({ fallback = null, children }) {
|
|
231
|
+
const { game } = useLoadedGame();
|
|
232
|
+
return game ? children : fallback;
|
|
233
|
+
}
|
|
234
|
+
//#endregion
|
|
177
235
|
//#region src/usePokemonDetails.ts
|
|
236
|
+
/** A Pokémon's editable fields and an `update` that applies a patch to it. Requires a loaded save. */
|
|
178
237
|
function usePokemonDetails(at) {
|
|
179
238
|
const engine = useEngine();
|
|
180
239
|
const details = useQuery("pokemon.details", at);
|
|
@@ -185,4 +244,4 @@ function usePokemonDetails(at) {
|
|
|
185
244
|
};
|
|
186
245
|
}
|
|
187
246
|
//#endregion
|
|
188
|
-
export { EngineProvider, useBox, useEngine, useEvents, useInventory, useLoadedGame, useParty, usePokemon, usePokemonDetails, useQuery, useTrainer };
|
|
247
|
+
export { EngineProvider, RequireGame, useBox, useEngine, useEngineStatus, useEvents, useInventory, useLoadedGame, useParty, usePokemon, usePokemonDetails, useQuery, useTrainer };
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { PokemonHandle, PokemonPatch } from '@pkhex-everywhere/engine';
|
|
2
|
+
/** A Pokémon's editable fields and an `update` that applies a patch to it. Requires a loaded save. */
|
|
2
3
|
export declare function usePokemonDetails(at: PokemonHandle): {
|
|
3
4
|
details: import("@pkhex-everywhere/engine").EditablePokemon;
|
|
4
5
|
update: (patch: PokemonPatch) => Promise<void>;
|
package/dist/useQuery.d.ts
CHANGED
|
@@ -1,7 +1,13 @@
|
|
|
1
1
|
import { type CallName, type EngineClient, type QueryName } from '@pkhex-everywhere/engine';
|
|
2
2
|
type Lookup<T, Path extends string> = Path extends `${infer Head}.${infer Rest}` ? Head extends keyof T ? Lookup<T[Head], Rest> : never : Path extends keyof T ? T[Path] : never;
|
|
3
3
|
type CallFunction<Name extends CallName> = Extract<Lookup<EngineClient, Name>, (...args: never[]) => Promise<unknown>>;
|
|
4
|
+
/** The arguments of the call `Name`, such as `[at: PokemonHandle]` for `pokemon.details`. */
|
|
4
5
|
export type CallArgs<Name extends CallName> = Parameters<CallFunction<Name>>;
|
|
6
|
+
/** What the call `Name` resolves to. */
|
|
5
7
|
export type CallResult<Name extends CallName> = Awaited<ReturnType<CallFunction<Name>>>;
|
|
6
|
-
|
|
8
|
+
/**
|
|
9
|
+
* Runs a query by name and suspends until it resolves. Refetches when a command changes a topic the query reads,
|
|
10
|
+
* and keeps showing the previous value while it does.
|
|
11
|
+
*/
|
|
12
|
+
export declare function useQuery<Name extends QueryName>(name: Name, ...args: CallArgs<Name>): CallResult<Name>;
|
|
7
13
|
export {};
|
package/package.json
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pkhex-everywhere/react",
|
|
3
|
-
"version": "0.1
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"description": "React hooks for @pkhex-everywhere/engine.",
|
|
5
5
|
"license": "GPL-3.0-or-later",
|
|
6
|
+
"homepage": "https://arley-space--docs.wawesome.app/docs/",
|
|
6
7
|
"repository": {
|
|
7
8
|
"type": "git",
|
|
8
9
|
"url": "git+https://github.com/arleypadua/PKHeX.Everywhere.git",
|
|
@@ -29,7 +30,7 @@
|
|
|
29
30
|
"test": "vitest run"
|
|
30
31
|
},
|
|
31
32
|
"peerDependencies": {
|
|
32
|
-
"@pkhex-everywhere/engine": "
|
|
33
|
+
"@pkhex-everywhere/engine": "^0.2.1",
|
|
33
34
|
"react": "^19.0.0"
|
|
34
35
|
},
|
|
35
36
|
"devDependencies": {
|