@taicode/common-web 1.1.10 → 1.1.13
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/output/catalyst/alert.jsx +55 -0
- package/output/catalyst/auth-layout.jsx +7 -0
- package/output/catalyst/avatar.jsx +45 -0
- package/output/catalyst/badge.jsx +53 -0
- package/output/catalyst/button.jsx +187 -0
- package/output/catalyst/checkbox.jsx +105 -0
- package/output/catalyst/combobox.jsx +120 -0
- package/output/catalyst/description-list.jsx +24 -0
- package/output/catalyst/dialog.jsx +55 -0
- package/output/catalyst/divider.jsx +16 -0
- package/output/catalyst/dropdown.jsx +102 -0
- package/output/catalyst/fieldset.jsx +41 -0
- package/output/catalyst/heading.jsx +22 -0
- package/output/catalyst/input.jsx +73 -0
- package/output/catalyst/link.jsx +14 -0
- package/output/catalyst/listbox.jsx +120 -0
- package/output/catalyst/navbar.jsx +67 -0
- package/output/catalyst/pagination.jsx +52 -0
- package/output/catalyst/radio.jsx +103 -0
- package/output/catalyst/select.jsx +59 -0
- package/output/catalyst/sidebar-layout.jsx +58 -0
- package/output/catalyst/sidebar.jsx +85 -0
- package/output/catalyst/stacked-layout.jsx +55 -0
- package/output/catalyst/switch.jsx +161 -0
- package/output/catalyst/table.jsx +68 -0
- package/output/catalyst/text.jsx +29 -0
- package/output/catalyst/textarea.jsx +49 -0
- package/output/helpers/cache-api/cache-api.d.ts +13 -0
- package/output/helpers/cache-api/cache-api.d.ts.map +1 -0
- package/output/helpers/cache-api/cache-api.js +114 -0
- package/output/helpers/cache-api/cache-api.test.d.ts +2 -0
- package/output/helpers/cache-api/cache-api.test.d.ts.map +1 -0
- package/output/helpers/cache-api/cache-api.test.js +348 -0
- package/output/helpers/cache-api/index.d.ts +2 -0
- package/output/helpers/cache-api/index.d.ts.map +1 -0
- package/output/helpers/cache-api/index.js +1 -0
- package/output/helpers/service/index.d.ts +1 -0
- package/output/helpers/service/index.d.ts.map +1 -0
- package/output/helpers/service/index.js +1 -0
- package/output/helpers/service/service.d.ts +5 -0
- package/output/helpers/service/service.d.ts.map +1 -0
- package/output/helpers/service/service.js +2 -0
- package/output/helpers/side-cache/index.d.ts +2 -0
- package/output/helpers/side-cache/index.d.ts.map +1 -0
- package/output/helpers/side-cache/index.js +1 -0
- package/output/helpers/side-cache/side-cache.d.ts +10 -0
- package/output/helpers/side-cache/side-cache.d.ts.map +1 -0
- package/output/helpers/side-cache/side-cache.js +137 -0
- package/output/helpers/side-cache/side-cache.test.d.ts +2 -0
- package/output/helpers/side-cache/side-cache.test.d.ts.map +1 -0
- package/output/helpers/side-cache/side-cache.test.js +179 -0
- package/output/helpers/use-observer/index.d.ts +2 -0
- package/output/helpers/use-observer/index.d.ts.map +1 -0
- package/output/helpers/use-observer/index.js +1 -0
- package/output/helpers/use-observer/use-observer.d.ts +3 -0
- package/output/helpers/use-observer/use-observer.d.ts.map +1 -0
- package/output/helpers/use-observer/use-observer.js +16 -0
- package/output/helpers/use-observer/use-observer.test.d.ts +2 -0
- package/output/helpers/use-observer/use-observer.test.d.ts.map +1 -0
- package/output/helpers/use-observer/use-observer.test.jsx +134 -0
- package/output/service/service.d.ts +98 -14
- package/output/service/service.d.ts.map +1 -1
- package/output/service/service.js +65 -10
- package/output/service/service.test.jsx +367 -0
- package/output/toaster/index.d.ts +6 -0
- package/output/toaster/index.d.ts.map +1 -1
- package/output/toaster/index.js +37 -21
- package/output/use-observer/use-observer.test.jsx +134 -0
- package/package.json +7 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../../source/service/service.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AAIH,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAA;AAC5D,OAAO,KAA2D,MAAM,OAAO,CAAA;AA+D/E
|
|
1
|
+
{"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../../source/service/service.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AAIH,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAA;AAC5D,OAAO,KAA2D,MAAM,OAAO,CAAA;AA+D/E;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwEG;AAEH,wBAAgB,UAAU,CAAC,CAAC,EAAE,MAAM,EAAE,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,CAAA;AAClD,wBAAgB,UAAU,CAAC,CAAC,EAAE,CAAC,EAAE,MAAM,EAAE,KAAK,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,OAAO,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAAA;AAgDlF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwEG;AACH,wBAAgB,eAAe,CAAC,CAAC,EAAE,YAAY,EAAE,KAAK,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,CAAC,GAAG,CAAC,CAAA;AAC9E,wBAAgB,eAAe,CAAC,CAAC,EAAE,CAAC,EAAE,YAAY,EAAE,KAAK,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,CAAC,EAAE,QAAQ,EAAE,CAAC,OAAO,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAAA;AAyF9G;;GAEG;AACH,UAAU,oBAAoB;IAC5B;;;;;OAKG;IACH,QAAQ,EAAE,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAA;IAC7B,UAAU;IACV,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAA;CAC1B;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiFG;AACH,6CAA6C;AAE7C,wBAAgB,eAAe,CAAC,KAAK,EAAE,oBAAoB,8EA2B1D"}
|
|
@@ -196,12 +196,14 @@ export function useLocalService(ServiceClass, selector) {
|
|
|
196
196
|
* 特性:
|
|
197
197
|
* - 自动复用父级容器,避免重复创建
|
|
198
198
|
* - 支持容器层级结构,子容器可以覆盖父容器的服务
|
|
199
|
-
* -
|
|
199
|
+
* - 支持两种注入方式:类注入和值注入
|
|
200
|
+
* - 自动初始化实现了 Service 接口的服务(仅限类注入)
|
|
200
201
|
*
|
|
201
202
|
* @param props 组件属性
|
|
202
203
|
*
|
|
203
204
|
* @example
|
|
204
205
|
* ```tsx
|
|
206
|
+
* // 方式 1: 类注入 - 自动实例化和初始化
|
|
205
207
|
* function App() {
|
|
206
208
|
* return (
|
|
207
209
|
* <ServiceProvider services={[UserService, ProductService]}>
|
|
@@ -215,11 +217,53 @@ export function useLocalService(ServiceClass, selector) {
|
|
|
215
217
|
* )
|
|
216
218
|
* }
|
|
217
219
|
*
|
|
218
|
-
* //
|
|
220
|
+
* // 方式 2: 值注入 - 在多个组件间共享局部服务实例
|
|
219
221
|
* function UserPage() {
|
|
222
|
+
* // 创建局部服务实例
|
|
223
|
+
* const localUserService = useMemo(() => {
|
|
224
|
+
* const service = new LocalUserService()
|
|
225
|
+
* service.init() // 手动初始化
|
|
226
|
+
* return service
|
|
227
|
+
* }, [])
|
|
228
|
+
*
|
|
220
229
|
* return (
|
|
221
|
-
* <ServiceProvider
|
|
230
|
+
* <ServiceProvider
|
|
231
|
+
* services={[
|
|
232
|
+
* { provide: LocalUserService, useValue: localUserService }
|
|
233
|
+
* ]}
|
|
234
|
+
* >
|
|
222
235
|
* <UserList />
|
|
236
|
+
* <UserDetail />
|
|
237
|
+
* </ServiceProvider>
|
|
238
|
+
* )
|
|
239
|
+
* }
|
|
240
|
+
*
|
|
241
|
+
* // UserList 和 UserDetail 共享同一个 localUserService 实例
|
|
242
|
+
* function UserList() {
|
|
243
|
+
* const users = useService(LocalUserService, s => s.users)
|
|
244
|
+
* return <div>{users.length} users</div>
|
|
245
|
+
* }
|
|
246
|
+
*
|
|
247
|
+
* function UserDetail() {
|
|
248
|
+
* const service = useService(LocalUserService)
|
|
249
|
+
* return <div>{service.currentUser?.name}</div>
|
|
250
|
+
* }
|
|
251
|
+
*
|
|
252
|
+
* // 方式 3: 混合使用类注入和值注入
|
|
253
|
+
* function App() {
|
|
254
|
+
* const configService = useMemo(() => {
|
|
255
|
+
* const config = new ConfigService()
|
|
256
|
+
* config.loadConfig() // 预配置
|
|
257
|
+
* return config
|
|
258
|
+
* }, [])
|
|
259
|
+
*
|
|
260
|
+
* return (
|
|
261
|
+
* <ServiceProvider services={[
|
|
262
|
+
* UserService, // 类注入 - 自动实例化和初始化
|
|
263
|
+
* ApiService, // 类注入
|
|
264
|
+
* { provide: ConfigService, useValue: configService } // 值注入 - 使用预配置的实例
|
|
265
|
+
* ]}>
|
|
266
|
+
* <Router />
|
|
223
267
|
* </ServiceProvider>
|
|
224
268
|
* )
|
|
225
269
|
* }
|
|
@@ -233,9 +277,11 @@ export function ServiceProvider(props) {
|
|
|
233
277
|
// 创建子容器,复用父容器的服务配置
|
|
234
278
|
const container = useMemo(() => {
|
|
235
279
|
const currentContainer = new Container(parentContainer);
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
280
|
+
// 直接绑定所有服务(包括类注入和值注入)
|
|
281
|
+
// needle-di 的 bind 方法可以处理所有类型的 Provider
|
|
282
|
+
services.forEach(service => {
|
|
283
|
+
currentContainer.bind(service);
|
|
284
|
+
});
|
|
239
285
|
return currentContainer;
|
|
240
286
|
}, [parentContainer, services]);
|
|
241
287
|
// 使用 React.createElement 避免将文件改为 .tsx
|
|
@@ -249,10 +295,15 @@ export function ServiceProvider(props) {
|
|
|
249
295
|
* 它会在组件挂载时检查每个服务是否需要初始化,并调用其 init 方法。
|
|
250
296
|
*
|
|
251
297
|
* 初始化流程:
|
|
252
|
-
* 1.
|
|
253
|
-
* 2.
|
|
254
|
-
* 3.
|
|
255
|
-
* 4.
|
|
298
|
+
* 1. 跳过值注入的服务(它们应该在传入前已经初始化)
|
|
299
|
+
* 2. 从容器中获取服务实例(仅类注入的服务)
|
|
300
|
+
* 3. 检查服务是否实现了 Service 接口(有 init 方法)
|
|
301
|
+
* 4. 检查服务是否已经初始化(inited 属性)
|
|
302
|
+
* 5. 如果未初始化,则调用 init 方法并更新状态
|
|
303
|
+
*
|
|
304
|
+
* 注意:
|
|
305
|
+
* - 值注入的服务需要在传入前手动初始化
|
|
306
|
+
* - 只有类注入的服务会自动调用 init 方法
|
|
256
307
|
*
|
|
257
308
|
* @param props 组件属性,与 ServiceProviderProps 相同
|
|
258
309
|
*/
|
|
@@ -269,6 +320,10 @@ function InitService(props) {
|
|
|
269
320
|
setInitError(prev => (prev === null ? prev : null));
|
|
270
321
|
// 异步初始化所有服务
|
|
271
322
|
const initPromises = services.map(async (service) => {
|
|
323
|
+
// 跳过值注入的服务(它们应该在传入前已经初始化)
|
|
324
|
+
if (service != null && typeof service === 'object' && 'provide' in service && 'useValue' in service) {
|
|
325
|
+
return;
|
|
326
|
+
}
|
|
272
327
|
// 尝试从容器中获取服务实例(可选的,避免未注册时报错)
|
|
273
328
|
const instance = container.get(service, { optional: true });
|
|
274
329
|
if (instance != null && typeof instance === 'object') {
|
|
@@ -0,0 +1,367 @@
|
|
|
1
|
+
var __runInitializers = (this && this.__runInitializers) || function (thisArg, initializers, value) {
|
|
2
|
+
var useValue = arguments.length > 2;
|
|
3
|
+
for (var i = 0; i < initializers.length; i++) {
|
|
4
|
+
value = useValue ? initializers[i].call(thisArg, value) : initializers[i].call(thisArg);
|
|
5
|
+
}
|
|
6
|
+
return useValue ? value : void 0;
|
|
7
|
+
};
|
|
8
|
+
var __esDecorate = (this && this.__esDecorate) || function (ctor, descriptorIn, decorators, contextIn, initializers, extraInitializers) {
|
|
9
|
+
function accept(f) { if (f !== void 0 && typeof f !== "function") throw new TypeError("Function expected"); return f; }
|
|
10
|
+
var kind = contextIn.kind, key = kind === "getter" ? "get" : kind === "setter" ? "set" : "value";
|
|
11
|
+
var target = !descriptorIn && ctor ? contextIn["static"] ? ctor : ctor.prototype : null;
|
|
12
|
+
var descriptor = descriptorIn || (target ? Object.getOwnPropertyDescriptor(target, contextIn.name) : {});
|
|
13
|
+
var _, done = false;
|
|
14
|
+
for (var i = decorators.length - 1; i >= 0; i--) {
|
|
15
|
+
var context = {};
|
|
16
|
+
for (var p in contextIn) context[p] = p === "access" ? {} : contextIn[p];
|
|
17
|
+
for (var p in contextIn.access) context.access[p] = contextIn.access[p];
|
|
18
|
+
context.addInitializer = function (f) { if (done) throw new TypeError("Cannot add initializers after decoration has completed"); extraInitializers.push(accept(f || null)); };
|
|
19
|
+
var result = (0, decorators[i])(kind === "accessor" ? { get: descriptor.get, set: descriptor.set } : descriptor[key], context);
|
|
20
|
+
if (kind === "accessor") {
|
|
21
|
+
if (result === void 0) continue;
|
|
22
|
+
if (result === null || typeof result !== "object") throw new TypeError("Object expected");
|
|
23
|
+
if (_ = accept(result.get)) descriptor.get = _;
|
|
24
|
+
if (_ = accept(result.set)) descriptor.set = _;
|
|
25
|
+
if (_ = accept(result.init)) initializers.unshift(_);
|
|
26
|
+
}
|
|
27
|
+
else if (_ = accept(result)) {
|
|
28
|
+
if (kind === "field") initializers.unshift(_);
|
|
29
|
+
else descriptor[key] = _;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
if (target) Object.defineProperty(target, contextIn.name, descriptor);
|
|
33
|
+
done = true;
|
|
34
|
+
};
|
|
35
|
+
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
|
|
36
|
+
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
|
|
37
|
+
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
|
|
38
|
+
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
39
|
+
};
|
|
40
|
+
var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
|
|
41
|
+
if (kind === "m") throw new TypeError("Private method is not writable");
|
|
42
|
+
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
|
|
43
|
+
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
|
|
44
|
+
return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
|
|
45
|
+
};
|
|
46
|
+
import React, { act } from 'react';
|
|
47
|
+
import { Service } from '@taicode/common-base';
|
|
48
|
+
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
49
|
+
import { render, renderHook, waitFor } from '@testing-library/react';
|
|
50
|
+
import { observable, makeObservable, runInAction, action } from 'mobx';
|
|
51
|
+
import { useService, ServiceProvider } from './service';
|
|
52
|
+
// 创建测试用的服务类
|
|
53
|
+
let TestService = (() => {
|
|
54
|
+
var _a, _TestService_count_accessor_storage, _TestService_users_accessor_storage, _TestService_inited_accessor_storage;
|
|
55
|
+
let _classSuper = Service;
|
|
56
|
+
let _instanceExtraInitializers = [];
|
|
57
|
+
let _count_decorators;
|
|
58
|
+
let _count_initializers = [];
|
|
59
|
+
let _count_extraInitializers = [];
|
|
60
|
+
let _users_decorators;
|
|
61
|
+
let _users_initializers = [];
|
|
62
|
+
let _users_extraInitializers = [];
|
|
63
|
+
let _inited_decorators;
|
|
64
|
+
let _inited_initializers = [];
|
|
65
|
+
let _inited_extraInitializers = [];
|
|
66
|
+
let _increment_decorators;
|
|
67
|
+
let _addUser_decorators;
|
|
68
|
+
return _a = class TestService extends _classSuper {
|
|
69
|
+
get count() { return __classPrivateFieldGet(this, _TestService_count_accessor_storage, "f"); }
|
|
70
|
+
set count(value) { __classPrivateFieldSet(this, _TestService_count_accessor_storage, value, "f"); }
|
|
71
|
+
get users() { return __classPrivateFieldGet(this, _TestService_users_accessor_storage, "f"); }
|
|
72
|
+
set users(value) { __classPrivateFieldSet(this, _TestService_users_accessor_storage, value, "f"); }
|
|
73
|
+
get inited() { return __classPrivateFieldGet(this, _TestService_inited_accessor_storage, "f"); }
|
|
74
|
+
set inited(value) { __classPrivateFieldSet(this, _TestService_inited_accessor_storage, value, "f"); }
|
|
75
|
+
constructor() {
|
|
76
|
+
super();
|
|
77
|
+
_TestService_count_accessor_storage.set(this, (__runInitializers(this, _instanceExtraInitializers), __runInitializers(this, _count_initializers, 0)));
|
|
78
|
+
_TestService_users_accessor_storage.set(this, (__runInitializers(this, _count_extraInitializers), __runInitializers(this, _users_initializers, [])));
|
|
79
|
+
_TestService_inited_accessor_storage.set(this, (__runInitializers(this, _users_extraInitializers), __runInitializers(this, _inited_initializers, false)));
|
|
80
|
+
__runInitializers(this, _inited_extraInitializers);
|
|
81
|
+
makeObservable(this);
|
|
82
|
+
}
|
|
83
|
+
async init() {
|
|
84
|
+
runInAction(() => {
|
|
85
|
+
this.users = ['Alice', 'Bob'];
|
|
86
|
+
});
|
|
87
|
+
return true;
|
|
88
|
+
}
|
|
89
|
+
increment() {
|
|
90
|
+
this.count++;
|
|
91
|
+
}
|
|
92
|
+
addUser(name) {
|
|
93
|
+
this.users.push(name);
|
|
94
|
+
}
|
|
95
|
+
},
|
|
96
|
+
_TestService_count_accessor_storage = new WeakMap(),
|
|
97
|
+
_TestService_users_accessor_storage = new WeakMap(),
|
|
98
|
+
_TestService_inited_accessor_storage = new WeakMap(),
|
|
99
|
+
(() => {
|
|
100
|
+
var _b;
|
|
101
|
+
const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create((_b = _classSuper[Symbol.metadata]) !== null && _b !== void 0 ? _b : null) : void 0;
|
|
102
|
+
_count_decorators = [observable];
|
|
103
|
+
_users_decorators = [observable];
|
|
104
|
+
_inited_decorators = [observable];
|
|
105
|
+
_increment_decorators = [action];
|
|
106
|
+
_addUser_decorators = [action];
|
|
107
|
+
__esDecorate(_a, null, _count_decorators, { kind: "accessor", name: "count", static: false, private: false, access: { has: obj => "count" in obj, get: obj => obj.count, set: (obj, value) => { obj.count = value; } }, metadata: _metadata }, _count_initializers, _count_extraInitializers);
|
|
108
|
+
__esDecorate(_a, null, _users_decorators, { kind: "accessor", name: "users", static: false, private: false, access: { has: obj => "users" in obj, get: obj => obj.users, set: (obj, value) => { obj.users = value; } }, metadata: _metadata }, _users_initializers, _users_extraInitializers);
|
|
109
|
+
__esDecorate(_a, null, _inited_decorators, { kind: "accessor", name: "inited", static: false, private: false, access: { has: obj => "inited" in obj, get: obj => obj.inited, set: (obj, value) => { obj.inited = value; } }, metadata: _metadata }, _inited_initializers, _inited_extraInitializers);
|
|
110
|
+
__esDecorate(_a, null, _increment_decorators, { kind: "method", name: "increment", static: false, private: false, access: { has: obj => "increment" in obj, get: obj => obj.increment }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
111
|
+
__esDecorate(_a, null, _addUser_decorators, { kind: "method", name: "addUser", static: false, private: false, access: { has: obj => "addUser" in obj, get: obj => obj.addUser }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
112
|
+
if (_metadata) Object.defineProperty(_a, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
|
|
113
|
+
})(),
|
|
114
|
+
_a;
|
|
115
|
+
})();
|
|
116
|
+
// 另一个测试服务类
|
|
117
|
+
let AnotherService = (() => {
|
|
118
|
+
var _a, _AnotherService_value_accessor_storage;
|
|
119
|
+
let _instanceExtraInitializers = [];
|
|
120
|
+
let _value_decorators;
|
|
121
|
+
let _value_initializers = [];
|
|
122
|
+
let _value_extraInitializers = [];
|
|
123
|
+
let _setValue_decorators;
|
|
124
|
+
return _a = class AnotherService {
|
|
125
|
+
get value() { return __classPrivateFieldGet(this, _AnotherService_value_accessor_storage, "f"); }
|
|
126
|
+
set value(value) { __classPrivateFieldSet(this, _AnotherService_value_accessor_storage, value, "f"); }
|
|
127
|
+
constructor() {
|
|
128
|
+
_AnotherService_value_accessor_storage.set(this, (__runInitializers(this, _instanceExtraInitializers), __runInitializers(this, _value_initializers, 'initial')));
|
|
129
|
+
__runInitializers(this, _value_extraInitializers);
|
|
130
|
+
makeObservable(this);
|
|
131
|
+
}
|
|
132
|
+
setValue(newValue) {
|
|
133
|
+
this.value = newValue;
|
|
134
|
+
}
|
|
135
|
+
},
|
|
136
|
+
_AnotherService_value_accessor_storage = new WeakMap(),
|
|
137
|
+
(() => {
|
|
138
|
+
const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(null) : void 0;
|
|
139
|
+
_value_decorators = [observable];
|
|
140
|
+
_setValue_decorators = [action];
|
|
141
|
+
__esDecorate(_a, null, _value_decorators, { kind: "accessor", name: "value", static: false, private: false, access: { has: obj => "value" in obj, get: obj => obj.value, set: (obj, value) => { obj.value = value; } }, metadata: _metadata }, _value_initializers, _value_extraInitializers);
|
|
142
|
+
__esDecorate(_a, null, _setValue_decorators, { kind: "method", name: "setValue", static: false, private: false, access: { has: obj => "setValue" in obj, get: obj => obj.setValue }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
143
|
+
if (_metadata) Object.defineProperty(_a, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
|
|
144
|
+
})(),
|
|
145
|
+
_a;
|
|
146
|
+
})();
|
|
147
|
+
// 没有 init 方法的服务类
|
|
148
|
+
let SimpleService = (() => {
|
|
149
|
+
var _a, _SimpleService_data_accessor_storage;
|
|
150
|
+
let _data_decorators;
|
|
151
|
+
let _data_initializers = [];
|
|
152
|
+
let _data_extraInitializers = [];
|
|
153
|
+
return _a = class SimpleService {
|
|
154
|
+
get data() { return __classPrivateFieldGet(this, _SimpleService_data_accessor_storage, "f"); }
|
|
155
|
+
set data(value) { __classPrivateFieldSet(this, _SimpleService_data_accessor_storage, value, "f"); }
|
|
156
|
+
constructor() {
|
|
157
|
+
_SimpleService_data_accessor_storage.set(this, __runInitializers(this, _data_initializers, 'simple'));
|
|
158
|
+
__runInitializers(this, _data_extraInitializers);
|
|
159
|
+
makeObservable(this);
|
|
160
|
+
}
|
|
161
|
+
},
|
|
162
|
+
_SimpleService_data_accessor_storage = new WeakMap(),
|
|
163
|
+
(() => {
|
|
164
|
+
const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(null) : void 0;
|
|
165
|
+
_data_decorators = [observable];
|
|
166
|
+
__esDecorate(_a, null, _data_decorators, { kind: "accessor", name: "data", static: false, private: false, access: { has: obj => "data" in obj, get: obj => obj.data, set: (obj, value) => { obj.data = value; } }, metadata: _metadata }, _data_initializers, _data_extraInitializers);
|
|
167
|
+
if (_metadata) Object.defineProperty(_a, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
|
|
168
|
+
})(),
|
|
169
|
+
_a;
|
|
170
|
+
})();
|
|
171
|
+
// 创建测试用的 Provider 组件
|
|
172
|
+
function createTestProvider(services) {
|
|
173
|
+
return function TestProvider({ children }) {
|
|
174
|
+
return (<ServiceProvider services={services}>
|
|
175
|
+
{children}
|
|
176
|
+
</ServiceProvider>);
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
describe('service integration', () => {
|
|
180
|
+
beforeEach(() => {
|
|
181
|
+
vi.clearAllMocks();
|
|
182
|
+
});
|
|
183
|
+
describe('useService', () => {
|
|
184
|
+
it('应该能够获取服务实例并选择数据', () => {
|
|
185
|
+
const TestProvider = createTestProvider([TestService]);
|
|
186
|
+
const { result } = renderHook(() => useService(TestService, service => service.count), { wrapper: TestProvider });
|
|
187
|
+
expect(result.current).toBe(0);
|
|
188
|
+
});
|
|
189
|
+
it('应该在服务数据变化时触发重新渲染', async () => {
|
|
190
|
+
const TestProvider = createTestProvider([TestService]);
|
|
191
|
+
const { result } = renderHook(() => ({
|
|
192
|
+
count: useService(TestService, service => service.count),
|
|
193
|
+
service: useService(TestService, service => service)
|
|
194
|
+
}), { wrapper: TestProvider });
|
|
195
|
+
expect(result.current.count).toBe(0);
|
|
196
|
+
// 修改服务数据
|
|
197
|
+
act(() => {
|
|
198
|
+
result.current.service.increment();
|
|
199
|
+
});
|
|
200
|
+
await waitFor(() => {
|
|
201
|
+
expect(result.current.count).toBe(1);
|
|
202
|
+
});
|
|
203
|
+
});
|
|
204
|
+
it('应该能够选择复杂数据结构', async () => {
|
|
205
|
+
const TestProvider = createTestProvider([TestService]);
|
|
206
|
+
const { result } = renderHook(() => ({
|
|
207
|
+
userCount: useService(TestService, service => service.users.length),
|
|
208
|
+
service: useService(TestService, service => service)
|
|
209
|
+
}), { wrapper: TestProvider });
|
|
210
|
+
// 等待初始化完成
|
|
211
|
+
await waitFor(() => {
|
|
212
|
+
expect(result.current.userCount).toBe(2);
|
|
213
|
+
});
|
|
214
|
+
// 添加新用户
|
|
215
|
+
act(() => {
|
|
216
|
+
result.current.service.addUser('Charlie');
|
|
217
|
+
});
|
|
218
|
+
await waitFor(() => {
|
|
219
|
+
expect(result.current.userCount).toBe(3);
|
|
220
|
+
});
|
|
221
|
+
});
|
|
222
|
+
it('应该在没有 ServiceProvider 时抛出错误', () => {
|
|
223
|
+
expect(() => {
|
|
224
|
+
renderHook(() => useService(TestService, service => service.count));
|
|
225
|
+
}).toThrow('Must be a child of ServiceProvider.');
|
|
226
|
+
});
|
|
227
|
+
it('应该能够处理多个服务', () => {
|
|
228
|
+
const TestProvider = createTestProvider([TestService, AnotherService]);
|
|
229
|
+
const { result } = renderHook(() => ({
|
|
230
|
+
count: useService(TestService, service => service.count),
|
|
231
|
+
value: useService(AnotherService, service => service.value)
|
|
232
|
+
}), { wrapper: TestProvider });
|
|
233
|
+
expect(result.current.count).toBe(0);
|
|
234
|
+
expect(result.current.value).toBe('initial');
|
|
235
|
+
});
|
|
236
|
+
});
|
|
237
|
+
describe('ServiceProvider', () => {
|
|
238
|
+
it('应该能够渲染子组件', () => {
|
|
239
|
+
const { container } = render(<ServiceProvider services={[TestService]}>
|
|
240
|
+
<div data-testid="child">Test Child</div>
|
|
241
|
+
</ServiceProvider>);
|
|
242
|
+
expect(container.querySelector('[data-testid="child"]')).toBeTruthy();
|
|
243
|
+
});
|
|
244
|
+
it('应该能够嵌套使用', () => {
|
|
245
|
+
const ParentProvider = createTestProvider([TestService]);
|
|
246
|
+
const ChildProvider = createTestProvider([AnotherService]);
|
|
247
|
+
const { result } = renderHook(() => ({
|
|
248
|
+
count: useService(TestService, service => service.count),
|
|
249
|
+
value: useService(AnotherService, service => service.value)
|
|
250
|
+
}), {
|
|
251
|
+
wrapper: ({ children }) => (<ParentProvider>
|
|
252
|
+
<ChildProvider>
|
|
253
|
+
{children}
|
|
254
|
+
</ChildProvider>
|
|
255
|
+
</ParentProvider>)
|
|
256
|
+
});
|
|
257
|
+
expect(result.current.count).toBe(0);
|
|
258
|
+
expect(result.current.value).toBe('initial');
|
|
259
|
+
});
|
|
260
|
+
it('应该自动初始化实现了 Service 接口的服务', async () => {
|
|
261
|
+
const TestProvider = createTestProvider([TestService]);
|
|
262
|
+
const { result } = renderHook(() => useService(TestService, service => ({
|
|
263
|
+
users: service.users,
|
|
264
|
+
inited: service.inited
|
|
265
|
+
})), { wrapper: TestProvider });
|
|
266
|
+
// 等待初始化完成
|
|
267
|
+
await waitFor(() => {
|
|
268
|
+
expect(result.current.inited).toBe(true);
|
|
269
|
+
expect(result.current.users).toEqual(['Alice', 'Bob']);
|
|
270
|
+
});
|
|
271
|
+
});
|
|
272
|
+
it('应该能够处理没有 init 方法的服务', () => {
|
|
273
|
+
const TestProvider = createTestProvider([SimpleService]);
|
|
274
|
+
const { result } = renderHook(() => useService(SimpleService, service => service.data), { wrapper: TestProvider });
|
|
275
|
+
expect(result.current).toBe('simple');
|
|
276
|
+
});
|
|
277
|
+
it('应该能够处理空的服务数组', () => {
|
|
278
|
+
const { container } = render(<ServiceProvider services={[]}>
|
|
279
|
+
<div data-testid="child">Test Child</div>
|
|
280
|
+
</ServiceProvider>);
|
|
281
|
+
expect(container.querySelector('[data-testid="child"]')).toBeTruthy();
|
|
282
|
+
});
|
|
283
|
+
it('应该能够处理初始化失败的服务', async () => {
|
|
284
|
+
// 创建一个初始化会失败的服务
|
|
285
|
+
let FailingService = (() => {
|
|
286
|
+
var _a, _FailingService_inited_accessor_storage;
|
|
287
|
+
let _classSuper = Service;
|
|
288
|
+
let _inited_decorators;
|
|
289
|
+
let _inited_initializers = [];
|
|
290
|
+
let _inited_extraInitializers = [];
|
|
291
|
+
return _a = class FailingService extends _classSuper {
|
|
292
|
+
get inited() { return __classPrivateFieldGet(this, _FailingService_inited_accessor_storage, "f"); }
|
|
293
|
+
set inited(value) { __classPrivateFieldSet(this, _FailingService_inited_accessor_storage, value, "f"); }
|
|
294
|
+
constructor() {
|
|
295
|
+
super();
|
|
296
|
+
_FailingService_inited_accessor_storage.set(this, __runInitializers(this, _inited_initializers, false));
|
|
297
|
+
__runInitializers(this, _inited_extraInitializers);
|
|
298
|
+
makeObservable(this);
|
|
299
|
+
}
|
|
300
|
+
async init() {
|
|
301
|
+
throw new Error('Init failed');
|
|
302
|
+
}
|
|
303
|
+
},
|
|
304
|
+
_FailingService_inited_accessor_storage = new WeakMap(),
|
|
305
|
+
(() => {
|
|
306
|
+
var _b;
|
|
307
|
+
const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create((_b = _classSuper[Symbol.metadata]) !== null && _b !== void 0 ? _b : null) : void 0;
|
|
308
|
+
_inited_decorators = [observable];
|
|
309
|
+
__esDecorate(_a, null, _inited_decorators, { kind: "accessor", name: "inited", static: false, private: false, access: { has: obj => "inited" in obj, get: obj => obj.inited, set: (obj, value) => { obj.inited = value; } }, metadata: _metadata }, _inited_initializers, _inited_extraInitializers);
|
|
310
|
+
if (_metadata) Object.defineProperty(_a, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
|
|
311
|
+
})(),
|
|
312
|
+
_a;
|
|
313
|
+
})();
|
|
314
|
+
const TestProvider = createTestProvider([FailingService]);
|
|
315
|
+
const { result } = renderHook(() => useService(FailingService, service => service.inited), { wrapper: TestProvider });
|
|
316
|
+
// 等待初始化处理完成,即使失败也应该保持 inited 为 false
|
|
317
|
+
await waitFor(() => {
|
|
318
|
+
expect(result.current).toBe(false); // 因为初始化失败,inited 应该保持 false
|
|
319
|
+
});
|
|
320
|
+
});
|
|
321
|
+
});
|
|
322
|
+
describe('边界情况', () => {
|
|
323
|
+
it('useService 应该能够处理选择器返回 undefined 的情况', () => {
|
|
324
|
+
const TestProvider = createTestProvider([TestService]);
|
|
325
|
+
const { result } = renderHook(() => useService(TestService, () => undefined), { wrapper: TestProvider });
|
|
326
|
+
expect(result.current).toBeUndefined();
|
|
327
|
+
});
|
|
328
|
+
it('应该能够处理 selector 函数变化的情况', async () => {
|
|
329
|
+
const TestProvider = createTestProvider([TestService]);
|
|
330
|
+
let useCount = true;
|
|
331
|
+
const { result, rerender } = renderHook(() => ({
|
|
332
|
+
selected: useService(TestService, service => useCount ? service.count : service.users.length),
|
|
333
|
+
service: useService(TestService, service => service)
|
|
334
|
+
}), { wrapper: TestProvider });
|
|
335
|
+
expect(result.current.selected).toBe(0);
|
|
336
|
+
// 修改 selector
|
|
337
|
+
useCount = false;
|
|
338
|
+
rerender();
|
|
339
|
+
await waitFor(() => {
|
|
340
|
+
expect(result.current.selected).toBe(2); // users 初始化后包含 ['Alice', 'Bob'],长度为 2
|
|
341
|
+
});
|
|
342
|
+
});
|
|
343
|
+
});
|
|
344
|
+
describe('性能测试', () => {
|
|
345
|
+
it('useService 应该在 selector 返回相同值时不触发重渲染', async () => {
|
|
346
|
+
const TestProvider = createTestProvider([TestService]);
|
|
347
|
+
let renderCount = 0;
|
|
348
|
+
const { result } = renderHook(() => {
|
|
349
|
+
renderCount++;
|
|
350
|
+
return {
|
|
351
|
+
constValue: useService(TestService, () => 'constant'),
|
|
352
|
+
service: useService(TestService, service => service)
|
|
353
|
+
};
|
|
354
|
+
}, { wrapper: TestProvider });
|
|
355
|
+
const initialRenderCount = renderCount;
|
|
356
|
+
// 修改服务但选择器返回常量
|
|
357
|
+
act(() => {
|
|
358
|
+
result.current.service.increment();
|
|
359
|
+
});
|
|
360
|
+
// 等待一段时间确保没有额外渲染
|
|
361
|
+
await new Promise(resolve => setTimeout(resolve, 100));
|
|
362
|
+
// 由于选择器返回常量,理论上不应该有额外渲染
|
|
363
|
+
// 但 Mobx reaction 的具体行为可能会有差异
|
|
364
|
+
expect(renderCount).toBeGreaterThanOrEqual(initialRenderCount);
|
|
365
|
+
});
|
|
366
|
+
});
|
|
367
|
+
});
|
|
@@ -8,5 +8,11 @@ interface ToastCatcherProps {
|
|
|
8
8
|
}
|
|
9
9
|
/** 自动捕获和处理异步错误,无法识别的错误会保持原样 */
|
|
10
10
|
export declare function Toaster(props: ToastCatcherProps): import("react/jsx-runtime").JSX.Element;
|
|
11
|
+
export declare namespace Toaster {
|
|
12
|
+
var info: (content: React.ReactNode) => void;
|
|
13
|
+
var error: (content: React.ReactNode) => void;
|
|
14
|
+
var success: (content: React.ReactNode) => void;
|
|
15
|
+
var warning: (content: React.ReactNode) => void;
|
|
16
|
+
}
|
|
11
17
|
export {};
|
|
12
18
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../source/toaster/index.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAyC,MAAM,OAAO,CAAA;AAE7D,OAAO,EAA0B,eAAe,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAA;AAE7F,OAAO,uCAAuC,CAAA;AAE9C,UAAU,iBAAiB;IACzB,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAA;IACzB,QAAQ,EAAE,OAAO,CAAC,MAAM,CAAC,aAAa,GAAG,eAAe,EAAE,MAAM,CAAC,CAAC,CAAA;CACnE;AAED,+BAA+B;AAC/B,wBAAgB,OAAO,CAAC,KAAK,EAAE,iBAAiB,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../source/toaster/index.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAyC,MAAM,OAAO,CAAA;AAE7D,OAAO,EAA0B,eAAe,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAA;AAE7F,OAAO,uCAAuC,CAAA;AAE9C,UAAU,iBAAiB;IACzB,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAA;IACzB,QAAQ,EAAE,OAAO,CAAC,MAAM,CAAC,aAAa,GAAG,eAAe,EAAE,MAAM,CAAC,CAAC,CAAA;CACnE;AAED,+BAA+B;AAC/B,wBAAgB,OAAO,CAAC,KAAK,EAAE,iBAAiB,2CAyF/C;yBAzFe,OAAO;wBAmGE,KAAK,CAAC,SAAS;yBAId,KAAK,CAAC,SAAS;2BAIb,KAAK,CAAC,SAAS;2BAIf,KAAK,CAAC,SAAS"}
|
package/output/toaster/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import {
|
|
2
|
+
import { useEffect, useRef } from 'react';
|
|
3
3
|
import { ToastContainer, toast as baseToast } from 'react-toastify';
|
|
4
4
|
import { SystemError, UserError } from '@taicode/common-base';
|
|
5
5
|
import 'react-toastify/dist/ReactToastify.css';
|
|
@@ -7,35 +7,42 @@ import 'react-toastify/dist/ReactToastify.css';
|
|
|
7
7
|
export function Toaster(props) {
|
|
8
8
|
const addEventListened = useRef(false);
|
|
9
9
|
const { messages = {}, defaultMessage } = props;
|
|
10
|
-
const
|
|
11
|
-
baseToast.error(_jsx(Toast, { type: 'error', error: error }), {
|
|
12
|
-
className: 'p-0 w-[400px] border-0',
|
|
13
|
-
closeButton: false,
|
|
14
|
-
});
|
|
15
|
-
}, []);
|
|
16
|
-
const sendSuccess = useCallback((error) => {
|
|
17
|
-
baseToast.error(_jsx(Toast, { type: 'error', error: error }), {
|
|
18
|
-
className: 'p-0 w-[400px] border-0',
|
|
19
|
-
closeButton: false,
|
|
20
|
-
});
|
|
21
|
-
}, []);
|
|
10
|
+
const handledErrors = useRef(new WeakSet());
|
|
22
11
|
useEffect(() => {
|
|
23
12
|
if (addEventListened.current)
|
|
24
13
|
return;
|
|
25
14
|
const errorHandler = (event) => {
|
|
26
15
|
const errorObject = event.error;
|
|
16
|
+
// 检查是否已经处理过这个错误对象
|
|
17
|
+
if (errorObject && handledErrors.current.has(errorObject)) {
|
|
18
|
+
event.preventDefault();
|
|
19
|
+
event.stopPropagation();
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
27
22
|
if (SystemError.is(errorObject) || UserError.is(errorObject)) {
|
|
23
|
+
console.error('Caught error:', errorObject);
|
|
24
|
+
// 标记此错误已处理
|
|
25
|
+
handledErrors.current.add(errorObject);
|
|
28
26
|
event.preventDefault();
|
|
29
27
|
event.stopPropagation();
|
|
30
|
-
|
|
28
|
+
Toaster.error(messages[errorObject.type] || defaultMessage);
|
|
31
29
|
}
|
|
32
30
|
};
|
|
33
31
|
const unhandledRejectionHandler = (event) => {
|
|
34
32
|
const errorObject = event.reason;
|
|
33
|
+
// 检查是否已经处理过这个错误对象
|
|
34
|
+
if (errorObject && handledErrors.current.has(errorObject)) {
|
|
35
|
+
event.preventDefault();
|
|
36
|
+
event.stopPropagation();
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
35
39
|
if (SystemError.is(errorObject) || UserError.is(errorObject)) {
|
|
40
|
+
console.error('Caught error:', errorObject);
|
|
41
|
+
// 标记此错误已处理
|
|
42
|
+
handledErrors.current.add(errorObject);
|
|
36
43
|
event.preventDefault();
|
|
37
44
|
event.stopPropagation();
|
|
38
|
-
|
|
45
|
+
Toaster.error(messages[errorObject.type] || defaultMessage);
|
|
39
46
|
}
|
|
40
47
|
};
|
|
41
48
|
window.addEventListener('error', errorHandler, true);
|
|
@@ -45,17 +52,26 @@ export function Toaster(props) {
|
|
|
45
52
|
window.removeEventListener('error', errorHandler, true);
|
|
46
53
|
window.removeEventListener('unhandledrejection', unhandledRejectionHandler, true);
|
|
47
54
|
addEventListened.current = false;
|
|
55
|
+
handledErrors.current = new WeakSet();
|
|
48
56
|
};
|
|
49
57
|
}, []);
|
|
50
58
|
return (_jsxs(_Fragment, { children: [props.children, _jsx(ToastContainer, { newestOnTop: true, hideProgressBar: true, closeButton: false, position: "top-center" })] }));
|
|
51
59
|
function ErrorIcon() {
|
|
52
60
|
return (_jsx("svg", { xmlns: "http://www.w3.org/2000/svg", fill: "none", viewBox: "0 0 24 24", strokeWidth: 1.5, stroke: "currentColor", className: "size-6 text-red-500", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "m9.75 9.75 4.5 4.5m0-4.5-4.5 4.5M21 12a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z" }) }));
|
|
53
61
|
}
|
|
54
|
-
function
|
|
55
|
-
return (
|
|
56
|
-
}
|
|
57
|
-
function Toast(props) {
|
|
58
|
-
const { type, error } = props;
|
|
59
|
-
return (_jsxs("div", { className: "flex items-center w-full p-4 space-x-4 rtl:space-x-reverse text-gray-500 bg-white divide-x rtl:divide-x-reverse divide-gray-200 rounded-lg shadow dark:text-gray-400 dark:divide-gray-700 dark:bg-gray-800", role: "alert", children: [props.type === 'error' && (_jsx(ErrorIcon, {})), props.type === 'success' && (_jsx(SuccessIcon, {})), _jsx("div", { className: "ps-4 text-sm font-normal", children: messages[error] || defaultMessage })] }));
|
|
62
|
+
function ErrorToast(props) {
|
|
63
|
+
return (_jsxs("div", { className: "flex items-center space-x-3", role: "alert", children: [_jsx(ErrorIcon, {}), _jsx("div", { className: "text-sm font-medium text-gray-900 dark:text-gray-100" })] }));
|
|
60
64
|
}
|
|
61
65
|
}
|
|
66
|
+
Toaster.info = (content) => {
|
|
67
|
+
baseToast.info(content);
|
|
68
|
+
};
|
|
69
|
+
Toaster.error = (content) => {
|
|
70
|
+
baseToast.error(content);
|
|
71
|
+
};
|
|
72
|
+
Toaster.success = (content) => {
|
|
73
|
+
baseToast.success(content);
|
|
74
|
+
};
|
|
75
|
+
Toaster.warning = (content) => {
|
|
76
|
+
baseToast.warning(content);
|
|
77
|
+
};
|