@okyrychenko-dev/react-action-guard 0.3.2 → 0.4.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/LICENSE +1 -1
- package/README.md +166 -19
- package/dist/index.cjs +146 -11
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +199 -2
- package/dist/index.d.ts +199 -2
- package/dist/index.js +141 -13
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
package/dist/index.d.ts
CHANGED
|
@@ -1,18 +1,32 @@
|
|
|
1
1
|
import * as zustand from 'zustand';
|
|
2
|
+
import { StoreApi } from 'zustand';
|
|
3
|
+
import { ReactNode } from 'react';
|
|
2
4
|
|
|
5
|
+
/**
|
|
6
|
+
* Options for useAsyncAction hook
|
|
7
|
+
*/
|
|
8
|
+
interface UseAsyncActionOptions {
|
|
9
|
+
/** Timeout in milliseconds after which the blocker will be automatically removed */
|
|
10
|
+
timeout?: number;
|
|
11
|
+
/** Callback invoked when the blocker is automatically removed due to timeout */
|
|
12
|
+
onTimeout?: (blockerId: string) => void;
|
|
13
|
+
}
|
|
3
14
|
/**
|
|
4
15
|
* Hook to wrap async functions with automatic blocking/unblocking
|
|
5
16
|
*
|
|
6
17
|
* Creates a wrapper function that automatically adds a blocker before executing
|
|
7
18
|
* the async function and removes it after completion (success or failure).
|
|
8
19
|
*
|
|
20
|
+
* Supports both global store and context store (via UIBlockingProvider).
|
|
21
|
+
*
|
|
9
22
|
* @template T - The return type of the async function
|
|
10
23
|
* @param actionId - Unique identifier for the action (used in blocker ID and reason)
|
|
11
24
|
* @param scope - Scope(s) to block during execution (default: "global")
|
|
25
|
+
* @param options - Optional configuration including timeout
|
|
12
26
|
* @returns Function that wraps async functions with blocking behavior
|
|
13
27
|
*
|
|
14
28
|
*/
|
|
15
|
-
declare const useAsyncAction: <T = unknown>(actionId: string, scope?: string | ReadonlyArray<string
|
|
29
|
+
declare const useAsyncAction: <T = unknown>(actionId: string, scope?: string | ReadonlyArray<string>, options?: UseAsyncActionOptions) => ((asyncFn: () => Promise<T>) => Promise<T>);
|
|
16
30
|
|
|
17
31
|
type AnalyticsEventData = Record<string, string | number | ReadonlyArray<string> | undefined>;
|
|
18
32
|
type AnalyticsProvider = "ga" | "mixpanel" | "amplitude";
|
|
@@ -43,6 +57,17 @@ type Middleware = (context: MiddlewareContext) => void | Promise<void>;
|
|
|
43
57
|
|
|
44
58
|
declare const createAnalyticsMiddleware: (config: AnalyticsConfig) => Middleware;
|
|
45
59
|
|
|
60
|
+
/**
|
|
61
|
+
* Configure middleware for the UI blocking store
|
|
62
|
+
*
|
|
63
|
+
* This is a convenience function that registers multiple middleware at once.
|
|
64
|
+
* Each middleware will be registered with an auto-generated name based on its index.
|
|
65
|
+
*
|
|
66
|
+
* @param middlewares - Array of middleware functions to register
|
|
67
|
+
*
|
|
68
|
+
*/
|
|
69
|
+
declare function configureMiddleware(middlewares: ReadonlyArray<Middleware>): void;
|
|
70
|
+
|
|
46
71
|
declare const loggerMiddleware: Middleware;
|
|
47
72
|
|
|
48
73
|
interface PerformanceConfig {
|
|
@@ -60,6 +85,10 @@ interface BlockerConfig {
|
|
|
60
85
|
reason?: string;
|
|
61
86
|
priority?: number;
|
|
62
87
|
timestamp?: number;
|
|
88
|
+
/** Automatically remove the blocker after N milliseconds */
|
|
89
|
+
timeout?: number;
|
|
90
|
+
/** Callback invoked when the blocker is automatically removed due to timeout */
|
|
91
|
+
onTimeout?: (blockerId: string) => void;
|
|
63
92
|
}
|
|
64
93
|
/**
|
|
65
94
|
* Stored blocker with all required fields
|
|
@@ -69,6 +98,12 @@ interface StoredBlocker {
|
|
|
69
98
|
reason: string;
|
|
70
99
|
priority: number;
|
|
71
100
|
timestamp: number;
|
|
101
|
+
/** Timeout duration in milliseconds (if set) */
|
|
102
|
+
timeout?: number;
|
|
103
|
+
/** Internal timeout ID for cleanup */
|
|
104
|
+
timeoutId?: ReturnType<typeof setTimeout>;
|
|
105
|
+
/** Callback invoked when the blocker is automatically removed due to timeout */
|
|
106
|
+
onTimeout?: (blockerId: string) => void;
|
|
72
107
|
}
|
|
73
108
|
/**
|
|
74
109
|
* Blocker information including its ID
|
|
@@ -124,6 +159,8 @@ declare const uiBlockingStoreApi: zustand.StoreApi<UIBlockingStore>;
|
|
|
124
159
|
* Automatically adds a blocker when the component mounts (or when activated)
|
|
125
160
|
* and removes it when the component unmounts (or when deactivated).
|
|
126
161
|
*
|
|
162
|
+
* Supports both global store and context store (via UIBlockingProvider).
|
|
163
|
+
*
|
|
127
164
|
* @param blockerId - Unique identifier for the blocker
|
|
128
165
|
* @param config - Blocker configuration (scope, reason, priority)
|
|
129
166
|
* @param isActive - Whether the blocker is currently active (default: true)
|
|
@@ -131,6 +168,24 @@ declare const uiBlockingStoreApi: zustand.StoreApi<UIBlockingStore>;
|
|
|
131
168
|
*/
|
|
132
169
|
declare const useBlocker: (blockerId: string, config: BlockerConfig, isActive?: boolean) => void;
|
|
133
170
|
|
|
171
|
+
/**
|
|
172
|
+
* Hook to get detailed blocking information for a scope
|
|
173
|
+
*
|
|
174
|
+
* Returns an array of blocker information objects for the specified scope,
|
|
175
|
+
* sorted by priority (highest first). Automatically re-renders when blockers change.
|
|
176
|
+
*
|
|
177
|
+
* Uses a stable reference - only re-renders when the actual blocker list changes,
|
|
178
|
+
* not on every store update.
|
|
179
|
+
*
|
|
180
|
+
* Supports both global store and context store (via UIBlockingProvider).
|
|
181
|
+
*
|
|
182
|
+
* @param scope - Scope to get blocking information for (default: "global")
|
|
183
|
+
* @returns Array of blocker information objects, sorted by priority
|
|
184
|
+
*
|
|
185
|
+
* ```
|
|
186
|
+
*/
|
|
187
|
+
declare const useBlockingInfo: (scope?: string) => ReadonlyArray<BlockerInfo>;
|
|
188
|
+
|
|
134
189
|
/**
|
|
135
190
|
* Configuration for conditional blocker hook.
|
|
136
191
|
*
|
|
@@ -154,6 +209,8 @@ interface ConditionalBlockerConfig<TState = unknown> extends Omit<BlockerConfig,
|
|
|
154
209
|
* Periodically checks a condition and blocks/unblocks based on the result.
|
|
155
210
|
* The condition is evaluated at the specified interval (default 1000ms).
|
|
156
211
|
*
|
|
212
|
+
* Supports both global store and context store (via UIBlockingProvider).
|
|
213
|
+
*
|
|
157
214
|
* @param blockerId - Unique identifier for this blocker
|
|
158
215
|
* @param config - Configuration including condition function and check interval
|
|
159
216
|
*
|
|
@@ -213,6 +270,8 @@ declare const useConfirmableBlocker: (blockerId: string, config: ConfirmableBloc
|
|
|
213
270
|
* Returns a boolean indicating whether the specified scope(s) are blocked.
|
|
214
271
|
* Automatically re-renders when the blocking state changes.
|
|
215
272
|
*
|
|
273
|
+
* Supports both global store and context store (via UIBlockingProvider).
|
|
274
|
+
*
|
|
216
275
|
* @param scope - Scope or array of scopes to check (default: "global")
|
|
217
276
|
* @returns True if any of the specified scopes are blocked
|
|
218
277
|
*
|
|
@@ -255,10 +314,148 @@ interface ScheduledBlockerConfig extends BlockerConfig {
|
|
|
255
314
|
* Supports start/end times as Date objects, ISO strings, or timestamps.
|
|
256
315
|
* If both duration and end are provided, duration takes precedence.
|
|
257
316
|
*
|
|
317
|
+
* Supports both global store and context store (via UIBlockingProvider).
|
|
318
|
+
*
|
|
258
319
|
* @param blockerId - Unique identifier for this blocker
|
|
259
320
|
* @param config - Configuration including schedule and callbacks
|
|
260
321
|
*
|
|
261
322
|
*/
|
|
262
323
|
declare const useScheduledBlocker: (blockerId: string, config: ScheduledBlockerConfig) => void;
|
|
263
324
|
|
|
264
|
-
|
|
325
|
+
/**
|
|
326
|
+
* Default scope type
|
|
327
|
+
*/
|
|
328
|
+
type DefaultScopes = "global";
|
|
329
|
+
/**
|
|
330
|
+
* Utility type for scope values - can be a single scope or array of scopes
|
|
331
|
+
*/
|
|
332
|
+
type ScopeValue<TScope extends string = string> = TScope | ReadonlyArray<TScope>;
|
|
333
|
+
/**
|
|
334
|
+
* Type-safe blocker configuration with generic scope type
|
|
335
|
+
*/
|
|
336
|
+
interface BlockerConfigTyped<TScope extends string> {
|
|
337
|
+
/** Scope(s) to block */
|
|
338
|
+
scope?: ScopeValue<TScope>;
|
|
339
|
+
/** Human-readable reason for blocking */
|
|
340
|
+
reason?: string;
|
|
341
|
+
/** Priority level (higher priority blockers take precedence) */
|
|
342
|
+
priority?: number;
|
|
343
|
+
/** Timestamp when the blocker was created */
|
|
344
|
+
timestamp?: number;
|
|
345
|
+
/** Timeout in milliseconds after which the blocker will be automatically removed */
|
|
346
|
+
timeout?: number;
|
|
347
|
+
/** Callback invoked when the blocker is automatically removed due to timeout */
|
|
348
|
+
onTimeout?: (blockerId: string) => void;
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
/**
|
|
352
|
+
* Return type for createTypedHooks factory
|
|
353
|
+
*/
|
|
354
|
+
interface TypedHooks<TScope extends string> {
|
|
355
|
+
/**
|
|
356
|
+
* Type-safe version of useBlocker hook
|
|
357
|
+
*/
|
|
358
|
+
useBlocker: (blockerId: string, config: BlockerConfigTyped<TScope>, isActive?: boolean) => void;
|
|
359
|
+
/**
|
|
360
|
+
* Type-safe version of useIsBlocked hook
|
|
361
|
+
*/
|
|
362
|
+
useIsBlocked: (scope?: ScopeValue<TScope>) => boolean;
|
|
363
|
+
/**
|
|
364
|
+
* Type-safe version of useAsyncAction hook
|
|
365
|
+
*/
|
|
366
|
+
useAsyncAction: <T = unknown>(actionId: string, scope?: ScopeValue<TScope>, options?: UseAsyncActionOptions) => (asyncFn: () => Promise<T>) => Promise<T>;
|
|
367
|
+
/**
|
|
368
|
+
* Type-safe version of useBlockingInfo hook
|
|
369
|
+
*/
|
|
370
|
+
useBlockingInfo: (scope: TScope) => ReturnType<typeof useBlockingInfo>;
|
|
371
|
+
}
|
|
372
|
+
/**
|
|
373
|
+
* Creates type-safe versions of all blocking hooks with your custom scope types
|
|
374
|
+
*
|
|
375
|
+
* @template TScope - Union type of allowed scope strings
|
|
376
|
+
* @returns Object with type-safe hook functions
|
|
377
|
+
*
|
|
378
|
+
* @example
|
|
379
|
+
* ```typescript
|
|
380
|
+
* // Define your app's scopes
|
|
381
|
+
* type AppScopes = "global" | "form" | "navigation" | "checkout";
|
|
382
|
+
*
|
|
383
|
+
* // Create typed hooks
|
|
384
|
+
* const { useBlocker, useIsBlocked, useAsyncAction } = createTypedHooks<AppScopes>();
|
|
385
|
+
*
|
|
386
|
+
* // Now TypeScript will catch typos
|
|
387
|
+
* useBlocker("id", { scope: "form" }); // ✓ OK
|
|
388
|
+
* useBlocker("id", { scope: "typo" }); // ✗ Type error!
|
|
389
|
+
* ```
|
|
390
|
+
*/
|
|
391
|
+
declare function createTypedHooks<TScope extends string>(): TypedHooks<TScope>;
|
|
392
|
+
|
|
393
|
+
/**
|
|
394
|
+
* Props for UIBlockingProvider component
|
|
395
|
+
*/
|
|
396
|
+
interface UIBlockingProviderProps {
|
|
397
|
+
children: ReactNode;
|
|
398
|
+
/** Enable Redux DevTools integration (default: true in development) */
|
|
399
|
+
enableDevtools?: boolean;
|
|
400
|
+
/** Name for Redux DevTools (default: "UIBlocking") */
|
|
401
|
+
devtoolsName?: string;
|
|
402
|
+
/** Initial middlewares to register */
|
|
403
|
+
middlewares?: ReadonlyArray<Middleware>;
|
|
404
|
+
}
|
|
405
|
+
/**
|
|
406
|
+
* Provider component for isolated UI blocking state
|
|
407
|
+
*
|
|
408
|
+
* Use this to create an isolated store instance for:
|
|
409
|
+
* - Server-side rendering (each request gets its own store)
|
|
410
|
+
* - Testing (no need for beforeEach cleanup)
|
|
411
|
+
* - Micro-frontends (isolated state per app)
|
|
412
|
+
*
|
|
413
|
+
* @example
|
|
414
|
+
* ```tsx
|
|
415
|
+
* // SSR - each request gets its own store
|
|
416
|
+
* function App() {
|
|
417
|
+
* return (
|
|
418
|
+
* <UIBlockingProvider>
|
|
419
|
+
* <MyComponent />
|
|
420
|
+
* </UIBlockingProvider>
|
|
421
|
+
* );
|
|
422
|
+
* }
|
|
423
|
+
*
|
|
424
|
+
* // Testing - isolated store per test
|
|
425
|
+
* function renderWithProvider(ui: ReactElement) {
|
|
426
|
+
* return render(
|
|
427
|
+
* <UIBlockingProvider>{ui}</UIBlockingProvider>
|
|
428
|
+
* );
|
|
429
|
+
* }
|
|
430
|
+
* ```
|
|
431
|
+
*/
|
|
432
|
+
declare function UIBlockingProvider({ children, enableDevtools, devtoolsName, middlewares, }: UIBlockingProviderProps): ReactNode;
|
|
433
|
+
/**
|
|
434
|
+
* Hook to access the UIBlocking store from context
|
|
435
|
+
*
|
|
436
|
+
* Throws an error if used outside of UIBlockingProvider.
|
|
437
|
+
* Use useUIBlockingStoreFromContext for safe access with fallback.
|
|
438
|
+
*
|
|
439
|
+
* @returns The store API from the nearest UIBlockingProvider
|
|
440
|
+
* @throws Error if used outside of UIBlockingProvider
|
|
441
|
+
*/
|
|
442
|
+
declare function useUIBlockingContext(): StoreApi<UIBlockingStore>;
|
|
443
|
+
/**
|
|
444
|
+
* Hook to check if we're inside a UIBlockingProvider
|
|
445
|
+
*
|
|
446
|
+
* @returns true if inside UIBlockingProvider, false otherwise
|
|
447
|
+
*/
|
|
448
|
+
declare function useIsInsideUIBlockingProvider(): boolean;
|
|
449
|
+
/**
|
|
450
|
+
* Hook to use the UIBlocking store from context with a selector
|
|
451
|
+
*
|
|
452
|
+
* This is similar to useUIBlockingStore but uses the context store.
|
|
453
|
+
* Automatically applies shallow comparison to prevent unnecessary re-renders.
|
|
454
|
+
*
|
|
455
|
+
* @param selector - Selector function to pick state from the store
|
|
456
|
+
* @returns Selected state value
|
|
457
|
+
* @throws Error if used outside of UIBlockingProvider
|
|
458
|
+
*/
|
|
459
|
+
declare function useUIBlockingStoreFromContext<T>(selector: (state: UIBlockingStore) => T): T;
|
|
460
|
+
|
|
461
|
+
export { type AmplitudeConfig, type AnalyticsConfig, type AnalyticsEventData, type AnalyticsProvider, type BlockerConfig, type BlockerConfigTyped, type BlockerInfo, type BlockingAction, type BlockingSchedule, type ConditionalBlockerConfig, type ConfirmDialogConfig, type ConfirmableBlockerConfig, type CustomAnalyticsConfig, type DefaultScopes, type GoogleAnalyticsConfig, type Middleware, type MiddlewareContext, type MixpanelConfig, type PerformanceConfig, type ScheduledBlockerConfig, type ScopeValue, type StoredBlocker, type TypedHooks, UIBlockingProvider, type UIBlockingProviderProps, type UIBlockingStore, type UIBlockingStoreActions, type UIBlockingStoreState, type UseAsyncActionOptions, type UseConfirmableBlockerReturn, configureMiddleware, createAnalyticsMiddleware, createPerformanceMiddleware, createTypedHooks, loggerMiddleware, uiBlockingStoreApi, useAsyncAction, useBlocker, useBlockingInfo, useConditionalBlocker, useConfirmableBlocker, useIsBlocked, useIsInsideUIBlockingProvider, useScheduledBlocker, useUIBlockingContext, useUIBlockingStore, useUIBlockingStoreFromContext };
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import { useCallback, useEffect,
|
|
2
|
-
import { devtools } from 'zustand/middleware';
|
|
1
|
+
import { createContext, useRef, useMemo, useContext, useCallback, useEffect, useState } from 'react';
|
|
3
2
|
import { createStore, useStore } from 'zustand';
|
|
3
|
+
import { devtools } from 'zustand/middleware';
|
|
4
4
|
import { useShallow } from 'zustand/shallow';
|
|
5
|
+
import { jsx } from 'react/jsx-runtime';
|
|
5
6
|
|
|
6
7
|
// src/hooks/useAsyncAction/useAsyncAction.ts
|
|
7
8
|
|
|
@@ -65,13 +66,40 @@ var createUIBlockingActions = (set, get) => ({
|
|
|
65
66
|
*
|
|
66
67
|
*/
|
|
67
68
|
addBlocker: (id, config = {}) => {
|
|
69
|
+
const existingBlocker = get().activeBlockers.get(id);
|
|
70
|
+
if (existingBlocker?.timeoutId) {
|
|
71
|
+
clearTimeout(existingBlocker.timeoutId);
|
|
72
|
+
}
|
|
73
|
+
let timeoutId;
|
|
74
|
+
if (config.timeout && config.timeout > 0) {
|
|
75
|
+
timeoutId = setTimeout(() => {
|
|
76
|
+
if (get().activeBlockers.has(id)) {
|
|
77
|
+
config.onTimeout?.(id);
|
|
78
|
+
void get().runMiddlewares({
|
|
79
|
+
action: "timeout",
|
|
80
|
+
blockerId: id,
|
|
81
|
+
config: {
|
|
82
|
+
scope: config.scope,
|
|
83
|
+
reason: config.reason,
|
|
84
|
+
priority: config.priority,
|
|
85
|
+
timeout: config.timeout
|
|
86
|
+
},
|
|
87
|
+
timestamp: Date.now()
|
|
88
|
+
});
|
|
89
|
+
get().removeBlocker(id);
|
|
90
|
+
}
|
|
91
|
+
}, config.timeout);
|
|
92
|
+
}
|
|
68
93
|
set((state) => {
|
|
69
94
|
const newBlockers = new Map(state.activeBlockers);
|
|
70
95
|
const storedBlocker = {
|
|
71
96
|
scope: config.scope ?? DEFAULT_SCOPE,
|
|
72
97
|
reason: config.reason ?? DEFAULT_REASON,
|
|
73
98
|
priority: config.priority ?? DEFAULT_PRIORITY,
|
|
74
|
-
timestamp: config.timestamp ?? Date.now()
|
|
99
|
+
timestamp: config.timestamp ?? Date.now(),
|
|
100
|
+
timeout: config.timeout,
|
|
101
|
+
timeoutId,
|
|
102
|
+
onTimeout: config.onTimeout
|
|
75
103
|
};
|
|
76
104
|
newBlockers.set(id, storedBlocker);
|
|
77
105
|
return { activeBlockers: newBlockers };
|
|
@@ -82,7 +110,8 @@ var createUIBlockingActions = (set, get) => ({
|
|
|
82
110
|
config: {
|
|
83
111
|
scope: config.scope,
|
|
84
112
|
reason: config.reason,
|
|
85
|
-
priority: config.priority
|
|
113
|
+
priority: config.priority,
|
|
114
|
+
timeout: config.timeout
|
|
86
115
|
},
|
|
87
116
|
timestamp: Date.now()
|
|
88
117
|
});
|
|
@@ -95,6 +124,9 @@ var createUIBlockingActions = (set, get) => ({
|
|
|
95
124
|
*/
|
|
96
125
|
removeBlocker: (id) => {
|
|
97
126
|
const prevBlocker = get().activeBlockers.get(id);
|
|
127
|
+
if (prevBlocker?.timeoutId) {
|
|
128
|
+
clearTimeout(prevBlocker.timeoutId);
|
|
129
|
+
}
|
|
98
130
|
set((state) => {
|
|
99
131
|
const newBlockers = new Map(state.activeBlockers);
|
|
100
132
|
newBlockers.delete(id);
|
|
@@ -153,6 +185,12 @@ var createUIBlockingActions = (set, get) => ({
|
|
|
153
185
|
* Clear all blockers from the store
|
|
154
186
|
*/
|
|
155
187
|
clearAllBlockers: () => {
|
|
188
|
+
const { activeBlockers } = get();
|
|
189
|
+
for (const [, blocker] of activeBlockers) {
|
|
190
|
+
if (blocker.timeoutId) {
|
|
191
|
+
clearTimeout(blocker.timeoutId);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
156
194
|
set({ activeBlockers: /* @__PURE__ */ new Map() });
|
|
157
195
|
},
|
|
158
196
|
/**
|
|
@@ -162,6 +200,13 @@ var createUIBlockingActions = (set, get) => ({
|
|
|
162
200
|
*
|
|
163
201
|
*/
|
|
164
202
|
clearBlockersForScope: (scope) => {
|
|
203
|
+
const { activeBlockers } = get();
|
|
204
|
+
for (const [, blocker] of activeBlockers) {
|
|
205
|
+
const blockerScopes = normalizeScopeToArray(blocker.scope);
|
|
206
|
+
if (blockerScopes.includes(scope) && blocker.timeoutId) {
|
|
207
|
+
clearTimeout(blocker.timeoutId);
|
|
208
|
+
}
|
|
209
|
+
}
|
|
165
210
|
set((state) => {
|
|
166
211
|
const newBlockers = /* @__PURE__ */ new Map();
|
|
167
212
|
for (const [id, blocker] of state.activeBlockers) {
|
|
@@ -174,6 +219,45 @@ var createUIBlockingActions = (set, get) => ({
|
|
|
174
219
|
});
|
|
175
220
|
}
|
|
176
221
|
});
|
|
222
|
+
var UIBlockingContext = createContext(null);
|
|
223
|
+
function UIBlockingProvider({
|
|
224
|
+
children,
|
|
225
|
+
enableDevtools = process.env.NODE_ENV === "development",
|
|
226
|
+
devtoolsName = "UIBlocking",
|
|
227
|
+
middlewares = []
|
|
228
|
+
}) {
|
|
229
|
+
const middlewaresRef = useRef(middlewares);
|
|
230
|
+
const store = useMemo(() => {
|
|
231
|
+
const baseStore = enableDevtools ? createStore(
|
|
232
|
+
devtools(createUIBlockingActions, {
|
|
233
|
+
name: devtoolsName,
|
|
234
|
+
enabled: enableDevtools
|
|
235
|
+
})
|
|
236
|
+
) : createStore(createUIBlockingActions);
|
|
237
|
+
middlewaresRef.current.forEach((mw, index) => {
|
|
238
|
+
baseStore.getState().registerMiddleware(`provider-middleware-${index.toString()}`, mw);
|
|
239
|
+
});
|
|
240
|
+
return baseStore;
|
|
241
|
+
}, [enableDevtools, devtoolsName]);
|
|
242
|
+
return /* @__PURE__ */ jsx(UIBlockingContext.Provider, { value: store, children });
|
|
243
|
+
}
|
|
244
|
+
function useUIBlockingContext() {
|
|
245
|
+
const store = useContext(UIBlockingContext);
|
|
246
|
+
if (!store) {
|
|
247
|
+
throw new Error(
|
|
248
|
+
"useUIBlockingContext must be used within UIBlockingProvider. Either wrap your app in <UIBlockingProvider> or use the global store via useUIBlockingStore."
|
|
249
|
+
);
|
|
250
|
+
}
|
|
251
|
+
return store;
|
|
252
|
+
}
|
|
253
|
+
function useIsInsideUIBlockingProvider() {
|
|
254
|
+
const store = useContext(UIBlockingContext);
|
|
255
|
+
return store !== null;
|
|
256
|
+
}
|
|
257
|
+
function useUIBlockingStoreFromContext(selector) {
|
|
258
|
+
const store = useUIBlockingContext();
|
|
259
|
+
return useStore(store, useShallow(selector));
|
|
260
|
+
}
|
|
177
261
|
|
|
178
262
|
// src/store/uiBlockingStore.config.ts
|
|
179
263
|
var devtoolsConfig = {
|
|
@@ -184,9 +268,19 @@ var devtoolsConfig = {
|
|
|
184
268
|
// src/store/uiBlockingStore.store.ts
|
|
185
269
|
var { useStore: useUIBlockingStore, useStoreApi: uiBlockingStoreApi } = createShallowStore(devtools(createUIBlockingActions, devtoolsConfig));
|
|
186
270
|
|
|
271
|
+
// src/context/useResolvedStore.ts
|
|
272
|
+
function useResolvedStore() {
|
|
273
|
+
const contextStore = useContext(UIBlockingContext);
|
|
274
|
+
return contextStore ?? uiBlockingStoreApi;
|
|
275
|
+
}
|
|
276
|
+
function useResolvedStoreWithSelector(selector) {
|
|
277
|
+
const store = useResolvedStore();
|
|
278
|
+
return useStore(store, useShallow(selector));
|
|
279
|
+
}
|
|
280
|
+
|
|
187
281
|
// src/hooks/useAsyncAction/useAsyncAction.ts
|
|
188
|
-
var useAsyncAction = (actionId, scope) => {
|
|
189
|
-
const { addBlocker, removeBlocker } =
|
|
282
|
+
var useAsyncAction = (actionId, scope, options) => {
|
|
283
|
+
const { addBlocker, removeBlocker } = useResolvedStoreWithSelector((state) => ({
|
|
190
284
|
addBlocker: state.addBlocker,
|
|
191
285
|
removeBlocker: state.removeBlocker
|
|
192
286
|
}));
|
|
@@ -197,19 +291,21 @@ var useAsyncAction = (actionId, scope) => {
|
|
|
197
291
|
addBlocker(blockerId, {
|
|
198
292
|
scope,
|
|
199
293
|
reason: `Executing ${actionId}`,
|
|
200
|
-
priority: ASYNC_ACTION_PRIORITY
|
|
294
|
+
priority: ASYNC_ACTION_PRIORITY,
|
|
295
|
+
timeout: options?.timeout,
|
|
296
|
+
onTimeout: options?.onTimeout
|
|
201
297
|
});
|
|
202
298
|
return await asyncFn();
|
|
203
299
|
} finally {
|
|
204
300
|
removeBlocker(blockerId);
|
|
205
301
|
}
|
|
206
302
|
},
|
|
207
|
-
[actionId, scope, addBlocker, removeBlocker]
|
|
303
|
+
[actionId, scope, options?.timeout, options?.onTimeout, addBlocker, removeBlocker]
|
|
208
304
|
);
|
|
209
305
|
return executeWithBlocking;
|
|
210
306
|
};
|
|
211
307
|
var useBlocker = (blockerId, config, isActive = true) => {
|
|
212
|
-
const { addBlocker, removeBlocker } =
|
|
308
|
+
const { addBlocker, removeBlocker } = useResolvedStoreWithSelector((state) => ({
|
|
213
309
|
addBlocker: state.addBlocker,
|
|
214
310
|
removeBlocker: state.removeBlocker
|
|
215
311
|
}));
|
|
@@ -232,6 +328,13 @@ function createBlockerConfig(config) {
|
|
|
232
328
|
priority: config.priority
|
|
233
329
|
};
|
|
234
330
|
}
|
|
331
|
+
var useBlockingInfo = (scope = DEFAULT_SCOPE) => {
|
|
332
|
+
const store = useResolvedStore();
|
|
333
|
+
const activeBlockers = useStore(store, (state) => state.activeBlockers);
|
|
334
|
+
return useMemo(() => {
|
|
335
|
+
return store.getState().getBlockingInfo(scope);
|
|
336
|
+
}, [activeBlockers, scope, store]);
|
|
337
|
+
};
|
|
235
338
|
function useConfigRef(config) {
|
|
236
339
|
const configRef = useRef(config);
|
|
237
340
|
useEffect(() => {
|
|
@@ -242,7 +345,7 @@ function useConfigRef(config) {
|
|
|
242
345
|
|
|
243
346
|
// src/hooks/useConditionalBlocker/useConditionalBlocker.ts
|
|
244
347
|
var useConditionalBlocker = (blockerId, config) => {
|
|
245
|
-
const { addBlocker, removeBlocker } =
|
|
348
|
+
const { addBlocker, removeBlocker } = useResolvedStoreWithSelector((state) => ({
|
|
246
349
|
addBlocker: state.addBlocker,
|
|
247
350
|
removeBlocker: state.removeBlocker
|
|
248
351
|
}));
|
|
@@ -325,7 +428,7 @@ var useConfirmableBlocker = (blockerId, config) => {
|
|
|
325
428
|
|
|
326
429
|
// src/hooks/useIsBlocked/useIsBlocked.ts
|
|
327
430
|
var useIsBlocked = (scope) => {
|
|
328
|
-
return
|
|
431
|
+
return useResolvedStoreWithSelector((state) => state.isBlocked(scope));
|
|
329
432
|
};
|
|
330
433
|
|
|
331
434
|
// src/hooks/useScheduledBlocker/useScheduledBlocker.utils.ts
|
|
@@ -367,7 +470,7 @@ function isScheduleInPast(startTime, endTime, now) {
|
|
|
367
470
|
|
|
368
471
|
// src/hooks/useScheduledBlocker/useScheduledBlocker.ts
|
|
369
472
|
var useScheduledBlocker = (blockerId, config) => {
|
|
370
|
-
const { addBlocker, removeBlocker } =
|
|
473
|
+
const { addBlocker, removeBlocker } = useResolvedStoreWithSelector((state) => ({
|
|
371
474
|
addBlocker: state.addBlocker,
|
|
372
475
|
removeBlocker: state.removeBlocker
|
|
373
476
|
}));
|
|
@@ -504,6 +607,15 @@ var createAnalyticsMiddleware = (config) => {
|
|
|
504
607
|
};
|
|
505
608
|
};
|
|
506
609
|
|
|
610
|
+
// src/middleware/configureMiddleware.ts
|
|
611
|
+
function configureMiddleware(middlewares) {
|
|
612
|
+
const store = uiBlockingStoreApi.getState();
|
|
613
|
+
middlewares.forEach((middleware, index) => {
|
|
614
|
+
const name = `middleware-${index.toString()}`;
|
|
615
|
+
store.registerMiddleware(name, middleware);
|
|
616
|
+
});
|
|
617
|
+
}
|
|
618
|
+
|
|
507
619
|
// src/middleware/loggerMiddleware.utils.ts
|
|
508
620
|
var getActionEmoji = (action) => {
|
|
509
621
|
switch (action) {
|
|
@@ -589,6 +701,22 @@ var createPerformanceMiddleware = (config = {}) => {
|
|
|
589
701
|
};
|
|
590
702
|
};
|
|
591
703
|
|
|
592
|
-
|
|
704
|
+
// src/createTypedHooks.ts
|
|
705
|
+
function createTypedHooks() {
|
|
706
|
+
return {
|
|
707
|
+
useBlocker: (blockerId, config, isActive) => {
|
|
708
|
+
useBlocker(blockerId, config, isActive);
|
|
709
|
+
},
|
|
710
|
+
useIsBlocked: (scope) => {
|
|
711
|
+
return useIsBlocked(scope);
|
|
712
|
+
},
|
|
713
|
+
useAsyncAction: (actionId, scope, options) => {
|
|
714
|
+
return useAsyncAction(actionId, scope, options);
|
|
715
|
+
},
|
|
716
|
+
useBlockingInfo: (scope) => useBlockingInfo(scope)
|
|
717
|
+
};
|
|
718
|
+
}
|
|
719
|
+
|
|
720
|
+
export { UIBlockingProvider, configureMiddleware, createAnalyticsMiddleware, createPerformanceMiddleware, createTypedHooks, loggerMiddleware, uiBlockingStoreApi, useAsyncAction, useBlocker, useBlockingInfo, useConditionalBlocker, useConfirmableBlocker, useIsBlocked, useIsInsideUIBlockingProvider, useScheduledBlocker, useUIBlockingContext, useUIBlockingStore, useUIBlockingStoreFromContext };
|
|
593
721
|
//# sourceMappingURL=index.js.map
|
|
594
722
|
//# sourceMappingURL=index.js.map
|