@okyrychenko-dev/react-action-guard 0.6.1 → 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/README.md +23 -13
- package/dist/index.cjs +179 -132
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +446 -411
- package/dist/index.d.ts +446 -411
- package/dist/index.js +174 -130
- package/dist/index.js.map +1 -1
- package/package.json +12 -11
package/dist/index.cjs
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var react = require('react');
|
|
4
3
|
var reactZustandToolkit = require('@okyrychenko-dev/react-zustand-toolkit');
|
|
5
4
|
var zustand = require('zustand');
|
|
6
5
|
require('zustand/react/shallow');
|
|
7
6
|
var jsxRuntime = require('react/jsx-runtime');
|
|
8
7
|
var shallow = require('zustand/shallow');
|
|
9
8
|
var middleware = require('zustand/middleware');
|
|
9
|
+
var react = require('react');
|
|
10
10
|
|
|
11
|
-
// src/
|
|
11
|
+
// src/context/UIBlockingContext.tsx
|
|
12
12
|
|
|
13
13
|
// src/store/uiBlockingStore.constants.ts
|
|
14
14
|
var DEFAULT_SCOPE = "global";
|
|
@@ -24,16 +24,18 @@ function normalizeScopeToArray(scope) {
|
|
|
24
24
|
}
|
|
25
25
|
|
|
26
26
|
// src/store/uiBlockingStore.actions.ts
|
|
27
|
-
|
|
27
|
+
function normalizePriority(priority, fallback = DEFAULT_PRIORITY) {
|
|
28
28
|
return priority !== void 0 ? Math.max(0, priority) : fallback;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
}
|
|
29
|
+
}
|
|
30
|
+
function createMiddlewareContext(action, blockerId, config, prevState) {
|
|
31
|
+
return {
|
|
32
|
+
action,
|
|
33
|
+
blockerId,
|
|
34
|
+
config,
|
|
35
|
+
timestamp: Date.now(),
|
|
36
|
+
...prevState && { prevState }
|
|
37
|
+
};
|
|
38
|
+
}
|
|
37
39
|
var createUIBlockingActions = (set, get) => ({
|
|
38
40
|
// State
|
|
39
41
|
activeBlockers: /* @__PURE__ */ new Map(),
|
|
@@ -54,25 +56,24 @@ var createUIBlockingActions = (set, get) => ({
|
|
|
54
56
|
},
|
|
55
57
|
runMiddlewares: async (context) => {
|
|
56
58
|
const { middlewares } = get();
|
|
57
|
-
for (const [
|
|
59
|
+
for (const [, middleware] of middlewares) {
|
|
58
60
|
try {
|
|
59
61
|
await middleware(context);
|
|
60
|
-
} catch
|
|
61
|
-
console.error(`[UIBlocking] Middleware "${name}" error:`, error);
|
|
62
|
+
} catch {
|
|
62
63
|
}
|
|
63
64
|
}
|
|
64
65
|
},
|
|
65
66
|
// Actions
|
|
66
67
|
/**
|
|
67
68
|
* Adds a new blocker or overwrites an existing one.
|
|
68
|
-
*
|
|
69
|
+
*
|
|
69
70
|
* If a blocker with the same ID already exists, it will be replaced and any
|
|
70
71
|
* existing timeout will be cleared. The blocker is active immediately after
|
|
71
72
|
* being added. Triggers middleware with 'add' action.
|
|
72
|
-
*
|
|
73
|
+
*
|
|
73
74
|
* @param id - Unique identifier for the blocker. Must be unique within the store.
|
|
74
75
|
* @param config - Blocker configuration. All fields are optional with defaults applied.
|
|
75
|
-
*
|
|
76
|
+
*
|
|
76
77
|
* @example
|
|
77
78
|
* Basic usage
|
|
78
79
|
* ```ts
|
|
@@ -82,7 +83,7 @@ var createUIBlockingActions = (set, get) => ({
|
|
|
82
83
|
* priority: 70
|
|
83
84
|
* });
|
|
84
85
|
* ```
|
|
85
|
-
*
|
|
86
|
+
*
|
|
86
87
|
* @example
|
|
87
88
|
* With timeout
|
|
88
89
|
* ```ts
|
|
@@ -93,7 +94,7 @@ var createUIBlockingActions = (set, get) => ({
|
|
|
93
94
|
* onTimeout: (id) => console.warn(`${id} timed out`)
|
|
94
95
|
* });
|
|
95
96
|
* ```
|
|
96
|
-
*
|
|
97
|
+
*
|
|
97
98
|
* @see {@link removeBlocker} to remove a blocker
|
|
98
99
|
* @see {@link updateBlocker} to update an existing blocker
|
|
99
100
|
*/
|
|
@@ -144,13 +145,13 @@ var createUIBlockingActions = (set, get) => ({
|
|
|
144
145
|
},
|
|
145
146
|
/**
|
|
146
147
|
* Updates an existing blocker or creates it if it doesn't exist.
|
|
147
|
-
*
|
|
148
|
+
*
|
|
148
149
|
* Merges the provided configuration with existing blocker data. If the timeout
|
|
149
150
|
* is changed, the timeout timer is restarted. Triggers middleware with 'update' action.
|
|
150
|
-
*
|
|
151
|
+
*
|
|
151
152
|
* @param id - Identifier of the blocker to update
|
|
152
153
|
* @param config - Partial configuration to merge with existing blocker data
|
|
153
|
-
*
|
|
154
|
+
*
|
|
154
155
|
* @example
|
|
155
156
|
* Update blocker reason
|
|
156
157
|
* ```ts
|
|
@@ -158,7 +159,7 @@ var createUIBlockingActions = (set, get) => ({
|
|
|
158
159
|
* reason: 'Saving additional data...'
|
|
159
160
|
* });
|
|
160
161
|
* ```
|
|
161
|
-
*
|
|
162
|
+
*
|
|
162
163
|
* @example
|
|
163
164
|
* Extend timeout
|
|
164
165
|
* ```ts
|
|
@@ -166,7 +167,7 @@ var createUIBlockingActions = (set, get) => ({
|
|
|
166
167
|
* timeout: 60000 // Extend to 60 seconds
|
|
167
168
|
* });
|
|
168
169
|
* ```
|
|
169
|
-
*
|
|
170
|
+
*
|
|
170
171
|
* @see {@link addBlocker} to create a new blocker
|
|
171
172
|
*/
|
|
172
173
|
updateBlocker: (id, config = {}) => {
|
|
@@ -242,12 +243,12 @@ var createUIBlockingActions = (set, get) => ({
|
|
|
242
243
|
},
|
|
243
244
|
/**
|
|
244
245
|
* Removes a blocker from the store.
|
|
245
|
-
*
|
|
246
|
+
*
|
|
246
247
|
* Clears any associated timeout and triggers middleware with 'remove' action.
|
|
247
248
|
* Safe to call even if the blocker doesn't exist (no-op in that case).
|
|
248
|
-
*
|
|
249
|
+
*
|
|
249
250
|
* @param id - Identifier of the blocker to remove
|
|
250
|
-
*
|
|
251
|
+
*
|
|
251
252
|
* @example
|
|
252
253
|
* ```ts
|
|
253
254
|
* // Add and remove a blocker
|
|
@@ -255,7 +256,7 @@ var createUIBlockingActions = (set, get) => ({
|
|
|
255
256
|
* // ... later
|
|
256
257
|
* store.getState().removeBlocker('temp');
|
|
257
258
|
* ```
|
|
258
|
-
*
|
|
259
|
+
*
|
|
259
260
|
* @see {@link addBlocker} to add a blocker
|
|
260
261
|
* @see {@link clearAllBlockers} to remove all blockers at once
|
|
261
262
|
*/
|
|
@@ -275,28 +276,28 @@ var createUIBlockingActions = (set, get) => ({
|
|
|
275
276
|
},
|
|
276
277
|
/**
|
|
277
278
|
* Checks if one or more scopes are currently blocked.
|
|
278
|
-
*
|
|
279
|
+
*
|
|
279
280
|
* Returns true if ANY blocker affects the specified scope(s). Global scope
|
|
280
281
|
* blockers will cause this to return true for any scope check.
|
|
281
|
-
*
|
|
282
|
+
*
|
|
282
283
|
* @param scope - Scope(s) to check. Defaults to "global" if not specified.
|
|
283
284
|
* Can be a single scope string or array of scopes.
|
|
284
285
|
* @returns true if any of the specified scopes are currently blocked
|
|
285
|
-
*
|
|
286
|
+
*
|
|
286
287
|
* @example
|
|
287
288
|
* Single scope check
|
|
288
289
|
* ```ts
|
|
289
290
|
* const isFormBlocked = store.getState().isBlocked('form');
|
|
290
291
|
* console.log(isFormBlocked); // true or false
|
|
291
292
|
* ```
|
|
292
|
-
*
|
|
293
|
+
*
|
|
293
294
|
* @example
|
|
294
295
|
* Multiple scopes check
|
|
295
296
|
* ```ts
|
|
296
297
|
* const isBlocked = store.getState().isBlocked(['form', 'navigation']);
|
|
297
298
|
* // Returns true if either 'form' OR 'navigation' has active blockers
|
|
298
299
|
* ```
|
|
299
|
-
*
|
|
300
|
+
*
|
|
300
301
|
* @see {@link getBlockingInfo} for detailed blocker information
|
|
301
302
|
*/
|
|
302
303
|
isBlocked: (scope = DEFAULT_SCOPE) => {
|
|
@@ -315,14 +316,14 @@ var createUIBlockingActions = (set, get) => ({
|
|
|
315
316
|
},
|
|
316
317
|
/**
|
|
317
318
|
* Gets detailed information about all blockers affecting a specific scope.
|
|
318
|
-
*
|
|
319
|
+
*
|
|
319
320
|
* Returns an array of blocker objects sorted by priority (highest first).
|
|
320
321
|
* Includes global blockers and scope-specific blockers. Each blocker object
|
|
321
322
|
* contains id, reason, priority, scope, timestamp, and timeout information.
|
|
322
|
-
*
|
|
323
|
+
*
|
|
323
324
|
* @param scope - Scope to get blocking information for
|
|
324
325
|
* @returns Array of {@link BlockerInfo} objects, sorted by priority descending
|
|
325
|
-
*
|
|
326
|
+
*
|
|
326
327
|
* @example
|
|
327
328
|
* Display blocker details
|
|
328
329
|
* ```ts
|
|
@@ -332,7 +333,7 @@ var createUIBlockingActions = (set, get) => ({
|
|
|
332
333
|
* console.log(`Priority: ${blocker.priority}`);
|
|
333
334
|
* });
|
|
334
335
|
* ```
|
|
335
|
-
*
|
|
336
|
+
*
|
|
336
337
|
* @example
|
|
337
338
|
* Get highest priority blocker
|
|
338
339
|
* ```ts
|
|
@@ -342,7 +343,7 @@ var createUIBlockingActions = (set, get) => ({
|
|
|
342
343
|
* console.log(`Top blocker: ${topBlocker.reason}`);
|
|
343
344
|
* }
|
|
344
345
|
* ```
|
|
345
|
-
*
|
|
346
|
+
*
|
|
346
347
|
* @see {@link isBlocked} for a simple boolean check
|
|
347
348
|
* @see {@link BlockerInfo} for the structure of returned objects
|
|
348
349
|
*/
|
|
@@ -352,23 +353,24 @@ var createUIBlockingActions = (set, get) => ({
|
|
|
352
353
|
for (const [id, blocker] of activeBlockers) {
|
|
353
354
|
const blockerScopes = normalizeScopeToArray(blocker.scope);
|
|
354
355
|
if (blocker.scope === DEFAULT_SCOPE || blockerScopes.includes(scope)) {
|
|
355
|
-
|
|
356
|
+
const { timeoutId: _timeoutId, ...publicBlocker } = blocker;
|
|
357
|
+
blockers.push({ id, ...publicBlocker });
|
|
356
358
|
}
|
|
357
359
|
}
|
|
358
360
|
return blockers.sort((a, b) => b.priority - a.priority);
|
|
359
361
|
},
|
|
360
362
|
/**
|
|
361
363
|
* Removes all active blockers from the store.
|
|
362
|
-
*
|
|
364
|
+
*
|
|
363
365
|
* Clears all timeouts and triggers middleware with 'clear' action.
|
|
364
366
|
* Useful for resetting the blocking state.
|
|
365
|
-
*
|
|
367
|
+
*
|
|
366
368
|
* @example
|
|
367
369
|
* ```ts
|
|
368
370
|
* // Clear all blockers on logout
|
|
369
371
|
* store.getState().clearAllBlockers();
|
|
370
372
|
* ```
|
|
371
|
-
*
|
|
373
|
+
*
|
|
372
374
|
* @see {@link clearBlockersForScope} to clear blockers for a specific scope
|
|
373
375
|
*/
|
|
374
376
|
clearAllBlockers: () => {
|
|
@@ -391,26 +393,26 @@ var createUIBlockingActions = (set, get) => ({
|
|
|
391
393
|
},
|
|
392
394
|
/**
|
|
393
395
|
* Removes all blockers that affect a specific scope.
|
|
394
|
-
*
|
|
396
|
+
*
|
|
395
397
|
* Clears timeouts for removed blockers and triggers middleware with
|
|
396
398
|
* 'clear_scope' action. Global scope blockers are NOT cleared by this method.
|
|
397
|
-
*
|
|
399
|
+
*
|
|
398
400
|
* @param scope - Scope to clear blockers for
|
|
399
|
-
*
|
|
401
|
+
*
|
|
400
402
|
* @example
|
|
401
403
|
* Clear form blockers
|
|
402
404
|
* ```ts
|
|
403
405
|
* // Clear all blockers affecting 'form' scope
|
|
404
406
|
* store.getState().clearBlockersForScope('form');
|
|
405
407
|
* ```
|
|
406
|
-
*
|
|
408
|
+
*
|
|
407
409
|
* @example
|
|
408
410
|
* Clear on navigation
|
|
409
411
|
* ```ts
|
|
410
412
|
* // Clear navigation blockers when user navigates away
|
|
411
413
|
* store.getState().clearBlockersForScope('navigation');
|
|
412
414
|
* ```
|
|
413
|
-
*
|
|
415
|
+
*
|
|
414
416
|
* @see {@link clearAllBlockers} to clear all blockers
|
|
415
417
|
*/
|
|
416
418
|
clearBlockersForScope: (scope) => {
|
|
@@ -448,18 +450,24 @@ var createUIBlockingActions = (set, get) => ({
|
|
|
448
450
|
});
|
|
449
451
|
var {
|
|
450
452
|
Provider: BaseUIBlockingProvider,
|
|
451
|
-
|
|
452
|
-
useContextStore:
|
|
453
|
+
useContextStoreApi: useUIBlockingContext,
|
|
454
|
+
useContextStore: baseUseUIBlockingStoreFromContext,
|
|
453
455
|
useIsInsideProvider: useIsInsideUIBlockingProvider,
|
|
454
|
-
|
|
456
|
+
useContextStoreOptional: useOptionalUIBlockingContext
|
|
455
457
|
} = reactZustandToolkit.createStoreProvider(createUIBlockingActions, "UIBlocking");
|
|
458
|
+
function useUIBlockingStoreFromContext(selector, equalityFn) {
|
|
459
|
+
if (selector) {
|
|
460
|
+
return baseUseUIBlockingStoreFromContext(selector, equalityFn);
|
|
461
|
+
}
|
|
462
|
+
return baseUseUIBlockingStoreFromContext();
|
|
463
|
+
}
|
|
456
464
|
function UIBlockingProvider({
|
|
457
465
|
children,
|
|
458
466
|
enableDevtools = process.env.NODE_ENV === "development",
|
|
459
467
|
devtoolsName = "UIBlocking",
|
|
460
468
|
middlewares = []
|
|
461
469
|
}) {
|
|
462
|
-
const
|
|
470
|
+
const handleStoreInit = (store) => {
|
|
463
471
|
middlewares.forEach((mw, index) => {
|
|
464
472
|
store.getState().registerMiddleware(`provider-middleware-${index.toString()}`, mw);
|
|
465
473
|
});
|
|
@@ -469,7 +477,7 @@ function UIBlockingProvider({
|
|
|
469
477
|
{
|
|
470
478
|
enableDevtools,
|
|
471
479
|
devtoolsName,
|
|
472
|
-
|
|
480
|
+
onStoreInit: handleStoreInit,
|
|
473
481
|
children
|
|
474
482
|
}
|
|
475
483
|
);
|
|
@@ -482,24 +490,31 @@ var devtoolsConfig = {
|
|
|
482
490
|
};
|
|
483
491
|
|
|
484
492
|
// src/store/uiBlockingStore.store.ts
|
|
485
|
-
var
|
|
493
|
+
var storeBindings = reactZustandToolkit.createShallowStore(
|
|
494
|
+
middleware.devtools(createUIBlockingActions, devtoolsConfig)
|
|
495
|
+
);
|
|
496
|
+
var uiBlockingStoreApi = storeBindings.useStoreApi;
|
|
497
|
+
function useUIBlockingStore(selector, equalityFn) {
|
|
498
|
+
if (selector) {
|
|
499
|
+
return storeBindings.useStore(selector, equalityFn);
|
|
500
|
+
}
|
|
501
|
+
return storeBindings.useStore();
|
|
502
|
+
}
|
|
486
503
|
|
|
487
504
|
// src/context/useResolvedStore.ts
|
|
488
505
|
function useOptionalContextStore() {
|
|
489
|
-
return
|
|
506
|
+
return useOptionalUIBlockingContext();
|
|
490
507
|
}
|
|
491
|
-
function
|
|
508
|
+
function useResolvedStoreApi() {
|
|
492
509
|
const contextStore = useOptionalContextStore();
|
|
493
510
|
return contextStore ?? uiBlockingStoreApi;
|
|
494
511
|
}
|
|
495
|
-
function
|
|
496
|
-
const store =
|
|
512
|
+
function useResolvedValue(selector) {
|
|
513
|
+
const store = useResolvedStoreApi();
|
|
497
514
|
return zustand.useStore(store, shallow.useShallow(selector));
|
|
498
515
|
}
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
var useAsyncAction = (actionId, scope, options) => {
|
|
502
|
-
const { addBlocker, removeBlocker } = useResolvedStoreWithSelector((state) => ({
|
|
516
|
+
function useAsyncAction(actionId, scope, options) {
|
|
517
|
+
const { addBlocker, removeBlocker } = useResolvedValue((state) => ({
|
|
503
518
|
addBlocker: state.addBlocker,
|
|
504
519
|
removeBlocker: state.removeBlocker
|
|
505
520
|
}));
|
|
@@ -524,22 +539,36 @@ var useAsyncAction = (actionId, scope, options) => {
|
|
|
524
539
|
[actionId, scope, options?.timeout, options?.onTimeout, addBlocker, removeBlocker]
|
|
525
540
|
);
|
|
526
541
|
return executeWithBlocking;
|
|
527
|
-
}
|
|
528
|
-
|
|
529
|
-
const { addBlocker, removeBlocker } =
|
|
542
|
+
}
|
|
543
|
+
function useBlocker(blockerId, config, isActive = true) {
|
|
544
|
+
const { addBlocker, removeBlocker, updateBlocker } = useResolvedValue((state) => ({
|
|
530
545
|
addBlocker: state.addBlocker,
|
|
531
|
-
removeBlocker: state.removeBlocker
|
|
546
|
+
removeBlocker: state.removeBlocker,
|
|
547
|
+
updateBlocker: state.updateBlocker
|
|
532
548
|
}));
|
|
549
|
+
const lastConfigRef = react.useRef(null);
|
|
533
550
|
react.useEffect(() => {
|
|
534
551
|
if (!isActive || !blockerId) {
|
|
535
552
|
return;
|
|
536
553
|
}
|
|
537
554
|
addBlocker(blockerId, config);
|
|
555
|
+
lastConfigRef.current = config;
|
|
538
556
|
return () => {
|
|
557
|
+
lastConfigRef.current = null;
|
|
539
558
|
removeBlocker(blockerId);
|
|
540
559
|
};
|
|
541
560
|
}, [blockerId, isActive, addBlocker, removeBlocker]);
|
|
542
|
-
|
|
561
|
+
react.useEffect(() => {
|
|
562
|
+
if (!isActive || !blockerId) {
|
|
563
|
+
return;
|
|
564
|
+
}
|
|
565
|
+
if (lastConfigRef.current === config) {
|
|
566
|
+
return;
|
|
567
|
+
}
|
|
568
|
+
updateBlocker(blockerId, config);
|
|
569
|
+
lastConfigRef.current = config;
|
|
570
|
+
}, [blockerId, config, isActive, updateBlocker]);
|
|
571
|
+
}
|
|
543
572
|
|
|
544
573
|
// src/hooks/useBlocker/useBlocker.utils.ts
|
|
545
574
|
function createBlockerConfig(config) {
|
|
@@ -549,13 +578,13 @@ function createBlockerConfig(config) {
|
|
|
549
578
|
priority: config.priority
|
|
550
579
|
};
|
|
551
580
|
}
|
|
552
|
-
|
|
553
|
-
const store =
|
|
581
|
+
function useBlockingInfo(scope = DEFAULT_SCOPE) {
|
|
582
|
+
const store = useResolvedStoreApi();
|
|
554
583
|
const activeBlockers = zustand.useStore(store, (state) => state.activeBlockers);
|
|
555
584
|
return react.useMemo(() => {
|
|
556
585
|
return store.getState().getBlockingInfo(scope);
|
|
557
586
|
}, [activeBlockers, scope, store]);
|
|
558
|
-
}
|
|
587
|
+
}
|
|
559
588
|
function useConfigRef(config) {
|
|
560
589
|
const configRef = react.useRef(config);
|
|
561
590
|
react.useEffect(() => {
|
|
@@ -565,8 +594,8 @@ function useConfigRef(config) {
|
|
|
565
594
|
}
|
|
566
595
|
|
|
567
596
|
// src/hooks/useConditionalBlocker/useConditionalBlocker.ts
|
|
568
|
-
|
|
569
|
-
const { addBlocker, removeBlocker } =
|
|
597
|
+
function useConditionalBlocker(blockerId, config) {
|
|
598
|
+
const { addBlocker, removeBlocker } = useResolvedValue((state) => ({
|
|
570
599
|
addBlocker: state.addBlocker,
|
|
571
600
|
removeBlocker: state.removeBlocker
|
|
572
601
|
}));
|
|
@@ -601,8 +630,8 @@ var useConditionalBlocker = (blockerId, config) => {
|
|
|
601
630
|
}
|
|
602
631
|
};
|
|
603
632
|
}, [checkCondition, blockerId, removeBlocker]);
|
|
604
|
-
}
|
|
605
|
-
|
|
633
|
+
}
|
|
634
|
+
function useConfirmableBlocker(blockerId, config) {
|
|
606
635
|
const [isDialogOpen, setIsDialogOpen] = react.useState(false);
|
|
607
636
|
const [isExecuting, setIsExecuting] = react.useState(false);
|
|
608
637
|
useBlocker(
|
|
@@ -621,9 +650,6 @@ var useConfirmableBlocker = (blockerId, config) => {
|
|
|
621
650
|
setIsExecuting(true);
|
|
622
651
|
try {
|
|
623
652
|
await config.onConfirm();
|
|
624
|
-
} catch (error) {
|
|
625
|
-
console.error("[UIBlocking] Confirmable action error:", error);
|
|
626
|
-
throw error;
|
|
627
653
|
} finally {
|
|
628
654
|
setIsExecuting(false);
|
|
629
655
|
}
|
|
@@ -645,12 +671,12 @@ var useConfirmableBlocker = (blockerId, config) => {
|
|
|
645
671
|
onConfirm,
|
|
646
672
|
onCancel
|
|
647
673
|
};
|
|
648
|
-
}
|
|
674
|
+
}
|
|
649
675
|
|
|
650
676
|
// src/hooks/useIsBlocked/useIsBlocked.ts
|
|
651
|
-
|
|
652
|
-
return
|
|
653
|
-
}
|
|
677
|
+
function useIsBlocked(scope) {
|
|
678
|
+
return useResolvedValue((state) => state.isBlocked(scope));
|
|
679
|
+
}
|
|
654
680
|
|
|
655
681
|
// src/hooks/useScheduledBlocker/useScheduledBlocker.utils.ts
|
|
656
682
|
var MAX_TIMEOUT_MS = 2147483647;
|
|
@@ -690,8 +716,8 @@ function isScheduleInPast(startTime, endTime, now) {
|
|
|
690
716
|
}
|
|
691
717
|
|
|
692
718
|
// src/hooks/useScheduledBlocker/useScheduledBlocker.ts
|
|
693
|
-
|
|
694
|
-
const { addBlocker, removeBlocker } =
|
|
719
|
+
function useScheduledBlocker(blockerId, config) {
|
|
720
|
+
const { addBlocker, removeBlocker } = useResolvedValue((state) => ({
|
|
695
721
|
addBlocker: state.addBlocker,
|
|
696
722
|
removeBlocker: state.removeBlocker
|
|
697
723
|
}));
|
|
@@ -712,7 +738,6 @@ var useScheduledBlocker = (blockerId, config) => {
|
|
|
712
738
|
const startTime = parseDate(currentConfig.schedule.start);
|
|
713
739
|
const now = Date.now();
|
|
714
740
|
if (!isValidTimestamp(startTime)) {
|
|
715
|
-
console.error(`[UIBlocking] Invalid start time for blocker "${blockerId}"`);
|
|
716
741
|
return;
|
|
717
742
|
}
|
|
718
743
|
const scheduleEnd = () => {
|
|
@@ -737,9 +762,6 @@ var useScheduledBlocker = (blockerId, config) => {
|
|
|
737
762
|
if (startTime > now) {
|
|
738
763
|
const delay = startTime - now;
|
|
739
764
|
if (!isSafeTimeout(delay)) {
|
|
740
|
-
console.warn(
|
|
741
|
-
`[UIBlocking] Schedule delay exceeds maximum timeout (${MAX_TIMEOUT_MS.toString()}ms) for blocker "${blockerId}"`
|
|
742
|
-
);
|
|
743
765
|
return;
|
|
744
766
|
}
|
|
745
767
|
timeoutsRef.current.start = setTimeout(startBlocking, delay);
|
|
@@ -748,7 +770,7 @@ var useScheduledBlocker = (blockerId, config) => {
|
|
|
748
770
|
if (isInBlockingPeriod(startTime, endTime, now)) {
|
|
749
771
|
startBlocking();
|
|
750
772
|
} else if (isScheduleInPast(startTime, endTime, now)) {
|
|
751
|
-
|
|
773
|
+
return;
|
|
752
774
|
}
|
|
753
775
|
}
|
|
754
776
|
return () => {
|
|
@@ -756,13 +778,19 @@ var useScheduledBlocker = (blockerId, config) => {
|
|
|
756
778
|
removeBlocker(blockerId);
|
|
757
779
|
};
|
|
758
780
|
}, [blockerId, addBlocker, removeBlocker, cleanup]);
|
|
759
|
-
}
|
|
781
|
+
}
|
|
760
782
|
|
|
761
783
|
// src/middleware/analyticsMiddleware.utils.ts
|
|
762
|
-
|
|
784
|
+
function capitalizeFirst(str) {
|
|
763
785
|
return str.charAt(0).toUpperCase() + str.slice(1);
|
|
764
|
-
}
|
|
765
|
-
|
|
786
|
+
}
|
|
787
|
+
function getAnalyticsWindow() {
|
|
788
|
+
if (typeof window === "undefined") {
|
|
789
|
+
return void 0;
|
|
790
|
+
}
|
|
791
|
+
return window;
|
|
792
|
+
}
|
|
793
|
+
function buildEventData(context) {
|
|
766
794
|
const eventData = {
|
|
767
795
|
blocker_id: context.blockerId
|
|
768
796
|
};
|
|
@@ -777,33 +805,36 @@ var buildEventData = (context) => {
|
|
|
777
805
|
eventData.priority = priority;
|
|
778
806
|
}
|
|
779
807
|
return eventData;
|
|
780
|
-
}
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
808
|
+
}
|
|
809
|
+
function trackGoogleAnalytics(action, eventData) {
|
|
810
|
+
const analyticsWindow = getAnalyticsWindow();
|
|
811
|
+
if (analyticsWindow?.gtag) {
|
|
812
|
+
analyticsWindow.gtag("event", `ui_blocking_${action}`, eventData);
|
|
784
813
|
}
|
|
785
|
-
}
|
|
786
|
-
|
|
787
|
-
|
|
814
|
+
}
|
|
815
|
+
function trackMixpanel(action, eventData) {
|
|
816
|
+
const analyticsWindow = getAnalyticsWindow();
|
|
817
|
+
if (analyticsWindow?.mixpanel) {
|
|
788
818
|
const eventName = `UI Blocking ${action.split("_").map(capitalizeFirst).join(" ")}`;
|
|
789
|
-
|
|
819
|
+
analyticsWindow.mixpanel.track(eventName, eventData);
|
|
790
820
|
}
|
|
791
|
-
}
|
|
792
|
-
|
|
793
|
-
|
|
821
|
+
}
|
|
822
|
+
function trackAmplitude(action, eventData) {
|
|
823
|
+
const analyticsWindow = getAnalyticsWindow();
|
|
824
|
+
if (analyticsWindow?.amplitude) {
|
|
794
825
|
const eventName = `UI Blocking ${action.split("_").map(capitalizeFirst).join(" ")}`;
|
|
795
|
-
|
|
826
|
+
analyticsWindow.amplitude.track(eventName, eventData);
|
|
796
827
|
}
|
|
797
|
-
}
|
|
798
|
-
|
|
828
|
+
}
|
|
829
|
+
function trackCustom(trackFn, action, eventData) {
|
|
799
830
|
try {
|
|
800
831
|
trackFn(`ui_blocking_${action}`, eventData);
|
|
801
832
|
} catch {
|
|
802
833
|
}
|
|
803
|
-
}
|
|
834
|
+
}
|
|
804
835
|
|
|
805
836
|
// src/middleware/analyticsMiddleware.ts
|
|
806
|
-
|
|
837
|
+
function trackEvent(config, context, eventData) {
|
|
807
838
|
if ("provider" in config) {
|
|
808
839
|
switch (config.provider) {
|
|
809
840
|
case "ga":
|
|
@@ -820,25 +851,31 @@ var trackEvent = (config, context, eventData) => {
|
|
|
820
851
|
if ("track" in config) {
|
|
821
852
|
trackCustom(config.track, context.action, eventData);
|
|
822
853
|
}
|
|
823
|
-
}
|
|
824
|
-
|
|
854
|
+
}
|
|
855
|
+
function createAnalyticsMiddleware(config) {
|
|
825
856
|
return (context) => {
|
|
826
857
|
const eventData = buildEventData(context);
|
|
827
858
|
trackEvent(config, context, eventData);
|
|
828
859
|
};
|
|
829
|
-
}
|
|
860
|
+
}
|
|
830
861
|
|
|
831
862
|
// src/middleware/configureMiddleware.ts
|
|
863
|
+
var CONFIGURE_MIDDLEWARE_PREFIX = "middleware-";
|
|
832
864
|
function configureMiddleware(middlewares) {
|
|
833
865
|
const store = uiBlockingStoreApi.getState();
|
|
866
|
+
store.middlewares.forEach((_, name) => {
|
|
867
|
+
if (name.startsWith(CONFIGURE_MIDDLEWARE_PREFIX)) {
|
|
868
|
+
store.unregisterMiddleware(name);
|
|
869
|
+
}
|
|
870
|
+
});
|
|
834
871
|
middlewares.forEach((middleware, index) => {
|
|
835
|
-
const name =
|
|
872
|
+
const name = `${CONFIGURE_MIDDLEWARE_PREFIX}${index.toString()}`;
|
|
836
873
|
store.registerMiddleware(name, middleware);
|
|
837
874
|
});
|
|
838
875
|
}
|
|
839
876
|
|
|
840
877
|
// src/middleware/loggerMiddleware.utils.ts
|
|
841
|
-
|
|
878
|
+
function getActionEmoji(action) {
|
|
842
879
|
switch (action) {
|
|
843
880
|
case "add":
|
|
844
881
|
return "\u2795";
|
|
@@ -855,8 +892,8 @@ var getActionEmoji = (action) => {
|
|
|
855
892
|
default:
|
|
856
893
|
return "\u2753";
|
|
857
894
|
}
|
|
858
|
-
}
|
|
859
|
-
|
|
895
|
+
}
|
|
896
|
+
function extractConfigDetails(config) {
|
|
860
897
|
if (!config) {
|
|
861
898
|
return {};
|
|
862
899
|
}
|
|
@@ -869,8 +906,8 @@ var extractConfigDetails = (config) => {
|
|
|
869
906
|
details.priority = priority;
|
|
870
907
|
}
|
|
871
908
|
return details;
|
|
872
|
-
}
|
|
873
|
-
|
|
909
|
+
}
|
|
910
|
+
function formatLogData(context) {
|
|
874
911
|
const configDetails = extractConfigDetails(context.config);
|
|
875
912
|
const logData = {};
|
|
876
913
|
if (context.scope !== void 0) {
|
|
@@ -889,26 +926,26 @@ var formatLogData = (context) => {
|
|
|
889
926
|
return configDetails;
|
|
890
927
|
}
|
|
891
928
|
return Object.keys(logData).length === 1 && logData.config ? configDetails : logData;
|
|
892
|
-
}
|
|
929
|
+
}
|
|
893
930
|
|
|
894
931
|
// src/middleware/loggerMiddleware.ts
|
|
895
932
|
var LOG_PREFIX = "[UIBlocking]";
|
|
896
|
-
|
|
933
|
+
function loggerMiddleware(context) {
|
|
897
934
|
const emoji = getActionEmoji(context.action);
|
|
898
935
|
const logData = formatLogData(context);
|
|
899
936
|
console.log(LOG_PREFIX, emoji, context.blockerId, logData);
|
|
900
|
-
}
|
|
937
|
+
}
|
|
901
938
|
|
|
902
939
|
// src/middleware/performanceMiddleware.utils.ts
|
|
903
|
-
|
|
940
|
+
function handleAddAction(blockerId, timestamp, blockStartTimes) {
|
|
904
941
|
blockStartTimes.set(blockerId, timestamp);
|
|
905
|
-
}
|
|
942
|
+
}
|
|
906
943
|
|
|
907
944
|
// src/middleware/performanceMiddleware.ts
|
|
908
945
|
var DEFAULT_SLOW_BLOCK_THRESHOLD = 3e3;
|
|
909
946
|
var ACTION_ADD = "add";
|
|
910
947
|
var ACTION_REMOVE = "remove";
|
|
911
|
-
|
|
948
|
+
function handleRemoveAction(context, blockStartTimes, slowBlockThreshold, onSlowBlock) {
|
|
912
949
|
const startTime = blockStartTimes.get(context.blockerId);
|
|
913
950
|
if (startTime === void 0) {
|
|
914
951
|
return;
|
|
@@ -916,13 +953,10 @@ var handleRemoveAction = (context, blockStartTimes, slowBlockThreshold, onSlowBl
|
|
|
916
953
|
const duration = context.timestamp - startTime;
|
|
917
954
|
blockStartTimes.delete(context.blockerId);
|
|
918
955
|
if (duration >= slowBlockThreshold) {
|
|
919
|
-
console.warn(
|
|
920
|
-
`[UIBlocking] Slow block detected: "${context.blockerId}" took ${duration.toString()}ms`
|
|
921
|
-
);
|
|
922
956
|
onSlowBlock?.(context.blockerId, duration);
|
|
923
957
|
}
|
|
924
|
-
}
|
|
925
|
-
|
|
958
|
+
}
|
|
959
|
+
function createPerformanceMiddleware(config = {}) {
|
|
926
960
|
const { slowBlockThreshold = DEFAULT_SLOW_BLOCK_THRESHOLD, onSlowBlock } = config;
|
|
927
961
|
const blockStartTimes = /* @__PURE__ */ new Map();
|
|
928
962
|
return (context) => {
|
|
@@ -932,7 +966,7 @@ var createPerformanceMiddleware = (config = {}) => {
|
|
|
932
966
|
handleRemoveAction(context, blockStartTimes, slowBlockThreshold, onSlowBlock);
|
|
933
967
|
}
|
|
934
968
|
};
|
|
935
|
-
}
|
|
969
|
+
}
|
|
936
970
|
|
|
937
971
|
// src/createTypedHooks.ts
|
|
938
972
|
function createTypedHooks() {
|
|
@@ -950,10 +984,17 @@ function createTypedHooks() {
|
|
|
950
984
|
};
|
|
951
985
|
}
|
|
952
986
|
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
}
|
|
987
|
+
// src/index.ts
|
|
988
|
+
function useOptionalContext() {
|
|
989
|
+
return useOptionalUIBlockingContext();
|
|
990
|
+
}
|
|
991
|
+
function useResolvedStore() {
|
|
992
|
+
return useResolvedStoreApi();
|
|
993
|
+
}
|
|
994
|
+
function useResolvedStoreWithSelector(selector) {
|
|
995
|
+
return useResolvedValue(selector);
|
|
996
|
+
}
|
|
997
|
+
|
|
957
998
|
exports.UIBlockingProvider = UIBlockingProvider;
|
|
958
999
|
exports.configureMiddleware = configureMiddleware;
|
|
959
1000
|
exports.createAnalyticsMiddleware = createAnalyticsMiddleware;
|
|
@@ -968,6 +1009,12 @@ exports.useConditionalBlocker = useConditionalBlocker;
|
|
|
968
1009
|
exports.useConfirmableBlocker = useConfirmableBlocker;
|
|
969
1010
|
exports.useIsBlocked = useIsBlocked;
|
|
970
1011
|
exports.useIsInsideUIBlockingProvider = useIsInsideUIBlockingProvider;
|
|
1012
|
+
exports.useOptionalContext = useOptionalContext;
|
|
1013
|
+
exports.useOptionalUIBlockingContext = useOptionalUIBlockingContext;
|
|
1014
|
+
exports.useResolvedStore = useResolvedStore;
|
|
1015
|
+
exports.useResolvedStoreApi = useResolvedStoreApi;
|
|
1016
|
+
exports.useResolvedStoreWithSelector = useResolvedStoreWithSelector;
|
|
1017
|
+
exports.useResolvedValue = useResolvedValue;
|
|
971
1018
|
exports.useScheduledBlocker = useScheduledBlocker;
|
|
972
1019
|
exports.useUIBlockingContext = useUIBlockingContext;
|
|
973
1020
|
exports.useUIBlockingStore = useUIBlockingStore;
|