koota 0.6.0 → 0.6.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/README.md CHANGED
@@ -72,11 +72,7 @@ createRoot(document.getElementById('root')!).render(
72
72
  function RocketRenderer() {
73
73
  // Reactively update whenever the query updates with new entities
74
74
  const rockets = useQuery(Position, Velocity)
75
- return (
76
- <>
77
- {rockets.map((entity) => <RocketView key={entity} entity={entity} />)}
78
- </>
79
- )
75
+ return rockets.map((entity) => <RocketView key={entity} entity={entity} />)
80
76
  }
81
77
 
82
78
  function RocketView({ entity }) {
@@ -84,7 +80,7 @@ function RocketView({ entity }) {
84
80
  const position = useTrait(entity, Position)
85
81
  return (
86
82
  <div style={{ position: 'absolute', left: position.x ?? 0, top: position.y ?? 0 }}>
87
- 🚀
83
+ 🚀
88
84
  </div>
89
85
  )
90
86
  }
@@ -95,10 +91,10 @@ function RocketView({ entity }) {
95
91
  Use actions to safely modify Koota from inside of React in either effects or events.
96
92
 
97
93
  ```js
98
- import { createActions } from 'koota'
94
+ import { defineActions } from 'koota'
99
95
  import { useActions } from 'koota/react'
100
96
 
101
- const actions = createActions((world) => ({
97
+ const actions = defineActions((world) => ({
102
98
  spawnShip: (position) => world.spawn(Position(position), Velocity),
103
99
  destroyAllShips: () => {
104
100
  world.query(Position, Velocity).forEach((entity) => {
@@ -286,6 +282,29 @@ const parent = world.spawn()
286
282
  const changedChildren = world.query(Changed(ChildOf), ChildOf(parent))
287
283
  ```
288
284
 
285
+ #### Relation events
286
+
287
+ Relations emit events per **relation pair**. This makes it easy to know exactly which target was involved.
288
+
289
+ - `onAdd(Relation, (entity, target) => {})` triggers when `entity.add(Relation(target))` is called.
290
+ - `onRemove(Relation, (entity, target) => {})` triggers when `entity.remove(Relation(target))` is called.
291
+ - `onChange(Relation, (entity, target) => {})` triggers when relation **store data** is updated with `entity.set(Relation(target), data)` (only for relations created with a `store`).
292
+
293
+ ```js
294
+ const ChildOf = relation({ store: { priority: 0 } })
295
+
296
+ const unsubAdd = world.onAdd(ChildOf, (entity, target) => {})
297
+ const unsubRemove = world.onRemove(ChildOf, (entity, target) => {})
298
+ const unsubChange = world.onChange(ChildOf, (entity, target) => {})
299
+
300
+ const parent = world.spawn()
301
+ const child = world.spawn()
302
+
303
+ child.add(ChildOf(parent)) // onAdd(child, parent)
304
+ child.set(ChildOf(parent), { priority: 1 }) // onChange(child, parent)
305
+ child.remove(ChildOf(parent)) // onRemove(child, parent)
306
+ ```
307
+
289
308
  ### Query modifiers
290
309
 
291
310
  Modifiers are used to filter query results enabling powerful patterns. All modifiers can be mixed together.
@@ -394,9 +413,14 @@ entity.set(Position, { x: 10, y: 20 })
394
413
  entity.remove(Position)
395
414
  ```
396
415
 
416
+ When subscribing to relations, callbacks receive `(entity, target)` so you know which relation pair changed. Relation `onChange` events are triggered by `entity.set(Relation(target), data)` and only on relations with data via the store prop.
417
+
397
418
  ```js
398
- // Returns all queryable entities
399
- const allQueryableEntities = world.query()
419
+ const Likes = relation()
420
+
421
+ const unsub = world.onAdd(Likes, (entity, target) => {
422
+ console.log(`Entity ${entity} likes ${target}`)
423
+ })
400
424
  ```
401
425
 
402
426
  ### Change detection with `updateEach`
@@ -808,7 +832,7 @@ const positions = getStore(world, Position)
808
832
 
809
833
  A Koota query is a lot like a database query. Parameters define how to find entities and efficiently process them in batches. Queries are the primary way to update and transform your app state, similar to how you'd use SQL to filter and modify database records.
810
834
 
811
- #### Caching queries
835
+ #### Defining queries
812
836
 
813
837
  Inline queries are great for readability and are optimized to be as fast as possible, but there is still some small overhead in hashing the query each time it is called.
814
838
 
@@ -820,13 +844,13 @@ function updateMovement(world) {
820
844
  }
821
845
  ```
822
846
 
823
- While this is not likely to be a bottleneck in your code compared to the actual update function, if you want to save these CPU cycles you can cache the query ahead of time and use the returned key. This will have the additional effect of creating the internal query immediately on a worlds, otherwise it will get created the first time it is run.
847
+ While this is not likely to be a bottleneck in your code compared to the actual update function, if you want to save these CPU cycles you can cache the query ahead of time and use the returned ref. This will have the additional effect of creating the internal query immediately on all worlds, otherwise it will get created the first time it is run.
824
848
 
825
849
  ```js
826
850
  // The internal query is created immediately before it is invoked
827
- const movementQuery = cacheQuery(Position, Velocity)
851
+ const movementQuery = defineQuery(Position, Velocity)
828
852
 
829
- // They query key is hashed ahead of time and we just use it
853
+ // The query ref is used for fast array-based lookup
830
854
  function updateMovement(world) {
831
855
  world.query(movementQuery).updateEach(([pos, vel]) => {})
832
856
  }
@@ -834,7 +858,7 @@ function updateMovement(world) {
834
858
 
835
859
  #### Query all entities
836
860
 
837
- To get all queryable entities you simply query with no parameters.
861
+ To get all queryable entities you simply query the world with no parameters.
838
862
 
839
863
  ```js
840
864
  const allEntities = world.query()
@@ -1014,13 +1038,51 @@ useTraitEffect(world, GameState, (state) => {
1014
1038
  })
1015
1039
  ```
1016
1040
 
1041
+ ### `useTarget`
1042
+
1043
+ Observes an entity, or world, for a relation and reactively returns the first target entity. Returns `undefined` if no target exists.
1044
+
1045
+ ```js
1046
+ const ChildOf = relation()
1047
+
1048
+ function ParentDisplay({ entity }) {
1049
+ // Returns the first target of the ChildOf relation
1050
+ const parent = useTarget(entity, ChildOf)
1051
+
1052
+ if (!parent) return <div>No parent</div>
1053
+
1054
+ return <div>Parent: {parent.id()}</div>
1055
+ }
1056
+ ```
1057
+
1058
+ ### `useTargets`
1059
+
1060
+ Observes an entity, or world, for a relation and reactively returns all target entities as an array. Returns an empty array if no targets exist.
1061
+
1062
+ ```js
1063
+ const Contains = relation()
1064
+
1065
+ function InventoryDisplay({ entity }) {
1066
+ // Returns all targets of the Contains relation
1067
+ const items = useTargets(entity, Contains)
1068
+
1069
+ return (
1070
+ <ul>
1071
+ {items.map((item) => (
1072
+ <li key={item.id()}>Item {item.id()}</li>
1073
+ ))}
1074
+ </ul>
1075
+ )
1076
+ }
1077
+ ```
1078
+
1017
1079
  ### `useActions`
1018
1080
 
1019
- Returns actions bound to the world that is context. Use actions created by `createActions`.
1081
+ Returns actions bound to the world that is in context. Use actions created by `defineActions`.
1020
1082
 
1021
1083
  ```js
1022
1084
  // Create actions
1023
- const actions = createActions((world) => ({
1085
+ const actions = defineActions((world) => ({
1024
1086
  spawnPlayer: () => world.spawn(IsPlayer).
1025
1087
  destroyAllPlayers: () => {
1026
1088
  world.query(IsPlayer).forEach((player) => {