react-native-fxview 1.0.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/FXView.tsx ADDED
@@ -0,0 +1,90 @@
1
+ import React, { Component } from "react";
2
+ import { View, ViewProps } from "react-native";
3
+ import { ComponentItem } from "./types";
4
+ import { FXViewManager } from "./FXViewManager";
5
+
6
+ interface FXViewProps extends ViewProps {
7
+ fxViewId?: string; // 动态标识属性,可选
8
+ }
9
+
10
+ interface FXViewState {
11
+ components: ComponentItem[];
12
+ }
13
+
14
+ export default class FXView extends Component<FXViewProps, FXViewState> {
15
+ state: FXViewState = {
16
+ components: [],
17
+ };
18
+
19
+ private viewId: string;
20
+
21
+ constructor(props: FXViewProps) {
22
+ super(props);
23
+ // 使用 fxViewId,如果没有则生成一个唯一 ID
24
+ this.viewId =
25
+ props.fxViewId ||
26
+ `auto_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;
27
+ }
28
+
29
+ componentDidMount() {
30
+ console.log("FXView mounted with viewId:", this.viewId);
31
+ // 注册到视图管理器
32
+ FXViewManager.getInstance().registerView(
33
+ this.viewId,
34
+ this.updateComponents,
35
+ );
36
+ // 初始更新
37
+ this.updateComponents();
38
+ }
39
+
40
+ componentDidUpdate(_prevProps: FXViewProps) {
41
+ console.log("FXView updated", this.viewId);
42
+ const newViewId = this.props.fxViewId || this.viewId;
43
+
44
+ if (newViewId !== this.viewId) {
45
+ // ID 变化时重新注册
46
+ FXViewManager.getInstance().unregisterView(this.viewId);
47
+
48
+ this.viewId = newViewId;
49
+
50
+ FXViewManager.getInstance().registerView(
51
+ this.viewId,
52
+ this.updateComponents,
53
+ );
54
+ this.updateComponents();
55
+ }
56
+ }
57
+
58
+ componentWillUnmount() {
59
+ console.log("FXView unmounted", this.viewId);
60
+ // 注销视图(会自动清理所有组件和回调)
61
+ FXViewManager.getInstance().unregisterView(this.viewId);
62
+ }
63
+
64
+ updateComponents = () => {
65
+ const components = FXViewManager.getInstance().getComponents(this.viewId);
66
+ this.setState({ components: components });
67
+ };
68
+
69
+ render() {
70
+ const { children } = this.props;
71
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
72
+ const { fxViewId, ...restProps } = this.props;
73
+ const { components } = this.state;
74
+ console.log("rendering fxView components", components.length);
75
+ return (
76
+ <View {...restProps}>
77
+ {children}
78
+ {components.map((item) =>
79
+ item.visible ? (
80
+ <React.Fragment key={item.componentId}>
81
+ {item.component}
82
+ </React.Fragment>
83
+ ) : null,
84
+ )}
85
+ </View>
86
+ );
87
+ }
88
+ }
89
+
90
+ export type { FXViewProps };
@@ -0,0 +1,380 @@
1
+ import React from "react";
2
+ import { ComponentItem, ComponentController } from "./types";
3
+ import { HeapType, PriorityOrder, PriorityQueue } from "../queue/PriorityQueue";
4
+
5
+ export class FXViewCategoryController {
6
+ public fxViewId: string;
7
+ public categoryId: string;
8
+ private updateCallback?: () => void;
9
+
10
+ // 存储组件的 Map 和优先队列
11
+ private componentMap: Map<string, ComponentItem> = new Map();
12
+ private componentQueue: PriorityQueue<ComponentItem> = new PriorityQueue(
13
+ HeapType.MAX_HEAP,
14
+ PriorityOrder.FIFO,
15
+ );
16
+
17
+ constructor(
18
+ fxViewId: string,
19
+ categoryId: string,
20
+ triggerUpdate?: () => void,
21
+ ) {
22
+ this.fxViewId = fxViewId;
23
+ this.categoryId = categoryId;
24
+ this.updateCallback = triggerUpdate;
25
+ }
26
+
27
+ // ========== 主要 API 方法 ==========
28
+
29
+ /**
30
+ * 创建并展示组件
31
+ * @param component 要显示的组件
32
+ * @param componentId 可选的组件 ID
33
+ * @returns 组件控制器
34
+ */
35
+ add(component: React.ReactNode, componentId?: string): ComponentController {
36
+ console.log(
37
+ `FXViewCategoryController.add`,
38
+ `[${this.categoryId}]`,
39
+ componentId,
40
+ );
41
+
42
+ const finalComponentId = componentId || this.autoComponentId();
43
+
44
+ // 如果组件已存在,先移除旧的
45
+ if (this.componentMap.has(finalComponentId)) {
46
+ console.warn(
47
+ `Component ${finalComponentId} already exists, removing old one`,
48
+ );
49
+ this.remove(finalComponentId);
50
+ }
51
+
52
+ const componentItem: ComponentItem = {
53
+ componentId: finalComponentId,
54
+ component,
55
+ visible: true, // add 方法默认可见
56
+ };
57
+
58
+ this.componentMap.set(finalComponentId, componentItem);
59
+ this.componentQueue.enqueue(componentItem);
60
+ this.triggerUpdate();
61
+
62
+ return this.createController(finalComponentId);
63
+ }
64
+
65
+ /**
66
+ * 创建但不显示组件
67
+ * @param component 要创建的组件
68
+ * @param componentId 可选的组件 ID
69
+ * @returns 组件控制器
70
+ */
71
+ build(component: React.ReactNode, componentId?: string): ComponentController {
72
+ console.log(
73
+ `FXViewCategoryController.build`,
74
+ `[${this.categoryId}]`,
75
+ componentId,
76
+ );
77
+
78
+ const finalComponentId = componentId || this.autoComponentId();
79
+
80
+ // 如果组件已存在,先移除旧的
81
+ if (this.componentMap.has(finalComponentId)) {
82
+ console.warn(
83
+ `Component ${finalComponentId} already exists, removing old one`,
84
+ );
85
+ this.remove(finalComponentId);
86
+ }
87
+
88
+ const componentItem: ComponentItem = {
89
+ componentId: finalComponentId,
90
+ component,
91
+ visible: false, // build 方法默认不可见
92
+ };
93
+
94
+ this.componentMap.set(finalComponentId, componentItem);
95
+ this.componentQueue.enqueue(componentItem);
96
+
97
+ // build 不触发更新,因为组件不可见
98
+ // this.triggerUpdate();
99
+
100
+ return this.createController(finalComponentId);
101
+ }
102
+
103
+ /**
104
+ * 显示已存在的组件
105
+ * @param componentId 组件 ID
106
+ */
107
+ show(componentId: string): void {
108
+ console.log(
109
+ `FXViewCategoryController.show`,
110
+ `[${this.categoryId}]`,
111
+ componentId,
112
+ );
113
+
114
+ const componentItem = this.componentMap.get(componentId);
115
+
116
+ if (!componentItem) {
117
+ console.warn(
118
+ `Component ${componentId} not found in category ${this.categoryId}`,
119
+ );
120
+ return;
121
+ }
122
+
123
+ // 如果已经可见,不需要重复操作
124
+ if (componentItem.visible) {
125
+ console.log(`Component ${componentId} is already visible`);
126
+ return;
127
+ }
128
+
129
+ componentItem.visible = true;
130
+ this.triggerUpdate();
131
+ }
132
+
133
+ /**
134
+ * 隐藏但不删除组件
135
+ * @param componentId 组件 ID
136
+ */
137
+ hide(componentId: string): void {
138
+ console.log(
139
+ `FXViewCategoryController.hide`,
140
+ `[${this.categoryId}]`,
141
+ componentId,
142
+ );
143
+
144
+ const componentItem = this.componentMap.get(componentId);
145
+
146
+ if (!componentItem) {
147
+ console.warn(
148
+ `Component ${componentId} not found in category ${this.categoryId}`,
149
+ );
150
+ return;
151
+ }
152
+
153
+ // 如果已经隐藏,不需要重复操作
154
+ if (!componentItem.visible) {
155
+ console.log(`Component ${componentId} is already hidden`);
156
+ return;
157
+ }
158
+
159
+ componentItem.visible = false;
160
+ this.triggerUpdate();
161
+ }
162
+
163
+ /**
164
+ * 更新组件内容
165
+ * @param componentId 组件 ID
166
+ * @param component 新的组件内容
167
+ */
168
+ update(componentId: string, component: React.ReactNode): void {
169
+ console.log(
170
+ `FXViewCategoryController.update`,
171
+ `[${this.categoryId}]`,
172
+ componentId,
173
+ );
174
+
175
+ const componentItem = this.componentMap.get(componentId);
176
+
177
+ if (!componentItem) {
178
+ console.warn(
179
+ `Component ${componentId} not found in category ${this.categoryId}`,
180
+ );
181
+ return;
182
+ }
183
+
184
+ // 更新组件内容
185
+ componentItem.component = component;
186
+
187
+ // 只有在组件可见时才触发更新
188
+ if (componentItem.visible) {
189
+ this.triggerUpdate();
190
+ } else {
191
+ console.log(
192
+ `Component ${componentId} is hidden, update without re-render`,
193
+ );
194
+ }
195
+ }
196
+
197
+ /**
198
+ * 彻底删除组件
199
+ * @param componentId 组件 ID(必需)
200
+ */
201
+ remove(componentId: string): void {
202
+ console.log(
203
+ `FXViewCategoryController.remove`,
204
+ `[${this.categoryId}]`,
205
+ componentId,
206
+ );
207
+
208
+ const componentItem = this.componentMap.get(componentId);
209
+
210
+ if (!componentItem) {
211
+ console.warn(
212
+ `Component ${componentId} not found in category ${this.categoryId}`,
213
+ );
214
+ return;
215
+ }
216
+
217
+ // 从 Map 中删除
218
+ this.componentMap.delete(componentId);
219
+
220
+ // 从优先队列中删除
221
+ this.componentQueue.remove(componentItem);
222
+
223
+ this.triggerUpdate();
224
+ }
225
+
226
+ /**
227
+ * 移除最后一个组件(可选:用于向后兼容)
228
+ */
229
+ removeLast(): void {
230
+ console.log(`FXViewCategoryController.removeLast`, `[${this.categoryId}]`);
231
+
232
+ const lastComponent = this.componentQueue.peek();
233
+
234
+ if (!lastComponent) {
235
+ console.warn(`No component to remove in category ${this.categoryId}`);
236
+ return;
237
+ }
238
+
239
+ this.remove(lastComponent.componentId);
240
+ }
241
+
242
+ /**
243
+ * 清空所有组件
244
+ */
245
+ clearAll(): void {
246
+ console.log(`FXViewCategoryController.clearAll`, `[${this.categoryId}]`);
247
+
248
+ const hadVisibleComponents = Array.from(this.componentMap.values()).some(
249
+ (item) => item.visible,
250
+ );
251
+
252
+ this.componentMap.clear();
253
+ this.componentQueue.clear();
254
+
255
+ // 只有在有可见组件时才触发更新
256
+ if (hadVisibleComponents) {
257
+ this.triggerUpdate();
258
+ }
259
+ }
260
+
261
+ /**
262
+ * 注册更新回调
263
+ * @param updateCallback 更新回调函数
264
+ */
265
+ registerUpdateCallback(updateCallback?: () => void): void {
266
+ this.updateCallback = updateCallback;
267
+ }
268
+
269
+ /**
270
+ * 获取组件列表
271
+ * @returns 组件列表
272
+ */
273
+ getComponents(): ComponentItem[] {
274
+ return this.componentQueue.getAll();
275
+ }
276
+
277
+ /**
278
+ * 获取组件数量
279
+ * @returns 组件数量
280
+ */
281
+ getComponentCount(): number {
282
+ return this.componentMap.size;
283
+ }
284
+
285
+ /**
286
+ * 获取可见组件数量
287
+ * @returns 可见组件数量
288
+ */
289
+ getVisibleComponentCount(): number {
290
+ return Array.from(this.componentMap.values()).filter((item) => item.visible)
291
+ .length;
292
+ }
293
+
294
+ /**
295
+ * 检查组件是否存在
296
+ * @param componentId 组件 ID
297
+ * @returns 是否存在
298
+ */
299
+ hasComponent(componentId: string): boolean {
300
+ return this.componentMap.has(componentId);
301
+ }
302
+
303
+ /**
304
+ * 检查组件是否可见
305
+ * @param componentId 组件 ID
306
+ * @returns 是否可见
307
+ */
308
+ isVisible(componentId: string): boolean {
309
+ const component = this.componentMap.get(componentId);
310
+ return component?.visible ?? false;
311
+ }
312
+
313
+ // ========== 私有方法 ==========
314
+
315
+ /**
316
+ * 触发更新
317
+ */
318
+ private triggerUpdate(): void {
319
+ console.log(
320
+ `FXViewCategoryController.triggerUpdate`,
321
+ `[${this.categoryId}]`,
322
+ );
323
+
324
+ if (this.updateCallback) {
325
+ this.updateCallback();
326
+ }
327
+ }
328
+
329
+ /**
330
+ * 创建组件控制器
331
+ * @param componentId 组件 ID
332
+ * @returns 组件控制器
333
+ */
334
+ private createController(componentId: string): ComponentController {
335
+ console.log(
336
+ `FXViewCategoryController.createController`,
337
+ `[${this.categoryId}]`,
338
+ componentId,
339
+ );
340
+
341
+ // ✅ 修复:不在闭包中捕获 componentItem,每次都从 Map 获取最新引用
342
+ return {
343
+ show: () => {
344
+ this.show(componentId);
345
+ },
346
+ hide: () => {
347
+ this.hide(componentId);
348
+ },
349
+ remove: () => {
350
+ this.remove(componentId);
351
+ },
352
+ update: (component: React.ReactNode) => {
353
+ this.update(componentId, component);
354
+ },
355
+ getFxViewId: () => this.fxViewId,
356
+ getCategoryId: () => this.categoryId,
357
+ getComponentId: () => componentId,
358
+ getComponent: () => {
359
+ // ✅ 每次都从 Map 获取最新的组件引用
360
+ return this.componentMap.get(componentId)?.component;
361
+ },
362
+ isVisible: () => {
363
+ return this.isVisible(componentId);
364
+ },
365
+ exists: () => {
366
+ return this.hasComponent(componentId);
367
+ },
368
+ };
369
+ }
370
+
371
+ /**
372
+ * 自动生成 componentId
373
+ * @returns 生成的 componentId
374
+ */
375
+ private autoComponentId(): string {
376
+ return `component_${Date.now()}_${Math.random()
377
+ .toString(36)
378
+ .substring(2, 9)}`;
379
+ }
380
+ }
@@ -0,0 +1,370 @@
1
+ import React from "react";
2
+ import { ComponentController, ComponentItem } from "./types";
3
+ import { FXViewCategoryController } from "./FXViewCategoryController";
4
+
5
+ export class FXViewController {
6
+ public fxViewId: string;
7
+ public updateCallback?: () => void;
8
+
9
+ // 存储各分类的控制器
10
+ private categoryControllerMap: Map<string, FXViewCategoryController> =
11
+ new Map();
12
+
13
+ constructor(fxViewId: string) {
14
+ this.fxViewId = fxViewId;
15
+ }
16
+
17
+ //#region ========== 主要 API 方法 ==========
18
+
19
+ /**
20
+ * 创建并显示组件
21
+ * @param component 组件内容
22
+ * @param categoryId 分类 ID(可选,默认 "default")
23
+ * @param componentId 组件 ID(可选,自动生成)
24
+ * @returns 组件控制器
25
+ */
26
+ add(
27
+ component: React.ReactNode,
28
+ categoryId?: string,
29
+ componentId?: string,
30
+ ): ComponentController {
31
+ console.log("FXViewController.add", `[${this.fxViewId}]`, {
32
+ categoryId,
33
+ componentId,
34
+ });
35
+
36
+ const categoryController = this.getCategoryControllerOrCreate(categoryId);
37
+ return categoryController.add(component, componentId);
38
+ }
39
+
40
+ /**
41
+ * 创建但不显示组件
42
+ * @param component 组件内容
43
+ * @param categoryId 分类 ID(可选,默认 "default")
44
+ * @param componentId 组件 ID(可选,自动生成)
45
+ * @returns 组件控制器
46
+ */
47
+ build(
48
+ component: React.ReactNode,
49
+ categoryId?: string,
50
+ componentId?: string,
51
+ ): ComponentController {
52
+ console.log("FXViewController.build", `[${this.fxViewId}]`, {
53
+ categoryId,
54
+ componentId,
55
+ });
56
+
57
+ const categoryController = this.getCategoryControllerOrCreate(categoryId);
58
+ return categoryController.build(component, componentId);
59
+ }
60
+
61
+ /**
62
+ * 显示已存在的组件
63
+ * @param componentId 组件 ID
64
+ * @param categoryId 分类 ID(可选,默认 "default")
65
+ */
66
+ show(componentId: string, categoryId?: string): void {
67
+ console.log("FXViewController.show", `[${this.fxViewId}]`, {
68
+ categoryId,
69
+ componentId,
70
+ });
71
+
72
+ const categoryController = this.getCategoryController(categoryId);
73
+ if (!categoryController) {
74
+ console.warn(
75
+ `CategoryController ${categoryId || "default"} not found in view ${this.fxViewId}`,
76
+ );
77
+ return;
78
+ }
79
+
80
+ categoryController.show(componentId);
81
+ }
82
+
83
+ /**
84
+ * 隐藏但不删除组件
85
+ * @param componentId 组件 ID
86
+ * @param categoryId 分类 ID(可选,默认 "default")
87
+ */
88
+ hide(componentId: string, categoryId?: string): void {
89
+ console.log("FXViewController.hide", `[${this.fxViewId}]`, {
90
+ categoryId,
91
+ componentId,
92
+ });
93
+
94
+ const categoryController = this.getCategoryController(categoryId);
95
+ if (!categoryController) {
96
+ console.warn(
97
+ `CategoryController ${categoryId || "default"} not found in view ${this.fxViewId}`,
98
+ );
99
+ return;
100
+ }
101
+
102
+ categoryController.hide(componentId);
103
+ }
104
+
105
+ /**
106
+ * 更新组件内容
107
+ * @param componentId 组件 ID
108
+ * @param component 新的组件内容
109
+ * @param categoryId 分类 ID(可选,默认 "default")
110
+ */
111
+ update(
112
+ componentId: string,
113
+ component: React.ReactNode,
114
+ categoryId?: string,
115
+ ): void {
116
+ console.log("FXViewController.update", `[${this.fxViewId}]`, {
117
+ categoryId,
118
+ componentId,
119
+ });
120
+
121
+ const categoryController = this.getCategoryController(categoryId);
122
+ if (!categoryController) {
123
+ console.warn(
124
+ `CategoryController ${categoryId || "default"} not found in view ${this.fxViewId}`,
125
+ );
126
+ return;
127
+ }
128
+
129
+ categoryController.update(componentId, component);
130
+ }
131
+
132
+ /**
133
+ * 彻底删除组件
134
+ * @param componentId 组件 ID
135
+ * @param categoryId 分类 ID(可选,默认 "default")
136
+ */
137
+ remove(componentId: string, categoryId?: string): void {
138
+ console.log("FXViewController.remove", `[${this.fxViewId}]`, {
139
+ categoryId,
140
+ componentId,
141
+ });
142
+
143
+ const categoryController = this.getCategoryController(categoryId);
144
+ if (!categoryController) {
145
+ console.warn(
146
+ `CategoryController ${categoryId || "default"} not found in view ${this.fxViewId}`,
147
+ );
148
+ return;
149
+ }
150
+
151
+ categoryController.remove(componentId);
152
+
153
+ // 如果该分类没有组件了,删除整个分类控制器
154
+ if (categoryController.getComponentCount() === 0) {
155
+ this.removeCategory(categoryId);
156
+ }
157
+ }
158
+
159
+ /**
160
+ * 移除最后一个组件(向后兼容)
161
+ * @param categoryId 分类 ID(可选,默认 "default")
162
+ */
163
+ removeLast(categoryId?: string): void {
164
+ console.log("FXViewController.removeLast", `[${this.fxViewId}]`, {
165
+ categoryId,
166
+ });
167
+
168
+ const categoryController = this.getCategoryController(categoryId);
169
+ if (!categoryController) {
170
+ console.warn(
171
+ `CategoryController ${categoryId || "default"} not found in view ${this.fxViewId}`,
172
+ );
173
+ return;
174
+ }
175
+
176
+ categoryController.removeLast();
177
+
178
+ // 如果该分类没有组件了,删除整个分类控制器
179
+ if (categoryController.getComponentCount() === 0) {
180
+ this.removeCategory(categoryId);
181
+ }
182
+ }
183
+
184
+ /**
185
+ * 清空所有分类的所有组件
186
+ */
187
+ clearAll(): void {
188
+ console.log("FXViewController.clearAll", `[${this.fxViewId}]`);
189
+
190
+ this.categoryControllerMap.forEach((controller) => {
191
+ controller.clearAll();
192
+ });
193
+ this.categoryControllerMap.clear();
194
+ }
195
+
196
+ /**
197
+ * 清空指定分类的所有组件
198
+ * @param categoryId 分类 ID
199
+ */
200
+ clearCategory(categoryId?: string): void {
201
+ const finalCategoryId = categoryId || "default";
202
+ console.log("FXViewController.clearCategory", `[${this.fxViewId}]`, {
203
+ categoryId: finalCategoryId,
204
+ });
205
+
206
+ const categoryController = this.getCategoryController(categoryId);
207
+ if (!categoryController) {
208
+ console.warn(
209
+ `CategoryController ${finalCategoryId} not found in view ${this.fxViewId}`,
210
+ );
211
+ return;
212
+ }
213
+
214
+ categoryController.clearAll();
215
+ this.categoryControllerMap.delete(finalCategoryId);
216
+ }
217
+
218
+ /**
219
+ * 注册更新回调
220
+ * @param callback 更新回调函数
221
+ */
222
+ registerUpdateCallback(callback?: () => void): void {
223
+ this.updateCallback = callback;
224
+
225
+ // 将回调传递给所有已存在的分类控制器
226
+ this.categoryControllerMap.forEach((controller) => {
227
+ controller.registerUpdateCallback(callback);
228
+ });
229
+ }
230
+
231
+ /**
232
+ * 获取组件列表
233
+ * @param filterOptions 过滤选项
234
+ * @returns 组件列表
235
+ */
236
+ getComponents(categoryId?: string): ComponentItem[] {
237
+ const result: ComponentItem[] = [];
238
+
239
+ this.categoryControllerMap.forEach((categoryController, category) => {
240
+ // 如果指定了类别,只处理指定类别
241
+ if (categoryId && categoryId !== category) {
242
+ return;
243
+ }
244
+ const categoryComponents = categoryController.getComponents();
245
+ result.push(...categoryComponents);
246
+ });
247
+
248
+ return result;
249
+ }
250
+
251
+ /**
252
+ * 获取总组件数量
253
+ * @returns 组件数量
254
+ */
255
+ getComponentCount(): number {
256
+ let count = 0;
257
+ this.categoryControllerMap.forEach((controller) => {
258
+ count += controller.getComponentCount();
259
+ });
260
+ return count;
261
+ }
262
+
263
+ /**
264
+ * 获取可见组件数量
265
+ * @returns 可见组件数量
266
+ */
267
+ getVisibleComponentCount(): number {
268
+ let count = 0;
269
+ this.categoryControllerMap.forEach((controller) => {
270
+ count += controller.getVisibleComponentCount();
271
+ });
272
+ return count;
273
+ }
274
+
275
+ /**
276
+ * 检查组件是否存在
277
+ * @param componentId 组件 ID
278
+ * @param categoryId 分类 ID(可选)
279
+ * @returns 是否存在
280
+ */
281
+ hasComponent(componentId: string, categoryId?: string): boolean {
282
+ if (categoryId) {
283
+ const controller = this.getCategoryController(categoryId);
284
+ return controller?.hasComponent(componentId) ?? false;
285
+ }
286
+
287
+ // 如果没有指定分类,搜索所有分类
288
+ for (const controller of this.categoryControllerMap.values()) {
289
+ if (controller.hasComponent(componentId)) {
290
+ return true;
291
+ }
292
+ }
293
+ return false;
294
+ }
295
+
296
+ /**
297
+ * 检查组件是否可见
298
+ * @param componentId 组件 ID
299
+ * @param categoryId 分类 ID(可选)
300
+ * @returns 是否可见
301
+ */
302
+ isComponentVisible(componentId: string, categoryId?: string): boolean {
303
+ if (categoryId) {
304
+ const controller = this.getCategoryController(categoryId);
305
+ return controller?.isVisible(componentId) ?? false;
306
+ }
307
+
308
+ // 如果没有指定分类,搜索所有分类
309
+ for (const controller of this.categoryControllerMap.values()) {
310
+ if (controller.hasComponent(componentId)) {
311
+ return controller.isVisible(componentId);
312
+ }
313
+ }
314
+ return false;
315
+ }
316
+
317
+ //#endregion
318
+
319
+ //#region ========== 私有方法 ==========
320
+
321
+ /**
322
+ * 获取分类控制器
323
+ * @param categoryId 分类 ID
324
+ * @returns 分类控制器或 null
325
+ */
326
+ private getCategoryController(
327
+ categoryId?: string,
328
+ ): FXViewCategoryController | null {
329
+ const finalCategoryId = categoryId || "default";
330
+ return this.categoryControllerMap.get(finalCategoryId) || null;
331
+ }
332
+
333
+ /**
334
+ * 获取或创建分类控制器
335
+ * @param categoryId 分类 ID
336
+ * @returns 分类控制器
337
+ */
338
+ private getCategoryControllerOrCreate(
339
+ categoryId?: string,
340
+ ): FXViewCategoryController {
341
+ const finalCategoryId = categoryId || "default";
342
+
343
+ let categoryController = this.categoryControllerMap.get(finalCategoryId);
344
+
345
+ if (!categoryController) {
346
+ categoryController = new FXViewCategoryController(
347
+ this.fxViewId,
348
+ finalCategoryId,
349
+ this.updateCallback,
350
+ );
351
+ this.categoryControllerMap.set(finalCategoryId, categoryController);
352
+ }
353
+
354
+ return categoryController;
355
+ }
356
+
357
+ /**
358
+ * 删除分类控制器
359
+ * @param categoryId 分类 ID
360
+ */
361
+ private removeCategory(categoryId?: string): void {
362
+ const finalCategoryId = categoryId || "default";
363
+ console.log("FXViewController.removeCategory", `[${this.fxViewId}]`, {
364
+ categoryId: finalCategoryId,
365
+ });
366
+ this.categoryControllerMap.delete(finalCategoryId);
367
+ }
368
+
369
+ //#endregion
370
+ }
@@ -0,0 +1,421 @@
1
+ import React from "react";
2
+ import { ComponentController, ComponentItem } from "./types";
3
+ import { FXViewController } from "./FXViewController";
4
+ import { HeapType, PriorityOrder, PriorityQueue } from "../queue/PriorityQueue";
5
+
6
+ export class FXViewManager {
7
+ private static instance: FXViewManager;
8
+
9
+ // 存储视图控制器
10
+ private viewControllerMap: Map<string, FXViewController> = new Map();
11
+ private viewControllerQueue: PriorityQueue<FXViewController> =
12
+ new PriorityQueue(HeapType.MAX_HEAP, PriorityOrder.LIFO);
13
+
14
+ static getInstance(): FXViewManager {
15
+ if (!FXViewManager.instance) {
16
+ FXViewManager.instance = new FXViewManager();
17
+ }
18
+ return FXViewManager.instance;
19
+ }
20
+
21
+ //#region ========== 主要 API 方法 ==========
22
+
23
+ /**
24
+ * 创建并显示组件
25
+ * @param component 组件内容
26
+ * @param fxViewId 视图 ID(可选,使用最近的视图)
27
+ * @param categoryId 分类 ID(可选,默认 "default")
28
+ * @param componentId 组件 ID(可选,自动生成)
29
+ * @returns 组件控制器
30
+ */
31
+ add(
32
+ component: React.ReactNode,
33
+ fxViewId?: string,
34
+ categoryId?: string,
35
+ componentId?: string,
36
+ ): ComponentController {
37
+ console.log("FXViewManager.add", {
38
+ fxViewId,
39
+ categoryId,
40
+ componentId,
41
+ });
42
+
43
+ const viewController = this.getViewControllerOrCreate(fxViewId);
44
+ return viewController.add(component, categoryId, componentId);
45
+ }
46
+
47
+ /**
48
+ * 创建但不显示组件
49
+ * @param component 组件内容
50
+ * @param fxViewId 视图 ID(可选,使用最近的视图)
51
+ * @param categoryId 分类 ID(可选,默认 "default")
52
+ * @param componentId 组件 ID(可选,自动生成)
53
+ * @returns 组件控制器
54
+ */
55
+ build(
56
+ component: React.ReactNode,
57
+ fxViewId?: string,
58
+ categoryId?: string,
59
+ componentId?: string,
60
+ ): ComponentController {
61
+ console.log("FXViewManager.build", {
62
+ fxViewId,
63
+ categoryId,
64
+ componentId,
65
+ });
66
+
67
+ const viewController = this.getViewControllerOrCreate(fxViewId);
68
+ return viewController.build(component, categoryId, componentId);
69
+ }
70
+
71
+ /**
72
+ * 显示已存在的组件
73
+ * @param componentId 组件 ID
74
+ * @param fxViewId 视图 ID(可选,使用最近的视图)
75
+ * @param categoryId 分类 ID(可选,默认 "default")
76
+ */
77
+ show(componentId: string, fxViewId?: string, categoryId?: string): void {
78
+ console.log("FXViewManager.show", {
79
+ fxViewId,
80
+ categoryId,
81
+ componentId,
82
+ });
83
+
84
+ const viewController = this.getViewController(fxViewId);
85
+ if (!viewController) {
86
+ console.warn(`FXView ${fxViewId || "latest"} not found`);
87
+ return;
88
+ }
89
+
90
+ viewController.show(componentId, categoryId);
91
+ }
92
+
93
+ /**
94
+ * 隐藏但不删除组件
95
+ * @param componentId 组件 ID
96
+ * @param fxViewId 视图 ID(可选,使用最近的视图)
97
+ * @param categoryId 分类 ID(可选,默认 "default")
98
+ */
99
+ hide(componentId: string, fxViewId?: string, categoryId?: string): void {
100
+ console.log("FXViewManager.hide", {
101
+ fxViewId,
102
+ categoryId,
103
+ componentId,
104
+ });
105
+
106
+ const viewController = this.getViewController(fxViewId);
107
+ if (!viewController) {
108
+ console.warn(`FXView ${fxViewId || "latest"} not found`);
109
+ return;
110
+ }
111
+
112
+ viewController.hide(componentId, categoryId);
113
+ }
114
+
115
+ /**
116
+ * 更新组件内容
117
+ * @param componentId 组件 ID
118
+ * @param component 新的组件内容
119
+ * @param fxViewId 视图 ID(可选,使用最近的视图)
120
+ * @param categoryId 分类 ID(可选,默认 "default")
121
+ */
122
+ update(
123
+ componentId: string,
124
+ component: React.ReactNode,
125
+ fxViewId?: string,
126
+ categoryId?: string,
127
+ ): void {
128
+ console.log("FXViewManager.update", {
129
+ fxViewId,
130
+ categoryId,
131
+ componentId,
132
+ });
133
+
134
+ const viewController = this.getViewController(fxViewId);
135
+ if (!viewController) {
136
+ console.warn(`FXView ${fxViewId || "latest"} not found`);
137
+ return;
138
+ }
139
+
140
+ viewController.update(componentId, component, categoryId);
141
+ }
142
+
143
+ /**
144
+ * 彻底删除组件
145
+ * @param componentId 组件 ID
146
+ * @param fxViewId 视图 ID(可选,使用最近的视图)
147
+ * @param categoryId 分类 ID(可选,默认 "default")
148
+ */
149
+ remove(componentId: string, fxViewId?: string, categoryId?: string): void {
150
+ console.log("FXViewManager.remove", {
151
+ fxViewId,
152
+ categoryId,
153
+ componentId,
154
+ });
155
+
156
+ const viewController = this.getViewController(fxViewId);
157
+ if (!viewController) {
158
+ console.warn(`FXView ${fxViewId || "latest"} not found`);
159
+ return;
160
+ }
161
+
162
+ viewController.remove(componentId, categoryId);
163
+ }
164
+
165
+ /**
166
+ * 移除最后一个组件(向后兼容)
167
+ * @param fxViewId 视图 ID(可选,使用最近的视图)
168
+ * @param categoryId 分类 ID(可选,默认 "default")
169
+ */
170
+ removeLast(fxViewId?: string, categoryId?: string): void {
171
+ console.log("FXViewManager.removeLast", {
172
+ fxViewId,
173
+ categoryId,
174
+ });
175
+
176
+ const viewController = this.getViewController(fxViewId);
177
+ if (!viewController) {
178
+ console.warn(`FXView ${fxViewId || "latest"} not found`);
179
+ return;
180
+ }
181
+
182
+ viewController.removeLast(categoryId);
183
+ }
184
+
185
+ /**
186
+ * 清空指定视图的所有组件
187
+ * @param fxViewId 视图 ID(可选,清空所有视图)
188
+ */
189
+ clearAll(fxViewId?: string): void {
190
+ console.log("FXViewManager.clearAll", { fxViewId });
191
+
192
+ if (fxViewId) {
193
+ // 清空指定视图
194
+ const viewController = this.getViewController(fxViewId);
195
+ if (viewController) {
196
+ viewController.clearAll();
197
+ }
198
+ } else {
199
+ // 清空所有视图
200
+ this.viewControllerMap.forEach((controller) => {
201
+ controller.clearAll();
202
+ });
203
+ }
204
+ }
205
+
206
+ /**
207
+ * 清空指定分类的所有组件
208
+ * @param categoryId 分类 ID
209
+ * @param fxViewId 视图 ID(可选,使用最近的视图)
210
+ */
211
+ clearCategory(categoryId: string, fxViewId?: string): void {
212
+ console.log("FXViewManager.clearCategory", { fxViewId, categoryId });
213
+
214
+ const viewController = this.getViewController(fxViewId);
215
+ if (!viewController) {
216
+ console.warn(`FXView ${fxViewId || "latest"} not found`);
217
+ return;
218
+ }
219
+
220
+ viewController.clearCategory(categoryId);
221
+ }
222
+
223
+ //#endregion
224
+
225
+ //#region ========== 视图管理方法 ==========
226
+
227
+ /**
228
+ * 注册视图(FXView 挂载时调用)
229
+ * @param fxViewId 视图 ID
230
+ * @param updateCallback 更新回调函数
231
+ * @returns 视图控制器
232
+ */
233
+ registerView(fxViewId: string, updateCallback: () => void): FXViewController {
234
+ console.log("FXViewManager.registerView", fxViewId);
235
+
236
+ let viewController = this.viewControllerMap.get(fxViewId);
237
+
238
+ if (!viewController) {
239
+ viewController = new FXViewController(fxViewId);
240
+ this.viewControllerMap.set(fxViewId, viewController);
241
+ }
242
+
243
+ // 注册更新回调
244
+ viewController.registerUpdateCallback(updateCallback);
245
+
246
+ // 更新视图栈(移除旧的引用,添加新的)
247
+ const existingController = this.viewControllerQueue
248
+ .getAll()
249
+ .find((vc) => vc.fxViewId === fxViewId);
250
+ if (existingController) {
251
+ this.viewControllerQueue.remove(existingController);
252
+ }
253
+ this.viewControllerQueue.enqueue(viewController);
254
+
255
+ return viewController;
256
+ }
257
+
258
+ /**
259
+ * 注销视图(FXView 卸载时调用)
260
+ * @param fxViewId 视图 ID
261
+ * @returns 是否成功注销
262
+ */
263
+ unregisterView(fxViewId: string): boolean {
264
+ console.log("FXViewManager.unregisterView", fxViewId);
265
+
266
+ const viewController = this.getViewController(fxViewId);
267
+ if (!viewController) {
268
+ console.warn(`FXView ${fxViewId} not found, cannot unregister`);
269
+ return false;
270
+ }
271
+
272
+ // 清空回调
273
+ viewController.registerUpdateCallback(undefined);
274
+
275
+ // 清空所有组件
276
+ viewController.clearAll();
277
+
278
+ // 从队列中移除
279
+ this.viewControllerQueue.remove(viewController);
280
+
281
+ // 从 Map 中删除
282
+ this.viewControllerMap.delete(fxViewId);
283
+
284
+ return true;
285
+ }
286
+
287
+ /**
288
+ * 获取最近使用的视图 ID(栈顶)
289
+ * @returns 最近的视图 ID 或 null
290
+ */
291
+ getLatestFXViewId(): string | null {
292
+ return this.viewControllerQueue.peek()?.fxViewId || null;
293
+ }
294
+
295
+ /**
296
+ * 获取组件列表
297
+ * @param fxViewId 视图 ID(可选,使用最近的视图)
298
+ * @param categoryId 分类 ID(可选)
299
+ * @returns 组件列表
300
+ */
301
+ getComponents(fxViewId?: string, categoryId?: string): ComponentItem[] {
302
+ console.log("FXViewManager.getComponents", { fxViewId, categoryId });
303
+
304
+ const finalFXViewId = fxViewId || this.getLatestFXViewId();
305
+ if (!finalFXViewId) {
306
+ console.warn("No FXView available");
307
+ return [];
308
+ }
309
+
310
+ const viewController = this.getViewController(finalFXViewId);
311
+ if (!viewController) {
312
+ console.warn(`FXViewController ${finalFXViewId} not found`);
313
+ return [];
314
+ }
315
+
316
+ return viewController.getComponents(categoryId);
317
+ }
318
+
319
+ /**
320
+ * 获取组件数量
321
+ * @param fxViewId 视图 ID(可选,使用最近的视图)
322
+ * @returns 组件数量
323
+ */
324
+ getComponentCount(fxViewId?: string): number {
325
+ const viewController = this.getViewController(fxViewId);
326
+ return viewController?.getComponentCount() ?? 0;
327
+ }
328
+
329
+ /**
330
+ * 获取可见组件数量
331
+ * @param fxViewId 视图 ID(可选,使用最近的视图)
332
+ * @returns 可见组件数量
333
+ */
334
+ getVisibleComponentCount(fxViewId?: string): number {
335
+ const viewController = this.getViewController(fxViewId);
336
+ return viewController?.getVisibleComponentCount() ?? 0;
337
+ }
338
+
339
+ /**
340
+ * 检查组件是否存在
341
+ * @param componentId 组件 ID
342
+ * @param fxViewId 视图 ID(可选,使用最近的视图)
343
+ * @param categoryId 分类 ID(可选)
344
+ * @returns 是否存在
345
+ */
346
+ hasComponent(
347
+ componentId: string,
348
+ fxViewId?: string,
349
+ categoryId?: string,
350
+ ): boolean {
351
+ const viewController = this.getViewController(fxViewId);
352
+ return viewController?.hasComponent(componentId, categoryId) ?? false;
353
+ }
354
+
355
+ /**
356
+ * 检查组件是否可见
357
+ * @param componentId 组件 ID
358
+ * @param fxViewId 视图 ID(可选,使用最近的视图)
359
+ * @param categoryId 分类 ID(可选)
360
+ * @returns 是否可见
361
+ */
362
+ isComponentVisible(
363
+ componentId: string,
364
+ fxViewId?: string,
365
+ categoryId?: string,
366
+ ): boolean {
367
+ const viewController = this.getViewController(fxViewId);
368
+ return viewController?.isComponentVisible(componentId, categoryId) ?? false;
369
+ }
370
+
371
+ //#endregion
372
+
373
+ //#region ========== 私有方法 ==========
374
+
375
+ /**
376
+ * 获取视图控制器
377
+ * @param fxViewId 视图 ID(可选,使用最近的视图)
378
+ * @returns 视图控制器或 null
379
+ */
380
+ private getViewController(fxViewId?: string): FXViewController | null {
381
+ const finalFXViewId = fxViewId || this.getLatestFXViewId();
382
+ if (!finalFXViewId) {
383
+ console.warn("FXViewManager.getViewController: no fxViewId available");
384
+ return null;
385
+ }
386
+ return this.viewControllerMap.get(finalFXViewId) || null;
387
+ }
388
+
389
+ /**
390
+ * 获取或创建视图控制器
391
+ * @param fxViewId 视图 ID(可选,使用最近的视图或创建新的)
392
+ * @returns 视图控制器
393
+ */
394
+ private getViewControllerOrCreate(fxViewId?: string): FXViewController {
395
+ const finalFXViewId =
396
+ fxViewId || this.getLatestFXViewId() || this.autoFXViewId();
397
+
398
+ let viewController = this.viewControllerMap.get(finalFXViewId);
399
+
400
+ if (!viewController) {
401
+ console.warn(
402
+ `FXViewController ${finalFXViewId} not registered, creating a temporary one`,
403
+ );
404
+ viewController = new FXViewController(finalFXViewId);
405
+ this.viewControllerMap.set(finalFXViewId, viewController);
406
+ this.viewControllerQueue.enqueue(viewController);
407
+ }
408
+
409
+ return viewController;
410
+ }
411
+
412
+ /**
413
+ * 自动生成 fxViewId
414
+ * @returns 生成的 fxViewId
415
+ */
416
+ private autoFXViewId(): string {
417
+ return `fxView_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;
418
+ }
419
+
420
+ //#endregion
421
+ }
package/package.json ADDED
@@ -0,0 +1,11 @@
1
+ {
2
+ "name": "react-native-fxview",
3
+ "version": "1.0.0",
4
+ "description": "dynamic view ",
5
+ "main": "index.js",
6
+ "scripts": {
7
+ "test": "echo \"Error: no test specified\" && exit 1"
8
+ },
9
+ "author": "",
10
+ "license": "ISC"
11
+ }
package/types.ts ADDED
@@ -0,0 +1,39 @@
1
+ import type React from "react";
2
+
3
+ // ========== 参数配置接口 ==========
4
+
5
+ /**
6
+ * 组件控制器接口
7
+ * 用于控制动态组件的显示、隐藏、更新等操作
8
+ */
9
+ export interface ComponentController {
10
+ /** 显示组件 */
11
+ show: () => void;
12
+ /** 隐藏组件 */
13
+ hide: () => void;
14
+ /** 移除组件 */
15
+ remove: () => void;
16
+ /** 更新组件内容 */
17
+ update: (component: React.ReactNode) => void;
18
+ /** 获取所属的 FXView ID */
19
+ getFxViewId: () => string;
20
+ /** 获取所属的分类 ID */
21
+ getCategoryId: () => string;
22
+ /** 获取组件 ID */
23
+ getComponentId: () => string;
24
+ /** 获取组件实例 */
25
+ getComponent: () => React.ReactNode;
26
+ /** 判断组件是否可见 */
27
+ isVisible: () => boolean;
28
+ /** 判断组件是否存在 */
29
+ exists: () => boolean;
30
+ }
31
+
32
+ /**
33
+ * 组件项配置接口
34
+ */
35
+ export interface ComponentItem {
36
+ componentId: string; // 组件唯一标识(必填)
37
+ component?: React.ReactNode; // 组件内容
38
+ visible?: boolean; // 是否可见,默认为 true
39
+ }