@hf-chimera/react 0.2.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/README.md +241 -0
- package/dist/index.cjs +48 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +11 -0
- package/dist/index.d.cts.map +1 -0
- package/dist/index.d.mts +11 -0
- package/dist/index.d.mts.map +1 -0
- package/dist/index.mjs +48 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +48 -0
package/README.md
ADDED
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
# ChimeraEntityStore React Integration
|
|
2
|
+
|
|
3
|
+
This package provides React hooks for seamless integration with ChimeraEntityStore, enabling reactive data management in React applications.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **React Hooks**: Custom hooks for managing ChimeraEntityStore queries
|
|
8
|
+
- **TypeScript Support**: Full type safety with TypeScript
|
|
9
|
+
- **Automatic State Management**: Automatic re-rendering when data changes
|
|
10
|
+
|
|
11
|
+
## Installation
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
# Install both the core store and React adapter
|
|
15
|
+
npm install @hf-chimera/store @hf-chimera/react
|
|
16
|
+
|
|
17
|
+
# React is a peer dependency
|
|
18
|
+
npm install react
|
|
19
|
+
|
|
20
|
+
# Or install all at once
|
|
21
|
+
npm install @hf-chimera/store @hf-chimera/react react
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Usage
|
|
25
|
+
|
|
26
|
+
### For React Applications
|
|
27
|
+
|
|
28
|
+
Import from the React-specific entry point:
|
|
29
|
+
|
|
30
|
+
```tsx
|
|
31
|
+
import { createChimeraHooks } from "@hf-chimera/store/react";
|
|
32
|
+
// In your app, call createChimeraHooks(store) and export the returned hooks
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
### For Non-React Applications
|
|
36
|
+
|
|
37
|
+
Import from the main package:
|
|
38
|
+
|
|
39
|
+
```ts
|
|
40
|
+
import { createChimeraEntityStore } from "@hf-chimera/store";
|
|
41
|
+
import { createChimeraStoreHooks } from "@hf-chimera/react";
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## Quick Start
|
|
45
|
+
|
|
46
|
+
### 1. Prepare hooks for the store
|
|
47
|
+
|
|
48
|
+
Use the `createChimeraStoreHooks` function to create hooks for your entity store:
|
|
49
|
+
|
|
50
|
+
```ts
|
|
51
|
+
import { createChimeraStoreHooks } from "@hf-chimera/react";
|
|
52
|
+
import { createChimeraEntityStore } from "@hf-chimera/store";
|
|
53
|
+
|
|
54
|
+
// Define your entity type
|
|
55
|
+
type Customer = {
|
|
56
|
+
id: number;
|
|
57
|
+
name: string;
|
|
58
|
+
email: string;
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
// Create your entity store instance
|
|
62
|
+
const customerStore = createChimeraEntityStore<"customer", Customer>({
|
|
63
|
+
name: "customer",
|
|
64
|
+
idGetter: "id",
|
|
65
|
+
async collectionFetcher(params, requestParams) {
|
|
66
|
+
const response = await fetch("/api/customers", {
|
|
67
|
+
method: "POST",
|
|
68
|
+
headers: { "Content-Type": "application/json" },
|
|
69
|
+
body: JSON.stringify({ filter: params.filter, order: params.order }),
|
|
70
|
+
signal: requestParams.signal,
|
|
71
|
+
});
|
|
72
|
+
return { data: await response.json() };
|
|
73
|
+
},
|
|
74
|
+
async itemFetcher(params, requestParams) {
|
|
75
|
+
const response = await fetch(`/api/customers/${params.id}`, {
|
|
76
|
+
signal: requestParams.signal,
|
|
77
|
+
});
|
|
78
|
+
return { data: await response.json() };
|
|
79
|
+
},
|
|
80
|
+
// ... other CRUD operations
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
// Create hooks bound to your customer store
|
|
84
|
+
// This generates: useChimeraCustomerStore, useChimeraCustomerCollection, useChimeraCustomerItem
|
|
85
|
+
export const {
|
|
86
|
+
useChimeraCustomerStore,
|
|
87
|
+
useChimeraCustomerCollection,
|
|
88
|
+
useChimeraCustomerItem,
|
|
89
|
+
} = createChimeraStoreHooks(customerStore);
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
### 2. Use Collection Queries
|
|
93
|
+
|
|
94
|
+
```tsx
|
|
95
|
+
import { useChimeraCustomerCollection } from "./store";
|
|
96
|
+
|
|
97
|
+
function CustomerList() {
|
|
98
|
+
const customers = useChimeraCustomerCollection({
|
|
99
|
+
filter: { status: "active" },
|
|
100
|
+
order: [{ field: "name", direction: "asc" }],
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
if (!customers.ready) return <div>Loading...</div>;
|
|
104
|
+
if (customers.lastError)
|
|
105
|
+
return <div>Error: {String(customers.lastError)}</div>;
|
|
106
|
+
|
|
107
|
+
return (
|
|
108
|
+
<div>
|
|
109
|
+
{customers.map((customer) => (
|
|
110
|
+
<div key={customer.id}>
|
|
111
|
+
{customer.name}
|
|
112
|
+
<button onClick={() => customers.delete(customer.id)}>Delete</button>
|
|
113
|
+
</div>
|
|
114
|
+
))}
|
|
115
|
+
<button onClick={() => customers.create({ name: "New Customer" })}>
|
|
116
|
+
Add Customer
|
|
117
|
+
</button>
|
|
118
|
+
</div>
|
|
119
|
+
);
|
|
120
|
+
}
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
### 3. Use Item Queries
|
|
124
|
+
|
|
125
|
+
```tsx
|
|
126
|
+
import { useChimeraCustomerItem } from "./store";
|
|
127
|
+
|
|
128
|
+
function CustomerDetail({ customerId }: { customerId: string }) {
|
|
129
|
+
const customer = useChimeraCustomerItem(customerId);
|
|
130
|
+
|
|
131
|
+
if (!customer.ready) return <div>Loading...</div>;
|
|
132
|
+
if (customer.lastError) return <div>Error: {String(customer.lastError)}</div>;
|
|
133
|
+
if (!customer.data) return <div>Customer not found</div>;
|
|
134
|
+
|
|
135
|
+
const handleUpdate = () => {
|
|
136
|
+
customer.mutable.name = "Updated Name";
|
|
137
|
+
customer.commit();
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
return (
|
|
141
|
+
<div>
|
|
142
|
+
<h3>{customer.data.name}</h3>
|
|
143
|
+
<button onClick={handleUpdate}>Update Name</button>
|
|
144
|
+
</div>
|
|
145
|
+
);
|
|
146
|
+
}
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
### 4. Using with Query Builder
|
|
150
|
+
|
|
151
|
+
You can use the `ChimeraQueryBuilder` with React hooks by passing a builder function instead of the query descriptor. This provides a more fluent and type-safe API:
|
|
152
|
+
|
|
153
|
+
```tsx
|
|
154
|
+
import { useChimeraCollection, useChimeraItem } from "./store";
|
|
155
|
+
|
|
156
|
+
function ActiveUsers() {
|
|
157
|
+
// Pass a builder function - the hook will call it and build the query
|
|
158
|
+
const activeUsers = useChimeraCollection("customer", (q) => {
|
|
159
|
+
q.where("email", "contains", "@example.com").orderBy("createdAt", true);
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
if (!activeUsers.ready) {
|
|
163
|
+
return <div>Loading...</div>;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
return (
|
|
167
|
+
<div>
|
|
168
|
+
{activeUsers.map((user) => (
|
|
169
|
+
<div key={user.id}>{user.name}</div>
|
|
170
|
+
))}
|
|
171
|
+
</div>
|
|
172
|
+
);
|
|
173
|
+
}
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
**Complex Query with Groups:**
|
|
177
|
+
|
|
178
|
+
```tsx
|
|
179
|
+
import { useChimeraCollection } from "./store";
|
|
180
|
+
|
|
181
|
+
function FeaturedOrders() {
|
|
182
|
+
const orders = useChimeraCollection("order", (q) => {
|
|
183
|
+
q.where("status", "eq", "completed")
|
|
184
|
+
// Must be either high value OR from VIP customer
|
|
185
|
+
.group("or", (group) => {
|
|
186
|
+
group
|
|
187
|
+
.where("totalAmount", "gte", 1000)
|
|
188
|
+
.where("customerId", "in", [1, 2, 3]); // VIP customers
|
|
189
|
+
})
|
|
190
|
+
// But not cancelled
|
|
191
|
+
.whereNot("status", "eq", "cancelled")
|
|
192
|
+
.orderBy("totalAmount", true);
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
return (
|
|
196
|
+
<div>
|
|
197
|
+
{orders.map((order) => (
|
|
198
|
+
<div key={order.id}>{order.productName}</div>
|
|
199
|
+
))}
|
|
200
|
+
</div>
|
|
201
|
+
);
|
|
202
|
+
}
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
For more information on using the query builder, see the [ChimeraQueryBuilder documentation](../../qb/README.md).
|
|
206
|
+
|
|
207
|
+
## API Reference
|
|
208
|
+
|
|
209
|
+
### Hooks
|
|
210
|
+
|
|
211
|
+
#### `useChimeraCustomerCollection<Meta>(params, deps?)`
|
|
212
|
+
|
|
213
|
+
Hook for collection queries with automatic state management.
|
|
214
|
+
|
|
215
|
+
**Parameters:**
|
|
216
|
+
|
|
217
|
+
- `entityName: EntityName` - Name of the entity
|
|
218
|
+
- `params: ChimeraCollectionParams | QueryBuilderCreator` - Query parameters or query builder function
|
|
219
|
+
- `deps?: unknown[]` - Optional dependency array for memoization
|
|
220
|
+
|
|
221
|
+
**Returns:** `ChimeraCollectionQuery<Item, OperatorsMap>`, see [ChimeraCollectionQuery documentation](../../../README.md#chimeracollectionquery)
|
|
222
|
+
|
|
223
|
+
#### `useChimeraItem<Store, EntityName, Meta>(entityName, id, meta?)`
|
|
224
|
+
|
|
225
|
+
Hook for individual item queries with automatic state management.
|
|
226
|
+
|
|
227
|
+
**Parameters:**
|
|
228
|
+
|
|
229
|
+
- `entityName: EntityName` - Name of the entity
|
|
230
|
+
- `id: ChimeraEntityId` - Item ID
|
|
231
|
+
- `meta?: Meta` - Optional metadata
|
|
232
|
+
|
|
233
|
+
**Returns:** `ChimeraItemQuery<Item>`, see [ChimeraCollectionQuery documentation](../../../README.md#chimeraitemquery)
|
|
234
|
+
|
|
235
|
+
## Tips
|
|
236
|
+
|
|
237
|
+
1. **Type Safety**: Use `getChimeraTypedHooks` for full type safety
|
|
238
|
+
2. **Error Handling**: Always check `ready` and `lastError` properties
|
|
239
|
+
3. **Optimistic Updates**: Use the `mutable` property for optimistic updates on item queries
|
|
240
|
+
4. **Batch Operations**: Use batch operations for multiple items when possible
|
|
241
|
+
5. **Query Builder**: Use the query builder function syntax for better type safety and readability
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
let _hf_chimera_adapters_shared = require("@hf-chimera/adapters-shared");
|
|
2
|
+
let _hf_chimera_query_builder = require("@hf-chimera/query-builder");
|
|
3
|
+
let react = require("react");
|
|
4
|
+
|
|
5
|
+
//#region hooks.ts
|
|
6
|
+
const capitalize = (s) => s !== "" ? s[0].toUpperCase() + s.slice(1) : "";
|
|
7
|
+
const useSubscribedValue = (value, events) => {
|
|
8
|
+
const trigger = (0, react.useState)(() => ({}))[1];
|
|
9
|
+
(0, react.useEffect)(() => {
|
|
10
|
+
const handler = () => trigger({});
|
|
11
|
+
events.forEach((event) => {
|
|
12
|
+
value.on(event, handler);
|
|
13
|
+
});
|
|
14
|
+
return () => {
|
|
15
|
+
events.forEach((event) => {
|
|
16
|
+
value.off(event, handler);
|
|
17
|
+
});
|
|
18
|
+
};
|
|
19
|
+
}, [
|
|
20
|
+
value,
|
|
21
|
+
events,
|
|
22
|
+
trigger
|
|
23
|
+
]);
|
|
24
|
+
return value;
|
|
25
|
+
};
|
|
26
|
+
function createChimeraStoreHooks(store, createQueryBuilder) {
|
|
27
|
+
createQueryBuilder ||= () => new _hf_chimera_query_builder.DefaultChimeraQueryBuilder();
|
|
28
|
+
return {
|
|
29
|
+
[`useChimera${capitalize(store.name)}Store`]: () => useSubscribedValue(store, _hf_chimera_adapters_shared.CHIMERA_ENTITY_STORE_UPDATE_EVENTS),
|
|
30
|
+
[`useChimera${capitalize(store.name)}Collection`]: (params, deps) => {
|
|
31
|
+
const oldDeps = (0, react.useRef)(deps);
|
|
32
|
+
if (oldDeps.current && !deps || !oldDeps.current && deps) console.warn(`useChimera${capitalize(store.name)}Collection deps is not a reactive param!\nUse deps if you want to control dependencies manually.
|
|
33
|
+
Omit it if you already have a stable reference to params`);
|
|
34
|
+
oldDeps.current = deps;
|
|
35
|
+
const stableParams = (0, react.useMemo)(() => (0, _hf_chimera_adapters_shared.normalizeParams)(createQueryBuilder, params), deps ? deps : [params]);
|
|
36
|
+
return useSubscribedValue((0, react.useMemo)(() => store.getCollection(stableParams), [store, stableParams]), _hf_chimera_adapters_shared.CHIMERA_COLLECTION_UPDATE_EVENTS);
|
|
37
|
+
},
|
|
38
|
+
[`useChimera${capitalize(store.name)}Item`]: (id, meta) => useSubscribedValue((0, react.useMemo)(() => store.getItem(id, meta), [
|
|
39
|
+
store,
|
|
40
|
+
id,
|
|
41
|
+
meta
|
|
42
|
+
]), _hf_chimera_adapters_shared.CHIMERA_ITEM_UPDATE_EVENTS)
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
//#endregion
|
|
47
|
+
exports.createChimeraStoreHooks = createChimeraStoreHooks;
|
|
48
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.cjs","names":["DefaultChimeraQueryBuilder","CHIMERA_ENTITY_STORE_UPDATE_EVENTS","CHIMERA_COLLECTION_UPDATE_EVENTS","CHIMERA_ITEM_UPDATE_EVENTS"],"sources":["../hooks.ts"],"sourcesContent":["import {\n\ttype AnyChimeraParams,\n\tCHIMERA_COLLECTION_UPDATE_EVENTS,\n\tCHIMERA_ENTITY_STORE_UPDATE_EVENTS,\n\tCHIMERA_ITEM_UPDATE_EVENTS,\n\tnormalizeParams,\n} from \"@hf-chimera/adapters-shared\";\nimport type { ChimeraQueryBuilder } from \"@hf-chimera/query-builder\";\nimport { DefaultChimeraQueryBuilder } from \"@hf-chimera/query-builder\";\nimport type {\n\tAnyChimeraEntityStore,\n\tAnyChimeraEventEmitter,\n\tChimeraCollectionQuery,\n\tChimeraEntityId,\n\tChimeraEntityStoreEntity,\n\tChimeraEntityStoreName,\n\tChimeraEntityStoreOperatorsMap,\n\tChimeraEventEmitterEventNames,\n\tChimeraItemQuery,\n} from \"@hf-chimera/store\";\nimport { useEffect, useMemo, useRef, useState } from \"react\";\n\ntype ChimeraHooks<\n\tTStore extends AnyChimeraEntityStore,\n\tTQueryBuilder extends ChimeraQueryBuilder<TStore>,\n\tTEntityName extends ChimeraEntityStoreName<TStore> = ChimeraEntityStoreName<TStore>,\n> = {\n\t[K in TEntityName as `useChimera${Capitalize<K>}Store`]: () => TStore;\n} & {\n\t[K in TEntityName as `useChimera${Capitalize<K>}Collection`]: <Meta = any>(\n\t\tparams: AnyChimeraParams<TStore, Meta, Extract<TQueryBuilder, ChimeraQueryBuilder<TStore>>>,\n\t\tdeps?: unknown[],\n\t) => ChimeraCollectionQuery<TEntityName, ChimeraEntityStoreEntity<TStore>, ChimeraEntityStoreOperatorsMap<TStore>>;\n} & {\n\t[K in TEntityName as `useChimera${Capitalize<K>}Item`]: <Meta = any>(\n\t\tid: ChimeraEntityId,\n\t\tmeta?: Meta,\n\t) => ChimeraItemQuery<TEntityName, ChimeraEntityStoreEntity<TStore>>;\n};\n\nconst capitalize = <T extends string>(s: T): Capitalize<T> =>\n\ts !== \"\" ? (((s[0] as string).toUpperCase() + s.slice(1)) as Capitalize<T>) : (\"\" as Capitalize<T>);\n\nconst useSubscribedValue = <TEventEmitter extends AnyChimeraEventEmitter>(\n\tvalue: TEventEmitter,\n\tevents: ChimeraEventEmitterEventNames<TEventEmitter>[],\n): TEventEmitter => {\n\tconst trigger = useState(() => ({}))[1];\n\tuseEffect(() => {\n\t\tconst handler = () => trigger({});\n\t\tevents.forEach((event) => {\n\t\t\tvalue.on(event, handler);\n\t\t});\n\t\treturn () => {\n\t\t\tevents.forEach((event) => {\n\t\t\t\tvalue.off(event, handler);\n\t\t\t});\n\t\t};\n\t}, [value, events, trigger]);\n\treturn value;\n};\n\nexport function createChimeraStoreHooks<TStore extends AnyChimeraEntityStore>(\n\tstore: TStore,\n): ChimeraHooks<TStore, DefaultChimeraQueryBuilder<TStore>>;\nexport function createChimeraStoreHooks<\n\tTStore extends AnyChimeraEntityStore,\n\tTQueryBuilder extends ChimeraQueryBuilder<TStore>,\n>(store: TStore, createQueryBuilder: () => TQueryBuilder): ChimeraHooks<TStore, TQueryBuilder>;\nexport function createChimeraStoreHooks<\n\tTStore extends AnyChimeraEntityStore,\n\tTQueryBuilder extends ChimeraQueryBuilder<TStore>,\n>(store: TStore, createQueryBuilder?: () => TQueryBuilder): ChimeraHooks<TStore, TQueryBuilder> {\n\tcreateQueryBuilder ||= () => new DefaultChimeraQueryBuilder() as unknown as TQueryBuilder;\n\n\treturn {\n\t\t[`useChimera${capitalize(store.name)}Store`]: () => useSubscribedValue(store, CHIMERA_ENTITY_STORE_UPDATE_EVENTS),\n\t\t[`useChimera${capitalize(store.name)}Collection`]: <TMeta = any>(\n\t\t\tparams: AnyChimeraParams<TStore, TMeta, Extract<TQueryBuilder, ChimeraQueryBuilder<TStore>>>,\n\t\t\tdeps?: unknown[],\n\t\t) => {\n\t\t\tconst oldDeps = useRef(deps);\n\t\t\tif ((oldDeps.current && !deps) || (!oldDeps.current && deps)) {\n\t\t\t\tconsole.warn(\n\t\t\t\t\t`useChimera${capitalize(store.name)}Collection deps is not a reactive param!\\n` +\n\t\t\t\t\t\t\"Use deps if you want to control dependencies manually.\\n\" +\n\t\t\t\t\t\t\"Omit it if you already have a stable reference to params\",\n\t\t\t\t);\n\t\t\t}\n\t\t\toldDeps.current = deps;\n\n\t\t\tconst stableParams = useMemo(\n\t\t\t\t() => normalizeParams(createQueryBuilder, params),\n\t\t\t\t// biome-ignore lint/correctness/useExhaustiveDependencies: Very unlikely it will be changed over time, anyway warning for this already added.\n\t\t\t\tdeps ? deps : [params],\n\t\t\t);\n\n\t\t\treturn useSubscribedValue(\n\t\t\t\tuseMemo(() => store.getCollection(stableParams), [store, stableParams]),\n\t\t\t\tCHIMERA_COLLECTION_UPDATE_EVENTS,\n\t\t\t);\n\t\t},\n\t\t[`useChimera${capitalize(store.name)}Item`]: <Meta = any>(id: ChimeraEntityId, meta?: Meta) =>\n\t\t\tuseSubscribedValue(\n\t\t\t\tuseMemo(() => store.getItem(id, meta), [store, id, meta]),\n\t\t\t\tCHIMERA_ITEM_UPDATE_EVENTS,\n\t\t\t),\n\t} as ChimeraHooks<TStore, TQueryBuilder>;\n}\n"],"mappings":";;;;;AAwCA,MAAM,cAAgC,MACrC,MAAM,KAAQ,EAAE,GAAc,aAAa,GAAG,EAAE,MAAM,EAAE,GAAuB;AAEhF,MAAM,sBACL,OACA,WACmB;CACnB,MAAM,qCAA0B,EAAE,EAAE,CAAC;AACrC,4BAAgB;EACf,MAAM,gBAAgB,QAAQ,EAAE,CAAC;AACjC,SAAO,SAAS,UAAU;AACzB,SAAM,GAAG,OAAO,QAAQ;IACvB;AACF,eAAa;AACZ,UAAO,SAAS,UAAU;AACzB,UAAM,IAAI,OAAO,QAAQ;KACxB;;IAED;EAAC;EAAO;EAAQ;EAAQ,CAAC;AAC5B,QAAO;;AAUR,SAAgB,wBAGd,OAAe,oBAA+E;AAC/F,8BAA6B,IAAIA,sDAA4B;AAE7D,QAAO;GACL,aAAa,WAAW,MAAM,KAAK,CAAC,eAAe,mBAAmB,OAAOC,+DAAmC;GAChH,aAAa,WAAW,MAAM,KAAK,CAAC,eACpC,QACA,SACI;GACJ,MAAM,4BAAiB,KAAK;AAC5B,OAAK,QAAQ,WAAW,CAAC,QAAU,CAAC,QAAQ,WAAW,KACtD,SAAQ,KACP,aAAa,WAAW,MAAM,KAAK,CAAC;0DAGpC;AAEF,WAAQ,UAAU;GAElB,MAAM,yFACiB,oBAAoB,OAAO,EAEjD,OAAO,OAAO,CAAC,OAAO,CACtB;AAED,UAAO,4CACQ,MAAM,cAAc,aAAa,EAAE,CAAC,OAAO,aAAa,CAAC,EACvEC,6DACA;;GAED,aAAa,WAAW,MAAM,KAAK,CAAC,SAAqB,IAAqB,SAC9E,4CACe,MAAM,QAAQ,IAAI,KAAK,EAAE;GAAC;GAAO;GAAI;GAAK,CAAC,EACzDC,uDACA;EACF"}
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { AnyChimeraParams } from "@hf-chimera/adapters-shared";
|
|
2
|
+
import { ChimeraQueryBuilder, DefaultChimeraQueryBuilder } from "@hf-chimera/query-builder";
|
|
3
|
+
import { AnyChimeraEntityStore, ChimeraCollectionQuery, ChimeraEntityId, ChimeraEntityStoreEntity, ChimeraEntityStoreName, ChimeraEntityStoreOperatorsMap, ChimeraItemQuery } from "@hf-chimera/store";
|
|
4
|
+
|
|
5
|
+
//#region hooks.d.ts
|
|
6
|
+
type ChimeraHooks<TStore extends AnyChimeraEntityStore, TQueryBuilder extends ChimeraQueryBuilder<TStore>, TEntityName extends ChimeraEntityStoreName<TStore> = ChimeraEntityStoreName<TStore>> = { [K in TEntityName as `useChimera${Capitalize<K>}Store`]: () => TStore } & { [K in TEntityName as `useChimera${Capitalize<K>}Collection`]: <Meta = any>(params: AnyChimeraParams<TStore, Meta, Extract<TQueryBuilder, ChimeraQueryBuilder<TStore>>>, deps?: unknown[]) => ChimeraCollectionQuery<TEntityName, ChimeraEntityStoreEntity<TStore>, ChimeraEntityStoreOperatorsMap<TStore>> } & { [K in TEntityName as `useChimera${Capitalize<K>}Item`]: <Meta = any>(id: ChimeraEntityId, meta?: Meta) => ChimeraItemQuery<TEntityName, ChimeraEntityStoreEntity<TStore>> };
|
|
7
|
+
declare function createChimeraStoreHooks<TStore extends AnyChimeraEntityStore>(store: TStore): ChimeraHooks<TStore, DefaultChimeraQueryBuilder<TStore>>;
|
|
8
|
+
declare function createChimeraStoreHooks<TStore extends AnyChimeraEntityStore, TQueryBuilder extends ChimeraQueryBuilder<TStore>>(store: TStore, createQueryBuilder: () => TQueryBuilder): ChimeraHooks<TStore, TQueryBuilder>;
|
|
9
|
+
//#endregion
|
|
10
|
+
export { createChimeraStoreHooks };
|
|
11
|
+
//# sourceMappingURL=index.d.cts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.cts","names":[],"sources":["../hooks.ts"],"sourcesContent":[],"mappings":";;;;;KAsBK,4BACW,6CACO,oBAAoB,6BACtB,uBAAuB,UAAU,uBAAuB,mBAEtE,WALU,IAAA,aAKkB,UALlB,CAK6B,CAL7B,CAAA,OAAA,GAAA,GAAA,GAK+C,MAL/C,EACD,GAAA,QAMT,WALoC,IAAA,aAKR,UALQ,CAKG,CALH,CAAA,YAAA,GAAA,CAAA,OAAA,GAAA,CAAA,CAAA,MAAA,EAMjC,gBANiC,CAMhB,MANgB,EAMR,IANQ,EAMF,OANE,CAMM,aANN,EAMqB,mBANrB,CAMyC,MANzC,CAAA,CAAA,CAAA,EAAA,IAAA,CAAA,EAAA,OAAA,EAAA,EAAA,GAQrC,sBARqC,CAQd,WARc,EAQD,wBARC,CAQwB,MARxB,CAAA,EAQiC,8BARjC,CAQgE,MARhE,CAAA,CAAA,EAApB,GAAA,QAUhB,WATqC,IAAA,aAST,UATS,CASE,CATF,CAAA,MAAA,GAAA,CAAA,OAAA,GAAA,CAAA,CAAA,EAAA,EAUtC,eAVsC,EAAA,IAAA,CAAA,EAWnC,IAXmC,EAAA,GAYtC,gBAZsC,CAYrB,WAZqB,EAYR,wBAZQ,CAYiB,MAZjB,CAAA,CAAA,EAAvB;AAAwD,iBAqC7D,uBArC6D,CAAA,eAqCtB,qBArCsB,CAAA,CAAA,KAAA,EAsCrE,MAtCqE,CAAA,EAuC1E,YAvC0E,CAuC7D,MAvC6D,EAuCrD,0BAvCqD,CAuC1B,MAvC0B,CAAA,CAAA;AAAvB,iBAwCtC,uBAxCsC,CAAA,eAyCtC,qBAzCsC,EAAA,sBA0C/B,mBA1C+B,CA0CX,MA1CW,CAAA,CAAA,CAAA,KAAA,EA2C7C,MA3C6C,EAAA,kBAAA,EAAA,GAAA,GA2CX,aA3CW,CAAA,EA2CK,YA3CL,CA2CkB,MA3ClB,EA2C0B,aA3C1B,CAAA"}
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { AnyChimeraParams } from "@hf-chimera/adapters-shared";
|
|
2
|
+
import { ChimeraQueryBuilder, DefaultChimeraQueryBuilder } from "@hf-chimera/query-builder";
|
|
3
|
+
import { AnyChimeraEntityStore, ChimeraCollectionQuery, ChimeraEntityId, ChimeraEntityStoreEntity, ChimeraEntityStoreName, ChimeraEntityStoreOperatorsMap, ChimeraItemQuery } from "@hf-chimera/store";
|
|
4
|
+
|
|
5
|
+
//#region hooks.d.ts
|
|
6
|
+
type ChimeraHooks<TStore extends AnyChimeraEntityStore, TQueryBuilder extends ChimeraQueryBuilder<TStore>, TEntityName extends ChimeraEntityStoreName<TStore> = ChimeraEntityStoreName<TStore>> = { [K in TEntityName as `useChimera${Capitalize<K>}Store`]: () => TStore } & { [K in TEntityName as `useChimera${Capitalize<K>}Collection`]: <Meta = any>(params: AnyChimeraParams<TStore, Meta, Extract<TQueryBuilder, ChimeraQueryBuilder<TStore>>>, deps?: unknown[]) => ChimeraCollectionQuery<TEntityName, ChimeraEntityStoreEntity<TStore>, ChimeraEntityStoreOperatorsMap<TStore>> } & { [K in TEntityName as `useChimera${Capitalize<K>}Item`]: <Meta = any>(id: ChimeraEntityId, meta?: Meta) => ChimeraItemQuery<TEntityName, ChimeraEntityStoreEntity<TStore>> };
|
|
7
|
+
declare function createChimeraStoreHooks<TStore extends AnyChimeraEntityStore>(store: TStore): ChimeraHooks<TStore, DefaultChimeraQueryBuilder<TStore>>;
|
|
8
|
+
declare function createChimeraStoreHooks<TStore extends AnyChimeraEntityStore, TQueryBuilder extends ChimeraQueryBuilder<TStore>>(store: TStore, createQueryBuilder: () => TQueryBuilder): ChimeraHooks<TStore, TQueryBuilder>;
|
|
9
|
+
//#endregion
|
|
10
|
+
export { createChimeraStoreHooks };
|
|
11
|
+
//# sourceMappingURL=index.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.mts","names":[],"sources":["../hooks.ts"],"sourcesContent":[],"mappings":";;;;;KAsBK,4BACW,6CACO,oBAAoB,6BACtB,uBAAuB,UAAU,uBAAuB,mBAEtE,WALU,IAAA,aAKkB,UALlB,CAK6B,CAL7B,CAAA,OAAA,GAAA,GAAA,GAK+C,MAL/C,EACD,GAAA,QAMT,WALoC,IAAA,aAKR,UALQ,CAKG,CALH,CAAA,YAAA,GAAA,CAAA,OAAA,GAAA,CAAA,CAAA,MAAA,EAMjC,gBANiC,CAMhB,MANgB,EAMR,IANQ,EAMF,OANE,CAMM,aANN,EAMqB,mBANrB,CAMyC,MANzC,CAAA,CAAA,CAAA,EAAA,IAAA,CAAA,EAAA,OAAA,EAAA,EAAA,GAQrC,sBARqC,CAQd,WARc,EAQD,wBARC,CAQwB,MARxB,CAAA,EAQiC,8BARjC,CAQgE,MARhE,CAAA,CAAA,EAApB,GAAA,QAUhB,WATqC,IAAA,aAST,UATS,CASE,CATF,CAAA,MAAA,GAAA,CAAA,OAAA,GAAA,CAAA,CAAA,EAAA,EAUtC,eAVsC,EAAA,IAAA,CAAA,EAWnC,IAXmC,EAAA,GAYtC,gBAZsC,CAYrB,WAZqB,EAYR,wBAZQ,CAYiB,MAZjB,CAAA,CAAA,EAAvB;AAAwD,iBAqC7D,uBArC6D,CAAA,eAqCtB,qBArCsB,CAAA,CAAA,KAAA,EAsCrE,MAtCqE,CAAA,EAuC1E,YAvC0E,CAuC7D,MAvC6D,EAuCrD,0BAvCqD,CAuC1B,MAvC0B,CAAA,CAAA;AAAvB,iBAwCtC,uBAxCsC,CAAA,eAyCtC,qBAzCsC,EAAA,sBA0C/B,mBA1C+B,CA0CX,MA1CW,CAAA,CAAA,CAAA,KAAA,EA2C7C,MA3C6C,EAAA,kBAAA,EAAA,GAAA,GA2CX,aA3CW,CAAA,EA2CK,YA3CL,CA2CkB,MA3ClB,EA2C0B,aA3C1B,CAAA"}
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { CHIMERA_COLLECTION_UPDATE_EVENTS, CHIMERA_ENTITY_STORE_UPDATE_EVENTS, CHIMERA_ITEM_UPDATE_EVENTS, normalizeParams } from "@hf-chimera/adapters-shared";
|
|
2
|
+
import { DefaultChimeraQueryBuilder } from "@hf-chimera/query-builder";
|
|
3
|
+
import { useEffect, useMemo, useRef, useState } from "react";
|
|
4
|
+
|
|
5
|
+
//#region hooks.ts
|
|
6
|
+
const capitalize = (s) => s !== "" ? s[0].toUpperCase() + s.slice(1) : "";
|
|
7
|
+
const useSubscribedValue = (value, events) => {
|
|
8
|
+
const trigger = useState(() => ({}))[1];
|
|
9
|
+
useEffect(() => {
|
|
10
|
+
const handler = () => trigger({});
|
|
11
|
+
events.forEach((event) => {
|
|
12
|
+
value.on(event, handler);
|
|
13
|
+
});
|
|
14
|
+
return () => {
|
|
15
|
+
events.forEach((event) => {
|
|
16
|
+
value.off(event, handler);
|
|
17
|
+
});
|
|
18
|
+
};
|
|
19
|
+
}, [
|
|
20
|
+
value,
|
|
21
|
+
events,
|
|
22
|
+
trigger
|
|
23
|
+
]);
|
|
24
|
+
return value;
|
|
25
|
+
};
|
|
26
|
+
function createChimeraStoreHooks(store, createQueryBuilder) {
|
|
27
|
+
createQueryBuilder ||= () => new DefaultChimeraQueryBuilder();
|
|
28
|
+
return {
|
|
29
|
+
[`useChimera${capitalize(store.name)}Store`]: () => useSubscribedValue(store, CHIMERA_ENTITY_STORE_UPDATE_EVENTS),
|
|
30
|
+
[`useChimera${capitalize(store.name)}Collection`]: (params, deps) => {
|
|
31
|
+
const oldDeps = useRef(deps);
|
|
32
|
+
if (oldDeps.current && !deps || !oldDeps.current && deps) console.warn(`useChimera${capitalize(store.name)}Collection deps is not a reactive param!\nUse deps if you want to control dependencies manually.
|
|
33
|
+
Omit it if you already have a stable reference to params`);
|
|
34
|
+
oldDeps.current = deps;
|
|
35
|
+
const stableParams = useMemo(() => normalizeParams(createQueryBuilder, params), deps ? deps : [params]);
|
|
36
|
+
return useSubscribedValue(useMemo(() => store.getCollection(stableParams), [store, stableParams]), CHIMERA_COLLECTION_UPDATE_EVENTS);
|
|
37
|
+
},
|
|
38
|
+
[`useChimera${capitalize(store.name)}Item`]: (id, meta) => useSubscribedValue(useMemo(() => store.getItem(id, meta), [
|
|
39
|
+
store,
|
|
40
|
+
id,
|
|
41
|
+
meta
|
|
42
|
+
]), CHIMERA_ITEM_UPDATE_EVENTS)
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
//#endregion
|
|
47
|
+
export { createChimeraStoreHooks };
|
|
48
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../hooks.ts"],"sourcesContent":["import {\n\ttype AnyChimeraParams,\n\tCHIMERA_COLLECTION_UPDATE_EVENTS,\n\tCHIMERA_ENTITY_STORE_UPDATE_EVENTS,\n\tCHIMERA_ITEM_UPDATE_EVENTS,\n\tnormalizeParams,\n} from \"@hf-chimera/adapters-shared\";\nimport type { ChimeraQueryBuilder } from \"@hf-chimera/query-builder\";\nimport { DefaultChimeraQueryBuilder } from \"@hf-chimera/query-builder\";\nimport type {\n\tAnyChimeraEntityStore,\n\tAnyChimeraEventEmitter,\n\tChimeraCollectionQuery,\n\tChimeraEntityId,\n\tChimeraEntityStoreEntity,\n\tChimeraEntityStoreName,\n\tChimeraEntityStoreOperatorsMap,\n\tChimeraEventEmitterEventNames,\n\tChimeraItemQuery,\n} from \"@hf-chimera/store\";\nimport { useEffect, useMemo, useRef, useState } from \"react\";\n\ntype ChimeraHooks<\n\tTStore extends AnyChimeraEntityStore,\n\tTQueryBuilder extends ChimeraQueryBuilder<TStore>,\n\tTEntityName extends ChimeraEntityStoreName<TStore> = ChimeraEntityStoreName<TStore>,\n> = {\n\t[K in TEntityName as `useChimera${Capitalize<K>}Store`]: () => TStore;\n} & {\n\t[K in TEntityName as `useChimera${Capitalize<K>}Collection`]: <Meta = any>(\n\t\tparams: AnyChimeraParams<TStore, Meta, Extract<TQueryBuilder, ChimeraQueryBuilder<TStore>>>,\n\t\tdeps?: unknown[],\n\t) => ChimeraCollectionQuery<TEntityName, ChimeraEntityStoreEntity<TStore>, ChimeraEntityStoreOperatorsMap<TStore>>;\n} & {\n\t[K in TEntityName as `useChimera${Capitalize<K>}Item`]: <Meta = any>(\n\t\tid: ChimeraEntityId,\n\t\tmeta?: Meta,\n\t) => ChimeraItemQuery<TEntityName, ChimeraEntityStoreEntity<TStore>>;\n};\n\nconst capitalize = <T extends string>(s: T): Capitalize<T> =>\n\ts !== \"\" ? (((s[0] as string).toUpperCase() + s.slice(1)) as Capitalize<T>) : (\"\" as Capitalize<T>);\n\nconst useSubscribedValue = <TEventEmitter extends AnyChimeraEventEmitter>(\n\tvalue: TEventEmitter,\n\tevents: ChimeraEventEmitterEventNames<TEventEmitter>[],\n): TEventEmitter => {\n\tconst trigger = useState(() => ({}))[1];\n\tuseEffect(() => {\n\t\tconst handler = () => trigger({});\n\t\tevents.forEach((event) => {\n\t\t\tvalue.on(event, handler);\n\t\t});\n\t\treturn () => {\n\t\t\tevents.forEach((event) => {\n\t\t\t\tvalue.off(event, handler);\n\t\t\t});\n\t\t};\n\t}, [value, events, trigger]);\n\treturn value;\n};\n\nexport function createChimeraStoreHooks<TStore extends AnyChimeraEntityStore>(\n\tstore: TStore,\n): ChimeraHooks<TStore, DefaultChimeraQueryBuilder<TStore>>;\nexport function createChimeraStoreHooks<\n\tTStore extends AnyChimeraEntityStore,\n\tTQueryBuilder extends ChimeraQueryBuilder<TStore>,\n>(store: TStore, createQueryBuilder: () => TQueryBuilder): ChimeraHooks<TStore, TQueryBuilder>;\nexport function createChimeraStoreHooks<\n\tTStore extends AnyChimeraEntityStore,\n\tTQueryBuilder extends ChimeraQueryBuilder<TStore>,\n>(store: TStore, createQueryBuilder?: () => TQueryBuilder): ChimeraHooks<TStore, TQueryBuilder> {\n\tcreateQueryBuilder ||= () => new DefaultChimeraQueryBuilder() as unknown as TQueryBuilder;\n\n\treturn {\n\t\t[`useChimera${capitalize(store.name)}Store`]: () => useSubscribedValue(store, CHIMERA_ENTITY_STORE_UPDATE_EVENTS),\n\t\t[`useChimera${capitalize(store.name)}Collection`]: <TMeta = any>(\n\t\t\tparams: AnyChimeraParams<TStore, TMeta, Extract<TQueryBuilder, ChimeraQueryBuilder<TStore>>>,\n\t\t\tdeps?: unknown[],\n\t\t) => {\n\t\t\tconst oldDeps = useRef(deps);\n\t\t\tif ((oldDeps.current && !deps) || (!oldDeps.current && deps)) {\n\t\t\t\tconsole.warn(\n\t\t\t\t\t`useChimera${capitalize(store.name)}Collection deps is not a reactive param!\\n` +\n\t\t\t\t\t\t\"Use deps if you want to control dependencies manually.\\n\" +\n\t\t\t\t\t\t\"Omit it if you already have a stable reference to params\",\n\t\t\t\t);\n\t\t\t}\n\t\t\toldDeps.current = deps;\n\n\t\t\tconst stableParams = useMemo(\n\t\t\t\t() => normalizeParams(createQueryBuilder, params),\n\t\t\t\t// biome-ignore lint/correctness/useExhaustiveDependencies: Very unlikely it will be changed over time, anyway warning for this already added.\n\t\t\t\tdeps ? deps : [params],\n\t\t\t);\n\n\t\t\treturn useSubscribedValue(\n\t\t\t\tuseMemo(() => store.getCollection(stableParams), [store, stableParams]),\n\t\t\t\tCHIMERA_COLLECTION_UPDATE_EVENTS,\n\t\t\t);\n\t\t},\n\t\t[`useChimera${capitalize(store.name)}Item`]: <Meta = any>(id: ChimeraEntityId, meta?: Meta) =>\n\t\t\tuseSubscribedValue(\n\t\t\t\tuseMemo(() => store.getItem(id, meta), [store, id, meta]),\n\t\t\t\tCHIMERA_ITEM_UPDATE_EVENTS,\n\t\t\t),\n\t} as ChimeraHooks<TStore, TQueryBuilder>;\n}\n"],"mappings":";;;;;AAwCA,MAAM,cAAgC,MACrC,MAAM,KAAQ,EAAE,GAAc,aAAa,GAAG,EAAE,MAAM,EAAE,GAAuB;AAEhF,MAAM,sBACL,OACA,WACmB;CACnB,MAAM,UAAU,gBAAgB,EAAE,EAAE,CAAC;AACrC,iBAAgB;EACf,MAAM,gBAAgB,QAAQ,EAAE,CAAC;AACjC,SAAO,SAAS,UAAU;AACzB,SAAM,GAAG,OAAO,QAAQ;IACvB;AACF,eAAa;AACZ,UAAO,SAAS,UAAU;AACzB,UAAM,IAAI,OAAO,QAAQ;KACxB;;IAED;EAAC;EAAO;EAAQ;EAAQ,CAAC;AAC5B,QAAO;;AAUR,SAAgB,wBAGd,OAAe,oBAA+E;AAC/F,8BAA6B,IAAI,4BAA4B;AAE7D,QAAO;GACL,aAAa,WAAW,MAAM,KAAK,CAAC,eAAe,mBAAmB,OAAO,mCAAmC;GAChH,aAAa,WAAW,MAAM,KAAK,CAAC,eACpC,QACA,SACI;GACJ,MAAM,UAAU,OAAO,KAAK;AAC5B,OAAK,QAAQ,WAAW,CAAC,QAAU,CAAC,QAAQ,WAAW,KACtD,SAAQ,KACP,aAAa,WAAW,MAAM,KAAK,CAAC;0DAGpC;AAEF,WAAQ,UAAU;GAElB,MAAM,eAAe,cACd,gBAAgB,oBAAoB,OAAO,EAEjD,OAAO,OAAO,CAAC,OAAO,CACtB;AAED,UAAO,mBACN,cAAc,MAAM,cAAc,aAAa,EAAE,CAAC,OAAO,aAAa,CAAC,EACvE,iCACA;;GAED,aAAa,WAAW,MAAM,KAAK,CAAC,SAAqB,IAAqB,SAC9E,mBACC,cAAc,MAAM,QAAQ,IAAI,KAAK,EAAE;GAAC;GAAO;GAAI;GAAK,CAAC,EACzD,2BACA;EACF"}
|
package/package.json
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@hf-chimera/react",
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "Cross-end reactivity API - React adapter",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"author": "hewston",
|
|
7
|
+
"keywords": [
|
|
8
|
+
"reactivity",
|
|
9
|
+
"reactive",
|
|
10
|
+
"react",
|
|
11
|
+
"hooks",
|
|
12
|
+
"typescript",
|
|
13
|
+
"state-management"
|
|
14
|
+
],
|
|
15
|
+
"type": "module",
|
|
16
|
+
"types": "./dist/index.d.ts",
|
|
17
|
+
"main": "./dist/index.cjs",
|
|
18
|
+
"module": "./dist/index.js",
|
|
19
|
+
"exports": {
|
|
20
|
+
".": {
|
|
21
|
+
"types": "./dist/index.d.ts",
|
|
22
|
+
"require": "./dist/index.cjs",
|
|
23
|
+
"import": "./dist/index.js"
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
"files": [
|
|
27
|
+
"dist"
|
|
28
|
+
],
|
|
29
|
+
"bugs": {
|
|
30
|
+
"url": "https://github.com/hf-chimera/store/issues"
|
|
31
|
+
},
|
|
32
|
+
"repository": {
|
|
33
|
+
"url": "https://github.com/hf-chimera/store"
|
|
34
|
+
},
|
|
35
|
+
"peerDependencies": {
|
|
36
|
+
"@hf-chimera/store": "workspace:^",
|
|
37
|
+
"@hf-chimera/adapters-shared": "workspace:^",
|
|
38
|
+
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
|
|
39
|
+
},
|
|
40
|
+
"scripts": {
|
|
41
|
+
"build": "tsdown",
|
|
42
|
+
"changeset:patch": "node ../../../scripts/changeset-patch.mjs @hf-chimera/react"
|
|
43
|
+
},
|
|
44
|
+
"publishConfig": {
|
|
45
|
+
"access": "public",
|
|
46
|
+
"provenance": true
|
|
47
|
+
}
|
|
48
|
+
}
|