@docstack/react 0.0.7 → 0.0.9
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/lib/components/StackProvider/index.js +77 -0
- package/lib/components/index.js +1 -0
- package/lib/hooks/class.js +370 -0
- package/lib/hooks/domain.js +366 -0
- package/lib/hooks/index.js +171 -0
- package/lib/index.js +8 -3
- package/package.json +4 -4
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { createContext, useContext, useRef, useCallback, useEffect, useState } from 'react';
|
|
3
|
+
import { DocStack } from '@docstack/client'; // Import your DocStack class
|
|
4
|
+
// You can give it a default value, e.g., null, which can be checked later.
|
|
5
|
+
/**
|
|
6
|
+
* Context object for the DocStack instance.
|
|
7
|
+
* It provides the current DocStack instance or null if not initialized.
|
|
8
|
+
*/
|
|
9
|
+
export const DocStackContext = createContext(null);
|
|
10
|
+
/**
|
|
11
|
+
* Hook to access the DocStack instance.
|
|
12
|
+
*
|
|
13
|
+
* @returns The current {@link DocStack} instance or null if not yet initialized.
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* ```tsx
|
|
17
|
+
* const MyComponent = () => {
|
|
18
|
+
* const docStack = useDocStack();
|
|
19
|
+
*
|
|
20
|
+
* if (!docStack) return <div>Loading...</div>;
|
|
21
|
+
*
|
|
22
|
+
* return <div>Connected to {docStack.getStacks().length} stacks</div>;
|
|
23
|
+
* };
|
|
24
|
+
* ```
|
|
25
|
+
*/
|
|
26
|
+
export const useDocStack = () => {
|
|
27
|
+
return useContext(DocStackContext);
|
|
28
|
+
};
|
|
29
|
+
/**
|
|
30
|
+
* A provider component that initializes the DocStack client and makes it available
|
|
31
|
+
* to child components via the {@link useDocStack} hook.
|
|
32
|
+
* It handles the asynchronous initialization of the stack(s).
|
|
33
|
+
*
|
|
34
|
+
* @example
|
|
35
|
+
* ```tsx
|
|
36
|
+
* import { StackProvider } from '@docstack/react';
|
|
37
|
+
*
|
|
38
|
+
* const App = () => (
|
|
39
|
+
* <StackProvider config={[{ name: 'my-db' }]}>
|
|
40
|
+
* <MyApp />
|
|
41
|
+
* </StackProvider>
|
|
42
|
+
* );
|
|
43
|
+
* ```
|
|
44
|
+
*/
|
|
45
|
+
const StackProvider = (props) => {
|
|
46
|
+
const { config, children, credentials } = props;
|
|
47
|
+
// Use a ref to store the DocStack instance
|
|
48
|
+
const docStackRef = useRef(null);
|
|
49
|
+
const [docStack, setDocStack] = useState(null);
|
|
50
|
+
const setsDocStackWhenReady = useCallback(() => {
|
|
51
|
+
setDocStack(docStackRef.current);
|
|
52
|
+
}, []);
|
|
53
|
+
useEffect(() => {
|
|
54
|
+
if (docStackRef.current === null && config.length) {
|
|
55
|
+
console.log("DocStack provider - init instance", { config });
|
|
56
|
+
const mergedConfig = config.map((cfg, idx) => {
|
|
57
|
+
const cred = Array.isArray(credentials) ? credentials[idx] : credentials;
|
|
58
|
+
if (typeof cfg === "string") {
|
|
59
|
+
return cred ? { connection: cfg, credentials: cred } : cfg;
|
|
60
|
+
}
|
|
61
|
+
return cred ? Object.assign(Object.assign({}, cfg), { credentials: cred }) : cfg;
|
|
62
|
+
});
|
|
63
|
+
const instance = new DocStack(...mergedConfig);
|
|
64
|
+
docStackRef.current = instance;
|
|
65
|
+
docStackRef.current.addEventListener("ready", setsDocStackWhenReady);
|
|
66
|
+
}
|
|
67
|
+
// Optional: Cleanup function to remove listeners
|
|
68
|
+
return () => {
|
|
69
|
+
if (docStackRef.current) {
|
|
70
|
+
// docStackRef.current.removeEventListener("ready", setsDocStackWhenReady);
|
|
71
|
+
// docStackRef.current.getStore().removeAllListeners();
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
}, [config, credentials, setsDocStackWhenReady]);
|
|
75
|
+
return (_jsx(DocStackContext.Provider, { value: docStack, children: children }));
|
|
76
|
+
};
|
|
77
|
+
export default StackProvider;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";
|
|
@@ -0,0 +1,370 @@
|
|
|
1
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
2
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
3
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
4
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
5
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
6
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
7
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
|
+
});
|
|
9
|
+
};
|
|
10
|
+
import { useContext, useCallback, useEffect, useRef, useState } from "react";
|
|
11
|
+
import { DocStackContext } from "../components/StackProvider/index.js";
|
|
12
|
+
import { Class } from "@docstack/client";
|
|
13
|
+
/**
|
|
14
|
+
* Hook to create a new Class in a specific stack.
|
|
15
|
+
*
|
|
16
|
+
* @param stack - The name of the stack to create the class in.
|
|
17
|
+
* @returns A callback function to create the class.
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* ```tsx
|
|
21
|
+
* const MyComponent = () => {
|
|
22
|
+
* const createClass = useClassCreate('my-stack');
|
|
23
|
+
*
|
|
24
|
+
* const handleCreate = async () => {
|
|
25
|
+
* const newClass = await createClass('NewClass', 'Description of new class');
|
|
26
|
+
* if (newClass) {
|
|
27
|
+
* console.log('Class created:', newClass.name);
|
|
28
|
+
* }
|
|
29
|
+
* };
|
|
30
|
+
*
|
|
31
|
+
* return <button onClick={handleCreate}>Create Class</button>;
|
|
32
|
+
* };
|
|
33
|
+
* ```
|
|
34
|
+
*/
|
|
35
|
+
export const useClassCreate = (stack) => {
|
|
36
|
+
const docStack = useContext(DocStackContext);
|
|
37
|
+
return useCallback((className, classDesc) => __awaiter(void 0, void 0, void 0, function* () {
|
|
38
|
+
try {
|
|
39
|
+
if (!docStack) {
|
|
40
|
+
// Handle the case where the provider is not yet initialized or missing
|
|
41
|
+
// You could throw an error or return an empty state.
|
|
42
|
+
console.error('useClassCreate must be used within a DocStackProvider.');
|
|
43
|
+
// setLoading(false);
|
|
44
|
+
return Promise.resolve(null);
|
|
45
|
+
}
|
|
46
|
+
// Run the initial query
|
|
47
|
+
const stackInstance = docStack.getStack(stack);
|
|
48
|
+
if (stackInstance) {
|
|
49
|
+
const classObj_ = yield Class.create(stackInstance, className, "class", classDesc);
|
|
50
|
+
yield stackInstance.addClass(classObj_);
|
|
51
|
+
return classObj_;
|
|
52
|
+
}
|
|
53
|
+
return null;
|
|
54
|
+
}
|
|
55
|
+
catch (err) {
|
|
56
|
+
// setError(err);
|
|
57
|
+
console.error(err);
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
}), [docStack, stack]);
|
|
61
|
+
};
|
|
62
|
+
/**
|
|
63
|
+
* Hook to retrieve a list of classes from a stack based on a selector.
|
|
64
|
+
* Maintains a real-time list of classes matching the selector.
|
|
65
|
+
*
|
|
66
|
+
* @param stack - The name of the stack to query.
|
|
67
|
+
* @param selector - Mango selector to filter classes.
|
|
68
|
+
* @returns Object containing the list of classes, loading state, and error.
|
|
69
|
+
*
|
|
70
|
+
* @example
|
|
71
|
+
* ```tsx
|
|
72
|
+
* const ClassList = () => {
|
|
73
|
+
* const { classList, loading } = useClassList('my-stack', {
|
|
74
|
+
* name: { $regex: '^User' }
|
|
75
|
+
* });
|
|
76
|
+
*
|
|
77
|
+
* if (loading) return <div>Loading...</div>;
|
|
78
|
+
*
|
|
79
|
+
* return (
|
|
80
|
+
* <ul>
|
|
81
|
+
* {classList.map(cls => <li key={cls.id}>{cls.name}</li>)}
|
|
82
|
+
* </ul>
|
|
83
|
+
* );
|
|
84
|
+
* };
|
|
85
|
+
* ```
|
|
86
|
+
*/
|
|
87
|
+
export const useClassList = (stack, selector) => {
|
|
88
|
+
const docStack = useContext(DocStackContext);
|
|
89
|
+
const [originClass, setOriginClass] = useState();
|
|
90
|
+
const [classList, setClassList] = useState([]);
|
|
91
|
+
const classListRef = useRef([]);
|
|
92
|
+
const [loading, setLoading] = useState(true);
|
|
93
|
+
const [error, setError] = useState(null);
|
|
94
|
+
useEffect(() => {
|
|
95
|
+
// Only run if the docStack is available and a className is provided
|
|
96
|
+
if (!docStack) {
|
|
97
|
+
setLoading(false);
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
const fetchClass = () => __awaiter(void 0, void 0, void 0, function* () {
|
|
101
|
+
setLoading(true);
|
|
102
|
+
setError(null);
|
|
103
|
+
try {
|
|
104
|
+
const stackInstance = docStack.getStack(stack);
|
|
105
|
+
if (stackInstance) {
|
|
106
|
+
const retrievedClass = yield stackInstance.getClass('class');
|
|
107
|
+
if (retrievedClass) {
|
|
108
|
+
setOriginClass(retrievedClass);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
catch (err) {
|
|
113
|
+
setError(err);
|
|
114
|
+
setLoading(false);
|
|
115
|
+
}
|
|
116
|
+
});
|
|
117
|
+
fetchClass();
|
|
118
|
+
return () => {
|
|
119
|
+
// clean what?
|
|
120
|
+
};
|
|
121
|
+
}, [docStack, stack]); // Dependency on docStack and stack
|
|
122
|
+
useEffect(() => {
|
|
123
|
+
if (!originClass) {
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
const runQueryAndListen = () => __awaiter(void 0, void 0, void 0, function* () {
|
|
127
|
+
setLoading(true);
|
|
128
|
+
try {
|
|
129
|
+
const initialClassModelList = yield originClass.getCards(selector);
|
|
130
|
+
const initialClassList = [];
|
|
131
|
+
const stackInstance = docStack.getStack(stack);
|
|
132
|
+
for (const cls of initialClassModelList) {
|
|
133
|
+
const classInstance = yield Class.buildFromModel(stackInstance, cls);
|
|
134
|
+
initialClassList.push(classInstance);
|
|
135
|
+
}
|
|
136
|
+
classListRef.current = initialClassList;
|
|
137
|
+
setClassList(classListRef.current);
|
|
138
|
+
}
|
|
139
|
+
catch (err) {
|
|
140
|
+
setError(err);
|
|
141
|
+
}
|
|
142
|
+
finally {
|
|
143
|
+
setLoading(false);
|
|
144
|
+
}
|
|
145
|
+
const changeListener = (change) => {
|
|
146
|
+
const doc = change.detail.doc;
|
|
147
|
+
console.log("useClassDocs - detail", { detail: change.detail });
|
|
148
|
+
if (!doc.active) {
|
|
149
|
+
// A doc was deleted
|
|
150
|
+
console.log("useClassDocs - a doc was deleted", { doc });
|
|
151
|
+
const docIndex = classListRef.current.findIndex((d) => d.id == doc._id);
|
|
152
|
+
if (docIndex != -1) {
|
|
153
|
+
classListRef.current = [
|
|
154
|
+
...classListRef.current.slice(0, docIndex),
|
|
155
|
+
...classListRef.current.slice(docIndex + 1, classListRef.current.length)
|
|
156
|
+
];
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
else {
|
|
160
|
+
// A doc was changed or added
|
|
161
|
+
const docIndex = classListRef.current.findIndex((d) => d.id == doc._id);
|
|
162
|
+
if (docIndex != -1) {
|
|
163
|
+
// A doc was changed
|
|
164
|
+
classListRef.current = [
|
|
165
|
+
...classListRef.current.slice(0, docIndex),
|
|
166
|
+
doc,
|
|
167
|
+
...classListRef.current.slice(docIndex + 1, classListRef.current.length)
|
|
168
|
+
];
|
|
169
|
+
}
|
|
170
|
+
else {
|
|
171
|
+
// A doc was added
|
|
172
|
+
classListRef.current.push(doc);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
setClassList([...classListRef.current]);
|
|
176
|
+
};
|
|
177
|
+
originClass.addEventListener('doc', changeListener);
|
|
178
|
+
return () => {
|
|
179
|
+
originClass.removeEventListener('doc', changeListener);
|
|
180
|
+
};
|
|
181
|
+
});
|
|
182
|
+
runQueryAndListen();
|
|
183
|
+
}, [originClass, JSON.stringify(selector)]); // Dependency on classObj and query
|
|
184
|
+
return { classList, loading, error };
|
|
185
|
+
};
|
|
186
|
+
/**
|
|
187
|
+
* Hook to retrieve a single Class instance by name.
|
|
188
|
+
*
|
|
189
|
+
* @param stack - The name of the stack.
|
|
190
|
+
* @param className - The name of the class to retrieve.
|
|
191
|
+
* @returns Object containing the Class instance, loading state, and error.
|
|
192
|
+
*
|
|
193
|
+
* @example
|
|
194
|
+
* ```tsx
|
|
195
|
+
* const ClassDetails = () => {
|
|
196
|
+
* const { classObj, loading } = useClass('my-stack', 'User');
|
|
197
|
+
*
|
|
198
|
+
* if (loading) return <div>Loading...</div>;
|
|
199
|
+
* if (!classObj) return <div>Class not found</div>;
|
|
200
|
+
*
|
|
201
|
+
* return <div>Class Description: {classObj.description}</div>;
|
|
202
|
+
* };
|
|
203
|
+
* ```
|
|
204
|
+
*/
|
|
205
|
+
export const useClass = (stack, className) => {
|
|
206
|
+
const docStack = useContext(DocStackContext);
|
|
207
|
+
const [loading, setLoading] = useState(false);
|
|
208
|
+
const [error, setError] = useState();
|
|
209
|
+
const [classObj, setClass] = useState();
|
|
210
|
+
const reqRef = useRef(false);
|
|
211
|
+
useEffect(() => {
|
|
212
|
+
if (!docStack) {
|
|
213
|
+
// Handle the case where the provider is not yet initialized or missing
|
|
214
|
+
// You could throw an error or return an empty state.
|
|
215
|
+
console.error('useClass must be used within a DocStackProvider.');
|
|
216
|
+
setLoading(false);
|
|
217
|
+
return;
|
|
218
|
+
}
|
|
219
|
+
const fetchClass = () => __awaiter(void 0, void 0, void 0, function* () {
|
|
220
|
+
try {
|
|
221
|
+
const stackInstance = docStack.getStack(stack);
|
|
222
|
+
if (stackInstance) {
|
|
223
|
+
const res = yield stackInstance.getClass(className);
|
|
224
|
+
// TODO: manage class model (schema!) updates
|
|
225
|
+
if (res) {
|
|
226
|
+
setClass(res);
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
catch (e) {
|
|
231
|
+
setError(e);
|
|
232
|
+
}
|
|
233
|
+
finally {
|
|
234
|
+
setLoading(false);
|
|
235
|
+
}
|
|
236
|
+
});
|
|
237
|
+
if (!reqRef.current) {
|
|
238
|
+
reqRef.current = true;
|
|
239
|
+
setLoading(true);
|
|
240
|
+
fetchClass();
|
|
241
|
+
}
|
|
242
|
+
return () => {
|
|
243
|
+
// reqRef.current = false;
|
|
244
|
+
};
|
|
245
|
+
}, [docStack, stack, className]);
|
|
246
|
+
return { loading, error, classObj };
|
|
247
|
+
};
|
|
248
|
+
/**
|
|
249
|
+
* Hook to retrieve documents (cards) of a specific class.
|
|
250
|
+
* Maintains a real-time list of documents matching the query.
|
|
251
|
+
*
|
|
252
|
+
* @param stack - The name of the stack.
|
|
253
|
+
* @param className - The class name to fetch documents for.
|
|
254
|
+
* @param query - Optional Mango selector to filter documents.
|
|
255
|
+
* @returns Object containing the list of documents, loading state, and error.
|
|
256
|
+
*
|
|
257
|
+
* @example
|
|
258
|
+
* ```tsx
|
|
259
|
+
* const UserList = () => {
|
|
260
|
+
* const { docs, loading } = useClassDocs('my-stack', 'User', {
|
|
261
|
+
* age: { $gt: 18 }
|
|
262
|
+
* });
|
|
263
|
+
*
|
|
264
|
+
* if (loading) return <div>Loading...</div>;
|
|
265
|
+
*
|
|
266
|
+
* return (
|
|
267
|
+
* <ul>
|
|
268
|
+
* {docs.map(doc => <li key={doc._id}>{doc.name}</li>)}
|
|
269
|
+
* </ul>
|
|
270
|
+
* );
|
|
271
|
+
* };
|
|
272
|
+
* ```
|
|
273
|
+
*/
|
|
274
|
+
export const useClassDocs = (stack, className, query = {}) => {
|
|
275
|
+
const docStack = useContext(DocStackContext);
|
|
276
|
+
const [classObj, setClass] = useState();
|
|
277
|
+
const [docs, setDocs] = useState([]);
|
|
278
|
+
const docsRef = useRef([]);
|
|
279
|
+
const [loading, setLoading] = useState(true);
|
|
280
|
+
const [error, setError] = useState(null);
|
|
281
|
+
useEffect(() => {
|
|
282
|
+
// Only run if the docStack is available and a className is provided
|
|
283
|
+
if (!docStack || !className) {
|
|
284
|
+
setLoading(false);
|
|
285
|
+
return;
|
|
286
|
+
}
|
|
287
|
+
const fetchClass = () => __awaiter(void 0, void 0, void 0, function* () {
|
|
288
|
+
setLoading(true);
|
|
289
|
+
setError(null);
|
|
290
|
+
try {
|
|
291
|
+
const stackInstance = docStack.getStack(stack);
|
|
292
|
+
if (stackInstance) {
|
|
293
|
+
const retrievedClass = yield stackInstance.getClass(className);
|
|
294
|
+
if (retrievedClass) {
|
|
295
|
+
setClass(retrievedClass);
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
catch (err) {
|
|
300
|
+
setError(err);
|
|
301
|
+
setLoading(false);
|
|
302
|
+
}
|
|
303
|
+
});
|
|
304
|
+
fetchClass();
|
|
305
|
+
return () => {
|
|
306
|
+
// clean what?
|
|
307
|
+
};
|
|
308
|
+
}, [docStack, stack, className]); // Dependency on docStack and className
|
|
309
|
+
useEffect(() => {
|
|
310
|
+
if (!classObj) {
|
|
311
|
+
return;
|
|
312
|
+
}
|
|
313
|
+
const runQueryAndListen = () => __awaiter(void 0, void 0, void 0, function* () {
|
|
314
|
+
setLoading(true);
|
|
315
|
+
try {
|
|
316
|
+
debugger;
|
|
317
|
+
const initialDocs = yield classObj.getCards(query);
|
|
318
|
+
docsRef.current = initialDocs;
|
|
319
|
+
setDocs(docsRef.current);
|
|
320
|
+
}
|
|
321
|
+
catch (err) {
|
|
322
|
+
setError(err);
|
|
323
|
+
}
|
|
324
|
+
finally {
|
|
325
|
+
setLoading(false);
|
|
326
|
+
}
|
|
327
|
+
const changeListener = (change) => {
|
|
328
|
+
const doc = change.detail.doc;
|
|
329
|
+
console.log("useClassDocs - detail", { detail: change.detail });
|
|
330
|
+
if (!doc.active) {
|
|
331
|
+
// A doc was deleted
|
|
332
|
+
console.log("useClassDocs - a doc was deleted", { doc });
|
|
333
|
+
const docIndex = docsRef.current.findIndex((d) => d._id == doc._id);
|
|
334
|
+
if (docIndex != -1) {
|
|
335
|
+
docsRef.current = [
|
|
336
|
+
...docsRef.current.slice(0, docIndex),
|
|
337
|
+
...docsRef.current.slice(docIndex + 1, docsRef.current.length)
|
|
338
|
+
];
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
else {
|
|
342
|
+
// A doc was changed or added
|
|
343
|
+
console.log("useClassDocs - a doc was changed or added", { doc });
|
|
344
|
+
const docIndex = docsRef.current.findIndex((d) => d._id == doc._id);
|
|
345
|
+
if (docIndex != -1) {
|
|
346
|
+
// A doc was changed
|
|
347
|
+
console.log("useClassDocs - a doc was changed", { doc });
|
|
348
|
+
docsRef.current = [
|
|
349
|
+
...docsRef.current.slice(0, docIndex),
|
|
350
|
+
doc,
|
|
351
|
+
...docsRef.current.slice(docIndex + 1, docsRef.current.length)
|
|
352
|
+
];
|
|
353
|
+
}
|
|
354
|
+
else {
|
|
355
|
+
// A doc was added
|
|
356
|
+
console.log("useClassDocs - a doc was added", { doc });
|
|
357
|
+
docsRef.current.push(doc);
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
setDocs([...docsRef.current]);
|
|
361
|
+
};
|
|
362
|
+
classObj.addEventListener('doc', changeListener);
|
|
363
|
+
return () => {
|
|
364
|
+
classObj.removeEventListener('doc', changeListener);
|
|
365
|
+
};
|
|
366
|
+
});
|
|
367
|
+
runQueryAndListen();
|
|
368
|
+
}, [classObj, JSON.stringify(query)]); // Dependency on classObj and query
|
|
369
|
+
return { docs, loading, error };
|
|
370
|
+
};
|
|
@@ -0,0 +1,366 @@
|
|
|
1
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
2
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
3
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
4
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
5
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
6
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
7
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
|
+
});
|
|
9
|
+
};
|
|
10
|
+
import { useContext, useCallback, useEffect, useRef, useState } from "react";
|
|
11
|
+
import { DocStackContext } from "../components/StackProvider/index.js";
|
|
12
|
+
import { Domain } from "@docstack/shared";
|
|
13
|
+
/**
|
|
14
|
+
* Hook to create a new Domain in a specific stack.
|
|
15
|
+
*
|
|
16
|
+
* @param stack - The name of the stack to create the domain in.
|
|
17
|
+
* @returns A callback function to create the domain.
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* ```tsx
|
|
21
|
+
* const CreateDomain = () => {
|
|
22
|
+
* const createDomain = useDomainCreate('my-stack');
|
|
23
|
+
* // Assume sourceClass and targetClass are available Class instances
|
|
24
|
+
*
|
|
25
|
+
* const handleCreate = async () => {
|
|
26
|
+
* const newDomain = await createDomain(
|
|
27
|
+
* 'UserProjects',
|
|
28
|
+
* '1:N',
|
|
29
|
+
* userClass,
|
|
30
|
+
* projectClass,
|
|
31
|
+
* 'User has many projects'
|
|
32
|
+
* );
|
|
33
|
+
* };
|
|
34
|
+
*
|
|
35
|
+
* return <button onClick={handleCreate}>Create Domain</button>;
|
|
36
|
+
* };
|
|
37
|
+
* ```
|
|
38
|
+
*/
|
|
39
|
+
export const useDomainCreate = (stack) => {
|
|
40
|
+
const docStack = useContext(DocStackContext);
|
|
41
|
+
return useCallback((domainName, cardinality, sourceClass, targetClass, domainDesc) => __awaiter(void 0, void 0, void 0, function* () {
|
|
42
|
+
try {
|
|
43
|
+
if (!docStack) {
|
|
44
|
+
// Handle the case where the provider is not yet initialized or missing
|
|
45
|
+
// You could throw an error or return an empty state.
|
|
46
|
+
console.error('useDomainCreate must be used within a DocStackProvider.');
|
|
47
|
+
// setLoading(false);
|
|
48
|
+
return Promise.resolve(null);
|
|
49
|
+
}
|
|
50
|
+
// Run the initial query
|
|
51
|
+
const stackInstance = docStack.getStack(stack);
|
|
52
|
+
if (stackInstance) {
|
|
53
|
+
const domain = yield Domain.create(stackInstance, null, domainName, "domain", cardinality, sourceClass, targetClass, domainDesc);
|
|
54
|
+
return domain;
|
|
55
|
+
}
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
catch (err) {
|
|
59
|
+
// setError(err);
|
|
60
|
+
console.error(err);
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
}), [docStack, stack]);
|
|
64
|
+
};
|
|
65
|
+
/**
|
|
66
|
+
* Hook to retrieve a list of domains from a stack based on a selector.
|
|
67
|
+
* Maintains a real-time list of domains matching the selector.
|
|
68
|
+
*
|
|
69
|
+
* @param stack - The name of the stack to query.
|
|
70
|
+
* @param selector - Mango selector to filter domains.
|
|
71
|
+
* @returns Object containing the list of domains, loading state, and error.
|
|
72
|
+
*
|
|
73
|
+
* @example
|
|
74
|
+
* ```tsx
|
|
75
|
+
* const DomainList = () => {
|
|
76
|
+
* const { domainList, loading } = useDomainList('my-stack', {
|
|
77
|
+
* relation: { $eq: '1:N' }
|
|
78
|
+
* });
|
|
79
|
+
*
|
|
80
|
+
* if (loading) return <div>Loading...</div>;
|
|
81
|
+
*
|
|
82
|
+
* return (
|
|
83
|
+
* <ul>
|
|
84
|
+
* {domainList.map(d => <li key={d.id}>{d.name} ({d.relation})</li>)}
|
|
85
|
+
* </ul>
|
|
86
|
+
* );
|
|
87
|
+
* };
|
|
88
|
+
* ```
|
|
89
|
+
*/
|
|
90
|
+
export const useDomainList = (stack, selector) => {
|
|
91
|
+
const docStack = useContext(DocStackContext);
|
|
92
|
+
const [originClass, setOriginClass] = useState();
|
|
93
|
+
const [domainList, setDomainList] = useState([]);
|
|
94
|
+
const domainListRef = useRef([]);
|
|
95
|
+
const [loading, setLoading] = useState(true);
|
|
96
|
+
const [error, setError] = useState(null);
|
|
97
|
+
useEffect(() => {
|
|
98
|
+
// Only run if the docStack is available and a className is provided
|
|
99
|
+
if (!docStack) {
|
|
100
|
+
setLoading(false);
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
const fetchClass = () => __awaiter(void 0, void 0, void 0, function* () {
|
|
104
|
+
setLoading(true);
|
|
105
|
+
setError(null);
|
|
106
|
+
try {
|
|
107
|
+
const stackInstance = docStack.getStack(stack);
|
|
108
|
+
if (stackInstance) {
|
|
109
|
+
const retrievedClass = yield stackInstance.getClass('domain');
|
|
110
|
+
if (retrievedClass) {
|
|
111
|
+
setOriginClass(retrievedClass);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
catch (err) {
|
|
116
|
+
setError(err);
|
|
117
|
+
setLoading(false);
|
|
118
|
+
}
|
|
119
|
+
});
|
|
120
|
+
fetchClass();
|
|
121
|
+
return () => {
|
|
122
|
+
// clean what?
|
|
123
|
+
};
|
|
124
|
+
}, [docStack, stack]); // Dependency on docStack and stack
|
|
125
|
+
useEffect(() => {
|
|
126
|
+
if (!originClass) {
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
const runQueryAndListen = () => __awaiter(void 0, void 0, void 0, function* () {
|
|
130
|
+
setLoading(true);
|
|
131
|
+
try {
|
|
132
|
+
const stackInstance = docStack.getStack(stack);
|
|
133
|
+
const initialDomainModelList = yield originClass.getCards(selector);
|
|
134
|
+
const initDomainList = yield Promise.all(initialDomainModelList.map((dm) => __awaiter(void 0, void 0, void 0, function* () { return yield Domain.buildFromModel(stackInstance, dm); })));
|
|
135
|
+
domainListRef.current = initDomainList;
|
|
136
|
+
setDomainList(domainListRef.current);
|
|
137
|
+
}
|
|
138
|
+
catch (err) {
|
|
139
|
+
setError(err);
|
|
140
|
+
}
|
|
141
|
+
finally {
|
|
142
|
+
setLoading(false);
|
|
143
|
+
}
|
|
144
|
+
const changeListener = (change) => {
|
|
145
|
+
const doc = change.detail.doc;
|
|
146
|
+
if (!doc.active) {
|
|
147
|
+
// A doc was deleted
|
|
148
|
+
const docIndex = domainListRef.current.findIndex((d) => d.id == doc._id);
|
|
149
|
+
if (docIndex != -1) {
|
|
150
|
+
domainListRef.current = [
|
|
151
|
+
...domainListRef.current.slice(0, docIndex),
|
|
152
|
+
...domainListRef.current.slice(docIndex + 1, domainListRef.current.length)
|
|
153
|
+
];
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
else {
|
|
157
|
+
// A doc was changed or added
|
|
158
|
+
const docIndex = domainListRef.current.findIndex((d) => d.id == doc._id);
|
|
159
|
+
if (docIndex != -1) {
|
|
160
|
+
// A doc was changed
|
|
161
|
+
domainListRef.current = [
|
|
162
|
+
...domainListRef.current.slice(0, docIndex),
|
|
163
|
+
doc,
|
|
164
|
+
...domainListRef.current.slice(docIndex + 1, domainListRef.current.length)
|
|
165
|
+
];
|
|
166
|
+
}
|
|
167
|
+
else {
|
|
168
|
+
// A doc was added
|
|
169
|
+
domainListRef.current.push(doc);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
setDomainList([...domainListRef.current]);
|
|
173
|
+
};
|
|
174
|
+
originClass.addEventListener('doc', changeListener);
|
|
175
|
+
return () => {
|
|
176
|
+
originClass.removeEventListener('doc', changeListener);
|
|
177
|
+
};
|
|
178
|
+
});
|
|
179
|
+
runQueryAndListen();
|
|
180
|
+
}, [originClass, JSON.stringify(selector)]); // Dependency on classObj and query
|
|
181
|
+
return { domainList, loading, error };
|
|
182
|
+
};
|
|
183
|
+
/**
|
|
184
|
+
* Hook to retrieve a single Domain instance by name.
|
|
185
|
+
*
|
|
186
|
+
* @param stack - The name of the stack.
|
|
187
|
+
* @param domainName - The name of the domain to retrieve.
|
|
188
|
+
* @returns Object containing the Domain instance, loading state, and error.
|
|
189
|
+
*
|
|
190
|
+
* @example
|
|
191
|
+
* ```tsx
|
|
192
|
+
* const DomainDetails = () => {
|
|
193
|
+
* const { domain, loading } = useDomain('my-stack', 'UserProjects');
|
|
194
|
+
*
|
|
195
|
+
* if (loading) return <div>Loading...</div>;
|
|
196
|
+
* if (!domain) return <div>Domain not found</div>;
|
|
197
|
+
*
|
|
198
|
+
* return <div>Relation Type: {domain.relation}</div>;
|
|
199
|
+
* };
|
|
200
|
+
* ```
|
|
201
|
+
*/
|
|
202
|
+
export const useDomain = (stack, domainName) => {
|
|
203
|
+
const docStack = useContext(DocStackContext);
|
|
204
|
+
const [loading, setLoading] = useState(false);
|
|
205
|
+
const [error, setError] = useState();
|
|
206
|
+
const [domain, setDomain] = useState();
|
|
207
|
+
const reqRef = useRef(false);
|
|
208
|
+
useEffect(() => {
|
|
209
|
+
if (!docStack) {
|
|
210
|
+
// Handle the case where the provider is not yet initialized or missing
|
|
211
|
+
// You could throw an error or return an empty state.
|
|
212
|
+
console.error('useDomain must be used within a DocStackProvider.');
|
|
213
|
+
setLoading(false);
|
|
214
|
+
return;
|
|
215
|
+
}
|
|
216
|
+
const fetchClass = () => __awaiter(void 0, void 0, void 0, function* () {
|
|
217
|
+
try {
|
|
218
|
+
const stackInstance = docStack.getStack(stack);
|
|
219
|
+
if (stackInstance) {
|
|
220
|
+
const res = yield stackInstance.getDomain(domainName);
|
|
221
|
+
// TODO: manage class model (schema!) updates
|
|
222
|
+
if (res) {
|
|
223
|
+
setDomain(res);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
catch (e) {
|
|
228
|
+
setError(e);
|
|
229
|
+
}
|
|
230
|
+
finally {
|
|
231
|
+
setLoading(false);
|
|
232
|
+
}
|
|
233
|
+
});
|
|
234
|
+
if (!reqRef.current) {
|
|
235
|
+
reqRef.current = true;
|
|
236
|
+
setLoading(true);
|
|
237
|
+
fetchClass();
|
|
238
|
+
}
|
|
239
|
+
return () => {
|
|
240
|
+
// reqRef.current = false;
|
|
241
|
+
};
|
|
242
|
+
}, [docStack, stack, domainName]);
|
|
243
|
+
return { loading, error, domain };
|
|
244
|
+
};
|
|
245
|
+
/**
|
|
246
|
+
* Hook to retrieve relation documents for a specific domain.
|
|
247
|
+
* Maintains a real-time list of relations matching the query.
|
|
248
|
+
*
|
|
249
|
+
* @param stack - The name of the stack.
|
|
250
|
+
* @param domainName - The domain name to fetch relations for.
|
|
251
|
+
* @param query - Optional Mango selector to filter relations.
|
|
252
|
+
* @returns Object containing the list of relation documents, loading state, and error.
|
|
253
|
+
*
|
|
254
|
+
* @example
|
|
255
|
+
* ```tsx
|
|
256
|
+
* const ProjectTasks = () => {
|
|
257
|
+
* const { docs, loading } = useDomainRelations('my-stack', 'ProjectTasks', {
|
|
258
|
+
* sourceId: { $eq: 'Project-123' }
|
|
259
|
+
* });
|
|
260
|
+
*
|
|
261
|
+
* if (loading) return <div>Loading...</div>;
|
|
262
|
+
*
|
|
263
|
+
* return (
|
|
264
|
+
* <ul>
|
|
265
|
+
* {docs.map(rel => (
|
|
266
|
+
* <li key={rel._id}>Linked Task: {rel.targetId}</li>
|
|
267
|
+
* ))}
|
|
268
|
+
* </ul>
|
|
269
|
+
* );
|
|
270
|
+
* };
|
|
271
|
+
* ```
|
|
272
|
+
*/
|
|
273
|
+
export const useDomainRelations = (stack, domainName, query = {}) => {
|
|
274
|
+
const docStack = useContext(DocStackContext);
|
|
275
|
+
const [domain, setDomain] = useState();
|
|
276
|
+
const [docs, setDocs] = useState([]);
|
|
277
|
+
const docsRef = useRef([]);
|
|
278
|
+
const [loading, setLoading] = useState(true);
|
|
279
|
+
const [error, setError] = useState(null);
|
|
280
|
+
useEffect(() => {
|
|
281
|
+
// Only run if the docStack is available and a className is provided
|
|
282
|
+
if (!docStack || !domainName) {
|
|
283
|
+
setLoading(false);
|
|
284
|
+
return;
|
|
285
|
+
}
|
|
286
|
+
const fetchClass = () => __awaiter(void 0, void 0, void 0, function* () {
|
|
287
|
+
setLoading(true);
|
|
288
|
+
setError(null);
|
|
289
|
+
try {
|
|
290
|
+
const stackInstance = docStack.getStack(stack);
|
|
291
|
+
if (stackInstance) {
|
|
292
|
+
const retrievedDomain = yield stackInstance.getDomain(domainName);
|
|
293
|
+
if (retrievedDomain) {
|
|
294
|
+
setDomain(retrievedDomain);
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
catch (err) {
|
|
299
|
+
setError(err);
|
|
300
|
+
setLoading(false);
|
|
301
|
+
}
|
|
302
|
+
});
|
|
303
|
+
fetchClass();
|
|
304
|
+
return () => {
|
|
305
|
+
// clean what?
|
|
306
|
+
};
|
|
307
|
+
}, [docStack, stack, domainName]); // Dependency on docStack and className
|
|
308
|
+
useEffect(() => {
|
|
309
|
+
if (!domain) {
|
|
310
|
+
return;
|
|
311
|
+
}
|
|
312
|
+
const runQueryAndListen = () => __awaiter(void 0, void 0, void 0, function* () {
|
|
313
|
+
setLoading(true);
|
|
314
|
+
try {
|
|
315
|
+
const initialDocs = yield domain.getRelations(query);
|
|
316
|
+
docsRef.current = initialDocs;
|
|
317
|
+
setDocs(docsRef.current);
|
|
318
|
+
}
|
|
319
|
+
catch (err) {
|
|
320
|
+
setError(err);
|
|
321
|
+
}
|
|
322
|
+
finally {
|
|
323
|
+
setLoading(false);
|
|
324
|
+
}
|
|
325
|
+
const changeListener = (change) => {
|
|
326
|
+
const doc = change.detail.doc;
|
|
327
|
+
if (!doc.active) {
|
|
328
|
+
// A doc was deleted
|
|
329
|
+
const docIndex = docsRef.current.findIndex((d) => d._id == doc._id);
|
|
330
|
+
if (docIndex != -1) {
|
|
331
|
+
docsRef.current = [
|
|
332
|
+
...docsRef.current.slice(0, docIndex),
|
|
333
|
+
...docsRef.current.slice(docIndex + 1, docsRef.current.length)
|
|
334
|
+
];
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
else {
|
|
338
|
+
// A doc was changed or added
|
|
339
|
+
console.log("useDomainRelations - a doc was changed or added", { doc });
|
|
340
|
+
const docIndex = docsRef.current.findIndex((d) => d._id == doc._id);
|
|
341
|
+
if (docIndex != -1) {
|
|
342
|
+
// A doc was changed
|
|
343
|
+
console.log("useDomainRelations - a doc was changed", { doc });
|
|
344
|
+
docsRef.current = [
|
|
345
|
+
...docsRef.current.slice(0, docIndex),
|
|
346
|
+
doc,
|
|
347
|
+
...docsRef.current.slice(docIndex + 1, docsRef.current.length)
|
|
348
|
+
];
|
|
349
|
+
}
|
|
350
|
+
else {
|
|
351
|
+
// A doc was added
|
|
352
|
+
console.log("useDomainRelations - a doc was added", { doc });
|
|
353
|
+
docsRef.current.push(doc);
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
setDocs([...docsRef.current]);
|
|
357
|
+
};
|
|
358
|
+
domain.addEventListener('doc', changeListener);
|
|
359
|
+
return () => {
|
|
360
|
+
domain.removeEventListener('doc', changeListener);
|
|
361
|
+
};
|
|
362
|
+
});
|
|
363
|
+
runQueryAndListen();
|
|
364
|
+
}, [domain, JSON.stringify(query)]); // Dependency on classObj and query
|
|
365
|
+
return { docs, loading, error };
|
|
366
|
+
};
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
2
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
3
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
4
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
5
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
6
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
7
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
|
+
});
|
|
9
|
+
};
|
|
10
|
+
// src/hooks/useFind.js
|
|
11
|
+
import { useContext, useEffect, useRef, useState } from 'react';
|
|
12
|
+
import { DocStackContext } from '../components/StackProvider/index.js';
|
|
13
|
+
/**
|
|
14
|
+
* Hook to execute a SQL query against a specific stack.
|
|
15
|
+
*
|
|
16
|
+
* @param stack - The name of the stack to query.
|
|
17
|
+
* @param sql - The SQL query string.
|
|
18
|
+
* @param params - Optional parameters for the SQL query.
|
|
19
|
+
* @returns Object containing the query result (rows and AST), loading state, and error.
|
|
20
|
+
*
|
|
21
|
+
* @example
|
|
22
|
+
* ```tsx
|
|
23
|
+
* const UserList = () => {
|
|
24
|
+
* const { result, loading } = useQuerySQL('my-stack', 'SELECT * FROM User WHERE age > ?', 18);
|
|
25
|
+
*
|
|
26
|
+
* if (loading) return <div>Loading...</div>;
|
|
27
|
+
*
|
|
28
|
+
* return (
|
|
29
|
+
* <ul>
|
|
30
|
+
* {result.rows.map(user => <li key={user._id}>{user.name}</li>)}
|
|
31
|
+
* </ul>
|
|
32
|
+
* );
|
|
33
|
+
* };
|
|
34
|
+
* ```
|
|
35
|
+
*/
|
|
36
|
+
export const useQuerySQL = (stack, sql, ...params) => {
|
|
37
|
+
const docStack = useContext(DocStackContext);
|
|
38
|
+
const [result, setResult] = useState({ rows: [], ast: [] });
|
|
39
|
+
const [loading, setLoading] = useState(true);
|
|
40
|
+
const [error, setError] = useState(null);
|
|
41
|
+
// [TODO] Solve bounce of component because of StrictMode or other reasons
|
|
42
|
+
const queryRef = useRef(false);
|
|
43
|
+
useEffect(() => {
|
|
44
|
+
if (!docStack) {
|
|
45
|
+
// Handle the case where the provider is not yet initialized or missing
|
|
46
|
+
// You could throw an error or return an empty state.
|
|
47
|
+
console.error('useClassList must be used within a DocStackProvider.');
|
|
48
|
+
setLoading(false);
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
const runQuery = () => __awaiter(void 0, void 0, void 0, function* () {
|
|
52
|
+
try {
|
|
53
|
+
const stackInstance = docStack.getStack(stack);
|
|
54
|
+
if (stackInstance) {
|
|
55
|
+
// Run the initial query
|
|
56
|
+
console.log("Preparing to run query", { sql, params });
|
|
57
|
+
// debugger
|
|
58
|
+
const queryResult = yield stackInstance.query(sql, ...params);
|
|
59
|
+
setResult(queryResult);
|
|
60
|
+
}
|
|
61
|
+
else {
|
|
62
|
+
console.log("Could not find corresponding stack", { stack });
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
catch (err) {
|
|
66
|
+
console.log("Got error while running query", { error: err });
|
|
67
|
+
setError(err);
|
|
68
|
+
}
|
|
69
|
+
finally {
|
|
70
|
+
setLoading(false);
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
if (!queryRef.current) {
|
|
74
|
+
queryRef.current = true;
|
|
75
|
+
setLoading(true);
|
|
76
|
+
runQuery();
|
|
77
|
+
}
|
|
78
|
+
else {
|
|
79
|
+
console.log("Already performing query");
|
|
80
|
+
}
|
|
81
|
+
return () => {
|
|
82
|
+
//
|
|
83
|
+
};
|
|
84
|
+
}, [docStack, stack, params]);
|
|
85
|
+
return { loading, result, error };
|
|
86
|
+
};
|
|
87
|
+
/**
|
|
88
|
+
* Hook to find documents in a stack using a Mango selector.
|
|
89
|
+
*
|
|
90
|
+
* @param stack - The name of the stack to query.
|
|
91
|
+
* @param query - Object containing the selector and optional fields projection.
|
|
92
|
+
* @param sort - Optional sort criteria.
|
|
93
|
+
* @param limit - Maximum number of documents to return (default: 50).
|
|
94
|
+
* @returns Object containing the list of documents, loading state, and error.
|
|
95
|
+
*
|
|
96
|
+
* @example
|
|
97
|
+
* ```tsx
|
|
98
|
+
* const ActiveTasks = () => {
|
|
99
|
+
* const { docs, loading } = useFind('my-stack', {
|
|
100
|
+
* selector: {
|
|
101
|
+
* "~class": "Task",
|
|
102
|
+
* active: true
|
|
103
|
+
* },
|
|
104
|
+
* fields: ['_id', 'title']
|
|
105
|
+
* });
|
|
106
|
+
*
|
|
107
|
+
* if (loading) return <div>Loading...</div>;
|
|
108
|
+
*
|
|
109
|
+
* return (
|
|
110
|
+
* <ul>
|
|
111
|
+
* {docs.map(doc => <li key={doc._id}>{doc.title}</li>)}
|
|
112
|
+
* </ul>
|
|
113
|
+
* );
|
|
114
|
+
* };
|
|
115
|
+
* ```
|
|
116
|
+
*/
|
|
117
|
+
export const useFind = (stack, query, sort, limit = 50) => {
|
|
118
|
+
const docStack = useContext(DocStackContext);
|
|
119
|
+
const [docs, setDocs] = useState([]);
|
|
120
|
+
const [loading, setLoading] = useState(true);
|
|
121
|
+
const [error, setError] = useState(null);
|
|
122
|
+
useEffect(() => {
|
|
123
|
+
// Check if the docStack instance is available
|
|
124
|
+
if (!docStack) {
|
|
125
|
+
// Handle the case where the provider is not yet initialized or missing
|
|
126
|
+
// You could throw an error or return an empty state.
|
|
127
|
+
console.error('useFind must be used within a DocStackProvider.');
|
|
128
|
+
setLoading(false);
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
setLoading(true);
|
|
132
|
+
const runQuery = () => __awaiter(void 0, void 0, void 0, function* () {
|
|
133
|
+
try {
|
|
134
|
+
const stackInstance = docStack.getStack(stack);
|
|
135
|
+
if (stackInstance) {
|
|
136
|
+
// Run the initial query
|
|
137
|
+
const initialDocs = yield stackInstance.findDocuments(query.selector, query.fields);
|
|
138
|
+
if (initialDocs.docs.length) {
|
|
139
|
+
let docs = initialDocs.docs; // [TODO] Check types
|
|
140
|
+
setDocs(docs);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
catch (err) {
|
|
145
|
+
setError(err);
|
|
146
|
+
}
|
|
147
|
+
finally {
|
|
148
|
+
setLoading(false);
|
|
149
|
+
}
|
|
150
|
+
});
|
|
151
|
+
runQuery();
|
|
152
|
+
// Set up the listener for changes
|
|
153
|
+
const changeListener = (change) => {
|
|
154
|
+
// Logic to handle the change and update the docs state
|
|
155
|
+
// This part is crucial for real-time updates.
|
|
156
|
+
// You'll need to re-run the query or intelligently update the docs array
|
|
157
|
+
// based on the change object (add, update, delete).
|
|
158
|
+
// A simple way is to re-run the query.
|
|
159
|
+
// runQuery();
|
|
160
|
+
};
|
|
161
|
+
// [TODO] Implement events
|
|
162
|
+
docStack.addEventListener('change', changeListener);
|
|
163
|
+
// Cleanup function: remove the listener when the component unmounts
|
|
164
|
+
return () => {
|
|
165
|
+
docStack.removeEventListener('change', changeListener);
|
|
166
|
+
};
|
|
167
|
+
}, [docStack, JSON.stringify(query)]); // Re-run if docStack or query changes
|
|
168
|
+
return { docs, loading, error };
|
|
169
|
+
};
|
|
170
|
+
export const useClassCreate = () => {
|
|
171
|
+
};
|
package/lib/index.js
CHANGED
|
@@ -1,3 +1,8 @@
|
|
|
1
|
-
|
|
2
|
-
import{
|
|
3
|
-
|
|
1
|
+
import StackProvider, { DocStackContext, useDocStack } from "./components/StackProvider/index.js";
|
|
2
|
+
import { useFind, useQuerySQL } from "./hooks/index.js";
|
|
3
|
+
import { useClass, useClassList, useClassDocs, useClassCreate } from "./hooks/class.js";
|
|
4
|
+
import { useDomainList, useDomain, useDomainRelations, useDomainCreate } from "./hooks/domain.js";
|
|
5
|
+
export { StackProvider, DocStackContext, useDocStack };
|
|
6
|
+
export { useFind, useQuerySQL };
|
|
7
|
+
export { useClassList, useClass, useClassDocs, useClassCreate };
|
|
8
|
+
export { useDomainList, useDomain, useDomainRelations, useDomainCreate };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@docstack/react",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.9",
|
|
4
4
|
"description": "One does not simply stack documents.",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"module": "lib/index.js",
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
"access": "public"
|
|
11
11
|
},
|
|
12
12
|
"scripts": {
|
|
13
|
-
"build": "
|
|
13
|
+
"build": "npx tsc -build --force ./tsconfig.json",
|
|
14
14
|
"build:dev": "webpack --node-env=development",
|
|
15
15
|
"build:prod": "webpack --node-env=production"
|
|
16
16
|
},
|
|
@@ -38,8 +38,8 @@
|
|
|
38
38
|
"react-dom": "^19.2.3"
|
|
39
39
|
},
|
|
40
40
|
"dependencies": {
|
|
41
|
-
"@docstack/client": "^0.1.
|
|
42
|
-
"@docstack/shared": "^0.0.
|
|
41
|
+
"@docstack/client": "^0.1.4",
|
|
42
|
+
"@docstack/shared": "^0.0.5",
|
|
43
43
|
"react": "^19.2.3",
|
|
44
44
|
"react-dom": "^19.2.3"
|
|
45
45
|
},
|