Skip to content

Commit 9bcd1e2

Browse files
authored
refactor(voxel-renderer): make hooks type-safe (#266)
1 parent 345ebf1 commit 9bcd1e2

File tree

3 files changed

+134
-59
lines changed

3 files changed

+134
-59
lines changed

.changeset/cyan-singers-unite.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@jolly-pixel/voxel.renderer": minor
3+
---
4+
5+
Refactor hooks to have type-safe metadata

packages/voxel-renderer/docs/Hooks.md

Lines changed: 26 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -13,50 +13,36 @@ import {
1313
function onLayerUpdated(
1414
event: VoxelLayerHookEvent
1515
): void {
16-
// Send information over the network
17-
console.log(event);
16+
// Narrow on `action` to get a fully-typed `metadata`.
17+
if (event.action === "voxel-set") {
18+
console.log(event.metadata.position, event.metadata.blockId);
19+
}
1820
}
1921

2022
const vr = new VoxelRenderer({
2123
onLayerUpdated,
2224
});
2325
```
2426

25-
## Types
26-
27-
```ts
28-
export type VoxelLayerHookAction =
29-
// Voxel-layer actions
30-
| "added" // a voxel layer was created
31-
| "removed" // a voxel layer was deleted
32-
| "updated" // layer properties (visibility, …) changed
33-
| "offset-updated" // layer world offset changed
34-
| "voxel-set" // a voxel was placed in a layer
35-
| "voxel-removed" // a voxel was removed from a layer
36-
| "reordered" // layer render order changed
37-
// Object-layer actions
38-
| "object-layer-added" // a new object layer was created
39-
| "object-layer-removed" // an object layer was deleted
40-
| "object-layer-updated" // object layer properties (e.g. visibility) changed
41-
| "object-added" // an object was added to an object layer
42-
| "object-removed" // an object was removed from an object layer
43-
| "object-updated"; // an object's properties were patched
44-
45-
// Describes a change related to a layer.
46-
export interface VoxelLayerHookEvent {
47-
// The name of the affected layer.
48-
layerName: string;
49-
50-
// The action that occurred on the layer.
51-
action: VoxelLayerHookAction;
52-
53-
// Additional data related to the action.
54-
// Use `unknown` to encourage callers
55-
// to validate the payload before use.
56-
metadata: Record<string, unknown>;
57-
}
58-
59-
export type VoxelLayerHookListener = (
60-
event: VoxelLayerHookEvent
61-
) => void;
62-
```
27+
## Event reference
28+
29+
`VoxelLayerHookEvent` is a discriminated union keyed on `action`. Narrowing on `action`
30+
gives you a precise `metadata` type with no casting required.
31+
32+
| `action` | `metadata` shape |
33+
|---|---|
34+
| `"added"` | `{ options: VoxelLayerConfigurableOptions }` |
35+
| `"removed"` | `{}` |
36+
| `"updated"` | `{ options: Partial<VoxelLayerConfigurableOptions> }` |
37+
| `"offset-updated"` | `{ offset: VoxelCoord }` or `{ delta: VoxelCoord }` |
38+
| `"voxel-set"` | `{ position, blockId, rotation, flipX, flipZ, flipY }` |
39+
| `"voxel-removed"` | `{ position: Vector3Like }` |
40+
| `"reordered"` | `{ direction: "up" \| "down" }` |
41+
| `"object-layer-added"` | `{}` |
42+
| `"object-layer-removed"` | `{}` |
43+
| `"object-layer-updated"` | `{ patch: { visible?: boolean } }` |
44+
| `"object-added"` | `{ objectId: string }` |
45+
| `"object-removed"` | `{ objectId: string }` |
46+
| `"object-updated"` | `{ objectId: string; patch: Partial<VoxelObjectJSON> }` |
47+
48+
`VoxelLayerHookAction` is a convenience alias for `VoxelLayerHookEvent["action"]`.
Lines changed: 103 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,107 @@
1-
export type VoxelLayerHookAction =
2-
| "added"
3-
| "removed"
4-
| "updated"
5-
| "offset-updated"
6-
| "voxel-set"
7-
| "voxel-removed"
8-
| "reordered"
9-
| "object-layer-added"
10-
| "object-layer-removed"
11-
| "object-layer-updated"
12-
| "object-added"
13-
| "object-removed"
14-
| "object-updated";
1+
// Import Third-party Dependencies
2+
import type { Vector3Like } from "three";
3+
4+
// Import Internal Dependencies
5+
import type { VoxelLayerConfigurableOptions } from "./world/VoxelLayer.ts";
6+
import type { VoxelCoord } from "./world/types.ts";
7+
import type {
8+
VoxelObjectLayerJSON,
9+
VoxelObjectJSON
10+
} from "./serialization/VoxelSerializer.ts";
11+
12+
export type VoxelLayerHookEvent =
13+
| {
14+
action: "added";
15+
layerName: string;
16+
metadata: {
17+
options: VoxelLayerConfigurableOptions;
18+
};
19+
}
20+
| {
21+
action: "removed";
22+
layerName: string;
23+
metadata: Record<string, never>;
24+
}
25+
| {
26+
action: "updated";
27+
layerName: string;
28+
metadata: {
29+
options: Partial<VoxelLayerConfigurableOptions>;
30+
};
31+
}
32+
| {
33+
action: "offset-updated";
34+
layerName: string;
35+
metadata: { offset: VoxelCoord; } | { delta: VoxelCoord; };
36+
}
37+
| {
38+
action: "voxel-set";
39+
layerName: string;
40+
metadata: {
41+
position: Vector3Like;
42+
blockId: number;
43+
rotation: number;
44+
flipX: boolean;
45+
flipZ: boolean;
46+
flipY: boolean;
47+
};
48+
}
49+
| {
50+
action: "voxel-removed";
51+
layerName: string;
52+
metadata: {
53+
position: Vector3Like;
54+
};
55+
}
56+
| {
57+
action: "reordered";
58+
layerName: string;
59+
metadata: {
60+
direction: "up" | "down";
61+
};
62+
}
63+
| {
64+
action: "object-layer-added";
65+
layerName: string;
66+
metadata: Record<string, never>;
67+
}
68+
| {
69+
action: "object-layer-removed";
70+
layerName: string;
71+
metadata: Record<string, never>;
72+
}
73+
| {
74+
action: "object-layer-updated";
75+
layerName: string;
76+
metadata: {
77+
patch: Partial<Pick<VoxelObjectLayerJSON, "visible">>;
78+
};
79+
}
80+
| {
81+
action: "object-added";
82+
layerName: string;
83+
metadata: {
84+
objectId: string;
85+
};
86+
}
87+
| {
88+
action: "object-removed";
89+
layerName: string;
90+
metadata: {
91+
objectId: string;
92+
};
93+
}
94+
| {
95+
action: "object-updated";
96+
layerName: string;
97+
metadata: {
98+
objectId: string;
99+
patch: Partial<VoxelObjectJSON>;
100+
};
101+
};
102+
103+
export type VoxelLayerHookAction = VoxelLayerHookEvent["action"];
15104

16-
export interface VoxelLayerHookEvent {
17-
layerName: string;
18-
action: VoxelLayerHookAction;
19-
metadata: Record<string, any>;
20-
}
21105
export type VoxelLayerHookListener = (
22106
event: VoxelLayerHookEvent
23107
) => void;

0 commit comments

Comments
 (0)