@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/LICENSE
CHANGED
package/README.md
CHANGED
|
@@ -11,13 +11,15 @@
|
|
|
11
11
|
- Priority-based blocking system
|
|
12
12
|
- Scoped blocking (global, specific areas, or multiple scopes)
|
|
13
13
|
- Automatic cleanup on unmount
|
|
14
|
+
- **Timeout mechanism** - auto-remove blockers after specified time
|
|
15
|
+
- **Provider pattern** - isolated stores for SSR, testing, and micro-frontends
|
|
14
16
|
- Advanced hooks for different use cases:
|
|
15
17
|
- Confirmable blockers with custom dialogs
|
|
16
18
|
- Scheduled blocking for maintenance windows
|
|
17
19
|
- Conditional blocking based on application state
|
|
18
|
-
- Async action wrapping
|
|
20
|
+
- Async action wrapping with timeout support
|
|
19
21
|
- Advanced middleware system for analytics, logging, and performance monitoring
|
|
20
|
-
- TypeScript support with full type safety
|
|
22
|
+
- TypeScript support with full type safety (including optional type-safe scopes)
|
|
21
23
|
- Built on Zustand for efficient state management
|
|
22
24
|
- Tree-shakeable - import only what you need
|
|
23
25
|
- Hooks-based API
|
|
@@ -34,8 +36,8 @@ pnpm add @okyrychenko-dev/react-action-guard zustand
|
|
|
34
36
|
|
|
35
37
|
This package requires the following peer dependencies:
|
|
36
38
|
|
|
37
|
-
- [React](https://react.dev/) ^17.0.0 || ^18.0.0
|
|
38
|
-
- [Zustand](https://zustand-demo.pmnd.rs/) - State management library
|
|
39
|
+
- [React](https://react.dev/) ^17.0.0 || ^18.0.0 || ^19.0.0
|
|
40
|
+
- [Zustand](https://zustand-demo.pmnd.rs/) ^4.5.7 || ^5.0.0 - State management library
|
|
39
41
|
|
|
40
42
|
## Quick Start
|
|
41
43
|
|
|
@@ -74,6 +76,8 @@ Automatically adds a blocker when the component mounts and removes it on unmount
|
|
|
74
76
|
- `scope?: string | string[]` - Scope(s) to block (default: "global")
|
|
75
77
|
- `reason?: string` - Reason for blocking (for debugging)
|
|
76
78
|
- `priority?: number` - Priority level (higher = more important)
|
|
79
|
+
- `timeout?: number` - Auto-remove after N milliseconds
|
|
80
|
+
- `onTimeout?: (blockerId: string) => void` - Callback when auto-removed
|
|
77
81
|
- `isActive?: boolean` - Whether the blocker is active (default: true)
|
|
78
82
|
|
|
79
83
|
**Example:**
|
|
@@ -88,6 +92,33 @@ function MyComponent() {
|
|
|
88
92
|
|
|
89
93
|
return <div>Content</div>;
|
|
90
94
|
}
|
|
95
|
+
|
|
96
|
+
// With timeout - auto-removes after 30 seconds
|
|
97
|
+
function SaveButton() {
|
|
98
|
+
const [isSaving, setIsSaving] = useState(false);
|
|
99
|
+
|
|
100
|
+
useBlocker("save-operation", {
|
|
101
|
+
scope: "form",
|
|
102
|
+
reason: "Saving...",
|
|
103
|
+
timeout: 30000,
|
|
104
|
+
onTimeout: (id) => {
|
|
105
|
+
console.warn(`Operation ${id} timed out`);
|
|
106
|
+
showNotification("Operation timed out");
|
|
107
|
+
setIsSaving(false);
|
|
108
|
+
},
|
|
109
|
+
}, isSaving);
|
|
110
|
+
|
|
111
|
+
const handleSave = async () => {
|
|
112
|
+
setIsSaving(true);
|
|
113
|
+
try {
|
|
114
|
+
await saveData();
|
|
115
|
+
} finally {
|
|
116
|
+
setIsSaving(false);
|
|
117
|
+
}
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
return <button onClick={handleSave} disabled={isSaving}>Save</button>;
|
|
121
|
+
}
|
|
91
122
|
```
|
|
92
123
|
|
|
93
124
|
#### `useIsBlocked(scope)`
|
|
@@ -110,7 +141,44 @@ function SubmitButton() {
|
|
|
110
141
|
}
|
|
111
142
|
```
|
|
112
143
|
|
|
113
|
-
#### `
|
|
144
|
+
#### `useBlockingInfo(scope)`
|
|
145
|
+
|
|
146
|
+
Gets detailed information about all active blockers for a specific scope.
|
|
147
|
+
|
|
148
|
+
**Parameters:**
|
|
149
|
+
|
|
150
|
+
- `scope?: string` - Scope to get blocking information for (default: "global")
|
|
151
|
+
|
|
152
|
+
**Returns:** `ReadonlyArray<BlockerInfo>` - Array of blocker information objects, sorted by priority (highest first)
|
|
153
|
+
|
|
154
|
+
**BlockerInfo:**
|
|
155
|
+
|
|
156
|
+
- `id: string` - Unique identifier of the blocker
|
|
157
|
+
- `reason?: string` - Reason for blocking
|
|
158
|
+
- `priority: number` - Priority level (0-100)
|
|
159
|
+
- `scope: string | string[]` - Scope(s) being blocked
|
|
160
|
+
- `timestamp: number` - When the blocker was added
|
|
161
|
+
|
|
162
|
+
**Example:**
|
|
163
|
+
|
|
164
|
+
```jsx
|
|
165
|
+
function CheckoutButton() {
|
|
166
|
+
const blockers = useBlockingInfo("checkout");
|
|
167
|
+
|
|
168
|
+
if (blockers.length > 0) {
|
|
169
|
+
const topBlocker = blockers[0]; // Highest priority blocker
|
|
170
|
+
return (
|
|
171
|
+
<Tooltip content={`Blocked: ${topBlocker.reason}`}>
|
|
172
|
+
<Button disabled>Checkout ({blockers.length} blockers)</Button>
|
|
173
|
+
</Tooltip>
|
|
174
|
+
);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
return <Button>Checkout</Button>;
|
|
178
|
+
}
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
#### `useAsyncAction(actionId, scope, options)`
|
|
114
182
|
|
|
115
183
|
Wraps an async function with automatic blocking/unblocking.
|
|
116
184
|
|
|
@@ -118,6 +186,9 @@ Wraps an async function with automatic blocking/unblocking.
|
|
|
118
186
|
|
|
119
187
|
- `actionId: string` - Identifier for the action
|
|
120
188
|
- `scope?: string | string[]` - Scope(s) to block during execution
|
|
189
|
+
- `options?: UseAsyncActionOptions` - Optional configuration
|
|
190
|
+
- `timeout?: number` - Auto-remove blocker after N milliseconds
|
|
191
|
+
- `onTimeout?: (blockerId: string) => void` - Callback when timed out
|
|
121
192
|
|
|
122
193
|
**Returns:** `(asyncFn: () => Promise<T>) => Promise<T>` - Function wrapper
|
|
123
194
|
|
|
@@ -135,6 +206,23 @@ function MyComponent() {
|
|
|
135
206
|
|
|
136
207
|
return <button onClick={handleSave}>Save</button>;
|
|
137
208
|
}
|
|
209
|
+
|
|
210
|
+
// With timeout - prevents infinite blocking if operation hangs
|
|
211
|
+
function ApiComponent() {
|
|
212
|
+
const execute = useAsyncAction("api-call", "global", {
|
|
213
|
+
timeout: 60000, // 1 minute timeout
|
|
214
|
+
onTimeout: (id) => {
|
|
215
|
+
showError("Request timed out. Please try again.");
|
|
216
|
+
},
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
const fetchData = () => execute(async () => {
|
|
220
|
+
const response = await fetch("/api/data");
|
|
221
|
+
return response.json();
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
return <button onClick={fetchData}>Fetch Data</button>;
|
|
225
|
+
}
|
|
138
226
|
```
|
|
139
227
|
|
|
140
228
|
#### `useConfirmableBlocker(blockerId, config, options)`
|
|
@@ -159,7 +247,7 @@ Creates a blocker that requires user confirmation before being removed.
|
|
|
159
247
|
**Example:**
|
|
160
248
|
|
|
161
249
|
```jsx
|
|
162
|
-
function UnsavedChangesGuard() {
|
|
250
|
+
function UnsavedChangesGuard({ hasUnsavedChanges, discardChanges }) {
|
|
163
251
|
const { isBlocking, requestRemoval } = useConfirmableBlocker(
|
|
164
252
|
"unsaved-changes",
|
|
165
253
|
{
|
|
@@ -269,6 +357,56 @@ function NetworkStatusBlocker() {
|
|
|
269
357
|
}
|
|
270
358
|
```
|
|
271
359
|
|
|
360
|
+
### Provider (Optional)
|
|
361
|
+
|
|
362
|
+
#### `UIBlockingProvider`
|
|
363
|
+
|
|
364
|
+
Provides an isolated store instance for SSR, testing, or micro-frontends. Without the provider, hooks use a global store.
|
|
365
|
+
|
|
366
|
+
**Props:**
|
|
367
|
+
|
|
368
|
+
- `children: ReactNode` - Child components
|
|
369
|
+
- `enableDevtools?: boolean` - Enable Redux DevTools (default: true in development)
|
|
370
|
+
- `devtoolsName?: string` - Name for DevTools (default: "UIBlocking")
|
|
371
|
+
- `middlewares?: Middleware[]` - Initial middlewares to register
|
|
372
|
+
|
|
373
|
+
**Example:**
|
|
374
|
+
|
|
375
|
+
```jsx
|
|
376
|
+
import { UIBlockingProvider } from "@okyrychenko-dev/react-action-guard";
|
|
377
|
+
|
|
378
|
+
// SSR - each request gets isolated state
|
|
379
|
+
function App() {
|
|
380
|
+
return (
|
|
381
|
+
<UIBlockingProvider>
|
|
382
|
+
<MyApp />
|
|
383
|
+
</UIBlockingProvider>
|
|
384
|
+
);
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
// Testing - isolated state per test, no cleanup needed
|
|
388
|
+
function renderWithProvider(ui) {
|
|
389
|
+
return render(
|
|
390
|
+
<UIBlockingProvider>{ui}</UIBlockingProvider>
|
|
391
|
+
);
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
// Micro-frontends - each app has its own blocking state
|
|
395
|
+
function MicroFrontend() {
|
|
396
|
+
return (
|
|
397
|
+
<UIBlockingProvider devtoolsName="MicroApp-Blocking">
|
|
398
|
+
<MicroApp />
|
|
399
|
+
</UIBlockingProvider>
|
|
400
|
+
);
|
|
401
|
+
}
|
|
402
|
+
```
|
|
403
|
+
|
|
404
|
+
#### Context Hooks
|
|
405
|
+
|
|
406
|
+
- `useUIBlockingContext()` - Get store from context (throws if outside provider)
|
|
407
|
+
- `useIsInsideUIBlockingProvider()` - Check if inside a provider
|
|
408
|
+
- `useUIBlockingStoreFromContext(selector)` - Select state from context store
|
|
409
|
+
|
|
272
410
|
### Store
|
|
273
411
|
|
|
274
412
|
#### `useUIBlockingStore`
|
|
@@ -332,20 +470,23 @@ The library includes a powerful middleware system that allows you to hook into b
|
|
|
332
470
|
Track blocker events with your analytics provider (Google Analytics, Mixpanel, Amplitude, or custom).
|
|
333
471
|
|
|
334
472
|
```jsx
|
|
335
|
-
import {
|
|
473
|
+
import {
|
|
474
|
+
configureMiddleware,
|
|
475
|
+
createAnalyticsMiddleware,
|
|
476
|
+
} from "@okyrychenko-dev/react-action-guard";
|
|
336
477
|
|
|
337
478
|
// Google Analytics
|
|
338
|
-
configureMiddleware([
|
|
479
|
+
configureMiddleware([createAnalyticsMiddleware({ provider: "ga" })]);
|
|
339
480
|
|
|
340
481
|
// Mixpanel
|
|
341
|
-
configureMiddleware([
|
|
482
|
+
configureMiddleware([createAnalyticsMiddleware({ provider: "mixpanel" })]);
|
|
342
483
|
|
|
343
484
|
// Amplitude
|
|
344
|
-
configureMiddleware([
|
|
485
|
+
configureMiddleware([createAnalyticsMiddleware({ provider: "amplitude" })]);
|
|
345
486
|
|
|
346
487
|
// Custom analytics
|
|
347
488
|
configureMiddleware([
|
|
348
|
-
|
|
489
|
+
createAnalyticsMiddleware({
|
|
349
490
|
track: (event, data) => {
|
|
350
491
|
myAnalytics.track(event, data);
|
|
351
492
|
},
|
|
@@ -368,10 +509,13 @@ configureMiddleware([loggerMiddleware]);
|
|
|
368
509
|
Monitor blocker performance and detect slow operations.
|
|
369
510
|
|
|
370
511
|
```jsx
|
|
371
|
-
import {
|
|
512
|
+
import {
|
|
513
|
+
configureMiddleware,
|
|
514
|
+
createPerformanceMiddleware,
|
|
515
|
+
} from "@okyrychenko-dev/react-action-guard";
|
|
372
516
|
|
|
373
517
|
configureMiddleware([
|
|
374
|
-
|
|
518
|
+
createPerformanceMiddleware({
|
|
375
519
|
onSlowBlock: (blockerId, duration) => {
|
|
376
520
|
console.warn(`Blocker ${blockerId} was active for ${duration}ms`);
|
|
377
521
|
},
|
|
@@ -409,15 +553,15 @@ You can combine multiple middleware for comprehensive monitoring:
|
|
|
409
553
|
```jsx
|
|
410
554
|
import {
|
|
411
555
|
configureMiddleware,
|
|
412
|
-
|
|
556
|
+
createAnalyticsMiddleware,
|
|
413
557
|
loggerMiddleware,
|
|
414
|
-
|
|
558
|
+
createPerformanceMiddleware,
|
|
415
559
|
} from "@okyrychenko-dev/react-action-guard";
|
|
416
560
|
|
|
417
561
|
configureMiddleware([
|
|
418
562
|
loggerMiddleware,
|
|
419
|
-
|
|
420
|
-
|
|
563
|
+
createAnalyticsMiddleware({ provider: "ga" }),
|
|
564
|
+
createPerformanceMiddleware({
|
|
421
565
|
slowBlockThreshold: 3000,
|
|
422
566
|
onSlowBlock: (blockerId, duration) => {
|
|
423
567
|
// Send to error tracking service
|
|
@@ -438,7 +582,10 @@ The library is fully tree-shakeable. Import only the features you need to keep y
|
|
|
438
582
|
import { useBlocker } from "@okyrychenko-dev/react-action-guard";
|
|
439
583
|
|
|
440
584
|
// Middleware is not included unless you import it
|
|
441
|
-
import {
|
|
585
|
+
import {
|
|
586
|
+
configureMiddleware,
|
|
587
|
+
createAnalyticsMiddleware,
|
|
588
|
+
} from "@okyrychenko-dev/react-action-guard";
|
|
442
589
|
```
|
|
443
590
|
|
|
444
591
|
The package is configured with `"sideEffects": false`, allowing modern bundlers (Webpack, Rollup, Vite) to eliminate unused code automatically.
|
|
@@ -703,4 +850,4 @@ See [CHANGELOG.md](./CHANGELOG.md) for a detailed list of changes in each versio
|
|
|
703
850
|
|
|
704
851
|
## License
|
|
705
852
|
|
|
706
|
-
MIT ©
|
|
853
|
+
MIT © Oleksii Kyrychenko
|
package/dist/index.cjs
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
var react = require('react');
|
|
4
|
-
var middleware = require('zustand/middleware');
|
|
5
4
|
var zustand = require('zustand');
|
|
5
|
+
var middleware = require('zustand/middleware');
|
|
6
6
|
var shallow = require('zustand/shallow');
|
|
7
|
+
var jsxRuntime = require('react/jsx-runtime');
|
|
7
8
|
|
|
8
9
|
// src/hooks/useAsyncAction/useAsyncAction.ts
|
|
9
10
|
|
|
@@ -67,13 +68,40 @@ var createUIBlockingActions = (set, get) => ({
|
|
|
67
68
|
*
|
|
68
69
|
*/
|
|
69
70
|
addBlocker: (id, config = {}) => {
|
|
71
|
+
const existingBlocker = get().activeBlockers.get(id);
|
|
72
|
+
if (existingBlocker?.timeoutId) {
|
|
73
|
+
clearTimeout(existingBlocker.timeoutId);
|
|
74
|
+
}
|
|
75
|
+
let timeoutId;
|
|
76
|
+
if (config.timeout && config.timeout > 0) {
|
|
77
|
+
timeoutId = setTimeout(() => {
|
|
78
|
+
if (get().activeBlockers.has(id)) {
|
|
79
|
+
config.onTimeout?.(id);
|
|
80
|
+
void get().runMiddlewares({
|
|
81
|
+
action: "timeout",
|
|
82
|
+
blockerId: id,
|
|
83
|
+
config: {
|
|
84
|
+
scope: config.scope,
|
|
85
|
+
reason: config.reason,
|
|
86
|
+
priority: config.priority,
|
|
87
|
+
timeout: config.timeout
|
|
88
|
+
},
|
|
89
|
+
timestamp: Date.now()
|
|
90
|
+
});
|
|
91
|
+
get().removeBlocker(id);
|
|
92
|
+
}
|
|
93
|
+
}, config.timeout);
|
|
94
|
+
}
|
|
70
95
|
set((state) => {
|
|
71
96
|
const newBlockers = new Map(state.activeBlockers);
|
|
72
97
|
const storedBlocker = {
|
|
73
98
|
scope: config.scope ?? DEFAULT_SCOPE,
|
|
74
99
|
reason: config.reason ?? DEFAULT_REASON,
|
|
75
100
|
priority: config.priority ?? DEFAULT_PRIORITY,
|
|
76
|
-
timestamp: config.timestamp ?? Date.now()
|
|
101
|
+
timestamp: config.timestamp ?? Date.now(),
|
|
102
|
+
timeout: config.timeout,
|
|
103
|
+
timeoutId,
|
|
104
|
+
onTimeout: config.onTimeout
|
|
77
105
|
};
|
|
78
106
|
newBlockers.set(id, storedBlocker);
|
|
79
107
|
return { activeBlockers: newBlockers };
|
|
@@ -84,7 +112,8 @@ var createUIBlockingActions = (set, get) => ({
|
|
|
84
112
|
config: {
|
|
85
113
|
scope: config.scope,
|
|
86
114
|
reason: config.reason,
|
|
87
|
-
priority: config.priority
|
|
115
|
+
priority: config.priority,
|
|
116
|
+
timeout: config.timeout
|
|
88
117
|
},
|
|
89
118
|
timestamp: Date.now()
|
|
90
119
|
});
|
|
@@ -97,6 +126,9 @@ var createUIBlockingActions = (set, get) => ({
|
|
|
97
126
|
*/
|
|
98
127
|
removeBlocker: (id) => {
|
|
99
128
|
const prevBlocker = get().activeBlockers.get(id);
|
|
129
|
+
if (prevBlocker?.timeoutId) {
|
|
130
|
+
clearTimeout(prevBlocker.timeoutId);
|
|
131
|
+
}
|
|
100
132
|
set((state) => {
|
|
101
133
|
const newBlockers = new Map(state.activeBlockers);
|
|
102
134
|
newBlockers.delete(id);
|
|
@@ -155,6 +187,12 @@ var createUIBlockingActions = (set, get) => ({
|
|
|
155
187
|
* Clear all blockers from the store
|
|
156
188
|
*/
|
|
157
189
|
clearAllBlockers: () => {
|
|
190
|
+
const { activeBlockers } = get();
|
|
191
|
+
for (const [, blocker] of activeBlockers) {
|
|
192
|
+
if (blocker.timeoutId) {
|
|
193
|
+
clearTimeout(blocker.timeoutId);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
158
196
|
set({ activeBlockers: /* @__PURE__ */ new Map() });
|
|
159
197
|
},
|
|
160
198
|
/**
|
|
@@ -164,6 +202,13 @@ var createUIBlockingActions = (set, get) => ({
|
|
|
164
202
|
*
|
|
165
203
|
*/
|
|
166
204
|
clearBlockersForScope: (scope) => {
|
|
205
|
+
const { activeBlockers } = get();
|
|
206
|
+
for (const [, blocker] of activeBlockers) {
|
|
207
|
+
const blockerScopes = normalizeScopeToArray(blocker.scope);
|
|
208
|
+
if (blockerScopes.includes(scope) && blocker.timeoutId) {
|
|
209
|
+
clearTimeout(blocker.timeoutId);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
167
212
|
set((state) => {
|
|
168
213
|
const newBlockers = /* @__PURE__ */ new Map();
|
|
169
214
|
for (const [id, blocker] of state.activeBlockers) {
|
|
@@ -176,6 +221,45 @@ var createUIBlockingActions = (set, get) => ({
|
|
|
176
221
|
});
|
|
177
222
|
}
|
|
178
223
|
});
|
|
224
|
+
var UIBlockingContext = react.createContext(null);
|
|
225
|
+
function UIBlockingProvider({
|
|
226
|
+
children,
|
|
227
|
+
enableDevtools = process.env.NODE_ENV === "development",
|
|
228
|
+
devtoolsName = "UIBlocking",
|
|
229
|
+
middlewares = []
|
|
230
|
+
}) {
|
|
231
|
+
const middlewaresRef = react.useRef(middlewares);
|
|
232
|
+
const store = react.useMemo(() => {
|
|
233
|
+
const baseStore = enableDevtools ? zustand.createStore(
|
|
234
|
+
middleware.devtools(createUIBlockingActions, {
|
|
235
|
+
name: devtoolsName,
|
|
236
|
+
enabled: enableDevtools
|
|
237
|
+
})
|
|
238
|
+
) : zustand.createStore(createUIBlockingActions);
|
|
239
|
+
middlewaresRef.current.forEach((mw, index) => {
|
|
240
|
+
baseStore.getState().registerMiddleware(`provider-middleware-${index.toString()}`, mw);
|
|
241
|
+
});
|
|
242
|
+
return baseStore;
|
|
243
|
+
}, [enableDevtools, devtoolsName]);
|
|
244
|
+
return /* @__PURE__ */ jsxRuntime.jsx(UIBlockingContext.Provider, { value: store, children });
|
|
245
|
+
}
|
|
246
|
+
function useUIBlockingContext() {
|
|
247
|
+
const store = react.useContext(UIBlockingContext);
|
|
248
|
+
if (!store) {
|
|
249
|
+
throw new Error(
|
|
250
|
+
"useUIBlockingContext must be used within UIBlockingProvider. Either wrap your app in <UIBlockingProvider> or use the global store via useUIBlockingStore."
|
|
251
|
+
);
|
|
252
|
+
}
|
|
253
|
+
return store;
|
|
254
|
+
}
|
|
255
|
+
function useIsInsideUIBlockingProvider() {
|
|
256
|
+
const store = react.useContext(UIBlockingContext);
|
|
257
|
+
return store !== null;
|
|
258
|
+
}
|
|
259
|
+
function useUIBlockingStoreFromContext(selector) {
|
|
260
|
+
const store = useUIBlockingContext();
|
|
261
|
+
return zustand.useStore(store, shallow.useShallow(selector));
|
|
262
|
+
}
|
|
179
263
|
|
|
180
264
|
// src/store/uiBlockingStore.config.ts
|
|
181
265
|
var devtoolsConfig = {
|
|
@@ -186,9 +270,19 @@ var devtoolsConfig = {
|
|
|
186
270
|
// src/store/uiBlockingStore.store.ts
|
|
187
271
|
var { useStore: useUIBlockingStore, useStoreApi: uiBlockingStoreApi } = createShallowStore(middleware.devtools(createUIBlockingActions, devtoolsConfig));
|
|
188
272
|
|
|
273
|
+
// src/context/useResolvedStore.ts
|
|
274
|
+
function useResolvedStore() {
|
|
275
|
+
const contextStore = react.useContext(UIBlockingContext);
|
|
276
|
+
return contextStore ?? uiBlockingStoreApi;
|
|
277
|
+
}
|
|
278
|
+
function useResolvedStoreWithSelector(selector) {
|
|
279
|
+
const store = useResolvedStore();
|
|
280
|
+
return zustand.useStore(store, shallow.useShallow(selector));
|
|
281
|
+
}
|
|
282
|
+
|
|
189
283
|
// src/hooks/useAsyncAction/useAsyncAction.ts
|
|
190
|
-
var useAsyncAction = (actionId, scope) => {
|
|
191
|
-
const { addBlocker, removeBlocker } =
|
|
284
|
+
var useAsyncAction = (actionId, scope, options) => {
|
|
285
|
+
const { addBlocker, removeBlocker } = useResolvedStoreWithSelector((state) => ({
|
|
192
286
|
addBlocker: state.addBlocker,
|
|
193
287
|
removeBlocker: state.removeBlocker
|
|
194
288
|
}));
|
|
@@ -199,19 +293,21 @@ var useAsyncAction = (actionId, scope) => {
|
|
|
199
293
|
addBlocker(blockerId, {
|
|
200
294
|
scope,
|
|
201
295
|
reason: `Executing ${actionId}`,
|
|
202
|
-
priority: ASYNC_ACTION_PRIORITY
|
|
296
|
+
priority: ASYNC_ACTION_PRIORITY,
|
|
297
|
+
timeout: options?.timeout,
|
|
298
|
+
onTimeout: options?.onTimeout
|
|
203
299
|
});
|
|
204
300
|
return await asyncFn();
|
|
205
301
|
} finally {
|
|
206
302
|
removeBlocker(blockerId);
|
|
207
303
|
}
|
|
208
304
|
},
|
|
209
|
-
[actionId, scope, addBlocker, removeBlocker]
|
|
305
|
+
[actionId, scope, options?.timeout, options?.onTimeout, addBlocker, removeBlocker]
|
|
210
306
|
);
|
|
211
307
|
return executeWithBlocking;
|
|
212
308
|
};
|
|
213
309
|
var useBlocker = (blockerId, config, isActive = true) => {
|
|
214
|
-
const { addBlocker, removeBlocker } =
|
|
310
|
+
const { addBlocker, removeBlocker } = useResolvedStoreWithSelector((state) => ({
|
|
215
311
|
addBlocker: state.addBlocker,
|
|
216
312
|
removeBlocker: state.removeBlocker
|
|
217
313
|
}));
|
|
@@ -234,6 +330,13 @@ function createBlockerConfig(config) {
|
|
|
234
330
|
priority: config.priority
|
|
235
331
|
};
|
|
236
332
|
}
|
|
333
|
+
var useBlockingInfo = (scope = DEFAULT_SCOPE) => {
|
|
334
|
+
const store = useResolvedStore();
|
|
335
|
+
const activeBlockers = zustand.useStore(store, (state) => state.activeBlockers);
|
|
336
|
+
return react.useMemo(() => {
|
|
337
|
+
return store.getState().getBlockingInfo(scope);
|
|
338
|
+
}, [activeBlockers, scope, store]);
|
|
339
|
+
};
|
|
237
340
|
function useConfigRef(config) {
|
|
238
341
|
const configRef = react.useRef(config);
|
|
239
342
|
react.useEffect(() => {
|
|
@@ -244,7 +347,7 @@ function useConfigRef(config) {
|
|
|
244
347
|
|
|
245
348
|
// src/hooks/useConditionalBlocker/useConditionalBlocker.ts
|
|
246
349
|
var useConditionalBlocker = (blockerId, config) => {
|
|
247
|
-
const { addBlocker, removeBlocker } =
|
|
350
|
+
const { addBlocker, removeBlocker } = useResolvedStoreWithSelector((state) => ({
|
|
248
351
|
addBlocker: state.addBlocker,
|
|
249
352
|
removeBlocker: state.removeBlocker
|
|
250
353
|
}));
|
|
@@ -327,7 +430,7 @@ var useConfirmableBlocker = (blockerId, config) => {
|
|
|
327
430
|
|
|
328
431
|
// src/hooks/useIsBlocked/useIsBlocked.ts
|
|
329
432
|
var useIsBlocked = (scope) => {
|
|
330
|
-
return
|
|
433
|
+
return useResolvedStoreWithSelector((state) => state.isBlocked(scope));
|
|
331
434
|
};
|
|
332
435
|
|
|
333
436
|
// src/hooks/useScheduledBlocker/useScheduledBlocker.utils.ts
|
|
@@ -369,7 +472,7 @@ function isScheduleInPast(startTime, endTime, now) {
|
|
|
369
472
|
|
|
370
473
|
// src/hooks/useScheduledBlocker/useScheduledBlocker.ts
|
|
371
474
|
var useScheduledBlocker = (blockerId, config) => {
|
|
372
|
-
const { addBlocker, removeBlocker } =
|
|
475
|
+
const { addBlocker, removeBlocker } = useResolvedStoreWithSelector((state) => ({
|
|
373
476
|
addBlocker: state.addBlocker,
|
|
374
477
|
removeBlocker: state.removeBlocker
|
|
375
478
|
}));
|
|
@@ -506,6 +609,15 @@ var createAnalyticsMiddleware = (config) => {
|
|
|
506
609
|
};
|
|
507
610
|
};
|
|
508
611
|
|
|
612
|
+
// src/middleware/configureMiddleware.ts
|
|
613
|
+
function configureMiddleware(middlewares) {
|
|
614
|
+
const store = uiBlockingStoreApi.getState();
|
|
615
|
+
middlewares.forEach((middleware, index) => {
|
|
616
|
+
const name = `middleware-${index.toString()}`;
|
|
617
|
+
store.registerMiddleware(name, middleware);
|
|
618
|
+
});
|
|
619
|
+
}
|
|
620
|
+
|
|
509
621
|
// src/middleware/loggerMiddleware.utils.ts
|
|
510
622
|
var getActionEmoji = (action) => {
|
|
511
623
|
switch (action) {
|
|
@@ -591,16 +703,39 @@ var createPerformanceMiddleware = (config = {}) => {
|
|
|
591
703
|
};
|
|
592
704
|
};
|
|
593
705
|
|
|
706
|
+
// src/createTypedHooks.ts
|
|
707
|
+
function createTypedHooks() {
|
|
708
|
+
return {
|
|
709
|
+
useBlocker: (blockerId, config, isActive) => {
|
|
710
|
+
useBlocker(blockerId, config, isActive);
|
|
711
|
+
},
|
|
712
|
+
useIsBlocked: (scope) => {
|
|
713
|
+
return useIsBlocked(scope);
|
|
714
|
+
},
|
|
715
|
+
useAsyncAction: (actionId, scope, options) => {
|
|
716
|
+
return useAsyncAction(actionId, scope, options);
|
|
717
|
+
},
|
|
718
|
+
useBlockingInfo: (scope) => useBlockingInfo(scope)
|
|
719
|
+
};
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
exports.UIBlockingProvider = UIBlockingProvider;
|
|
723
|
+
exports.configureMiddleware = configureMiddleware;
|
|
594
724
|
exports.createAnalyticsMiddleware = createAnalyticsMiddleware;
|
|
595
725
|
exports.createPerformanceMiddleware = createPerformanceMiddleware;
|
|
726
|
+
exports.createTypedHooks = createTypedHooks;
|
|
596
727
|
exports.loggerMiddleware = loggerMiddleware;
|
|
597
728
|
exports.uiBlockingStoreApi = uiBlockingStoreApi;
|
|
598
729
|
exports.useAsyncAction = useAsyncAction;
|
|
599
730
|
exports.useBlocker = useBlocker;
|
|
731
|
+
exports.useBlockingInfo = useBlockingInfo;
|
|
600
732
|
exports.useConditionalBlocker = useConditionalBlocker;
|
|
601
733
|
exports.useConfirmableBlocker = useConfirmableBlocker;
|
|
602
734
|
exports.useIsBlocked = useIsBlocked;
|
|
735
|
+
exports.useIsInsideUIBlockingProvider = useIsInsideUIBlockingProvider;
|
|
603
736
|
exports.useScheduledBlocker = useScheduledBlocker;
|
|
737
|
+
exports.useUIBlockingContext = useUIBlockingContext;
|
|
604
738
|
exports.useUIBlockingStore = useUIBlockingStore;
|
|
739
|
+
exports.useUIBlockingStoreFromContext = useUIBlockingStoreFromContext;
|
|
605
740
|
//# sourceMappingURL=index.cjs.map
|
|
606
741
|
//# sourceMappingURL=index.cjs.map
|