@aiao/rxdb-angular 0.0.7 → 0.0.8
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/LICENSE +21 -0
- package/fesm2022/aiao-rxdb-angular.mjs +423 -0
- package/fesm2022/aiao-rxdb-angular.mjs.map +1 -0
- package/package.json +26 -13
- package/types/aiao-rxdb-angular.d.ts +222 -0
- package/eslint.config.mjs +0 -58
- package/ng-package.json +0 -7
- package/project.json +0 -48
- package/src/InfiniteScrollingList.ts +0 -123
- package/src/devtools/devtools.html +0 -99
- package/src/devtools/devtools.interface.ts +0 -3
- package/src/devtools/devtools.scss +0 -49
- package/src/devtools/devtools.spec.ts +0 -30
- package/src/devtools/devtools.ts +0 -207
- package/src/devtools/entity-table-item.ts +0 -47
- package/src/devtools/entity-table-tools.html +0 -56
- package/src/devtools/entity-table-tools.scss +0 -8
- package/src/devtools/entity-table-tools.ts +0 -153
- package/src/devtools/event-tools.html +0 -15
- package/src/devtools/event-tools.scss +0 -18
- package/src/devtools/event-tools.ts +0 -45
- package/src/devtools/scroll-advanced-calc.service.ts +0 -41
- package/src/devtools/settings.html +0 -46
- package/src/devtools/settings.scss +0 -19
- package/src/devtools/settings.ts +0 -122
- package/src/hooks.ts +0 -307
- package/src/index.ts +0 -7
- package/src/rxdb-change-detector.directive.spec.ts +0 -94
- package/src/rxdb-change-detector.directive.ts +0 -35
- package/src/rxdb.provider.ts +0 -13
- package/src/rxdb.service.spec.ts +0 -31
- package/src/rxdb.service.ts +0 -35
- package/src/test-setup.ts +0 -14
- package/src/use-action.spec.ts +0 -88
- package/src/use-action.ts +0 -20
- package/src/use-state.spec.ts +0 -105
- package/src/use-state.ts +0 -28
- package/tsconfig.json +0 -33
- package/tsconfig.lib.json +0 -42
- package/tsconfig.lib.prod.json +0 -10
- package/tsconfig.spec.json +0 -23
- package/vite.config.mts +0 -55
package/src/devtools/settings.ts
DELETED
|
@@ -1,122 +0,0 @@
|
|
|
1
|
-
import { RxDB, RxDBEvent } from '@aiao/rxdb';
|
|
2
|
-
import {
|
|
3
|
-
ChangeDetectionStrategy,
|
|
4
|
-
Component,
|
|
5
|
-
inject,
|
|
6
|
-
output,
|
|
7
|
-
signal,
|
|
8
|
-
TemplateRef,
|
|
9
|
-
viewChild,
|
|
10
|
-
ViewContainerRef
|
|
11
|
-
} from '@angular/core';
|
|
12
|
-
import { LucideAngularModule, PanelBottom, PanelLeft, PanelRight } from 'lucide-angular';
|
|
13
|
-
|
|
14
|
-
interface FileEntry {
|
|
15
|
-
name: string;
|
|
16
|
-
path: string;
|
|
17
|
-
type: 'file' | 'directory';
|
|
18
|
-
handle: FileSystemDirectoryHandle | FileSystemFileHandle;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
@Component({
|
|
22
|
-
selector: 'rxdb-settings',
|
|
23
|
-
imports: [LucideAngularModule],
|
|
24
|
-
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
25
|
-
styleUrls: ['./settings.scss'],
|
|
26
|
-
templateUrl: './settings.html'
|
|
27
|
-
})
|
|
28
|
-
export class RxDBSettings {
|
|
29
|
-
#viewContainerRef = inject(ViewContainerRef);
|
|
30
|
-
#rxdb = inject(RxDB);
|
|
31
|
-
|
|
32
|
-
protected readonly PanelLeft = PanelLeft;
|
|
33
|
-
protected readonly PanelBottom = PanelBottom;
|
|
34
|
-
protected readonly PanelRight = PanelRight;
|
|
35
|
-
|
|
36
|
-
messageTmp = viewChild<TemplateRef<any>>('message');
|
|
37
|
-
|
|
38
|
-
$events = signal<RxDBEvent[]>([]);
|
|
39
|
-
|
|
40
|
-
sideChange = output<'right' | 'left' | 'bottom'>();
|
|
41
|
-
|
|
42
|
-
async delete_db() {
|
|
43
|
-
try {
|
|
44
|
-
await this.#rxdb.disconnectAll();
|
|
45
|
-
} catch {
|
|
46
|
-
//
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
try {
|
|
50
|
-
const root = await navigator.storage.getDirectory();
|
|
51
|
-
const files = await this.listAllFiles(root, 1);
|
|
52
|
-
for (const entry of files) {
|
|
53
|
-
if (entry.type === 'file') {
|
|
54
|
-
await root.removeEntry(entry.name, { recursive: true });
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
await this.clean_indexedDB();
|
|
58
|
-
localStorage.clear();
|
|
59
|
-
// 使用 baseURI 跳转回首页,避免在子路由刷新时因服务器配置问题导致 404 fallback 到主站
|
|
60
|
-
window.location.href = document.baseURI;
|
|
61
|
-
} catch (error: Error | any) {
|
|
62
|
-
if (error && error.name === 'NoModificationAllowedError') {
|
|
63
|
-
const ref = this.#viewContainerRef.createEmbeddedView<{ message: string }>(this.messageTmp()!, {
|
|
64
|
-
message: '关闭数据库失败,请检查是否在多个浏览器 tab 中打开页面'
|
|
65
|
-
});
|
|
66
|
-
setTimeout(() => {
|
|
67
|
-
ref.destroy();
|
|
68
|
-
}, 3000);
|
|
69
|
-
}
|
|
70
|
-
console.error(error);
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
async clean_indexedDB() {
|
|
75
|
-
const dbList = indexedDB.databases ? await indexedDB.databases() : [];
|
|
76
|
-
if (dbList.length) {
|
|
77
|
-
await Promise.all(
|
|
78
|
-
dbList.map(({ name }) => {
|
|
79
|
-
return new Promise<void>((resolve, reject) => {
|
|
80
|
-
const request = indexedDB.deleteDatabase(name!);
|
|
81
|
-
request.onsuccess = () => resolve();
|
|
82
|
-
request.onerror = () => reject(request.error);
|
|
83
|
-
request.onblocked = () => resolve();
|
|
84
|
-
});
|
|
85
|
-
})
|
|
86
|
-
);
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
async listAllFiles(
|
|
91
|
-
directoryHandle: FileSystemDirectoryHandle | any,
|
|
92
|
-
maxDeep = Number.MAX_SAFE_INTEGER,
|
|
93
|
-
path = '',
|
|
94
|
-
currentDeep = 1
|
|
95
|
-
) {
|
|
96
|
-
const entries: FileEntry[] = [];
|
|
97
|
-
for await (const [name, handle] of directoryHandle) {
|
|
98
|
-
const entryPath = path ? `${path}/${name}` : name;
|
|
99
|
-
if (handle.kind === 'file') {
|
|
100
|
-
entries.push({
|
|
101
|
-
name: name,
|
|
102
|
-
path: entryPath,
|
|
103
|
-
type: 'file',
|
|
104
|
-
handle: handle
|
|
105
|
-
});
|
|
106
|
-
} else if (handle.kind === 'directory') {
|
|
107
|
-
entries.push({
|
|
108
|
-
name: name,
|
|
109
|
-
path: entryPath,
|
|
110
|
-
type: 'directory',
|
|
111
|
-
handle: handle
|
|
112
|
-
});
|
|
113
|
-
|
|
114
|
-
if (currentDeep < maxDeep) {
|
|
115
|
-
const subEntries = await this.listAllFiles(handle, maxDeep, entryPath, currentDeep + 1);
|
|
116
|
-
entries.push(...subEntries);
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
return entries;
|
|
121
|
-
}
|
|
122
|
-
}
|
package/src/hooks.ts
DELETED
|
@@ -1,307 +0,0 @@
|
|
|
1
|
-
import { EntityStaticType, EntityType, TreeEntityType } from '@aiao/rxdb';
|
|
2
|
-
import { GraphEntityType } from '@aiao/rxdb-plugin-graph';
|
|
3
|
-
import { isFunction } from '@aiao/utils';
|
|
4
|
-
import { effect, Signal, signal } from '@angular/core';
|
|
5
|
-
import { toLazySignal } from 'ngxtension/to-lazy-signal';
|
|
6
|
-
import { Observable, Subscriber, Subscription } from 'rxjs';
|
|
7
|
-
|
|
8
|
-
type UseOptions<T> = T | (() => T);
|
|
9
|
-
|
|
10
|
-
export interface RxDBResource<T> {
|
|
11
|
-
/**
|
|
12
|
-
* 资源的值
|
|
13
|
-
*/
|
|
14
|
-
readonly value: Signal<T>;
|
|
15
|
-
/**
|
|
16
|
-
* 资源的错误
|
|
17
|
-
*/
|
|
18
|
-
readonly error: Signal<Error | undefined>;
|
|
19
|
-
/**
|
|
20
|
-
* 资源的加载状态
|
|
21
|
-
*/
|
|
22
|
-
readonly isLoading: Signal<boolean>;
|
|
23
|
-
/**
|
|
24
|
-
* 资源是否为空
|
|
25
|
-
*/
|
|
26
|
-
readonly isEmpty: Signal<boolean | undefined>;
|
|
27
|
-
/**
|
|
28
|
-
* 资源是否有值
|
|
29
|
-
*/
|
|
30
|
-
readonly hasValue: Signal<boolean>;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
export const useRepositoryQuery = <T extends EntityType, RT>(
|
|
34
|
-
EntityType: T,
|
|
35
|
-
method: string,
|
|
36
|
-
defaultValue: any,
|
|
37
|
-
options: UseOptions<any>
|
|
38
|
-
): RxDBResource<RT> => {
|
|
39
|
-
let _observer: Subscriber<any>;
|
|
40
|
-
let sub: Subscription;
|
|
41
|
-
|
|
42
|
-
const _has_sub = signal<boolean>(false);
|
|
43
|
-
const _refresh = signal<number>(0);
|
|
44
|
-
|
|
45
|
-
const _eff = effect(() => {
|
|
46
|
-
if (_has_sub() === false) return;
|
|
47
|
-
// re-run when refresh is called or options change
|
|
48
|
-
_refresh();
|
|
49
|
-
const opt = isFunction(options) ? options() : options;
|
|
50
|
-
if (sub) sub.unsubscribe();
|
|
51
|
-
// reset state for a new request
|
|
52
|
-
isLoading.set(true);
|
|
53
|
-
error.set(undefined);
|
|
54
|
-
hasValue.set(false);
|
|
55
|
-
isEmpty.set(undefined);
|
|
56
|
-
// 优化:使用 Record<string, any> 替代 any,以提高类型安全性
|
|
57
|
-
const func = (EntityType as unknown as Record<string, any>)[method];
|
|
58
|
-
sub =
|
|
59
|
-
func &&
|
|
60
|
-
func(opt).subscribe({
|
|
61
|
-
next: (d: any) => {
|
|
62
|
-
isLoading.set(false);
|
|
63
|
-
hasValue.set(true);
|
|
64
|
-
if (Array.isArray(d)) {
|
|
65
|
-
isEmpty.set(d.length === 0);
|
|
66
|
-
} else if (d == null) {
|
|
67
|
-
// null/undefined treated as empty
|
|
68
|
-
isEmpty.set(true);
|
|
69
|
-
} else {
|
|
70
|
-
isEmpty.set(false);
|
|
71
|
-
}
|
|
72
|
-
_observer.next(d);
|
|
73
|
-
},
|
|
74
|
-
error: (e: any) => {
|
|
75
|
-
isLoading.set(false);
|
|
76
|
-
error.set(e);
|
|
77
|
-
},
|
|
78
|
-
complete: () => {
|
|
79
|
-
// ensure loading is false even if observable completes without next
|
|
80
|
-
isLoading.set(false);
|
|
81
|
-
}
|
|
82
|
-
});
|
|
83
|
-
});
|
|
84
|
-
const ob = new Observable<RT>(observer => {
|
|
85
|
-
_has_sub.set(true);
|
|
86
|
-
_observer = observer;
|
|
87
|
-
_observer.next(defaultValue);
|
|
88
|
-
return () => {
|
|
89
|
-
_eff.destroy();
|
|
90
|
-
sub?.unsubscribe();
|
|
91
|
-
};
|
|
92
|
-
});
|
|
93
|
-
|
|
94
|
-
const value = toLazySignal<RT>(ob, {
|
|
95
|
-
initialValue: defaultValue
|
|
96
|
-
}) as Signal<RT>;
|
|
97
|
-
const error = signal<Error | undefined>(undefined);
|
|
98
|
-
const isLoading = signal<boolean>(false);
|
|
99
|
-
const hasValue = signal<boolean>(false);
|
|
100
|
-
const isEmpty = signal<boolean | undefined>(undefined);
|
|
101
|
-
return {
|
|
102
|
-
value,
|
|
103
|
-
error,
|
|
104
|
-
isLoading,
|
|
105
|
-
isEmpty,
|
|
106
|
-
hasValue
|
|
107
|
-
};
|
|
108
|
-
};
|
|
109
|
-
|
|
110
|
-
/*
|
|
111
|
-
* Repository
|
|
112
|
-
*/
|
|
113
|
-
|
|
114
|
-
/**
|
|
115
|
-
* 通过 ID 获取单个实体
|
|
116
|
-
*
|
|
117
|
-
* @param EntityType 实体类
|
|
118
|
-
* @param options 实体 ID 或查询参数对象
|
|
119
|
-
* @returns 返回包含实体的资源 signal
|
|
120
|
-
*
|
|
121
|
-
* @example
|
|
122
|
-
* ```typescript
|
|
123
|
-
* const user = useGet(User, 'user-1');
|
|
124
|
-
* ```
|
|
125
|
-
*/
|
|
126
|
-
export const useGet = <T extends EntityType>(EntityType: T, options: UseOptions<EntityStaticType<T, 'getOptions'>>) =>
|
|
127
|
-
useRepositoryQuery<T, InstanceType<T>>(EntityType, 'get', undefined, options);
|
|
128
|
-
|
|
129
|
-
/**
|
|
130
|
-
* 查找第一个匹配条件的实体
|
|
131
|
-
*
|
|
132
|
-
* @param EntityType 实体类
|
|
133
|
-
* @param options 查询参数(where、排序等)
|
|
134
|
-
* @returns 返回包含实体的资源 signal
|
|
135
|
-
*/
|
|
136
|
-
export const useFindOne = <T extends EntityType>(
|
|
137
|
-
EntityType: T,
|
|
138
|
-
options: UseOptions<EntityStaticType<T, 'findOneOptions'>>
|
|
139
|
-
) => useRepositoryQuery<T, InstanceType<T> | undefined>(EntityType, 'findOne', undefined, options);
|
|
140
|
-
|
|
141
|
-
/**
|
|
142
|
-
* 查找匹配的实体,未找到则抛出错误
|
|
143
|
-
*
|
|
144
|
-
* @param EntityType 实体类
|
|
145
|
-
* @param options 查询参数
|
|
146
|
-
* @returns 返回包含实体的资源 signal
|
|
147
|
-
*/
|
|
148
|
-
export const useFindOneOrFail = <T extends EntityType>(
|
|
149
|
-
EntityType: T,
|
|
150
|
-
options: UseOptions<EntityStaticType<T, 'findOneOrFailOptions'>>
|
|
151
|
-
) => useRepositoryQuery<T, InstanceType<T>>(EntityType, 'findOneOrFail', undefined, options);
|
|
152
|
-
|
|
153
|
-
/**
|
|
154
|
-
* 查找符合条件的多个实体
|
|
155
|
-
*
|
|
156
|
-
* @param EntityType 实体类
|
|
157
|
-
* @param options 查询参数
|
|
158
|
-
* @returns 返回包含实体数组的资源 signal
|
|
159
|
-
*/
|
|
160
|
-
export const useFind = <T extends EntityType>(EntityType: T, options: UseOptions<EntityStaticType<T, 'findOptions'>>) =>
|
|
161
|
-
useRepositoryQuery<T, InstanceType<T>[]>(EntityType, 'find', [], options);
|
|
162
|
-
|
|
163
|
-
/**
|
|
164
|
-
* 使用游标分页查找实体
|
|
165
|
-
*
|
|
166
|
-
* @param EntityType 实体类
|
|
167
|
-
* @param options 游标参数
|
|
168
|
-
* @returns 返回包含实体数组的资源 signal
|
|
169
|
-
*/
|
|
170
|
-
export const useFindByCursor = <T extends EntityType>(
|
|
171
|
-
EntityType: T,
|
|
172
|
-
options: UseOptions<EntityStaticType<T, 'findByCursorOptions'>>
|
|
173
|
-
) => useRepositoryQuery<T, InstanceType<T>[]>(EntityType, 'findByCursor', [], options);
|
|
174
|
-
|
|
175
|
-
/**
|
|
176
|
-
* 查找全部实体
|
|
177
|
-
*
|
|
178
|
-
* @param EntityType 实体类
|
|
179
|
-
* @param options 查询参数
|
|
180
|
-
* @returns 返回包含全部实体的资源 signal
|
|
181
|
-
*/
|
|
182
|
-
export const useFindAll = <T extends EntityType>(
|
|
183
|
-
EntityType: T,
|
|
184
|
-
options: UseOptions<EntityStaticType<T, 'findAllOptions'>>
|
|
185
|
-
) => useRepositoryQuery<T, InstanceType<T>[]>(EntityType, 'findAll', [], options);
|
|
186
|
-
|
|
187
|
-
/**
|
|
188
|
-
* 统计满足条件的实体数量
|
|
189
|
-
*
|
|
190
|
-
* @param EntityType 实体类
|
|
191
|
-
* @param options 查询参数
|
|
192
|
-
* @returns 返回包含数量的资源 signal
|
|
193
|
-
*/
|
|
194
|
-
export const useCount = <T extends EntityType>(
|
|
195
|
-
EntityType: T,
|
|
196
|
-
options: UseOptions<EntityStaticType<T, 'countOptions'>>
|
|
197
|
-
) => useRepositoryQuery<T, number>(EntityType, 'count', undefined, options);
|
|
198
|
-
|
|
199
|
-
/*
|
|
200
|
-
* TreeRepository
|
|
201
|
-
*/
|
|
202
|
-
|
|
203
|
-
/**
|
|
204
|
-
* 查找树结构中的所有子孙实体
|
|
205
|
-
*
|
|
206
|
-
* @param EntityType 实体类
|
|
207
|
-
* @param options 树查询参数(entityId、深度等)
|
|
208
|
-
* @returns 返回包含子孙实体的资源 signal
|
|
209
|
-
*/
|
|
210
|
-
export const useFindDescendants = <T extends TreeEntityType>(
|
|
211
|
-
EntityType: T,
|
|
212
|
-
options: UseOptions<EntityStaticType<T, 'findTreeOptions'>>
|
|
213
|
-
) => useRepositoryQuery<T, InstanceType<T>[]>(EntityType, 'findDescendants', [], options);
|
|
214
|
-
|
|
215
|
-
/**
|
|
216
|
-
* 统计树结构中的子孙数量
|
|
217
|
-
*
|
|
218
|
-
* @param EntityType 实体类
|
|
219
|
-
* @param options 树查询参数
|
|
220
|
-
* @returns 返回包含数量的资源 signal
|
|
221
|
-
*/
|
|
222
|
-
export const useCountDescendants = <T extends TreeEntityType>(
|
|
223
|
-
EntityType: T,
|
|
224
|
-
options: UseOptions<EntityStaticType<T, 'findTreeOptions'>>
|
|
225
|
-
) => useRepositoryQuery<T, number>(EntityType, 'countDescendants', undefined, options);
|
|
226
|
-
|
|
227
|
-
/**
|
|
228
|
-
* 查找树结构中的所有祖先实体
|
|
229
|
-
*
|
|
230
|
-
* @param EntityType 实体类
|
|
231
|
-
* @param options 树查询参数
|
|
232
|
-
* @returns 返回包含祖先实体的资源 signal
|
|
233
|
-
*/
|
|
234
|
-
export const useFindAncestors = <T extends TreeEntityType>(
|
|
235
|
-
EntityType: T,
|
|
236
|
-
options: UseOptions<EntityStaticType<T, 'findTreeOptions'>>
|
|
237
|
-
) => useRepositoryQuery<T, InstanceType<T>[]>(EntityType, 'findAncestors', [], options);
|
|
238
|
-
|
|
239
|
-
/**
|
|
240
|
-
* 统计树结构中的祖先数量
|
|
241
|
-
*
|
|
242
|
-
* @param EntityType 实体类
|
|
243
|
-
* @param options 树查询参数
|
|
244
|
-
* @returns 返回包含数量的资源 signal
|
|
245
|
-
*/
|
|
246
|
-
export const useCountAncestors = <T extends TreeEntityType>(
|
|
247
|
-
EntityType: T,
|
|
248
|
-
options: UseOptions<EntityStaticType<T, 'findTreeOptions'>>
|
|
249
|
-
) => useRepositoryQuery<T, number>(EntityType, 'countAncestors', undefined, options);
|
|
250
|
-
|
|
251
|
-
/*
|
|
252
|
-
* GraphRepository
|
|
253
|
-
*/
|
|
254
|
-
|
|
255
|
-
/**
|
|
256
|
-
* 查找图结构中的邻接实体
|
|
257
|
-
*
|
|
258
|
-
* @param EntityType 实体类
|
|
259
|
-
* @param options 图查询参数(entityId、方向、层级等)
|
|
260
|
-
* @returns 返回包含邻居实体的资源 signal
|
|
261
|
-
*
|
|
262
|
-
* @example
|
|
263
|
-
* ```typescript
|
|
264
|
-
* const friends = useGraphNeighbors(User, {
|
|
265
|
-
* entityId: 'user-1',
|
|
266
|
-
* direction: 'out',
|
|
267
|
-
* level: 1
|
|
268
|
-
* });
|
|
269
|
-
* ```
|
|
270
|
-
*/
|
|
271
|
-
export const useGraphNeighbors = <T extends GraphEntityType>(
|
|
272
|
-
EntityType: T,
|
|
273
|
-
options: UseOptions<EntityStaticType<T, 'findNeighborsOptions'>>
|
|
274
|
-
) => useRepositoryQuery<T, InstanceType<T>[]>(EntityType, 'findNeighbors', [], options);
|
|
275
|
-
|
|
276
|
-
/**
|
|
277
|
-
* 统计图结构中的邻接数量
|
|
278
|
-
*
|
|
279
|
-
* @param EntityType 实体类
|
|
280
|
-
* @param options 图查询参数
|
|
281
|
-
* @returns 返回包含数量的资源 signal
|
|
282
|
-
*/
|
|
283
|
-
export const useCountNeighbors = <T extends GraphEntityType>(
|
|
284
|
-
EntityType: T,
|
|
285
|
-
options: UseOptions<EntityStaticType<T, 'findNeighborsOptions'>>
|
|
286
|
-
) => useRepositoryQuery<T, number>(EntityType, 'countNeighbors', 0, options);
|
|
287
|
-
|
|
288
|
-
/**
|
|
289
|
-
* 查找图结构中两个实体之间的路径
|
|
290
|
-
*
|
|
291
|
-
* @param EntityType 实体类
|
|
292
|
-
* @param options 路径查询参数(fromId、toId、最大深度等)
|
|
293
|
-
* @returns 返回包含路径的资源 signal
|
|
294
|
-
*
|
|
295
|
-
* @example
|
|
296
|
-
* ```typescript
|
|
297
|
-
* const paths = useGraphPaths(User, {
|
|
298
|
-
* fromId: 'user-1',
|
|
299
|
-
* toId: 'user-2',
|
|
300
|
-
* maxDepth: 5
|
|
301
|
-
* });
|
|
302
|
-
* ```
|
|
303
|
-
*/
|
|
304
|
-
export const useGraphPaths = <T extends GraphEntityType>(
|
|
305
|
-
EntityType: T,
|
|
306
|
-
options: UseOptions<EntityStaticType<T, 'findPathsOptions'>>
|
|
307
|
-
) => useRepositoryQuery<T, any[]>(EntityType, 'findPaths', [], options);
|
package/src/index.ts
DELETED
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
export * from './hooks';
|
|
2
|
-
export * from './InfiniteScrollingList';
|
|
3
|
-
export { RxDBEntityChangeDirective } from './rxdb-change-detector.directive';
|
|
4
|
-
export { provideRxDB } from './rxdb.provider';
|
|
5
|
-
export * from './rxdb.service';
|
|
6
|
-
export * from './use-action';
|
|
7
|
-
export * from './use-state';
|
|
@@ -1,94 +0,0 @@
|
|
|
1
|
-
import { ChangeDetectionStrategy, Component, provideZonelessChangeDetection } from '@angular/core';
|
|
2
|
-
import { TestBed } from '@angular/core/testing';
|
|
3
|
-
import { Subject } from 'rxjs';
|
|
4
|
-
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
|
5
|
-
import { RxDBEntityChangeDirective } from './rxdb-change-detector.directive';
|
|
6
|
-
|
|
7
|
-
// Mock entity type
|
|
8
|
-
const mockEntity = {
|
|
9
|
-
id: 'test-1',
|
|
10
|
-
name: 'Test Entity'
|
|
11
|
-
};
|
|
12
|
-
|
|
13
|
-
const mockPatchesSubject = new Subject();
|
|
14
|
-
|
|
15
|
-
// Mock getEntityStatus
|
|
16
|
-
vi.mock('@aiao/rxdb', () => ({
|
|
17
|
-
getEntityStatus: vi.fn(() => ({
|
|
18
|
-
patches$: mockPatchesSubject.asObservable()
|
|
19
|
-
}))
|
|
20
|
-
}));
|
|
21
|
-
|
|
22
|
-
@Component({
|
|
23
|
-
// eslint-disable-next-line @angular-eslint/component-selector
|
|
24
|
-
selector: 'test-component',
|
|
25
|
-
standalone: true,
|
|
26
|
-
imports: [RxDBEntityChangeDirective],
|
|
27
|
-
template: '<div [rxdbChangeDetector]="entity"></div>',
|
|
28
|
-
changeDetection: ChangeDetectionStrategy.OnPush
|
|
29
|
-
})
|
|
30
|
-
class TestComponent {
|
|
31
|
-
entity = mockEntity;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
describe('RxDBEntityChangeDirective', () => {
|
|
35
|
-
beforeEach(() => {
|
|
36
|
-
TestBed.configureTestingModule({
|
|
37
|
-
providers: [provideZonelessChangeDetection()]
|
|
38
|
-
});
|
|
39
|
-
});
|
|
40
|
-
|
|
41
|
-
it('should create directive', () => {
|
|
42
|
-
const fixture = TestBed.createComponent(TestComponent);
|
|
43
|
-
expect(fixture).toBeDefined();
|
|
44
|
-
});
|
|
45
|
-
|
|
46
|
-
it('should subscribe to patches$ on initialization', () => {
|
|
47
|
-
const fixture = TestBed.createComponent(TestComponent);
|
|
48
|
-
fixture.detectChanges();
|
|
49
|
-
|
|
50
|
-
// Verify component was created successfully with the directive
|
|
51
|
-
expect(fixture.componentInstance).toBeDefined();
|
|
52
|
-
|
|
53
|
-
// 指令应该已经设置了订阅
|
|
54
|
-
// Testing the actual subscription behavior requires more complex mocking
|
|
55
|
-
});
|
|
56
|
-
|
|
57
|
-
it('should handle debounceTime input', () => {
|
|
58
|
-
@Component({
|
|
59
|
-
// eslint-disable-next-line @angular-eslint/component-selector
|
|
60
|
-
selector: 'test-debounce',
|
|
61
|
-
standalone: true,
|
|
62
|
-
imports: [RxDBEntityChangeDirective],
|
|
63
|
-
template: '<div [rxdbChangeDetector]="entity" [debounceTime]="100"></div>',
|
|
64
|
-
changeDetection: ChangeDetectionStrategy.OnPush
|
|
65
|
-
})
|
|
66
|
-
class TestDebounceComponent {
|
|
67
|
-
entity = mockEntity;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
const fixture = TestBed.createComponent(TestDebounceComponent);
|
|
71
|
-
fixture.detectChanges();
|
|
72
|
-
|
|
73
|
-
expect(fixture.componentInstance).toBeDefined();
|
|
74
|
-
});
|
|
75
|
-
|
|
76
|
-
it('should handle auditTime input', () => {
|
|
77
|
-
@Component({
|
|
78
|
-
// eslint-disable-next-line @angular-eslint/component-selector
|
|
79
|
-
selector: 'test-audit',
|
|
80
|
-
standalone: true,
|
|
81
|
-
imports: [RxDBEntityChangeDirective],
|
|
82
|
-
template: '<div [rxdbChangeDetector]="entity" [auditTime]="50"></div>',
|
|
83
|
-
changeDetection: ChangeDetectionStrategy.OnPush
|
|
84
|
-
})
|
|
85
|
-
class TestAuditComponent {
|
|
86
|
-
entity = mockEntity;
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
const fixture = TestBed.createComponent(TestAuditComponent);
|
|
90
|
-
fixture.detectChanges();
|
|
91
|
-
|
|
92
|
-
expect(fixture.componentInstance).toBeDefined();
|
|
93
|
-
});
|
|
94
|
-
});
|
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
import { EntityType, getEntityStatus } from '@aiao/rxdb';
|
|
2
|
-
import { ChangeDetectorRef, DestroyRef, Directive, effect, inject, input } from '@angular/core';
|
|
3
|
-
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
|
4
|
-
import { auditTime, debounceTime, Subscription } from 'rxjs';
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* RxDB 实体变化指令
|
|
8
|
-
* 如果你使用了 `changeDetection: ChangeDetectionStrategy.OnPush`
|
|
9
|
-
* 那么正在输入的变化不会触发 angular 渲染机制,使用这个指令可以实现实时渲染
|
|
10
|
-
*
|
|
11
|
-
* @example
|
|
12
|
-
* 当修改实体时,需要在多个地方预览修改变化时,可以使用这个指令
|
|
13
|
-
*/
|
|
14
|
-
@Directive({ selector: '[rxdbChangeDetector]' })
|
|
15
|
-
export class RxDBEntityChangeDirective {
|
|
16
|
-
#destroyRef = inject(DestroyRef);
|
|
17
|
-
#changeDetectorRef = inject(ChangeDetectorRef);
|
|
18
|
-
#sub?: Subscription;
|
|
19
|
-
public readonly rxdbChangeDetector = input<InstanceType<EntityType>>();
|
|
20
|
-
public readonly debounceTime = input<number>(0);
|
|
21
|
-
public readonly auditTime = input<number>(0);
|
|
22
|
-
|
|
23
|
-
constructor() {
|
|
24
|
-
effect(() => {
|
|
25
|
-
const rxdbChange = this.rxdbChangeDetector();
|
|
26
|
-
const dt = this.debounceTime();
|
|
27
|
-
const at = this.auditTime();
|
|
28
|
-
const status = getEntityStatus(rxdbChange);
|
|
29
|
-
this.#sub?.unsubscribe();
|
|
30
|
-
this.#sub = status.patches$
|
|
31
|
-
.pipe(takeUntilDestroyed(this.#destroyRef), debounceTime(dt), auditTime(at))
|
|
32
|
-
.subscribe(() => this.#changeDetectorRef.markForCheck());
|
|
33
|
-
});
|
|
34
|
-
}
|
|
35
|
-
}
|
package/src/rxdb.provider.ts
DELETED
package/src/rxdb.service.spec.ts
DELETED
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
import { provideZonelessChangeDetection } from '@angular/core';
|
|
2
|
-
import { TestBed } from '@angular/core/testing';
|
|
3
|
-
import { beforeEach, describe, expect, it } from 'vitest';
|
|
4
|
-
import { RxDBService } from './rxdb.service';
|
|
5
|
-
|
|
6
|
-
describe('RxDBService', () => {
|
|
7
|
-
let service: RxDBService;
|
|
8
|
-
|
|
9
|
-
beforeEach(() => {
|
|
10
|
-
TestBed.configureTestingModule({
|
|
11
|
-
providers: [provideZonelessChangeDetection(), RxDBService]
|
|
12
|
-
});
|
|
13
|
-
service = TestBed.inject(RxDBService);
|
|
14
|
-
});
|
|
15
|
-
|
|
16
|
-
it('should be created', () => {
|
|
17
|
-
expect(service).toBeDefined();
|
|
18
|
-
});
|
|
19
|
-
|
|
20
|
-
it('should not throw when closing devtools if not open', () => {
|
|
21
|
-
expect(() => service.closeDevTools()).not.toThrow();
|
|
22
|
-
});
|
|
23
|
-
|
|
24
|
-
it('should not throw when setting theme if devtools not open', () => {
|
|
25
|
-
expect(() => service.setDevToolsTheme('dark')).not.toThrow();
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
// Note: Tests for devtools loading are skipped because they require
|
|
29
|
-
// full RxDB setup with database connections, which is better tested
|
|
30
|
-
// in integration tests with actual Angular applications.
|
|
31
|
-
});
|
package/src/rxdb.service.ts
DELETED
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
import { ComponentRef, Injectable, ViewContainerRef } from '@angular/core';
|
|
2
|
-
import { RxDBDevtools } from './devtools/devtools';
|
|
3
|
-
import { RxDBDevToolsOptions } from './devtools/devtools.interface';
|
|
4
|
-
|
|
5
|
-
@Injectable({
|
|
6
|
-
providedIn: 'root'
|
|
7
|
-
})
|
|
8
|
-
export class RxDBService {
|
|
9
|
-
#componentRef?: ComponentRef<RxDBDevtools>;
|
|
10
|
-
|
|
11
|
-
async devtools(view: ViewContainerRef, options?: RxDBDevToolsOptions) {
|
|
12
|
-
if (this.#componentRef) {
|
|
13
|
-
this.closeDevTools();
|
|
14
|
-
return;
|
|
15
|
-
}
|
|
16
|
-
const { RxDBDevtools } = await import('./devtools/devtools.js');
|
|
17
|
-
const componentRef = view.createComponent(RxDBDevtools);
|
|
18
|
-
this.#componentRef = componentRef;
|
|
19
|
-
componentRef.setInput('componentRef', componentRef);
|
|
20
|
-
if (options) {
|
|
21
|
-
if (options.theme) this.setDevToolsTheme(options.theme);
|
|
22
|
-
}
|
|
23
|
-
componentRef.onDestroy(() => {
|
|
24
|
-
this.#componentRef = undefined;
|
|
25
|
-
});
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
closeDevTools() {
|
|
29
|
-
this.#componentRef?.instance.close_rxdb_devtools();
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
setDevToolsTheme(theme: 'light' | 'dark') {
|
|
33
|
-
this.#componentRef?.setInput('theme', theme);
|
|
34
|
-
}
|
|
35
|
-
}
|
package/src/test-setup.ts
DELETED
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import '@angular/compiler';
|
|
2
|
-
|
|
3
|
-
import { getTestBed } from '@angular/core/testing';
|
|
4
|
-
import { BrowserTestingModule, platformBrowserTesting } from '@angular/platform-browser/testing';
|
|
5
|
-
|
|
6
|
-
const testBed = getTestBed();
|
|
7
|
-
if (!testBed.platform) {
|
|
8
|
-
testBed.initTestEnvironment(BrowserTestingModule, platformBrowserTesting());
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
import { afterEach } from 'vitest';
|
|
12
|
-
afterEach(() => {
|
|
13
|
-
getTestBed().resetTestingModule();
|
|
14
|
-
});
|